internal/web/static/style.css

3f4afd2210627e46fd0cea1f5831857af71638f6
gitbay/internal/web/static/style.css history · blame · raw

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