internal/web/static/style.css

1570 lines · 54970 bytes

   1/* gitbay stylesheet — Harbor: black and white ground, two accents with
   2   separate jobs. Blue is what you can do (links, buttons, focus); orange
   3   is where you are and what wants you (active nav, counts, pending state,
   4   unverified signatures). Orange is never a button, so the two never
   5   compete. One file, light and dark via prefers-color-scheme. Tokens
   6   first; components use tokens only. No external requests: Atkinson
   7   Hyperlegible is served from this origin. */
   8
   9@font-face {
  10  font-family: "Atkinson Hyperlegible Next";
  11  font-style: normal;
  12  font-weight: 400 700;
  13  font-display: swap;
  14  src: url(/static/fonts/atkinson-next-roman.woff2) format("woff2");
  15}
  16@font-face {
  17  font-family: "Atkinson Hyperlegible Next";
  18  font-style: italic;
  19  font-weight: 400 700;
  20  font-display: swap;
  21  src: url(/static/fonts/atkinson-next-italic.woff2) format("woff2");
  22}
  23@font-face {
  24  font-family: "Atkinson Hyperlegible Mono";
  25  font-style: normal;
  26  font-weight: 400 600;
  27  font-display: swap;
  28  src: url(/static/fonts/atkinson-mono-roman.woff2) format("woff2");
  29}
  30
  31:root {
  32  /* declare native light+dark support: correct form controls and
  33     scrollbars per scheme, and opt out of browser auto-darkening */
  34  color-scheme: light dark;
  35
  36  /* ground and ink */
  37  --bg: #ffffff;
  38  --fg: #0d0d0d;
  39  --muted: #595959;          /* 7.00:1 on white */
  40  --faint: #eaeaea;          /* hairlines, row separators */
  41  --line: #d4d4d4;           /* control borders, table edges */
  42  --surface: #ffffff;
  43  --hover: #f5f5f5;
  44  --code-bg: #f5f5f5;
  45  --fill-subtle: #ececec;    /* hunk headers, summary hover */
  46  --diff-add: #e4f6ea;       /* row grounds: tinted enough to scan, light
  47                                enough that syntax colors stay legible */
  48  --diff-del: #fdeaea;
  49
  50  /* blue: what you can do */
  51  --accent: #0000f0;         /* links, focus — 9.30:1 on white */
  52  --accent-fg: #ffffff;
  53  --fill: #0000f0;           /* button grounds; white on it is 9.30:1 */
  54  --fill-edge: transparent; /* light: the fill is its own boundary */
  55
  56  /* orange: where you are, what wants you */
  57  --mark: #e4572e;           /* bars and underlines — non-text, 3.0 bar */
  58  --warn: #c2410c;           /* the same orange at text size — 5.18:1 */
  59
  60  /* semantic */
  61  --ok: #0a7d3c;
  62  --bad: #c62828;
  63  --done: #6b21a8;           /* merged */
  64  --neutral: #595959;
  65
  66  /* the rail is black in both schemes: the one brand constant */
  67  --shell-bg: #000000;
  68  --shell-fg: #f2f2f2;
  69  --shell-muted: #8f8f8f;
  70  --shell-line: #1c1c1c;
  71  --shell-hover: #141414;
  72  --shell-mark: #ff6b3d;     /* 7.42:1 on black, both schemes */
  73
  74  /* type */
  75  --sans: "Atkinson Hyperlegible Next", -apple-system, BlinkMacSystemFont,
  76          "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  77  --mono: "Atkinson Hyperlegible Mono", ui-monospace, "SF Mono",
  78          SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
  79  --fs-0: 0.78rem;
  80  --fs-1: 0.85rem;
  81  --fs-2: 0.92rem;
  82  --fs-3: 1rem;
  83  --fs-4: 1.2rem;
  84  --fs-5: 1.6rem;
  85  --fs-6: 1.8rem;   /* landing headline */
  86  --fs-7: 4rem;     /* the 404 code */
  87
  88  /* spacing */
  89  --sp-1: 0.25rem;
  90  --sp-2: 0.5rem;
  91  --sp-3: 0.75rem;
  92  --sp-4: 1rem;
  93  --sp-5: 1.5rem;
  94  --sp-6: 2.5rem;
  95
  96  --chev: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%23595959' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  97
  98  /* radii: 2px everywhere — chart plates, not pills */
  99  --r-sm: 2px;
 100  --r-md: 2px;
 101  --r-lg: 2px;
 102  --r-pill: 2px;
 103  --shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
 104}
 105@media (prefers-color-scheme: dark) {
 106  :root {
 107    --bg: #0a0a0a;
 108    --fg: #f2f2f2;
 109    --muted: #a1a1a1;        /* 7.66:1 on #0a0a0a */
 110    --faint: #1c1c1c;
 111    --line: #2e2e2e;
 112    --surface: #0f0f0f;
 113    --hover: #161616;
 114    --code-bg: #050505;
 115    --fill-subtle: #161616;
 116    --diff-add: #0d2a18;
 117    --diff-del: #2c1113;
 118    --accent: #6272ff;       /* pure #0000f0 is 2.13:1 here. Picked against
 119                                the lightest ground it lands on, not the page:
 120                                a link wrapping inline code sits on #161616,
 121                                where the old #5b6bff was 4.30:1. Now 4.63:1
 122                                there and 5.06:1 on the page. */
 123    --accent-fg: #ffffff;
 124    --fill: #0000f0;         /* fills stay pure; white on it clears 9:1 */
 125    --fill-edge: #7d8cff;    /* a button's boundary: #0000f0 on #0a0a0a is 1.3:1 (#120) */
 126    --shadow: 0 4px 16px rgba(0, 0, 0, 0.5);
 127    --mark: #ff6b3d;
 128    --warn: #ff6b3d;
 129    --ok: #3fce7a;
 130    --bad: #ff6b6b;
 131    --done: #c084fc;
 132    --neutral: #a1a1a1;
 133    --chev: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%23a1a1a1' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
 134  }
 135}
 136
 137* { box-sizing: border-box; }
 138html { font-size: 15px; -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
 139body {
 140  margin: 0;
 141  background: var(--bg);
 142  color: var(--fg);
 143  font: var(--fs-3)/1.6 var(--sans);
 144  min-height: 100vh;
 145}
 146
 147a { color: var(--accent); text-decoration: none; }
 148a:hover { text-decoration: underline; }
 149
 150/* one authored focus ring, so keyboard position is never the browser's
 151   guess. Blue, because focus is an interactive state. */
 152:focus-visible {
 153  outline: 2px solid var(--accent);
 154  outline-offset: 2px;
 155  border-radius: 1px;
 156}
 157/* the shell is black in both schemes, and --accent is a dark blue in the
 158   light one — a ring nobody could see. The shell's own mark clears 7:1 on
 159   black either way. */
 160.rail :focus-visible { outline-color: var(--shell-mark); }
 161
 162/* visually hidden, still read aloud: for headings a sighted reader gets
 163   from the layout but a screen reader would otherwise miss */
 164.vh {
 165  position: absolute;
 166  width: 1px; height: 1px;
 167  margin: -1px; padding: 0; border: 0;
 168  clip-path: inset(50%);
 169  overflow: hidden;
 170  white-space: nowrap;
 171}
 172/* the rail runs ahead of the content in source order, so give keyboard
 173   and screen-reader users a way past it */
 174a.skip {
 175  position: absolute;
 176  left: var(--sp-3); top: -3rem;
 177  z-index: 50;
 178  background: var(--fill);
 179  color: #ffffff;
 180  padding: var(--sp-2) var(--sp-4);
 181  font-weight: 600;
 182}
 183a.skip:focus { top: var(--sp-3); text-decoration: none; }
 184
 185h1 { font-size: var(--fs-5); font-weight: 700; line-height: 1.18;
 186     margin: 0 0 var(--sp-3); letter-spacing: -0.025em; text-wrap: balance; }
 187h2 { font-size: var(--fs-4); font-weight: 700; line-height: 1.3;
 188     margin: var(--sp-5) 0 var(--sp-3); letter-spacing: -0.015em; }
 189h3 { font-size: var(--fs-3); font-weight: 600; margin: var(--sp-5) 0 var(--sp-2); }
 190
 191code, pre, .code, td.mode, td.size {
 192  font-family: var(--mono);
 193  font-size: var(--fs-1);
 194}
 195
 196/* ---- shell: black rail, content pane ---- */
 197.shell { display: flex; align-items: stretch; min-height: 100vh; }
 198
 199.rail {
 200  flex: none;
 201  width: 13rem;
 202  background: var(--shell-bg);
 203  color: var(--shell-fg);
 204  border-right: 1px solid var(--shell-line);
 205  position: sticky;
 206  top: 0;
 207  height: 100vh;
 208  display: flex;
 209  flex-direction: column;
 210}
 211a.brand {
 212  display: flex;
 213  align-items: center;
 214  gap: var(--sp-2);
 215  padding: var(--sp-3) var(--sp-4);
 216  color: var(--shell-fg);
 217  font-weight: 700;
 218  font-size: var(--fs-3);
 219  letter-spacing: -0.02em;
 220  border-bottom: 1px solid var(--shell-line);
 221}
 222a.brand:hover { text-decoration: none; color: #ffffff; }
 223a.brand svg.mark { display: block; flex: none; }
 224a.brand span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
 225
 226.railbody { flex: 1; overflow-y: auto; padding: var(--sp-3) 0; }
 227/* search is reachable from every page, so it sits above the rail's links
 228   rather than on one page of its own */
 229form.railsearch { padding: 0 var(--sp-4) var(--sp-3); }
 230form.railsearch input[type="search"] {
 231  width: 100%;
 232  padding: var(--sp-1) var(--sp-2);
 233  font-size: var(--fs-1);
 234  color: var(--shell-fg);
 235  background: var(--shell-hover);
 236  border: 1px solid var(--shell-line);
 237  border-radius: var(--r-sm);
 238}
 239form.railsearch input[type="search"]::placeholder { color: var(--shell-muted); }
 240ul.raillist { list-style: none; margin: 0; padding: 0; }
 241ul.raillist a {
 242  display: block;
 243  padding: var(--sp-1) var(--sp-4);
 244  color: var(--shell-fg);
 245  font-size: var(--fs-2);
 246  border-left: 3px solid transparent;
 247  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
 248}
 249ul.raillist a:hover { background: var(--shell-hover); text-decoration: none; }
 250/* orange marks position — the rail's only accent */
 251ul.raillist a[aria-current] {
 252  border-left-color: var(--shell-mark);
 253  background: var(--shell-hover);
 254  font-weight: 600;
 255}
 256ul.raillist .owner { color: var(--shell-muted); }
 257ul.raillist.wide a { white-space: normal; padding-top: var(--sp-1); padding-bottom: var(--sp-1); }
 258ul.raillist.wide b { display: block; font-weight: 500; font-size: var(--fs-1); }
 259ul.raillist.wide .owner { font-size: var(--fs-0); }
 260
 261.raillabel {
 262  display: flex;
 263  align-items: center;
 264  gap: var(--sp-2);
 265  margin: var(--sp-4) 0 var(--sp-1);
 266  padding: 0 var(--sp-4);
 267  font-family: var(--mono);
 268  font-size: var(--fs-0);
 269  font-weight: 500;
 270  letter-spacing: 0.1em;
 271  text-transform: uppercase;
 272  color: var(--shell-muted);
 273}
 274.raillabel .count, ul.raillist .count {
 275  font-family: var(--sans);
 276  font-size: var(--fs-0);
 277  font-weight: 700;
 278  letter-spacing: 0;
 279  background: var(--shell-mark);
 280  color: #000000;
 281  padding: 0 0.35rem;
 282  border-radius: var(--r-sm);
 283}
 284/* pinned above the account cell: always reachable, never scrolled away */
 285.railpinned {
 286  flex: none;
 287  padding: var(--sp-2) 0;
 288  border-top: 1px solid var(--shell-line);
 289}
 290.railfoot {
 291  flex: none;
 292  border-top: 1px solid var(--shell-line);
 293  /* same vertical rhythm as the page footer, so the two rules line up
 294     when the page is scrolled to the bottom; horizontal padding stays on
 295     the rail's own grid so the avatar aligns with the nav items above */
 296  padding: var(--sp-4) var(--sp-4) var(--sp-5);
 297  display: flex;
 298  align-items: center;
 299  gap: var(--sp-3);
 300  font-size: var(--fs-2);
 301  line-height: 1.4;
 302}
 303.railfoot a { color: var(--shell-fg); }
 304a.railuser { display: inline-flex; align-items: center; gap: var(--sp-2); }
 305/* a descendant cannot cancel an ancestor's underline, so the link carries
 306   none and the name draws its own — the avatar square stays undecorated */
 307a.railuser:hover { text-decoration: none; }
 308a.railuser:hover .uname { text-decoration: underline; }
 309.railfoot form { margin-left: auto; }
 310.railfoot .linklike {
 311  background: none; border: 0; padding: 0; cursor: pointer;
 312  color: var(--shell-muted); font: inherit; text-decoration: underline;
 313}
 314.railfoot .linklike:hover { color: var(--shell-fg); }
 315.avatar {
 316  width: 1.4rem; height: 1.4rem; flex: none;
 317  display: inline-grid; place-items: center;
 318  background: var(--fill);
 319  color: #ffffff;
 320  font-size: var(--fs-0);
 321  font-weight: 700;
 322  border-radius: var(--r-sm);
 323}
 324
 325.pane { flex: 1; min-width: 0; display: flex; flex-direction: column; }
 326.content {
 327  flex: 1;
 328  padding: var(--sp-5) var(--sp-5) var(--sp-6);
 329  width: 100%;
 330}
 331
 332/* ---- repo header: identity and tabs on every page, the rest once ---- */
 333.repohead {
 334  padding: var(--sp-4) var(--sp-5) 0;
 335  border-bottom: 1px solid var(--line);
 336}
 337.repohead .identity { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
 338.repohead .identity .grow { flex: 1; }
 339/* h1 on the repo home, p elsewhere so each page keeps its own h1 — but
 340   identical in size, because the tabs sit below this and navigation must
 341   not move when you use it */
 342.repotitle {
 343  margin: 0;
 344  font-size: var(--fs-4);
 345  font-weight: 700;
 346  letter-spacing: -0.02em;
 347  line-height: 1.3;
 348}
 349.repotitle a { color: var(--fg); }
 350.repotitle a.owner { color: var(--muted); font-weight: 400; }
 351.repotitle a:hover { color: var(--accent); text-decoration: none; }
 352.repotitle .sep { color: var(--muted); margin: 0 0.12em; font-weight: 400; }
 353.repodesc { margin: var(--sp-2) 0 0; color: var(--muted); font-size: var(--fs-2); max-width: 62ch; }
 354.repometa { margin: var(--sp-1) 0 0; color: var(--muted); font-size: var(--fs-1); }
 355
 356button.btn {
 357  font: inherit;
 358  font-size: var(--fs-1);
 359  font-weight: 600;
 360  padding: 0.15rem 0.7rem;
 361  border: 1px solid var(--line);
 362  border-radius: var(--r-sm);
 363  background: var(--bg);
 364  color: var(--fg);
 365  cursor: pointer;
 366}
 367button.btn:hover { border-color: var(--accent); color: var(--accent); }
 368button.btn[aria-pressed="true"] { border-color: var(--accent); color: var(--accent); }
 369form.inline { display: inline; }
 370
 371nav.tabs {
 372  display: flex;
 373  gap: var(--sp-1);
 374  margin-top: var(--sp-4);
 375  overflow-x: auto;
 376  /* with overflow-y left visible the browser computes it to auto, which
 377     is a vertical drag on touch; the strip scrolls sideways only. */
 378  overflow-y: hidden;
 379  scrollbar-width: none;
 380}
 381nav.tabs::-webkit-scrollbar { display: none; }
 382nav.tabs a {
 383  display: inline-flex;
 384  align-items: center;
 385  gap: var(--sp-2);
 386  padding: var(--sp-2) var(--sp-3) 0.6rem;
 387  color: var(--muted);
 388  font-size: var(--fs-2);
 389  white-space: nowrap;
 390  border-bottom: 2px solid transparent;
 391  margin-bottom: -1px;
 392}
 393nav.tabs a:hover { color: var(--fg); text-decoration: none; }
 394/* orange marks position here too */
 395nav.tabs a[aria-current] {
 396  color: var(--fg);
 397  font-weight: 700;
 398  border-bottom-color: var(--mark);
 399}
 400nav.tabs a i {
 401  font-style: normal;
 402  font-family: var(--mono);
 403  font-size: var(--fs-0);
 404  background: var(--hover);
 405  border-radius: var(--r-sm);
 406  padding: 0 0.35rem;
 407}
 408
 409/* ---- mobile: the rail becomes a single strip, not a wall of links ---- */
 410@media (max-width: 52rem) {
 411  .shell { display: block; }
 412  .rail {
 413    width: auto;
 414    height: auto;
 415    position: static;
 416    flex-direction: row;
 417    align-items: center;
 418    gap: var(--sp-2);
 419    border-right: 0;
 420    border-bottom: 1px solid var(--shell-line);
 421    overflow-x: auto;
 422  }
 423  a.brand { border-bottom: 0; padding-right: var(--sp-2); }
 424  .railbody { flex: 1; overflow: visible; padding: 0; display: flex; align-items: center; }
 425  form.railsearch { padding: 0 var(--sp-2); }
 426  form.railsearch input[type="search"] { min-width: 8rem; }
 427  /* pinned repos and the review queue both live on the dashboard, so the
 428     strip keeps only what has nowhere else to go */
 429  .railgroup { display: none; }
 430  ul.raillist { display: flex; }
 431  ul.raillist a { border-left: 0; border-bottom: 2px solid transparent; padding: var(--sp-2) var(--sp-3); }
 432  ul.raillist a[aria-current] { border-left: 0; border-bottom-color: var(--shell-mark); }
 433  /* the rail is a horizontal strip here: keep the account cell compact */
 434  .railfoot { border-top: 0; padding: var(--sp-2); }
 435  .railpinned { border-top: 0; padding: 0; }
 436  .content { padding: var(--sp-4) var(--sp-4) var(--sp-5); }
 437  .repohead { padding: var(--sp-3) var(--sp-4) 0; }
 438  /* seven tabs do not fit a phone in one row; a hidden scrollbar made
 439     the ones past the edge look absent. Two rows show all of them. */
 440  nav.tabs { flex-wrap: wrap; overflow: visible; }
 441}
 442
 443/* tables */
 444table { border-collapse: collapse; width: 100%; }
 445td {
 446  padding: var(--sp-2) var(--sp-4) var(--sp-2) 0;
 447  border-bottom: 1px solid var(--faint);
 448  vertical-align: top;
 449  font-size: var(--fs-2);
 450}
 451td:last-child { padding-right: 0; }
 452td.size, td.mode { color: var(--muted); white-space: nowrap; }
 453
 454/* path bar on tree and blob pages */
 455.pathbar {
 456  display: flex;
 457  flex-wrap: wrap;
 458  align-items: center;
 459  gap: var(--sp-2) var(--sp-3);
 460  margin: var(--sp-3) 0;
 461  font-size: var(--fs-2);
 462}
 463.pathbar .spacer { flex: 1; }
 464.refchip {
 465  display: inline-flex;
 466  align-items: center;
 467  gap: 0.3rem;
 468  background: var(--surface);
 469  border: 1px solid var(--faint);
 470  border-radius: var(--r-md);
 471  padding: 0.1rem 0.55rem;
 472  font-family: var(--mono);
 473  font-size: var(--fs-1);
 474  color: var(--fg);
 475}
 476.pathbar .crumbs strong { color: var(--fg); }
 477details.refmenu { position: relative; }
 478details.refmenu summary { cursor: pointer; list-style: none; }
 479details.refmenu summary::-webkit-details-marker { display: none; }
 480details.refmenu summary::after { content: " ▾"; color: var(--muted); }
 481details.refmenu .refdrop {
 482  position: absolute;
 483  z-index: 10;
 484  top: calc(100% + 4px);
 485  left: 0;
 486  min-width: 12rem;
 487  max-height: 20rem;
 488  overflow-y: auto;
 489  background: var(--bg);
 490  border: 1px solid var(--line);
 491  border-radius: var(--r-md);
 492  box-shadow: var(--shadow);
 493  padding: var(--sp-1) 0;
 494}
 495details.refmenu .refdrop a {
 496  display: block;
 497  padding: var(--sp-1) var(--sp-3);
 498  color: var(--fg);
 499  font-family: var(--mono);
 500  font-size: var(--fs-1);
 501}
 502details.refmenu .refdrop a:hover { background: var(--hover); text-decoration: none; }
 503details.refmenu .refdrop a.allrefs { color: var(--accent); font-family: var(--sans); border-top: 1px solid var(--faint); margin-top: var(--sp-1); }
 504
 505/* file and refs tables: bordered cards, rows inside */
 506/* wide listings scroll inside their own box; the page never does */
 507.tablewrap { overflow-x: auto; max-width: 100%; }
 508
 509table.tree, table.refs {
 510  background: var(--surface);
 511  border: 1px solid var(--line);
 512  border-radius: var(--r-lg);
 513  border-collapse: separate;
 514  border-spacing: 0;
 515  overflow: hidden;
 516  margin-bottom: var(--sp-4);
 517}
 518table.tree td, table.refs td {
 519  padding: var(--sp-2) var(--sp-4);
 520  border-bottom: 1px solid var(--faint);
 521}
 522table.tree tr:last-child td, table.refs tr:last-child td { border-bottom: none; }
 523table.tree tr:hover td, table.refs tr:hover td { background: var(--hover); }
 524tr.cols th {
 525  text-align: left;
 526  font-weight: 500;
 527  font-size: var(--fs-1);
 528  color: var(--muted);
 529  padding: var(--sp-1) var(--sp-4);
 530  border-bottom: 1px solid var(--line);
 531  background: var(--bg);
 532}
 533tr.cols:hover th { background: var(--bg); }
 534/* right-aligned columns keep their header with their values; this needs to
 535   outrank the tr.cols th rule above, which is why it names both */
 536tr.cols th.age, tr.cols th.size { text-align: right; }
 537table.tree th.size { text-align: right; }
 538table.tree td.name { width: 100%; }
 539table.tree td.name a { color: var(--fg); }
 540table.tree td.name a:hover { color: var(--accent); }
 541table.tree td.size { text-align: right; }
 542table.tree td.empty, table.refs td.empty, li.empty, ul.loglist li.empty { color: var(--muted); }
 543
 544/* refs page */
 545table.refs th, table.assets th {
 546  text-align: left;
 547  font-size: var(--fs-0);
 548  font-weight: 500;
 549  color: var(--muted);
 550  padding: var(--sp-1) var(--sp-2);
 551  border-bottom: 1px solid var(--line);
 552}
 553table.refs td.name { width: 100%; }
 554table.refs td.sha { color: var(--muted); }
 555
 556/* card header (README) */
 557.cardhead {
 558  font-family: var(--mono);
 559  font-size: var(--fs-1);
 560  color: var(--muted);
 561  border-bottom: 1px solid var(--faint);
 562  margin: calc(-1 * var(--sp-4)) calc(-1 * var(--sp-5)) var(--sp-4);
 563  padding: var(--sp-2) var(--sp-5);
 564  background: var(--bg);
 565  border-radius: var(--r-lg) var(--r-lg) 0 0;
 566}
 567
 568/* log */
 569ul.loglist {
 570  list-style: none;
 571  margin: var(--sp-3) 0;
 572  padding: 0;
 573  background: var(--surface);
 574  border: 1px solid var(--line);
 575  border-radius: var(--r-lg);
 576  overflow: hidden;
 577}
 578ul.loglist li {
 579  display: flex;
 580  align-items: baseline;
 581  gap: var(--sp-3);
 582  padding: var(--sp-2) var(--sp-4);
 583  border-bottom: 1px solid var(--faint);
 584}
 585ul.loglist li:last-child { border-bottom: none; }
 586ul.loglist li:hover { background: var(--hover); }
 587ul.loglist .commitmain { flex: 1; min-width: 0; }
 588ul.loglist p { margin: 0; }
 589ul.loglist .subject a { color: var(--fg); font-weight: 550; }
 590ul.loglist .subject a:hover { color: var(--accent); }
 591ul.loglist .commitside {
 592  display: flex;
 593  align-items: baseline;
 594  gap: var(--sp-2);
 595  white-space: nowrap;
 596}
 597p.pager { margin-top: var(--sp-4); }
 598
 599/* commit page */
 600.commithead p { margin: var(--sp-1) 0; }
 601.commithead h2 { margin-bottom: var(--sp-1); }
 602code.fullsha { color: var(--muted); overflow-wrap: anywhere; }
 603.empty-note { color: var(--muted); }
 604.crumbs { color: var(--muted); font-size: var(--fs-2); }
 605.desc, td.desc { color: var(--muted); }
 606.error {
 607  color: var(--fg);
 608  border: 1px solid var(--bad);
 609  border-left-width: 3px;
 610  border-radius: var(--r-sm);
 611  padding: var(--sp-2) var(--sp-3);
 612  margin-bottom: var(--sp-4);
 613}
 614/* settings: one row per control, label then input then its own button */
 615form.setform {
 616  display: flex;
 617  flex-wrap: wrap;
 618  align-items: center;
 619  gap: var(--sp-3);
 620  padding: var(--sp-2) 0;
 621  border-bottom: 1px solid var(--faint);
 622}
 623/* the label absorbs the free space, so a row whose control does not
 624   stretch — a checkbox, a number — still finishes at the same right edge
 625   as the rows whose input fills the line */
 626form.setform label { min-width: 12rem; margin-right: auto; color: var(--muted); font-size: var(--fs-2); }
 627form.setform input[type="text"], form.setform select { flex: 1 1 16rem; }
 628form.setform input[type="number"] { width: 5rem; }
 629ul.protlist { list-style: none; padding: 0; margin: var(--sp-2) 0; }
 630ul.protlist li { padding: var(--sp-1) 0; display: flex; gap: var(--sp-3); align-items: baseline; }
 631
 632/* branch picker on the new merge request form */
 633p.branchpick {
 634  display: flex;
 635  flex-wrap: wrap;
 636  align-items: center;
 637  gap: var(--sp-2);
 638}
 639p.branchpick label { color: var(--muted); font-size: var(--fs-2); }
 640p.branchpick select { flex: 1 1 12rem; }
 641
 642/* merge request actions in the aside: stacked controls, full width */
 643.aside form.actions {
 644  display: flex;
 645  flex-wrap: wrap;
 646  gap: var(--sp-2);
 647  margin-top: var(--sp-2);
 648}
 649.aside form.actions button { flex: 1 1 auto; }
 650.aside form.actions select { width: 100%; }
 651form.threadact { margin: var(--sp-1) 0 0; padding: 0 var(--sp-3) var(--sp-2); }
 652
 653.meta { color: var(--muted); font-size: var(--fs-1); }
 654svg.icon { vertical-align: -0.125em; }
 655.lede { font-size: var(--fs-3); margin: var(--sp-2) 0; }
 656
 657/* repo listings (explore, owner pages, dashboard): one row per repo */
 658ul.repolist {
 659  list-style: none;
 660  margin: var(--sp-3) 0 var(--sp-5);
 661  padding: 0;
 662  background: var(--surface);
 663  border: 1px solid var(--line);
 664  border-radius: var(--r-lg);
 665  overflow: hidden;
 666}
 667ul.repolist li { padding: var(--sp-3) var(--sp-4); border-bottom: 1px solid var(--faint); }
 668/* pinned repos: a compact wrap of name pills, no description or meta */
 669ul.pinlist {
 670  list-style: none;
 671  margin: var(--sp-3) 0 var(--sp-5);
 672  padding: 0;
 673  display: flex;
 674  flex-wrap: wrap;
 675  gap: var(--sp-2);
 676}
 677ul.pinlist li {
 678  background: var(--surface);
 679  border: 1px solid var(--line);
 680  border-radius: var(--r-md);
 681  padding: var(--sp-1) var(--sp-3);
 682}
 683ul.pinlist li:hover { background: var(--hover); }
 684ul.pinlist li a { color: var(--fg); }
 685ul.pinlist li a:hover { color: var(--accent); text-decoration: none; }
 686ul.pinlist .sep { color: var(--muted); margin: 0 0.15em; }
 687ul.repolist li:last-child { border-bottom: none; }
 688ul.repolist li:hover { background: var(--hover); }
 689ul.repolist li.empty { color: var(--muted); }
 690ul.repolist p { margin: 0; }
 691ul.repolist .reponame { font-size: var(--fs-3); }
 692ul.repolist .reponame a { color: var(--fg); }
 693ul.repolist .reponame a strong { color: var(--accent); font-weight: 600; }
 694ul.repolist .reponame .sep { color: var(--muted); margin: 0 0.1em; }
 695ul.repolist .desc { margin-top: 0.1rem; }
 696ul.repolist .topics { margin-top: var(--sp-1); }
 697ul.repolist .meta { margin-top: var(--sp-1); }
 698
 699/* owner profile header */
 700.profilehead {
 701  border-bottom: 1px solid var(--faint);
 702  padding-bottom: var(--sp-4);
 703  margin-bottom: var(--sp-4);
 704}
 705.profilehead h1 { margin: var(--sp-4) 0 var(--sp-1); }
 706.profilehead .lede { margin: 0 0 var(--sp-1); }
 707.profilehead .meta { margin: var(--sp-1) 0 0; }
 708h2 .count { color: var(--muted); font-weight: 400; font-size: var(--fs-2); }
 709
 710/* activity graph: 53 week columns x 7 day rows, accent-tinted levels */
 711.actgraph-scroll { overflow-x: auto; padding-bottom: var(--sp-1); }
 712.actgraph { display: flex; gap: 3px; width: max-content; }
 713.actweek { display: flex; flex-direction: column; gap: 3px; }
 714.actday {
 715  width: 11px;
 716  height: 11px;
 717  border-radius: 2px;
 718  background: var(--faint);
 719}
 720.actday.pad { background: transparent; }
 721.actday.l1 { background: color-mix(in srgb, var(--accent) 25%, var(--bg)); }
 722.actday.l2 { background: color-mix(in srgb, var(--accent) 50%, var(--bg)); }
 723.actday.l3 { background: color-mix(in srgb, var(--accent) 75%, var(--bg)); }
 724.actday.l4 { background: var(--accent); }
 725
 726/* heading row with actions on the right */
 727.headrow {
 728  display: flex;
 729  align-items: baseline;
 730  gap: var(--sp-4);
 731  flex-wrap: wrap;
 732}
 733.headrow .spacer { flex: 1; }
 734
 735/* member and org chips on owner pages */
 736a.memberchip {
 737  display: inline-block;
 738  border: 1px solid var(--line);
 739  border-radius: var(--r-pill);
 740  padding: 0.05rem 0.6rem;
 741  color: var(--fg);
 742  font-size: var(--fs-1);
 743  margin: 0 0.15rem 0.15rem 0;
 744}
 745a.memberchip:hover { text-decoration: none; border-color: var(--accent); color: var(--accent); }
 746a.memberchip .role { color: var(--muted); }
 747
 748/* landing page */
 749.landing { max-width: 44rem; padding: var(--sp-6) 0; }
 750.landing h1 { font-size: var(--fs-6); margin-bottom: var(--sp-3); }
 751.landing .lede { font-size: var(--fs-4); color: var(--muted); line-height: 1.7; margin: 0 0 var(--sp-4); }
 752pre.quickstart {
 753  background: var(--code-bg);
 754  border: 1px solid var(--line);
 755  border-radius: var(--r-md);
 756  padding: var(--sp-3) var(--sp-4);
 757  overflow-x: auto;
 758  margin: 0 0 var(--sp-4);
 759}
 760.landing .explorelink { font-weight: 550; }
 761.landing .facets { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--sp-5); margin: var(--sp-5) 0; }
 762.landing .facets h2 { font-size: var(--fs-3); margin: 0 0 var(--sp-1); }
 763.landing .facets p { margin: 0; color: var(--muted); font-size: var(--fs-2); line-height: 1.6; }
 764@media (max-width: 40rem) { .landing .facets { grid-template-columns: 1fr; gap: var(--sp-4); } }
 765
 766/* index toolbar */
 767p.toolbar { color: var(--muted); font-size: var(--fs-2); margin-top: var(--sp-4); }
 768button.linklike {
 769  background: none;
 770  border: none;
 771  color: var(--accent);
 772  padding: 0;
 773  font: inherit;
 774  font-size: var(--fs-2);
 775  cursor: pointer;
 776}
 777button.linklike:hover { text-decoration: underline; filter: none; }
 778
 779/* cards: README, comments */
 780.readme, .code {
 781  background: var(--surface);
 782  border: 1px solid var(--line);
 783  border-radius: var(--r-lg);
 784  padding: var(--sp-4) var(--sp-5);
 785  margin-top: var(--sp-4);
 786  overflow-x: auto;
 787}
 788/* Highlighting is class-based with a palette per scheme, so code blocks
 789   follow the site's own colors. */
 790.code { background: var(--code-bg); color: var(--fg); padding: var(--sp-3) var(--sp-4); border-color: var(--line); }
 791.code pre { margin: 0; background: transparent !important; }
 792/* rendered markdown / org / html */
 793.rendered { max-width: 100%; overflow-wrap: anywhere; }
 794/* A linked line: #f2-n17 on a diff, #L17 on a file (#118). */
 795.difftable tr:target td, .code .line:has(a:target) { background: color-mix(in srgb, var(--mark) 18%, transparent); }
 796.difftable tr:target td.ln, .code .line:has(a:target) .ln { border-left: 3px solid var(--mark); }
 797.thread:target { outline: 2px solid var(--mark); outline-offset: 2px; }
 798/* GFM task lists: the box sits in the line, sized to the prose. */
 799.rendered li input[type="checkbox"] { width: 0.9em; height: 0.9em; margin: 0 0.35em 0 0; vertical-align: -0.05em; accent-color: var(--accent); }
 800.rendered li:has(> input[type="checkbox"]) { list-style: none; margin-left: -1.2em; }
 801/* line length is a reading constraint, so it applies to prose only —
 802   tables, code blocks and images keep the full column */
 803.rendered > p,
 804.rendered > ul,
 805.rendered > ol,
 806.rendered > blockquote { max-width: 78ch; }
 807.rendered pre {
 808  background: var(--code-bg);
 809  border: 1px solid var(--line);
 810  padding: var(--sp-3) var(--sp-4);
 811  border-radius: var(--r-md);
 812  overflow-x: auto;
 813}
 814.rendered code {
 815  background: var(--code-bg);
 816  border: 1px solid var(--line);
 817  padding: 0.05rem 0.3rem;
 818  border-radius: var(--r-sm);
 819}
 820.rendered pre code { background: transparent; border: none; padding: 0; }
 821.rendered blockquote {
 822  border-left: 3px solid var(--line);
 823  margin-left: 0;
 824  padding-left: var(--sp-4);
 825  color: var(--muted);
 826}
 827.rendered img { max-width: 100%; }
 828.rendered h1, .rendered h2 { border-bottom: 1px solid var(--faint); padding-bottom: var(--sp-1); }
 829.rendered table {
 830  border-collapse: collapse;
 831  margin: var(--sp-3) 0;
 832  display: block;
 833  max-width: 100%;
 834  overflow-x: auto;
 835}
 836.rendered th, .rendered td {
 837  border: 1px solid var(--line);
 838  padding: var(--sp-1) var(--sp-3);
 839  text-align: left;
 840  overflow-wrap: normal; /* scroll the table, don't shatter words */
 841}
 842.rendered th { background: var(--surface); font-weight: 600; }
 843.blobimage { text-align: center; }
 844.blobimage img {
 845  max-width: 100%;
 846  border: 1px solid var(--faint);
 847  border-radius: var(--r-md);
 848  background: var(--surface);
 849}
 850
 851pre.message {
 852  background: var(--code-bg);
 853  border: 1px solid var(--line);
 854  padding: var(--sp-3) var(--sp-4);
 855  border-radius: var(--r-md);
 856  white-space: pre-wrap;
 857  overflow-wrap: anywhere;
 858}
 859
 860/* repo facts: the counts a visitor sizes a project up with */
 861.teambody { padding: var(--sp-3); background: var(--bg); }
 862.teambody .memberchip { margin-right: var(--sp-2); }
 863
 864form.logfilter {
 865  display: flex; align-items: center; gap: var(--sp-2);
 866  margin: 0 0 var(--sp-4); flex-wrap: wrap;
 867}
 868form.logfilter input { flex: 1; min-width: 12rem; max-width: 28rem; }
 869
 870/* account settings */
 871table.keys td { padding: var(--sp-2) var(--sp-4) var(--sp-2) 0; }
 872/* these tables sit flush with the page text, so the header row drops the
 873   left padding the card-style listings use */
 874table.keys tr.cols th { padding-left: 0; background: transparent; }
 875table.keys td.mono, .mono { font-family: var(--mono); font-size: var(--fs-1); overflow-wrap: anywhere; }
 876table.keys td.act { text-align: right; }
 877ul.plain { list-style: none; margin: 0 0 var(--sp-3); padding: 0; }
 878ul.plain li { padding: var(--sp-1) 0; display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
 879form.setform.stack { display: block; }
 880form.setform.stack textarea, form.setform.stack select { width: 100%; max-width: 48rem; display: block; margin-bottom: var(--sp-2); }
 881p.notice {
 882  border-left: 3px solid var(--ok);
 883  background: var(--hover);
 884  padding: var(--sp-2) var(--sp-3);
 885  margin: 0 0 var(--sp-4);
 886}
 887
 888.facts { margin: 0 0 var(--sp-4); }
 889.facts .counts {
 890  display: flex; flex-wrap: wrap; gap: var(--sp-1) var(--sp-4);
 891  margin: 0 0 var(--sp-3); font-size: var(--fs-2);
 892}
 893.facts .counts a { color: var(--fg); text-decoration: none; }
 894.facts .counts a:hover { color: var(--accent); text-decoration: underline; }
 895.facts .counts .fact { color: var(--muted); }
 896.facts .counts strong { font-weight: 600; }
 897
 898.langbar {
 899  display: flex; height: 0.5rem; margin: 0 0 var(--sp-2);
 900  border-radius: var(--r-sm); overflow: hidden; background: var(--faint);
 901}
 902.langbar .lang { display: block; min-width: 2px; }
 903.langs {
 904  display: flex; flex-wrap: wrap; gap: var(--sp-1) var(--sp-4);
 905  margin: 0 0 var(--sp-3); font-size: var(--fs-1);
 906}
 907.langs .lang-name { display: inline-flex; align-items: center; gap: var(--sp-1); }
 908.langs .muted { color: var(--muted); }
 909.langs .dot { width: 0.6rem; height: 0.6rem; border-radius: 50%; display: inline-block; }
 910/* named where it reads as the language's own color, neutral otherwise */
 911.lang { background: var(--neutral); }
 912.lang-go { background: #00add8; }
 913.lang-rust { background: #dea584; }
 914.lang-c, .lang-c- { background: #555555; }
 915.lang-python { background: #3572a5; }
 916.lang-javascript { background: #f1e05a; }
 917.lang-typescript { background: #3178c6; }
 918.lang-shell { background: #89e051; }
 919.lang-ruby { background: #701516; }
 920.lang-java { background: #b07219; }
 921.lang-swift { background: #f05138; }
 922.lang-html { background: #e34c26; }
 923.lang-css { background: #563d7c; }
 924.lang-org { background: #77aa99; }
 925.lang-markdown { background: #083fa1; }
 926.lang-emacs-lisp { background: #c065db; }
 927.lang-nix { background: #7e7eff; }
 928.lang-sql { background: #e38c00; }
 929
 930.contribs {
 931  display: flex; flex-wrap: wrap; align-items: baseline;
 932  gap: var(--sp-1) var(--sp-3); margin: 0; font-size: var(--fs-1);
 933}
 934.contribs .label { color: var(--muted); }
 935
 936/* diffs: one foldable section per file, a table so the line-number
 937   gutters stay put while the code scrolls */
 938details.difffold {
 939  border: 1px solid var(--line);
 940  border-radius: var(--r-md);
 941  margin: 0 0 var(--sp-3);
 942  background: var(--code-bg);
 943}
 944details.difffold summary {
 945  cursor: pointer;
 946  display: flex;
 947  align-items: baseline;
 948  gap: var(--sp-3);
 949  padding: var(--sp-2) var(--sp-3);
 950  list-style: none;
 951  font-size: var(--fs-2);
 952}
 953details.difffold[open] summary { border-bottom: 1px solid var(--line); }
 954details.difffold summary::-webkit-details-marker { display: none; }
 955/* flex summaries lose the native disclosure marker; draw our own */
 956details.difffold summary::before { content: "\25b8"; color: var(--muted); }
 957details.difffold[open] summary::before { content: "\25be"; }
 958details.difffold summary:hover { background: var(--fill-subtle); }
 959summary .fpath { font-family: var(--mono); overflow-wrap: anywhere; }
 960summary .fpath .was { color: var(--muted); }
 961summary .fstat { margin-left: auto; font-family: var(--mono); white-space: nowrap; }
 962details.difffold summary .add { color: var(--ok); }
 963details.difffold summary .del { color: var(--bad); }
 964details.difffold > .none { padding: var(--sp-3); margin: 0; }
 965details.difffold .tablewrap { border-radius: 0 0 var(--r-md) var(--r-md); }
 966
 967table.difftable {
 968  border-collapse: collapse;
 969  width: 100%;
 970  font-family: var(--mono);
 971  font-size: var(--fs-1);
 972  line-height: 1.5;
 973}
 974table.difftable td {
 975  border: 0;
 976  padding: 0;
 977  vertical-align: top;
 978  font-size: inherit;
 979}
 980table.difftable td.ln {
 981  width: 1%;
 982  min-width: 2.5rem;
 983  padding: 0 var(--sp-2);
 984  text-align: right;
 985  color: var(--muted);
 986  user-select: none;
 987  border-right: 1px solid var(--line);
 988  white-space: nowrap;
 989}
 990/* not .code: that class is the blob/code-block style and carries its own
 991   background, which painted over every row tint */
 992table.difftable td.src {
 993  padding: 0 var(--sp-3);
 994  white-space: pre-wrap;
 995  overflow-wrap: anywhere;
 996}
 997/* the marker is decoration, so it lives in CSS and stays out of a copied
 998   selection */
 999table.difftable tr.add td.src::before { content: "+"; color: var(--ok); }
1000table.difftable tr.del td.src::before { content: "\2212"; color: var(--bad); }
1001table.difftable tr.ctx td.src::before { content: " "; }
1002table.difftable tr.add { background: var(--diff-add); }
1003table.difftable tr.del { background: var(--diff-del); }
1004table.difftable tr.hunk td, table.difftable tr.dmeta td {
1005  color: var(--muted);
1006  padding: var(--sp-1) var(--sp-3);
1007  background: var(--fill-subtle);
1008  border-top: 1px solid var(--line);
1009  border-bottom: 1px solid var(--line);
1010}
1011table.difftable tr.hunk td.src { color: var(--accent); }
1012table.difftable tr.threadrow td { padding: var(--sp-2) var(--sp-3); background: var(--bg); }
1013/* a line number is the handle for commenting on that line; it stays a
1014   plain number until pointed at, so the gutter does not read as a wall of
1015   links */
1016table.difftable td.ln a.cmt { color: inherit; text-decoration: none; }
1017table.difftable td.ln a.cmt:hover,
1018table.difftable td.ln a.cmt:focus { color: var(--accent); text-decoration: underline; }
1019
1020/* badges: signature and check states. Semantic colors stay distinct:
1021   green verified/success, amber pending/stale, red bad/failure, muted
1022   unsigned. */
1023.badge, .chip {
1024  --chip: var(--neutral);
1025  display: inline-block;
1026  padding: 0.05rem 0.55rem;
1027  border-radius: var(--r-pill);
1028  font-size: var(--fs-0);
1029  font-weight: 500;
1030  line-height: 1.5;
1031  color: var(--chip);
1032  border: 1px solid color-mix(in srgb, var(--chip) 35%, transparent);
1033  background: color-mix(in srgb, var(--chip) 7%, transparent);
1034  white-space: nowrap;
1035  vertical-align: middle;
1036}
1037.chip-open { --chip: var(--ok); }
1038.chip-closed { --chip: var(--bad); }
1039.chip-merged, .chip-done { --chip: var(--done); }
1040.chip-neutral, .chip-source_gone { --chip: var(--neutral); }
1041.chip-stale { --chip: var(--warn); }
1042.chip.label { font-weight: 500; }
1043.chip.topic { --chip: var(--accent); }
1044.repohead .topics { margin: 0 0 var(--sp-2); }
1045.badge-verified { --chip: var(--ok); }
1046.badge-unsigned { --chip: var(--neutral); }
1047.badge-signed_unknown_key { --chip: var(--warn); }
1048.badge-signed_email_mismatch { --chip: var(--bad); }
1049.badge-signed_key_expired { --chip: var(--warn); }
1050.badge-signed_key_revoked { --chip: var(--bad); }
1051.badge-bad_signature { --chip: var(--bad); }
1052.badge.check-success { --chip: var(--ok); }
1053.badge.check-pending { --chip: var(--warn); }
1054.badge.check-failure, .badge.check-error { --chip: var(--bad); }
1055.badge.check-cancelled, .chip.check-cancelled { --chip: var(--neutral); }
1056.badge.check-running, .chip.check-running { --chip: var(--warn); }
1057.chip.check-success { --chip: var(--ok); }
1058.chip.check-pending { --chip: var(--warn); }
1059.chip.check-failure { --chip: var(--bad); }
1060pre.buildlog { max-height: 40rem; overflow: auto; }
1061span.check-success { color: var(--ok); }
1062span.check-pending { color: var(--warn); }
1063span.check-failure, span.check-error { color: var(--bad); }
1064
1065/* issue and MR listings */
1066.listhead {
1067  display: flex;
1068  align-items: baseline;
1069  gap: var(--sp-4);
1070  flex-wrap: wrap;
1071}
1072.listhead h2 { margin-bottom: var(--sp-2); }
1073nav.filters { display: flex; gap: var(--sp-1); }
1074nav.filters a {
1075  color: var(--muted);
1076  font-size: var(--fs-2);
1077  padding: 0.05rem var(--sp-2);
1078  border-radius: var(--r-pill);
1079}
1080nav.filters a:hover { color: var(--fg); background: var(--hover); text-decoration: none; }
1081nav.filters a.active { color: var(--accent); background: color-mix(in srgb, var(--accent) 10%, transparent); }
1082
1083ul.issuelist {
1084  list-style: none;
1085  margin: var(--sp-2) 0;
1086  padding: 0;
1087  background: var(--surface);
1088  border: 1px solid var(--line);
1089  border-radius: var(--r-lg);
1090  overflow: hidden;
1091}
1092ul.issuelist li {
1093  display: flex;
1094  align-items: baseline;
1095  gap: var(--sp-3);
1096  padding: var(--sp-3) var(--sp-4);
1097  border-bottom: 1px solid var(--faint);
1098}
1099ul.issuelist li:last-child { border-bottom: none; }
1100ul.issuelist li:hover { background: var(--hover); }
1101ul.issuelist li.empty { display: block; color: var(--muted); }
1102ul.issuelist .issuemain { flex: 1; min-width: 0; }
1103ul.issuelist p { margin: 0; }
1104ul.issuelist .title a { color: var(--fg); font-weight: 550; }
1105ul.issuelist .title a:hover { color: var(--accent); }
1106
1107/* releases */
1108article.release {
1109  background: var(--surface);
1110  border: 1px solid var(--line);
1111  border-radius: var(--r-lg);
1112  padding: var(--sp-3) var(--sp-4);
1113  margin: var(--sp-4) 0;
1114}
1115.releasehead { background: none; border: 0; padding: 0; }
1116.releasehead h3 { margin: 0 0 var(--sp-1); }
1117.releasehead .meta { margin: 0 0 var(--sp-2); }
1118table.assets { margin-top: var(--sp-2); }
1119table.assets td.size { color: var(--muted); white-space: nowrap; text-align: right; }
1120table.assets td.name { width: 100%; font-family: var(--mono); font-size: var(--fs-1); }
1121
1122/* milestones */
1123ul.milestonelist {
1124  list-style: none;
1125  margin: var(--sp-2) 0;
1126  padding: 0;
1127  border: 1px solid var(--line);
1128  border-radius: var(--r-lg);
1129  overflow: hidden;
1130}
1131ul.milestonelist li { padding: var(--sp-3) var(--sp-4); border-bottom: 1px solid var(--faint); }
1132ul.milestonelist li:last-child { border-bottom: none; }
1133ul.milestonelist li.empty { color: var(--muted); }
1134ul.milestonelist p { margin: 0; }
1135ul.milestonelist .title { font-weight: 550; }
1136.progress {
1137  height: 6px;
1138  background: var(--faint);
1139  border-radius: var(--r-pill);
1140  margin-top: var(--sp-2);
1141  overflow: hidden;
1142  max-width: 24rem;
1143}
1144.progress .bar { height: 100%; background: var(--ok); }
1145.listhead .spacer { flex: 1; }
1146
1147/* issue / MR detail */
1148h2.issuetitle { margin-bottom: var(--sp-1); font-size: var(--fs-5); }
1149.issuenumber { color: var(--muted); font-weight: 400; }
1150.issuemeta { color: var(--muted); font-size: var(--fs-2); margin-top: 0; }
1151.review { font-size: var(--fs-2); }
1152
1153/* comment cards; discussion stays at a readable measure */
1154article.comment, form.commentform {
1155  max-width: 48rem;
1156}
1157article.comment {
1158  border: 1px solid var(--faint);
1159  border-radius: var(--r-lg);
1160  margin: var(--sp-4) 0;
1161}
1162article.comment .commenthead {
1163  background: var(--surface);
1164  border-bottom: 1px solid var(--faint);
1165  border-radius: var(--r-lg) var(--r-lg) 0 0;
1166  padding: var(--sp-2) var(--sp-4);
1167  font-size: var(--fs-2);
1168}
1169article.comment .commenthead .when { color: var(--muted); }
1170article.comment .rendered { padding: 0 var(--sp-4); }
1171article.comment .rendered > :first-child { margin-top: var(--sp-3); }
1172article.comment .rendered > :last-child { margin-bottom: var(--sp-3); }
1173form.commentform { margin: var(--sp-4) 0; }
1174details.editbox { margin: var(--sp-2) 0; max-width: 48rem; }
1175details.editbox summary { cursor: pointer; color: var(--muted); font-size: var(--fs-2); }
1176details.editbox summary:hover { color: var(--accent); }
1177details.editbox input[type="text"] { width: 100%; }
1178.syscomment {
1179  max-width: 48rem;
1180  color: var(--muted);
1181  font-size: var(--fs-2);
1182  margin: var(--sp-3) 0;
1183  padding-left: var(--sp-4);
1184  border-left: 3px solid var(--faint);
1185}
1186.syscomment p { display: inline; margin: 0; }
1187
1188/* review threads under diff lines */
1189.thread {
1190  border: 1px solid var(--faint);
1191  border-left: 3px solid var(--accent);
1192  border-radius: var(--r-md);
1193  padding: var(--sp-2) var(--sp-4);
1194  margin: var(--sp-2) 0 var(--sp-2) var(--sp-6);
1195}
1196.thread.resolved { border-left-color: var(--ok); opacity: 0.75; }
1197/* an unsubmitted comment is the viewer's own note, not the conversation */
1198.thread.pending { border-left-color: var(--neutral); border-left-style: dashed; }
1199.thread.stale { border-left-color: var(--warn); }
1200.thread p.commenthead { font-size: var(--fs-2); margin: var(--sp-2) 0 0; }
1201.thread .when { color: var(--muted); }
1202.thread .rendered p { margin: var(--sp-1) 0; }
1203.thread .threadstate { color: var(--muted); font-size: var(--fs-1); margin: var(--sp-1) 0 0; }
1204.thread.composing { border-left-color: var(--mark); }
1205.thread details.threadreply { margin: var(--sp-2) 0 0; }
1206.thread details.threadreply > summary { color: var(--accent); font-size: var(--fs-1); cursor: pointer; }
1207.thread textarea { width: 100%; }
1208.thread.composing p, .thread details.threadreply p { margin: var(--sp-2) 0 0; }
1209
1210/* forms: every control shares one shell, so a select reads as a sibling
1211   of the text inputs beside it rather than as an OS control dropped in */
1212input, textarea, button, select { font: inherit; color: inherit; }
1213input[type="text"], input[type="search"], input[type="email"],
1214input[type="password"], input:not([type]), textarea, select {
1215  background: var(--bg);
1216  color: var(--fg);
1217  border: 1px solid var(--line);
1218  border-radius: var(--r-md);
1219  padding: var(--sp-2) var(--sp-3);
1220  line-height: 1.4;
1221}
1222textarea { width: 100%; resize: vertical; }
1223textarea[name="content"] { font-family: var(--mono); font-size: var(--fs-1); }
1224/* the native arrow cannot be restyled, so draw our own and match the
1225   inputs exactly; --chev carries the stroke colour per scheme */
1226select {
1227  appearance: none;
1228  padding-right: 1.9rem;
1229  background-image: var(--chev);
1230  background-repeat: no-repeat;
1231  background-position: right 0.6rem center;
1232  background-size: 0.62rem;
1233  cursor: pointer;
1234}
1235input:hover, textarea:hover, select:hover { border-color: var(--muted); }
1236/* the global :focus-visible ring covers these; only the border moves */
1237input:focus, textarea:focus, select:focus { border-color: var(--accent); }
1238input[type="radio"] { accent-color: var(--fill); }
1239label:has(> input[type="radio"]), label:has(> input[type="checkbox"]),
1240label:has(> select) { display: inline-flex; align-items: center; gap: var(--sp-2); }
1241
1242/* A radio keeps the native control: a circle takes one answer, a square
1243   takes any number, and that shape is the cue that survives greyscale,
1244   high contrast and colour blindness. The checkbox is drawn instead,
1245   because at the size the OS renders it it reads as an afterthought
1246   beside the button it shares a row with. The square is kept square —
1247   the size changes, the cue does not. */
1248input[type="checkbox"] {
1249  appearance: none;
1250  flex: none;
1251  /* the height of a text input, a select and a button, so a settings row
1252     ends in one column of controls rather than a button and a speck */
1253  width: 2.5rem;
1254  height: 2.5rem;
1255  /* an empty box has nothing but its border to announce it, so the border
1256     is --muted rather than the --line the sized controls can afford */
1257  border: 1px solid var(--muted);
1258  border-radius: var(--r-sm);
1259  background: var(--bg);
1260  cursor: pointer;
1261}
1262input[type="checkbox"]:not(:checked):hover { border-color: var(--fg); }
1263input[type="checkbox"]:checked {
1264  background: var(--fill);
1265  border-color: var(--fill);
1266  /* the mark sits on the blue ground, so it is white in both schemes */
1267  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 10'%3E%3Cpath d='M1 5l3.4 3.4L11 1.4' fill='none' stroke='%23ffffff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
1268  background-repeat: no-repeat;
1269  background-position: center;
1270  background-size: 1.2rem;
1271}
1272
1273/* a segmented control for a small, mutually exclusive set: real radios
1274   underneath, so arrow keys and screen readers behave as they always did */
1275fieldset.segmented { border: 0; margin: 0 0 var(--sp-4); padding: 0; }
1276fieldset.segmented legend {
1277  padding: 0;
1278  margin-bottom: var(--sp-2);
1279  color: var(--muted);
1280  font-size: var(--fs-2);
1281}
1282fieldset.segmented .options { display: inline-flex; }
1283fieldset.segmented label { gap: 0; }
1284fieldset.segmented input {
1285  position: absolute;
1286  width: 1px;
1287  height: 1px;
1288  opacity: 0;
1289  pointer-events: none;
1290}
1291fieldset.segmented span {
1292  display: block;
1293  padding: var(--sp-2) var(--sp-4);
1294  border: 1px solid var(--line);
1295  font-size: var(--fs-2);
1296  cursor: pointer;
1297}
1298fieldset.segmented label + label span { border-left: 0; }
1299fieldset.segmented label:first-child span { border-radius: var(--r-sm) 0 0 var(--r-sm); }
1300fieldset.segmented label:last-child span { border-radius: 0 var(--r-sm) var(--r-sm) 0; }
1301fieldset.segmented span:hover { border-color: var(--muted); }
1302fieldset.segmented input:checked + span {
1303  background: var(--fill);
1304  border-color: var(--fill);
1305  color: #ffffff;
1306  font-weight: 600;
1307}
1308fieldset.segmented input:focus-visible + span {
1309  outline: 2px solid var(--accent);
1310  outline-offset: 2px;
1311}
1312
1313/* remaining input states */
1314::placeholder { color: var(--muted); opacity: 1; }
1315::selection { background: var(--fill); color: #ffffff; }
1316/* :user-invalid, not :invalid — a required field is invalid the moment
1317   the page loads, and colouring it red before anyone has typed is a
1318   scolding, not a hint */
1319input:user-invalid, textarea:user-invalid {
1320  border-color: var(--bad);
1321}
1322.hint { display: block; color: var(--muted); font-size: var(--fs-1); margin-top: var(--sp-1); }
1323button {
1324  background: var(--fill);
1325  color: #ffffff;
1326  border: 1px solid var(--fill-edge);
1327  border-radius: var(--r-md);
1328  padding: var(--sp-2) var(--sp-4);
1329  font-weight: 600;
1330  cursor: pointer;
1331}
1332button:hover { filter: brightness(1.15); }
1333
1334/* 404 */
1335.notfound { text-align: center; padding: var(--sp-6) 0; }
1336.notfound .bigcode {
1337  font-family: var(--mono);
1338  font-size: var(--fs-7);
1339  color: var(--faint);
1340  -webkit-text-stroke: 1px var(--line);
1341  margin: 0;
1342  line-height: 1;
1343}
1344.notfound h1 { margin-top: var(--sp-3); }
1345
1346/* footer */
1347footer {
1348  border-top: 1px solid var(--faint);
1349  padding: var(--sp-4) var(--sp-5) var(--sp-5);
1350  width: 100%;
1351  color: var(--muted);
1352  font-size: var(--fs-1);
1353}
1354footer p { margin: 0; }
1355footer a { color: var(--muted); text-decoration: underline; }
1356footer a:hover { color: var(--accent); }
1357
1358/* search */
1359form.searchform {
1360  display: flex;
1361  gap: var(--sp-2);
1362  margin: var(--sp-3) 0 var(--sp-4);
1363}
1364form.searchform input[type="search"] { flex: 1; max-width: 32rem; }
1365form.searchform.compact { margin: 0; }
1366form.searchform.compact input[type="search"] {
1367  padding: var(--sp-1) var(--sp-3);
1368  font-size: var(--fs-2);
1369  min-width: 18rem;
1370}
1371ul.matchlist { list-style: none; margin: var(--sp-3) 0; padding: 0; }
1372ul.matchlist li { margin-bottom: var(--sp-3); }
1373ul.matchlist .matchpath { font-family: var(--mono); font-size: var(--fs-1); }
1374pre.matchline {
1375  background: var(--code-bg);
1376  border: 1px solid var(--faint);
1377  border-radius: var(--r-md);
1378  padding: var(--sp-1) var(--sp-3);
1379  margin: var(--sp-1) 0 0;
1380  overflow-x: auto;
1381}
1382pre.matchline mark {
1383  background: color-mix(in srgb, var(--warn) 25%, transparent);
1384  color: inherit;
1385  border-radius: 2px;
1386}
1387
1388/* wiki */
1389.wikilayout { display: flex; gap: var(--sp-5); align-items: flex-start; }
1390.wikipage { flex: 1; min-width: 0; }
1391.wikinav { flex: none; width: 13rem; padding-top: var(--sp-4); }
1392.wikinav ul { list-style: none; margin: var(--sp-1) 0 var(--sp-4); padding: 0; }
1393.wikinav li a { display: block; padding: 0.15rem 0; color: var(--fg); }
1394.wikinav li a:hover { color: var(--accent); text-decoration: none; }
1395.wikinav li a.active { color: var(--accent); font-weight: 550; }
1396@media (max-width: 40rem) {
1397  /* Column direction + the desktop align-items:flex-start lets .wikipage
1398     take its content's intrinsic width, overflowing the viewport (and
1399     triggering iOS font inflation). Stretch pins it to the container. */
1400  .wikilayout { flex-direction: column; align-items: stretch; }
1401  .wikinav { width: auto; padding-top: 0; }
1402}
1403
1404/* blame */
1405.blame {
1406  border: 1px solid var(--line);
1407  border-radius: var(--r-md);
1408  overflow: hidden;
1409}
1410.blamehunk {
1411  display: flex;
1412  gap: var(--sp-3);
1413  border-top: 1px solid var(--line);
1414  align-items: flex-start;
1415}
1416.blamehunk:first-child { border-top: none; }
1417.blameinfo {
1418  flex: none;
1419  width: 17rem;
1420  padding: var(--sp-2) var(--sp-3);
1421  min-width: 0;
1422}
1423.blameinfo .subject {
1424  margin: 0;
1425  font-size: var(--fs-1);
1426  white-space: nowrap;
1427  overflow: hidden;
1428  text-overflow: ellipsis;
1429}
1430.blameinfo .meta { margin: 0; font-size: var(--fs-1); color: var(--muted); }
1431.blamecode {
1432  flex: 1;
1433  margin: 0;
1434  padding: var(--sp-2) var(--sp-3) var(--sp-2) 0;
1435  overflow-x: auto;
1436  border-left: 1px solid var(--line);
1437  align-self: stretch;
1438}
1439.lineno {
1440  display: inline-block;
1441  min-width: 3.5em;
1442  padding-right: 1em;
1443  text-align: right;
1444  color: var(--muted);
1445  user-select: none;
1446}
1447
1448/* mobile */
1449@media (max-width: 40rem) {
1450  .thread { margin-left: var(--sp-3); }
1451  /* the commit subject is the widest thing in the listing and the one
1452     column a phone can do without: the tipbar above already names the
1453     latest commit. */
1454  table.tree td.lastcommit, table.tree th.lastcommit { display: none; }
1455  table.tree td.mode, table.tree th.mode { display: none; }
1456  .clone, .pathbar { overflow-wrap: anywhere; }
1457  .readme, .code { padding: var(--sp-3); }
1458  .blamehunk { flex-direction: column; gap: 0; }
1459  .blameinfo { width: auto; }
1460  .blamecode { border-left: none; padding-left: var(--sp-3); width: 100%; }
1461  .cardhead {
1462    margin: calc(-1 * var(--sp-3)) calc(-1 * var(--sp-3)) var(--sp-3);
1463    padding: var(--sp-2) var(--sp-3);
1464  }
1465}
1466
1467/* tree listing: the last commit is what a file table is scanned for, so
1468   it leads. The file's own facts moved to the blob page. */
1469.tipbar {
1470  display: flex;
1471  align-items: center;
1472  gap: var(--sp-3);
1473  padding: var(--sp-2) var(--sp-4);
1474  background: var(--hover);
1475  border: 1px solid var(--line);
1476  border-bottom: 0;
1477  border-radius: var(--r-lg) var(--r-lg) 0 0;
1478  font-size: var(--fs-2);
1479}
1480.tipbar .who { font-weight: 600; }
1481.tipbar .subject { color: var(--fg); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1482.tipbar .subject:hover { color: var(--accent); }
1483.tipbar .spacer { flex: 1; }
1484.tipbar .age { color: var(--muted); white-space: nowrap; }
1485.tipbar + table.tree { border-radius: 0 0 var(--r-lg) var(--r-lg); }
1486table.tree th.lastcommit, table.tree td.lastcommit { width: 55%; }
1487table.tree td.lastcommit a { color: var(--muted); display: block; overflow: hidden;
1488                             text-overflow: ellipsis; white-space: nowrap; }
1489table.tree td.lastcommit a:hover { color: var(--accent); }
1490table.tree th.age, table.tree td.age {
1491  text-align: right;
1492  white-space: nowrap;
1493  color: var(--muted);
1494  font-variant-numeric: tabular-nums;
1495}
1496table.tree td.name { width: 25%; white-space: nowrap; }
1497table.tree td.name.dir a { color: var(--accent); }
1498p.clone { margin: 0 0 var(--sp-3); }
1499p.filefacts { color: var(--muted); font-size: var(--fs-1); margin: 0 0 var(--sp-3); }
1500.pathbar .actions { font-size: var(--fs-1); color: var(--muted); }
1501
1502/* merge request: a two-column split, so state has somewhere to live that
1503   is not a run-on sentence under the title */
1504.withaside {
1505  display: grid;
1506  grid-template-columns: minmax(0, 1fr) 15rem;
1507  gap: var(--sp-5);
1508  align-items: start;
1509}
1510.withaside .mainside { min-width: 0; }
1511@media (max-width: 62rem) { .withaside { grid-template-columns: 1fr; } }
1512
1513.aside { font-size: var(--fs-2); }
1514.aside .grp { border-top: 1px solid var(--line); padding: var(--sp-3) 0; }
1515.aside .grp:first-child { border-top: 0; padding-top: 0; }
1516.aside h2 {
1517  margin: 0 0 var(--sp-2);
1518  font-family: var(--mono);
1519  font-size: var(--fs-0);
1520  font-weight: 500;
1521  letter-spacing: 0.1em;
1522  text-transform: uppercase;
1523  color: var(--muted);
1524}
1525.aside .row { display: flex; align-items: center; gap: var(--sp-2); margin: 0 0 var(--sp-1); flex-wrap: wrap; }
1526.aside .row:last-child { margin-bottom: 0; }
1527.aside .none { color: var(--muted); }
1528/* a row's second line: when it happened, under what it happened to. The
1529   extra gap keeps the pair reading as one entry. */
1530.aside .row .sub { flex-basis: 100%; color: var(--muted); font-size: var(--fs-0); }
1531.aside .row:has(.sub) { margin-bottom: var(--sp-2); }
1532.aside .dot { width: 0.45rem; height: 0.45rem; flex: none; border-radius: 50%; background: var(--muted); }
1533.aside .dot.ok { background: var(--ok); }
1534.aside .dot.bad { background: var(--bad); }
1535.aside .dot.pend { background: var(--mark); }
1536
1537/* sub-navigation within an item; orange marks position as it does above */
1538nav.subtabs {
1539  display: flex;
1540  gap: var(--sp-4);
1541  border-bottom: 1px solid var(--line);
1542  margin: 0 0 var(--sp-4);
1543}
1544nav.subtabs a {
1545  display: inline-flex;
1546  align-items: center;
1547  gap: var(--sp-2);
1548  padding: 0 0 var(--sp-2);
1549  color: var(--muted);
1550  font-size: var(--fs-2);
1551  border-bottom: 2px solid transparent;
1552  margin-bottom: -1px;
1553}
1554nav.subtabs a:hover { color: var(--fg); text-decoration: none; }
1555nav.subtabs a[aria-current] { color: var(--fg); font-weight: 700; border-bottom-color: var(--mark); }
1556nav.subtabs a i {
1557  font-style: normal;
1558  font-family: var(--mono);
1559  font-size: var(--fs-0);
1560  background: var(--hover);
1561  border-radius: var(--r-sm);
1562  padding: 0 0.35rem;
1563}
1564p.diffstat { color: var(--muted); font-size: var(--fs-2); margin: 0 0 var(--sp-3); }
1565p.diffstat .add { color: var(--ok); }
1566p.diffstat .del { color: var(--bad); }
1567
1568/* file edit form */
1569.editform textarea { width: 100%; }
1570.editform input[name="message"] { width: 60%; }