internal/web/static/style.css

251a71053c93d2621ad97fa7d1ce8efacb119406
gitbay/internal/web/static/style.css history · blame · raw

1568 lines · 54812 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.thread.stale { border-left-color: var(--warn); }
1198.thread p.commenthead { font-size: var(--fs-2); margin: var(--sp-2) 0 0; }
1199.thread .when { color: var(--muted); }
1200.thread .rendered p { margin: var(--sp-1) 0; }
1201.thread .threadstate { color: var(--muted); font-size: var(--fs-1); margin: var(--sp-1) 0 0; }
1202.thread.composing { border-left-color: var(--mark); }
1203.thread details.threadreply { margin: var(--sp-2) 0 0; }
1204.thread details.threadreply > summary { color: var(--accent); font-size: var(--fs-1); cursor: pointer; }
1205.thread textarea { width: 100%; }
1206.thread.composing p, .thread details.threadreply p { margin: var(--sp-2) 0 0; }
1207
1208/* forms: every control shares one shell, so a select reads as a sibling
1209   of the text inputs beside it rather than as an OS control dropped in */
1210input, textarea, button, select { font: inherit; color: inherit; }
1211input[type="text"], input[type="search"], input[type="email"],
1212input[type="password"], input:not([type]), textarea, select {
1213  background: var(--bg);
1214  color: var(--fg);
1215  border: 1px solid var(--line);
1216  border-radius: var(--r-md);
1217  padding: var(--sp-2) var(--sp-3);
1218  line-height: 1.4;
1219}
1220textarea { width: 100%; resize: vertical; }
1221textarea[name="content"] { font-family: var(--mono); font-size: var(--fs-1); }
1222/* the native arrow cannot be restyled, so draw our own and match the
1223   inputs exactly; --chev carries the stroke colour per scheme */
1224select {
1225  appearance: none;
1226  padding-right: 1.9rem;
1227  background-image: var(--chev);
1228  background-repeat: no-repeat;
1229  background-position: right 0.6rem center;
1230  background-size: 0.62rem;
1231  cursor: pointer;
1232}
1233input:hover, textarea:hover, select:hover { border-color: var(--muted); }
1234/* the global :focus-visible ring covers these; only the border moves */
1235input:focus, textarea:focus, select:focus { border-color: var(--accent); }
1236input[type="radio"] { accent-color: var(--fill); }
1237label:has(> input[type="radio"]), label:has(> input[type="checkbox"]),
1238label:has(> select) { display: inline-flex; align-items: center; gap: var(--sp-2); }
1239
1240/* A radio keeps the native control: a circle takes one answer, a square
1241   takes any number, and that shape is the cue that survives greyscale,
1242   high contrast and colour blindness. The checkbox is drawn instead,
1243   because at the size the OS renders it it reads as an afterthought
1244   beside the button it shares a row with. The square is kept square —
1245   the size changes, the cue does not. */
1246input[type="checkbox"] {
1247  appearance: none;
1248  flex: none;
1249  /* the height of a text input, a select and a button, so a settings row
1250     ends in one column of controls rather than a button and a speck */
1251  width: 2.5rem;
1252  height: 2.5rem;
1253  /* an empty box has nothing but its border to announce it, so the border
1254     is --muted rather than the --line the sized controls can afford */
1255  border: 1px solid var(--muted);
1256  border-radius: var(--r-sm);
1257  background: var(--bg);
1258  cursor: pointer;
1259}
1260input[type="checkbox"]:not(:checked):hover { border-color: var(--fg); }
1261input[type="checkbox"]:checked {
1262  background: var(--fill);
1263  border-color: var(--fill);
1264  /* the mark sits on the blue ground, so it is white in both schemes */
1265  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");
1266  background-repeat: no-repeat;
1267  background-position: center;
1268  background-size: 1.2rem;
1269}
1270
1271/* a segmented control for a small, mutually exclusive set: real radios
1272   underneath, so arrow keys and screen readers behave as they always did */
1273fieldset.segmented { border: 0; margin: 0 0 var(--sp-4); padding: 0; }
1274fieldset.segmented legend {
1275  padding: 0;
1276  margin-bottom: var(--sp-2);
1277  color: var(--muted);
1278  font-size: var(--fs-2);
1279}
1280fieldset.segmented .options { display: inline-flex; }
1281fieldset.segmented label { gap: 0; }
1282fieldset.segmented input {
1283  position: absolute;
1284  width: 1px;
1285  height: 1px;
1286  opacity: 0;
1287  pointer-events: none;
1288}
1289fieldset.segmented span {
1290  display: block;
1291  padding: var(--sp-2) var(--sp-4);
1292  border: 1px solid var(--line);
1293  font-size: var(--fs-2);
1294  cursor: pointer;
1295}
1296fieldset.segmented label + label span { border-left: 0; }
1297fieldset.segmented label:first-child span { border-radius: var(--r-sm) 0 0 var(--r-sm); }
1298fieldset.segmented label:last-child span { border-radius: 0 var(--r-sm) var(--r-sm) 0; }
1299fieldset.segmented span:hover { border-color: var(--muted); }
1300fieldset.segmented input:checked + span {
1301  background: var(--fill);
1302  border-color: var(--fill);
1303  color: #ffffff;
1304  font-weight: 600;
1305}
1306fieldset.segmented input:focus-visible + span {
1307  outline: 2px solid var(--accent);
1308  outline-offset: 2px;
1309}
1310
1311/* remaining input states */
1312::placeholder { color: var(--muted); opacity: 1; }
1313::selection { background: var(--fill); color: #ffffff; }
1314/* :user-invalid, not :invalid — a required field is invalid the moment
1315   the page loads, and colouring it red before anyone has typed is a
1316   scolding, not a hint */
1317input:user-invalid, textarea:user-invalid {
1318  border-color: var(--bad);
1319}
1320.hint { display: block; color: var(--muted); font-size: var(--fs-1); margin-top: var(--sp-1); }
1321button {
1322  background: var(--fill);
1323  color: #ffffff;
1324  border: 1px solid var(--fill-edge);
1325  border-radius: var(--r-md);
1326  padding: var(--sp-2) var(--sp-4);
1327  font-weight: 600;
1328  cursor: pointer;
1329}
1330button:hover { filter: brightness(1.15); }
1331
1332/* 404 */
1333.notfound { text-align: center; padding: var(--sp-6) 0; }
1334.notfound .bigcode {
1335  font-family: var(--mono);
1336  font-size: var(--fs-7);
1337  color: var(--faint);
1338  -webkit-text-stroke: 1px var(--line);
1339  margin: 0;
1340  line-height: 1;
1341}
1342.notfound h1 { margin-top: var(--sp-3); }
1343
1344/* footer */
1345footer {
1346  border-top: 1px solid var(--faint);
1347  padding: var(--sp-4) var(--sp-5) var(--sp-5);
1348  width: 100%;
1349  color: var(--muted);
1350  font-size: var(--fs-1);
1351}
1352footer p { margin: 0; }
1353footer a { color: var(--muted); text-decoration: underline; }
1354footer a:hover { color: var(--accent); }
1355
1356/* search */
1357form.searchform {
1358  display: flex;
1359  gap: var(--sp-2);
1360  margin: var(--sp-3) 0 var(--sp-4);
1361}
1362form.searchform input[type="search"] { flex: 1; max-width: 32rem; }
1363form.searchform.compact { margin: 0; }
1364form.searchform.compact input[type="search"] {
1365  padding: var(--sp-1) var(--sp-3);
1366  font-size: var(--fs-2);
1367  min-width: 18rem;
1368}
1369ul.matchlist { list-style: none; margin: var(--sp-3) 0; padding: 0; }
1370ul.matchlist li { margin-bottom: var(--sp-3); }
1371ul.matchlist .matchpath { font-family: var(--mono); font-size: var(--fs-1); }
1372pre.matchline {
1373  background: var(--code-bg);
1374  border: 1px solid var(--faint);
1375  border-radius: var(--r-md);
1376  padding: var(--sp-1) var(--sp-3);
1377  margin: var(--sp-1) 0 0;
1378  overflow-x: auto;
1379}
1380pre.matchline mark {
1381  background: color-mix(in srgb, var(--warn) 25%, transparent);
1382  color: inherit;
1383  border-radius: 2px;
1384}
1385
1386/* wiki */
1387.wikilayout { display: flex; gap: var(--sp-5); align-items: flex-start; }
1388.wikipage { flex: 1; min-width: 0; }
1389.wikinav { flex: none; width: 13rem; padding-top: var(--sp-4); }
1390.wikinav ul { list-style: none; margin: var(--sp-1) 0 var(--sp-4); padding: 0; }
1391.wikinav li a { display: block; padding: 0.15rem 0; color: var(--fg); }
1392.wikinav li a:hover { color: var(--accent); text-decoration: none; }
1393.wikinav li a.active { color: var(--accent); font-weight: 550; }
1394@media (max-width: 40rem) {
1395  /* Column direction + the desktop align-items:flex-start lets .wikipage
1396     take its content's intrinsic width, overflowing the viewport (and
1397     triggering iOS font inflation). Stretch pins it to the container. */
1398  .wikilayout { flex-direction: column; align-items: stretch; }
1399  .wikinav { width: auto; padding-top: 0; }
1400}
1401
1402/* blame */
1403.blame {
1404  border: 1px solid var(--line);
1405  border-radius: var(--r-md);
1406  overflow: hidden;
1407}
1408.blamehunk {
1409  display: flex;
1410  gap: var(--sp-3);
1411  border-top: 1px solid var(--line);
1412  align-items: flex-start;
1413}
1414.blamehunk:first-child { border-top: none; }
1415.blameinfo {
1416  flex: none;
1417  width: 17rem;
1418  padding: var(--sp-2) var(--sp-3);
1419  min-width: 0;
1420}
1421.blameinfo .subject {
1422  margin: 0;
1423  font-size: var(--fs-1);
1424  white-space: nowrap;
1425  overflow: hidden;
1426  text-overflow: ellipsis;
1427}
1428.blameinfo .meta { margin: 0; font-size: var(--fs-1); color: var(--muted); }
1429.blamecode {
1430  flex: 1;
1431  margin: 0;
1432  padding: var(--sp-2) var(--sp-3) var(--sp-2) 0;
1433  overflow-x: auto;
1434  border-left: 1px solid var(--line);
1435  align-self: stretch;
1436}
1437.lineno {
1438  display: inline-block;
1439  min-width: 3.5em;
1440  padding-right: 1em;
1441  text-align: right;
1442  color: var(--muted);
1443  user-select: none;
1444}
1445
1446/* mobile */
1447@media (max-width: 40rem) {
1448  .thread { margin-left: var(--sp-3); }
1449  /* the commit subject is the widest thing in the listing and the one
1450     column a phone can do without: the tipbar above already names the
1451     latest commit. */
1452  table.tree td.lastcommit, table.tree th.lastcommit { display: none; }
1453  table.tree td.mode, table.tree th.mode { display: none; }
1454  .clone, .pathbar { overflow-wrap: anywhere; }
1455  .readme, .code { padding: var(--sp-3); }
1456  .blamehunk { flex-direction: column; gap: 0; }
1457  .blameinfo { width: auto; }
1458  .blamecode { border-left: none; padding-left: var(--sp-3); width: 100%; }
1459  .cardhead {
1460    margin: calc(-1 * var(--sp-3)) calc(-1 * var(--sp-3)) var(--sp-3);
1461    padding: var(--sp-2) var(--sp-3);
1462  }
1463}
1464
1465/* tree listing: the last commit is what a file table is scanned for, so
1466   it leads. The file's own facts moved to the blob page. */
1467.tipbar {
1468  display: flex;
1469  align-items: center;
1470  gap: var(--sp-3);
1471  padding: var(--sp-2) var(--sp-4);
1472  background: var(--hover);
1473  border: 1px solid var(--line);
1474  border-bottom: 0;
1475  border-radius: var(--r-lg) var(--r-lg) 0 0;
1476  font-size: var(--fs-2);
1477}
1478.tipbar .who { font-weight: 600; }
1479.tipbar .subject { color: var(--fg); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1480.tipbar .subject:hover { color: var(--accent); }
1481.tipbar .spacer { flex: 1; }
1482.tipbar .age { color: var(--muted); white-space: nowrap; }
1483.tipbar + table.tree { border-radius: 0 0 var(--r-lg) var(--r-lg); }
1484table.tree th.lastcommit, table.tree td.lastcommit { width: 55%; }
1485table.tree td.lastcommit a { color: var(--muted); display: block; overflow: hidden;
1486                             text-overflow: ellipsis; white-space: nowrap; }
1487table.tree td.lastcommit a:hover { color: var(--accent); }
1488table.tree th.age, table.tree td.age {
1489  text-align: right;
1490  white-space: nowrap;
1491  color: var(--muted);
1492  font-variant-numeric: tabular-nums;
1493}
1494table.tree td.name { width: 25%; white-space: nowrap; }
1495table.tree td.name.dir a { color: var(--accent); }
1496p.clone { margin: 0 0 var(--sp-3); }
1497p.filefacts { color: var(--muted); font-size: var(--fs-1); margin: 0 0 var(--sp-3); }
1498.pathbar .actions { font-size: var(--fs-1); color: var(--muted); }
1499
1500/* merge request: a two-column split, so state has somewhere to live that
1501   is not a run-on sentence under the title */
1502.withaside {
1503  display: grid;
1504  grid-template-columns: minmax(0, 1fr) 15rem;
1505  gap: var(--sp-5);
1506  align-items: start;
1507}
1508.withaside .mainside { min-width: 0; }
1509@media (max-width: 62rem) { .withaside { grid-template-columns: 1fr; } }
1510
1511.aside { font-size: var(--fs-2); }
1512.aside .grp { border-top: 1px solid var(--line); padding: var(--sp-3) 0; }
1513.aside .grp:first-child { border-top: 0; padding-top: 0; }
1514.aside h2 {
1515  margin: 0 0 var(--sp-2);
1516  font-family: var(--mono);
1517  font-size: var(--fs-0);
1518  font-weight: 500;
1519  letter-spacing: 0.1em;
1520  text-transform: uppercase;
1521  color: var(--muted);
1522}
1523.aside .row { display: flex; align-items: center; gap: var(--sp-2); margin: 0 0 var(--sp-1); flex-wrap: wrap; }
1524.aside .row:last-child { margin-bottom: 0; }
1525.aside .none { color: var(--muted); }
1526/* a row's second line: when it happened, under what it happened to. The
1527   extra gap keeps the pair reading as one entry. */
1528.aside .row .sub { flex-basis: 100%; color: var(--muted); font-size: var(--fs-0); }
1529.aside .row:has(.sub) { margin-bottom: var(--sp-2); }
1530.aside .dot { width: 0.45rem; height: 0.45rem; flex: none; border-radius: 50%; background: var(--muted); }
1531.aside .dot.ok { background: var(--ok); }
1532.aside .dot.bad { background: var(--bad); }
1533.aside .dot.pend { background: var(--mark); }
1534
1535/* sub-navigation within an item; orange marks position as it does above */
1536nav.subtabs {
1537  display: flex;
1538  gap: var(--sp-4);
1539  border-bottom: 1px solid var(--line);
1540  margin: 0 0 var(--sp-4);
1541}
1542nav.subtabs a {
1543  display: inline-flex;
1544  align-items: center;
1545  gap: var(--sp-2);
1546  padding: 0 0 var(--sp-2);
1547  color: var(--muted);
1548  font-size: var(--fs-2);
1549  border-bottom: 2px solid transparent;
1550  margin-bottom: -1px;
1551}
1552nav.subtabs a:hover { color: var(--fg); text-decoration: none; }
1553nav.subtabs a[aria-current] { color: var(--fg); font-weight: 700; border-bottom-color: var(--mark); }
1554nav.subtabs a i {
1555  font-style: normal;
1556  font-family: var(--mono);
1557  font-size: var(--fs-0);
1558  background: var(--hover);
1559  border-radius: var(--r-sm);
1560  padding: 0 0.35rem;
1561}
1562p.diffstat { color: var(--muted); font-size: var(--fs-2); margin: 0 0 var(--sp-3); }
1563p.diffstat .add { color: var(--ok); }
1564p.diffstat .del { color: var(--bad); }
1565
1566/* file edit form */
1567.editform textarea { width: 100%; }
1568.editform input[name="message"] { width: 60%; }