/* gsg — a quiet, text-first interface in the spirit of cgit: hairlines instead of boxes, no shadows, no hover animation. The one indulgence is a black-and-purple signal color -- used for links, hashes, and a thin two-tone stripe at the very top of the page -- so the theme still reads as its own thing rather than "yet another dark git UI". Loaded on every page: design tokens, reset, nav, page chrome, and the handful of components (cards, tables, chips, facts-bar, footer, notices) shared by more than one view. View-specific rules live in the other files under static/css/, linked only by the views that need them. */ :root { /* Base — near-black ink */ --bg: #0F111A; --bg-elev: #141725; --surface: #1C2032; --surface-2: #24283C; --line: #2A3045; --line-soft: #2E354E; /* Foreground */ --fg: #FFFFFF; --fg-muted: #9AA0B3; --fg-subtle: #6B7280; /* Accent — one signal color, not a rainbow */ --accent: #A78BFA; --accent-2: #7C3AED; --accent-grad: linear-gradient(90deg, var(--accent), var(--accent-2)); /* Status colors */ --add: #10B981; --add-bg: rgba(16, 185, 129, 0.15); --del: #EF4444; --del-bg: rgba(239, 68, 68, 0.15); /* Operator notice severities. One token pair per type; the .notice-* modifiers below are the only place the palette is applied. */ --notice-critical: #EF4444; --notice-critical-bg: rgba(239, 68, 68, 0.15); --notice-warning: #F59E0B; --notice-warning-bg: rgba(245, 158, 11, 0.15); --notice-info: #60A5FA; --notice-info-bg: rgba(96, 165, 250, 0.15); --notice-fix: #10B981; --notice-fix-bg: rgba(16, 185, 129, 0.15); /* Counterpart to --accent -- tags and other "second kind of thing" markers use this instead of a shade too close to the purple accent. */ --tag: #FAA78B; /* Syntax highlighting -- deliberately just four colours (keyword, string, number, name) plus a muted comment, not a full-spectrum scheme. Blob view only; see static/css/blob.css. */ --syn-kw: #818CF8; --syn-str: #34D399; --syn-num: #FAA78B; --syn-fn: #60A5FA; /* Lane colors — spectrum for telling graph lanes apart, independent of the brand accent above */ --lane-0: #818CF8; --lane-1: #10B981; --lane-2: #F59E0B; --lane-3: #EC4899; --lane-4: #3B82F6; --lane-5: #84CC16; /* Typography */ --mono: ui-monospace, "SFMono-Regular", "JetBrains Mono", "Cascadia Mono", Menlo, Consolas, "Liberation Mono", monospace; --sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; /* Spacing & radius */ --r: 8px; --r-sm: 4px; --pad: clamp(.85rem, 2.5vw, 1.5rem); } /**@media (prefers-color-scheme: light) { :root { --bg: #F3F4F6; --bg-elev: #FFFFFF; --surface: #FFFFFF; --surface-2: #F9FAFB; --line: #E5E7EB; --line-soft: #E5E7EB; --fg: #111827; --fg-muted: #6B7280; --fg-subtle: #9CA3AF; --accent: #7C3AED; --accent-2: #5B21B6; --add: #059669; --del: #DC2626; --tag: #B45309; --notice-critical: #DC2626; --notice-warning: #B45309; --notice-info: #1D4ED8; --notice-fix: #059669; --syn-kw: #4F46E5; --syn-str: #047857; --syn-num: #B45309; --syn-fn: #1D4ED8; } } **/ /* ---------------------------------------------------------------- reset & base */ *, *::before, *::after { box-sizing: border-box; } html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; } body { margin: 0; background: var(--bg); color: var(--fg); font-family: var(--mono); font-size: 13px; line-height: 1.55; font-variant-ligatures: none; -webkit-font-smoothing: antialiased; } /* The one graphic flourish: a thin two-tone stripe, always at the very top of the viewport. Small enough to be quiet, present enough to be a mark -- the rest of the page is just text and hairlines. */ body::before { content: ""; position: fixed; top: 0; left: 0; right: 0; height: 3px; background: var(--accent-grad); z-index: 200; } a { color: var(--accent); text-decoration: none; } a:hover { text-decoration: underline; } /* Full viewport width, minus a small gutter -- no centered max-width column. Anything wide (tables, diffs, the graph) scrolls inside its own container instead of forcing the page to scroll horizontally. */ .wrap { width: 100%; margin: 0; padding-inline: var(--pad); } /* ---------------------------------------------------------------- navigation */ .nav { position: sticky; top: 3px; z-index: 100; background: var(--bg-elev); border-bottom: 1px solid var(--line); } .nav-inner { display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); align-items: center; gap: .75rem; height: 3rem; } .nav-start { display: flex; align-items: center; gap: .75rem; grid-column: 1; min-width: 0; } .nav-end { grid-column: 3; justify-self: end; display: flex; align-items: center; gap: .75rem; } .nav-user { font-size: .78rem; color: var(--fg-muted); white-space: nowrap; } .nav-user a { color: var(--fg-muted); } .nav-user a:hover { color: var(--accent); } .brand { display: flex; align-items: center; gap: .5rem; color: var(--fg); font-weight: 700; font-size: .95rem; letter-spacing: -.02em; } .brand .logo { width: 22px; height: 22px; background: var(--accent); border-radius: var(--r-sm); display: grid; place-items: center; color: var(--bg); font-weight: 700; font-size: .78rem; } .brand:hover { color: var(--accent); text-decoration: none; } .repo-name { color: var(--fg); font-weight: 600; margin-left: .5rem; } .nav-repo-name { color: var(--line); } .nav-repo-name:hover { color: var(--accent); } /* Tabs — plain text, an underline marks the current page */ .tabs { display: flex; gap: 1rem; grid-column: 2; justify-self: center; } .tab { padding: .2rem 0; color: var(--fg-muted); font-size: .78rem; border-bottom: 2px solid transparent; } .tab:hover { color: var(--fg); text-decoration: none; } .tab.active { color: var(--fg); border-bottom-color: var(--accent); } /* Ref picker — plain, borderless select */ .refpicker { position: relative; } .refpicker select { font: inherit; font-size: .78rem; color: var(--fg-muted); background: none; border: 1px solid var(--line); border-radius: var(--r-sm); padding: .25rem 1.75rem .25rem .6rem; cursor: pointer; appearance: none; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%236B7280' d='M6 8L1 3h10z'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right .6rem center; background-size: 10px 10px; } .refpicker select:hover { border-color: var(--accent); color: var(--fg); } .refpicker select:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; } /* ---------------------------------------------------------------- page layout */ main.wrap { padding-top: 1.25rem; padding-bottom: 2rem; } /* Hero: a plain text header with a hairline underneath -- no box, no gradient. The page title carries the weight instead of a card. */ .hero { margin-bottom: 1.25rem; padding-bottom: .9rem; border-bottom: 1px solid var(--line); } .page-title { margin: 0; font-size: 1.3rem; font-weight: 700; letter-spacing: -.02em; line-height: 1.25; /* Repository titles are often long unbroken slugs ("my-project-name"). Without this they push past the viewport instead of wrapping. Balance avoids a single dangling word on the second line. */ overflow-wrap: anywhere; text-wrap: balance; } .page-subtitle { margin: .35rem 0 0; color: var(--fg-muted); font-size: .82rem; } .breadcrumbs { display: flex; flex-wrap: wrap; gap: .4rem; align-items: baseline; margin-top: 1rem; font-size: .9rem; color: var(--fg-muted); } .breadcrumbs a { color: var(--accent); } .breadcrumbs .here { color: var(--fg); } /* ---------------------------------------------------------------- cards & containers */ /* Reserved for content that genuinely needs a boundary (a diff, a grouped sub-section) -- a thin border, nothing else. Most pages don't use it. */ .card { border: 1px solid var(--line); border-radius: var(--r); padding: .7rem .9rem; margin-bottom: .75rem; } /* Facts bar — a plain line of label: value pairs, cgit-style, not a grid of stat tiles. */ .facts-bar { display: flex; flex-wrap: wrap; column-gap: 1.5rem; row-gap: .3rem; margin-bottom: 1rem; font-size: .82rem; } .fact { display: flex; gap: .4rem; align-items: baseline; } .fact-label { color: var(--fg-subtle); } .fact-label::after { content: ":"; } .fact-value { color: var(--fg); font-weight: 600; } /* ---------------------------------------------------------------- tables */ /* Borderless, hairline-separated rows -- like cgit's listings -- rather than a bordered card. Tables can still be wider than the viewport (long paths, many columns on mobile); scrolling lives on the table itself so the page body never gains a horizontal scrollbar. */ .table-wrap { overflow-x: auto; } .table { width: 100%; border-collapse: collapse; } .table th { color: var(--fg-subtle); font-size: .68rem; text-transform: uppercase; letter-spacing: .07em; padding: .4rem .6rem; text-align: left; font-weight: 600; border-bottom: 1px solid var(--line); } .table td { padding: .4rem .6rem; border-bottom: 1px solid var(--line-soft); vertical-align: middle; } .table tr:hover td { background: var(--surface-2); } /* ---------------------------------------------------------------- chips & badges */ .chips { display: flex; flex-wrap: wrap; gap: .4rem; list-style: none; margin: 0; padding: 0; } /* Plain bordered tags, not filled pill badges. */ .chip { display: inline-flex; align-items: center; gap: .35rem; font-size: .7rem; padding: .2rem .5rem; border: 1px solid var(--line); border-radius: var(--r-sm); color: var(--fg-muted); background: none; } .chip:hover { border-color: var(--accent); color: var(--fg); text-decoration: none; } .chip-tag:hover { border-color: var(--tag); } .chip-branch { color: var(--accent); border-color: currentColor; } .chip-tag { color: var(--tag); border-color: currentColor; } .chip-ok { color: var(--add); border-color: currentColor; } .chip-bad { color: var(--del); border-color: currentColor; } .compare-form { display: flex; align-items: center; gap: .4rem; } .compare-input { font: inherit; font-size: .78rem; color: var(--fg); background: none; border: 1px solid var(--line); border-radius: var(--r-sm); padding: .25rem .6rem; min-width: 0; flex: 1 1 20rem; } .compare-input:hover { border-color: var(--accent); } .compare-input:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; } .compare-form .chip { cursor: pointer; font: inherit; font-size: .7rem; } /* ---------------------------------------------------------------- footer */ footer { margin-top: 1.5rem; padding: 1rem 0; border-top: 1px solid var(--line); display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: .75rem; font-size: .8rem; } footer .footer-left { display: flex; gap: .75rem; align-items: center; } footer .footer-right { color: var(--fg-subtle); font-size: .75rem; } footer a { color: var(--accent); } /* ---------------------------------------------------------------- notices */ .notice { padding: .5rem .8rem; border-left: 2px solid var(--accent); color: var(--fg-muted); font-size: .8rem; margin: .75rem 0; } /* Operator notices. The severity only changes the left rule and the background tint; the base .notice keeps the shared shape. */ .notices { margin: .75rem 0 1rem; } .notice-critical { border-left-color: var(--notice-critical); background: var(--notice-critical-bg); } .notice-warning { border-left-color: var(--notice-warning); background: var(--notice-warning-bg); } .notice-info { border-left-color: var(--notice-info); background: var(--notice-info-bg); } .notice-fix { border-left-color: var(--notice-fix); background: var(--notice-fix-bg); } .notice-title { display: block; color: var(--fg); } .notice-body { white-space: pre-wrap; } .notice-link { margin-left: .35rem; } /* ---------------------------------------------------------------- error page */ .error-page { padding-top: 3rem; padding-bottom: 3rem; text-align: center; } .error-code { font-size: 3rem; font-weight: 700; color: var(--accent); margin: 0; letter-spacing: -.03em; } .error-page .page-title { margin-top: .25rem; } .error-page .page-subtitle { margin-bottom: 1rem; } /* ---------------------------------------------------------------- responsive */ @media (max-width: 640px) { .nav-inner { display: flex; flex-wrap: wrap; height: auto; padding-block: .5rem; } .nav-start { flex: 1 1 auto; /* Let the repo name drop below the brand rather than overflow the row. */ flex-wrap: wrap; row-gap: .15rem; } .nav-repo-name { min-width: 0; margin-left: 0; overflow-wrap: anywhere; } .nav-end { margin-left: auto; } .tabs { justify-self: auto; width: 100%; order: 3; gap: .75rem; } .facts-bar { flex-direction: column; gap: .25rem; } .hero { padding-bottom: .7rem; } .page-title { font-size: 1.1rem; } } @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation: none !important; transition: none !important; } } /* ---------------------------------------------------------------- utilities */ .hidden { display: none !important; } .muted { color: var(--fg-muted); } .text-accent { color: var(--accent); } .font-mono { font-family: var(--mono); } .w-full { width: 100%; } .text-right { text-align: right !important; } .text-center { text-align: center !important; } .text-sm { font-size: .85rem; } .text-xs { font-size: .75rem; } /* Focus states for accessibility */ :focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; } /* Scrollbar styling */ ::-webkit-scrollbar { height: 8px; width: 8px; } ::-webkit-scrollbar-track { background: var(--bg-elev); border-radius: 4px; } ::-webkit-scrollbar-thumb { background: var(--line); border-radius: 4px; } ::-webkit-scrollbar-thumb:hover { background: var(--fg-subtle); }