internal/web/static/style.css

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

1573 lines · 55151 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; }
 370input[name="confirm"] { width: auto; margin-right: var(--sp-2); }
 371
 372nav.tabs {
 373  display: flex;
 374  gap: var(--sp-1);
 375  margin-top: var(--sp-4);
 376  overflow-x: auto;
 377  /* with overflow-y left visible the browser computes it to auto, which
 378     is a vertical drag on touch; the strip scrolls sideways only. */
 379  overflow-y: hidden;
 380  scrollbar-width: none;
 381}
 382nav.tabs::-webkit-scrollbar { display: none; }
 383nav.tabs a {
 384  display: inline-flex;
 385  align-items: center;
 386  gap: var(--sp-2);
 387  padding: var(--sp-2) var(--sp-3) 0.6rem;
 388  color: var(--muted);
 389  font-size: var(--fs-2);
 390  white-space: nowrap;
 391  border-bottom: 2px solid transparent;
 392  margin-bottom: -1px;
 393}
 394nav.tabs a:hover { color: var(--fg); text-decoration: none; }
 395/* orange marks position here too */
 396nav.tabs a[aria-current] {
 397  color: var(--fg);
 398  font-weight: 700;
 399  border-bottom-color: var(--mark);
 400}
 401nav.tabs a i {
 402  font-style: normal;
 403  font-family: var(--mono);
 404  font-size: var(--fs-0);
 405  background: var(--hover);
 406  border-radius: var(--r-sm);
 407  padding: 0 0.35rem;
 408}
 409
 410/* ---- mobile: the rail becomes a single strip, not a wall of links ---- */
 411@media (max-width: 52rem) {
 412  .shell { display: block; }
 413  .rail {
 414    width: auto;
 415    height: auto;
 416    position: static;
 417    flex-direction: row;
 418    align-items: center;
 419    gap: var(--sp-2);
 420    border-right: 0;
 421    border-bottom: 1px solid var(--shell-line);
 422    overflow-x: auto;
 423  }
 424  a.brand { border-bottom: 0; padding-right: var(--sp-2); }
 425  .railbody { flex: 1; overflow: visible; padding: 0; display: flex; align-items: center; }
 426  form.railsearch { padding: 0 var(--sp-2); }
 427  form.railsearch input[type="search"] { min-width: 8rem; }
 428  /* pinned repos and the review queue both live on the dashboard, so the
 429     strip keeps only what has nowhere else to go */
 430  .railgroup { display: none; }
 431  ul.raillist { display: flex; }
 432  ul.raillist a { border-left: 0; border-bottom: 2px solid transparent; padding: var(--sp-2) var(--sp-3); }
 433  ul.raillist a[aria-current] { border-left: 0; border-bottom-color: var(--shell-mark); }
 434  /* the rail is a horizontal strip here: keep the account cell compact */
 435  .railfoot { border-top: 0; padding: var(--sp-2); }
 436  .railpinned { border-top: 0; padding: 0; }
 437  .content { padding: var(--sp-4) var(--sp-4) var(--sp-5); }
 438  .repohead { padding: var(--sp-3) var(--sp-4) 0; }
 439  /* seven tabs do not fit a phone in one row; a hidden scrollbar made
 440     the ones past the edge look absent. Two rows show all of them. */
 441  nav.tabs { flex-wrap: wrap; overflow: visible; }
 442}
 443
 444/* tables */
 445table { border-collapse: collapse; width: 100%; }
 446td {
 447  padding: var(--sp-2) var(--sp-4) var(--sp-2) 0;
 448  border-bottom: 1px solid var(--faint);
 449  vertical-align: top;
 450  font-size: var(--fs-2);
 451}
 452td:last-child { padding-right: 0; }
 453td.size, td.mode { color: var(--muted); white-space: nowrap; }
 454
 455/* path bar on tree and blob pages */
 456.pathbar {
 457  display: flex;
 458  flex-wrap: wrap;
 459  align-items: center;
 460  gap: var(--sp-2) var(--sp-3);
 461  margin: var(--sp-3) 0;
 462  font-size: var(--fs-2);
 463}
 464.pathbar .spacer { flex: 1; }
 465.refchip {
 466  display: inline-flex;
 467  align-items: center;
 468  gap: 0.3rem;
 469  background: var(--surface);
 470  border: 1px solid var(--faint);
 471  border-radius: var(--r-md);
 472  padding: 0.1rem 0.55rem;
 473  font-family: var(--mono);
 474  font-size: var(--fs-1);
 475  color: var(--fg);
 476}
 477.pathbar .crumbs strong { color: var(--fg); }
 478details.refmenu { position: relative; }
 479details.refmenu summary { cursor: pointer; list-style: none; }
 480details.refmenu summary::-webkit-details-marker { display: none; }
 481details.refmenu summary::after { content: " ▾"; color: var(--muted); }
 482details.refmenu .refdrop {
 483  position: absolute;
 484  z-index: 10;
 485  top: calc(100% + 4px);
 486  left: 0;
 487  min-width: 12rem;
 488  max-height: 20rem;
 489  overflow-y: auto;
 490  background: var(--bg);
 491  border: 1px solid var(--line);
 492  border-radius: var(--r-md);
 493  box-shadow: var(--shadow);
 494  padding: var(--sp-1) 0;
 495}
 496details.refmenu .refdrop a {
 497  display: block;
 498  padding: var(--sp-1) var(--sp-3);
 499  color: var(--fg);
 500  font-family: var(--mono);
 501  font-size: var(--fs-1);
 502}
 503details.refmenu .refdrop a:hover { background: var(--hover); text-decoration: none; }
 504details.refmenu .refdrop a.allrefs { color: var(--accent); font-family: var(--sans); border-top: 1px solid var(--faint); margin-top: var(--sp-1); }
 505
 506/* file and refs tables: bordered cards, rows inside */
 507/* wide listings scroll inside their own box; the page never does */
 508.tablewrap { overflow-x: auto; max-width: 100%; }
 509
 510table.tree, table.refs {
 511  background: var(--surface);
 512  border: 1px solid var(--line);
 513  border-radius: var(--r-lg);
 514  border-collapse: separate;
 515  border-spacing: 0;
 516  overflow: hidden;
 517  margin-bottom: var(--sp-4);
 518}
 519table.tree td, table.refs td {
 520  padding: var(--sp-2) var(--sp-4);
 521  border-bottom: 1px solid var(--faint);
 522}
 523table.tree tr:last-child td, table.refs tr:last-child td { border-bottom: none; }
 524table.tree tr:hover td, table.refs tr:hover td { background: var(--hover); }
 525tr.cols th {
 526  text-align: left;
 527  font-weight: 500;
 528  font-size: var(--fs-1);
 529  color: var(--muted);
 530  padding: var(--sp-1) var(--sp-4);
 531  border-bottom: 1px solid var(--line);
 532  background: var(--bg);
 533}
 534tr.cols:hover th { background: var(--bg); }
 535/* right-aligned columns keep their header with their values; this needs to
 536   outrank the tr.cols th rule above, which is why it names both */
 537tr.cols th.age, tr.cols th.size { text-align: right; }
 538table.tree th.size { text-align: right; }
 539table.tree td.name { width: 100%; }
 540table.tree td.name a { color: var(--fg); }
 541table.tree td.name a:hover { color: var(--accent); }
 542table.tree td.size { text-align: right; }
 543table.tree td.empty, table.refs td.empty, li.empty, ul.loglist li.empty { color: var(--muted); }
 544
 545/* refs page */
 546table.refs th, table.assets th {
 547  text-align: left;
 548  font-size: var(--fs-0);
 549  font-weight: 500;
 550  color: var(--muted);
 551  padding: var(--sp-1) var(--sp-2);
 552  border-bottom: 1px solid var(--line);
 553}
 554table.refs td.name { width: 100%; }
 555table.refs td.sha { color: var(--muted); }
 556
 557/* card header (README) */
 558.cardhead {
 559  font-family: var(--mono);
 560  font-size: var(--fs-1);
 561  color: var(--muted);
 562  border-bottom: 1px solid var(--faint);
 563  margin: calc(-1 * var(--sp-4)) calc(-1 * var(--sp-5)) var(--sp-4);
 564  padding: var(--sp-2) var(--sp-5);
 565  background: var(--bg);
 566  border-radius: var(--r-lg) var(--r-lg) 0 0;
 567}
 568
 569/* log */
 570ul.loglist {
 571  list-style: none;
 572  margin: var(--sp-3) 0;
 573  padding: 0;
 574  background: var(--surface);
 575  border: 1px solid var(--line);
 576  border-radius: var(--r-lg);
 577  overflow: hidden;
 578}
 579ul.loglist li {
 580  display: flex;
 581  align-items: baseline;
 582  gap: var(--sp-3);
 583  padding: var(--sp-2) var(--sp-4);
 584  border-bottom: 1px solid var(--faint);
 585}
 586ul.loglist li:last-child { border-bottom: none; }
 587ul.loglist li:hover { background: var(--hover); }
 588ul.loglist .commitmain { flex: 1; min-width: 0; }
 589ul.loglist p { margin: 0; }
 590ul.loglist .subject a { color: var(--fg); font-weight: 550; }
 591ul.loglist .subject a:hover { color: var(--accent); }
 592ul.loglist .commitside {
 593  display: flex;
 594  align-items: baseline;
 595  gap: var(--sp-2);
 596  white-space: nowrap;
 597}
 598p.pager { margin-top: var(--sp-4); }
 599
 600/* commit page */
 601.commithead p { margin: var(--sp-1) 0; }
 602.commithead h2 { margin-bottom: var(--sp-1); }
 603code.fullsha { color: var(--muted); overflow-wrap: anywhere; }
 604.empty-note { color: var(--muted); }
 605.crumbs { color: var(--muted); font-size: var(--fs-2); }
 606.desc, td.desc { color: var(--muted); }
 607.error {
 608  color: var(--fg);
 609  border: 1px solid var(--bad);
 610  border-left-width: 3px;
 611  border-radius: var(--r-sm);
 612  padding: var(--sp-2) var(--sp-3);
 613  margin-bottom: var(--sp-4);
 614}
 615/* settings: one row per control, label then input then its own button */
 616form.setform {
 617  display: flex;
 618  flex-wrap: wrap;
 619  align-items: center;
 620  gap: var(--sp-3);
 621  padding: var(--sp-2) 0;
 622  border-bottom: 1px solid var(--faint);
 623}
 624/* the label absorbs the free space, so a row whose control does not
 625   stretch — a checkbox, a number — still finishes at the same right edge
 626   as the rows whose input fills the line */
 627form.setform label { min-width: 12rem; margin-right: auto; color: var(--muted); font-size: var(--fs-2); }
 628form.setform input[type="text"], form.setform select { flex: 1 1 16rem; }
 629form.setform input[type="number"] { width: 5rem; }
 630ul.protlist { list-style: none; padding: 0; margin: var(--sp-2) 0; }
 631ul.protlist li { padding: var(--sp-1) 0; display: flex; gap: var(--sp-3); align-items: baseline; }
 632
 633/* branch picker on the new merge request form */
 634p.branchpick {
 635  display: flex;
 636  flex-wrap: wrap;
 637  align-items: center;
 638  gap: var(--sp-2);
 639}
 640p.branchpick label { color: var(--muted); font-size: var(--fs-2); }
 641p.branchpick select { flex: 1 1 12rem; }
 642
 643/* merge request actions in the aside: stacked controls, full width */
 644.aside form.actions {
 645  display: flex;
 646  flex-wrap: wrap;
 647  gap: var(--sp-2);
 648  margin-top: var(--sp-2);
 649}
 650.aside form.actions button { flex: 1 1 auto; }
 651.aside form.actions select { width: 100%; }
 652form.threadact { margin: var(--sp-1) 0 0; padding: 0 var(--sp-3) var(--sp-2); }
 653
 654.meta { color: var(--muted); font-size: var(--fs-1); }
 655svg.icon { vertical-align: -0.125em; }
 656.lede { font-size: var(--fs-3); margin: var(--sp-2) 0; }
 657
 658/* repo listings (explore, owner pages, dashboard): one row per repo */
 659ul.repolist {
 660  list-style: none;
 661  margin: var(--sp-3) 0 var(--sp-5);
 662  padding: 0;
 663  background: var(--surface);
 664  border: 1px solid var(--line);
 665  border-radius: var(--r-lg);
 666  overflow: hidden;
 667}
 668ul.repolist li { padding: var(--sp-3) var(--sp-4); border-bottom: 1px solid var(--faint); }
 669/* pinned repos: a compact wrap of name pills, no description or meta */
 670ul.pinlist {
 671  list-style: none;
 672  margin: var(--sp-3) 0 var(--sp-5);
 673  padding: 0;
 674  display: flex;
 675  flex-wrap: wrap;
 676  gap: var(--sp-2);
 677}
 678ul.pinlist li {
 679  background: var(--surface);
 680  border: 1px solid var(--line);
 681  border-radius: var(--r-md);
 682  padding: var(--sp-1) var(--sp-3);
 683}
 684ul.pinlist li:hover { background: var(--hover); }
 685ul.pinlist li a { color: var(--fg); }
 686ul.pinlist li a:hover { color: var(--accent); text-decoration: none; }
 687ul.pinlist .sep { color: var(--muted); margin: 0 0.15em; }
 688ul.repolist li:last-child { border-bottom: none; }
 689ul.repolist li:hover { background: var(--hover); }
 690ul.repolist li.empty { color: var(--muted); }
 691ul.repolist p { margin: 0; }
 692ul.repolist .reponame { font-size: var(--fs-3); }
 693ul.repolist .reponame a { color: var(--fg); }
 694ul.repolist .reponame a strong { color: var(--accent); font-weight: 600; }
 695ul.repolist .reponame .sep { color: var(--muted); margin: 0 0.1em; }
 696ul.repolist .desc { margin-top: 0.1rem; }
 697ul.repolist .topics { margin-top: var(--sp-1); }
 698ul.repolist .meta { margin-top: var(--sp-1); }
 699
 700/* owner profile header */
 701.profilehead {
 702  border-bottom: 1px solid var(--faint);
 703  padding-bottom: var(--sp-4);
 704  margin-bottom: var(--sp-4);
 705}
 706.profilehead h1 { margin: var(--sp-4) 0 var(--sp-1); }
 707.profilehead .lede { margin: 0 0 var(--sp-1); }
 708.profilehead .meta { margin: var(--sp-1) 0 0; }
 709h2 .count { color: var(--muted); font-weight: 400; font-size: var(--fs-2); }
 710
 711/* activity graph: 53 week columns x 7 day rows, accent-tinted levels */
 712.actgraph-scroll { overflow-x: auto; padding-bottom: var(--sp-1); }
 713.actgraph { display: flex; gap: 3px; width: max-content; }
 714.actweek { display: flex; flex-direction: column; gap: 3px; }
 715.actday {
 716  width: 11px;
 717  height: 11px;
 718  border-radius: 2px;
 719  background: var(--faint);
 720}
 721.actday.pad { background: transparent; }
 722.actday.l1 { background: color-mix(in srgb, var(--accent) 25%, var(--bg)); }
 723.actday.l2 { background: color-mix(in srgb, var(--accent) 50%, var(--bg)); }
 724.actday.l3 { background: color-mix(in srgb, var(--accent) 75%, var(--bg)); }
 725.actday.l4 { background: var(--accent); }
 726
 727/* heading row with actions on the right */
 728.headrow {
 729  display: flex;
 730  align-items: baseline;
 731  gap: var(--sp-4);
 732  flex-wrap: wrap;
 733}
 734.headrow .spacer { flex: 1; }
 735
 736/* member and org chips on owner pages */
 737a.memberchip {
 738  display: inline-block;
 739  border: 1px solid var(--line);
 740  border-radius: var(--r-pill);
 741  padding: 0.05rem 0.6rem;
 742  color: var(--fg);
 743  font-size: var(--fs-1);
 744  margin: 0 0.15rem 0.15rem 0;
 745}
 746a.memberchip:hover { text-decoration: none; border-color: var(--accent); color: var(--accent); }
 747a.memberchip .role { color: var(--muted); }
 748
 749/* landing page */
 750.landing { max-width: 44rem; padding: var(--sp-6) 0; }
 751.landing h1 { font-size: var(--fs-6); margin-bottom: var(--sp-3); }
 752.landing .lede { font-size: var(--fs-4); color: var(--muted); line-height: 1.7; margin: 0 0 var(--sp-4); }
 753pre.quickstart {
 754  background: var(--code-bg);
 755  border: 1px solid var(--line);
 756  border-radius: var(--r-md);
 757  padding: var(--sp-3) var(--sp-4);
 758  overflow-x: auto;
 759  margin: 0 0 var(--sp-4);
 760}
 761.landing .explorelink { font-weight: 550; }
 762.landing .facets { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--sp-5); margin: var(--sp-5) 0; }
 763.landing .facets h2 { font-size: var(--fs-3); margin: 0 0 var(--sp-1); }
 764.landing .facets p { margin: 0; color: var(--muted); font-size: var(--fs-2); line-height: 1.6; }
 765@media (max-width: 40rem) { .landing .facets { grid-template-columns: 1fr; gap: var(--sp-4); } }
 766
 767/* index toolbar */
 768p.toolbar { color: var(--muted); font-size: var(--fs-2); margin-top: var(--sp-4); }
 769button.linklike {
 770  background: none;
 771  border: none;
 772  color: var(--accent);
 773  padding: 0;
 774  font: inherit;
 775  font-size: var(--fs-2);
 776  cursor: pointer;
 777}
 778button.linklike:hover { text-decoration: underline; filter: none; }
 779
 780/* cards: README, comments */
 781.readme, .code {
 782  background: var(--surface);
 783  border: 1px solid var(--line);
 784  border-radius: var(--r-lg);
 785  padding: var(--sp-4) var(--sp-5);
 786  margin-top: var(--sp-4);
 787  overflow-x: auto;
 788}
 789/* Highlighting is class-based with a palette per scheme, so code blocks
 790   follow the site's own colors. */
 791.code { background: var(--code-bg); color: var(--fg); padding: var(--sp-3) var(--sp-4); border-color: var(--line); }
 792.code pre { margin: 0; background: transparent !important; }
 793/* rendered markdown / org / html */
 794.rendered { max-width: 100%; overflow-wrap: anywhere; }
 795/* A linked line: #f2-n17 on a diff, #L17 on a file (#118). */
 796.difftable tr:target td, .code .line:has(a:target) { background: color-mix(in srgb, var(--mark) 18%, transparent); }
 797.difftable tr:target td.ln, .code .line:has(a:target) .ln { border-left: 3px solid var(--mark); }
 798.thread:target { outline: 2px solid var(--mark); outline-offset: 2px; }
 799/* GFM task lists: the box sits in the line, sized to the prose. */
 800.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); }
 801.rendered li:has(> input[type="checkbox"]) { list-style: none; margin-left: -1.2em; }
 802/* line length is a reading constraint, so it applies to prose only —
 803   tables, code blocks and images keep the full column */
 804.rendered > p,
 805.rendered > ul,
 806.rendered > ol,
 807.rendered > blockquote { max-width: 78ch; }
 808.rendered pre {
 809  background: var(--code-bg);
 810  border: 1px solid var(--line);
 811  padding: var(--sp-3) var(--sp-4);
 812  border-radius: var(--r-md);
 813  overflow-x: auto;
 814}
 815.rendered code {
 816  background: var(--code-bg);
 817  border: 1px solid var(--line);
 818  padding: 0.05rem 0.3rem;
 819  border-radius: var(--r-sm);
 820}
 821.rendered pre code { background: transparent; border: none; padding: 0; }
 822.rendered blockquote {
 823  border-left: 3px solid var(--line);
 824  margin-left: 0;
 825  padding-left: var(--sp-4);
 826  color: var(--muted);
 827}
 828.rendered img { max-width: 100%; }
 829.rendered h1, .rendered h2 { border-bottom: 1px solid var(--faint); padding-bottom: var(--sp-1); }
 830.rendered table {
 831  border-collapse: collapse;
 832  margin: var(--sp-3) 0;
 833  display: block;
 834  max-width: 100%;
 835  overflow-x: auto;
 836}
 837.rendered th, .rendered td {
 838  border: 1px solid var(--line);
 839  padding: var(--sp-1) var(--sp-3);
 840  text-align: left;
 841  overflow-wrap: normal; /* scroll the table, don't shatter words */
 842}
 843.rendered th { background: var(--surface); font-weight: 600; }
 844.blobimage { text-align: center; }
 845.blobimage img {
 846  max-width: 100%;
 847  border: 1px solid var(--faint);
 848  border-radius: var(--r-md);
 849  background: var(--surface);
 850}
 851
 852pre.message {
 853  background: var(--code-bg);
 854  border: 1px solid var(--line);
 855  padding: var(--sp-3) var(--sp-4);
 856  border-radius: var(--r-md);
 857  white-space: pre-wrap;
 858  overflow-wrap: anywhere;
 859}
 860
 861/* repo facts: the counts a visitor sizes a project up with */
 862.teambody { padding: var(--sp-3); background: var(--bg); }
 863.teambody .memberchip { margin-right: var(--sp-2); }
 864
 865form.logfilter {
 866  display: flex; align-items: center; gap: var(--sp-2);
 867  margin: 0 0 var(--sp-4); flex-wrap: wrap;
 868}
 869form.logfilter input { flex: 1; min-width: 12rem; max-width: 28rem; }
 870
 871/* account settings */
 872table.keys td { padding: var(--sp-2) var(--sp-4) var(--sp-2) 0; }
 873/* these tables sit flush with the page text, so the header row drops the
 874   left padding the card-style listings use */
 875table.keys tr.cols th { padding-left: 0; background: transparent; }
 876table.keys td.mono, .mono { font-family: var(--mono); font-size: var(--fs-1); overflow-wrap: anywhere; }
 877table.keys td.act { text-align: right; }
 878ul.plain { list-style: none; margin: 0 0 var(--sp-3); padding: 0; }
 879ul.plain li { padding: var(--sp-1) 0; display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
 880form.setform.stack { display: block; }
 881form.setform.stack textarea, form.setform.stack select { width: 100%; max-width: 48rem; display: block; margin-bottom: var(--sp-2); }
 882p.notice {
 883  border-left: 3px solid var(--ok);
 884  background: var(--hover);
 885  padding: var(--sp-2) var(--sp-3);
 886  margin: 0 0 var(--sp-4);
 887}
 888
 889.facts { margin: 0 0 var(--sp-4); }
 890.facts .counts {
 891  display: flex; flex-wrap: wrap; gap: var(--sp-1) var(--sp-4);
 892  margin: 0 0 var(--sp-3); font-size: var(--fs-2);
 893}
 894.facts .counts a { color: var(--fg); text-decoration: none; }
 895.facts .counts a:hover { color: var(--accent); text-decoration: underline; }
 896.facts .counts .fact { color: var(--muted); }
 897.facts .counts strong { font-weight: 600; }
 898
 899.langbar {
 900  display: flex; height: 0.5rem; margin: 0 0 var(--sp-2);
 901  border-radius: var(--r-sm); overflow: hidden; background: var(--faint);
 902}
 903.langbar .lang { display: block; min-width: 2px; }
 904.langs {
 905  display: flex; flex-wrap: wrap; gap: var(--sp-1) var(--sp-4);
 906  margin: 0 0 var(--sp-3); font-size: var(--fs-1);
 907}
 908.langs .lang-name { display: inline-flex; align-items: center; gap: var(--sp-1); }
 909.langs .muted { color: var(--muted); }
 910.langs .dot { width: 0.6rem; height: 0.6rem; border-radius: 50%; display: inline-block; }
 911/* named where it reads as the language's own color, neutral otherwise */
 912.lang { background: var(--neutral); }
 913.lang-go { background: #00add8; }
 914.lang-rust { background: #dea584; }
 915.lang-c, .lang-c- { background: #555555; }
 916.lang-python { background: #3572a5; }
 917.lang-javascript { background: #f1e05a; }
 918.lang-typescript { background: #3178c6; }
 919.lang-shell { background: #89e051; }
 920.lang-ruby { background: #701516; }
 921.lang-java { background: #b07219; }
 922.lang-swift { background: #f05138; }
 923.lang-html { background: #e34c26; }
 924.lang-css { background: #563d7c; }
 925.lang-org { background: #77aa99; }
 926.lang-markdown { background: #083fa1; }
 927.lang-emacs-lisp { background: #c065db; }
 928.lang-nix { background: #7e7eff; }
 929.lang-sql { background: #e38c00; }
 930
 931.contribs {
 932  display: flex; flex-wrap: wrap; align-items: baseline;
 933  gap: var(--sp-1) var(--sp-3); margin: 0; font-size: var(--fs-1);
 934}
 935.contribs .label { color: var(--muted); }
 936
 937/* diffs: one foldable section per file, a table so the line-number
 938   gutters stay put while the code scrolls */
 939details.difffold {
 940  border: 1px solid var(--line);
 941  border-radius: var(--r-md);
 942  margin: 0 0 var(--sp-3);
 943  background: var(--code-bg);
 944}
 945details.difffold summary {
 946  cursor: pointer;
 947  display: flex;
 948  align-items: baseline;
 949  gap: var(--sp-3);
 950  padding: var(--sp-2) var(--sp-3);
 951  list-style: none;
 952  font-size: var(--fs-2);
 953}
 954details.difffold[open] summary { border-bottom: 1px solid var(--line); }
 955details.difffold summary::-webkit-details-marker { display: none; }
 956/* flex summaries lose the native disclosure marker; draw our own */
 957details.difffold summary::before { content: "\25b8"; color: var(--muted); }
 958details.difffold[open] summary::before { content: "\25be"; }
 959details.difffold summary:hover { background: var(--fill-subtle); }
 960summary .fpath { font-family: var(--mono); overflow-wrap: anywhere; }
 961summary .fpath .was { color: var(--muted); }
 962summary .fstat { margin-left: auto; font-family: var(--mono); white-space: nowrap; }
 963details.difffold summary .add { color: var(--ok); }
 964details.difffold summary .del { color: var(--bad); }
 965details.difffold > .none { padding: var(--sp-3); margin: 0; }
 966details.difffold .tablewrap { border-radius: 0 0 var(--r-md) var(--r-md); }
 967
 968table.difftable {
 969  border-collapse: collapse;
 970  width: 100%;
 971  font-family: var(--mono);
 972  font-size: var(--fs-1);
 973  line-height: 1.5;
 974}
 975table.difftable td {
 976  border: 0;
 977  padding: 0;
 978  vertical-align: top;
 979  font-size: inherit;
 980}
 981table.difftable td.ln {
 982  width: 1%;
 983  min-width: 2.5rem;
 984  padding: 0 var(--sp-2);
 985  text-align: right;
 986  color: var(--muted);
 987  user-select: none;
 988  border-right: 1px solid var(--line);
 989  white-space: nowrap;
 990}
 991/* not .code: that class is the blob/code-block style and carries its own
 992   background, which painted over every row tint */
 993table.difftable td.src {
 994  padding: 0 var(--sp-3);
 995  white-space: pre-wrap;
 996  overflow-wrap: anywhere;
 997}
 998/* the marker is decoration, so it lives in CSS and stays out of a copied
 999   selection */
1000table.difftable tr.add td.src::before { content: "+"; color: var(--ok); }
1001table.difftable tr.del td.src::before { content: "\2212"; color: var(--bad); }
1002table.difftable tr.ctx td.src::before { content: " "; }
1003table.difftable tr.add { background: var(--diff-add); }
1004table.difftable tr.del { background: var(--diff-del); }
1005table.difftable tr.hunk td, table.difftable tr.dmeta td {
1006  color: var(--muted);
1007  padding: var(--sp-1) var(--sp-3);
1008  background: var(--fill-subtle);
1009  border-top: 1px solid var(--line);
1010  border-bottom: 1px solid var(--line);
1011}
1012table.difftable tr.hunk td.src { color: var(--accent); }
1013table.difftable tr.threadrow td { padding: var(--sp-2) var(--sp-3); background: var(--bg); }
1014/* a line number is the handle for commenting on that line; it stays a
1015   plain number until pointed at, so the gutter does not read as a wall of
1016   links */
1017table.difftable td.ln a.cmt { color: inherit; text-decoration: none; }
1018table.difftable td.ln a.cmt:hover,
1019table.difftable td.ln a.cmt:focus { color: var(--accent); text-decoration: underline; }
1020
1021/* badges: signature and check states. Semantic colors stay distinct:
1022   green verified/success, amber pending/stale, red bad/failure, muted
1023   unsigned. */
1024.badge, .chip {
1025  --chip: var(--neutral);
1026  display: inline-block;
1027  padding: 0.05rem 0.55rem;
1028  border-radius: var(--r-pill);
1029  font-size: var(--fs-0);
1030  font-weight: 500;
1031  line-height: 1.5;
1032  color: var(--chip);
1033  border: 1px solid color-mix(in srgb, var(--chip) 35%, transparent);
1034  background: color-mix(in srgb, var(--chip) 7%, transparent);
1035  white-space: nowrap;
1036  vertical-align: middle;
1037}
1038.chip-open { --chip: var(--ok); }
1039.chip-closed { --chip: var(--bad); }
1040.chip-merged, .chip-done { --chip: var(--done); }
1041.chip-neutral, .chip-source_gone { --chip: var(--neutral); }
1042.chip-stale { --chip: var(--warn); }
1043.chip.label { font-weight: 500; }
1044.chip.topic { --chip: var(--accent); }
1045.repohead .topics { margin: 0 0 var(--sp-2); }
1046.badge-verified { --chip: var(--ok); }
1047.badge-unsigned { --chip: var(--neutral); }
1048.badge-signed_unknown_key { --chip: var(--warn); }
1049.badge-signed_email_mismatch { --chip: var(--bad); }
1050.badge-signed_key_expired { --chip: var(--warn); }
1051.badge-signed_key_revoked { --chip: var(--bad); }
1052.badge-bad_signature { --chip: var(--bad); }
1053.badge.check-success { --chip: var(--ok); }
1054.badge.check-pending { --chip: var(--warn); }
1055.badge.check-failure, .badge.check-error { --chip: var(--bad); }
1056.badge.check-cancelled, .chip.check-cancelled { --chip: var(--neutral); }
1057.badge.check-skipped, .chip.check-skipped { --chip: var(--neutral); }
1058.badge.check-running, .chip.check-running { --chip: var(--warn); }
1059.chip.check-success { --chip: var(--ok); }
1060.chip.check-pending { --chip: var(--warn); }
1061.chip.check-failure { --chip: var(--bad); }
1062pre.buildlog { max-height: 40rem; overflow: auto; }
1063span.check-success { color: var(--ok); }
1064span.check-pending { color: var(--warn); }
1065span.check-failure, span.check-error { color: var(--bad); }
1066
1067/* issue and MR listings */
1068.listhead {
1069  display: flex;
1070  align-items: baseline;
1071  gap: var(--sp-4);
1072  flex-wrap: wrap;
1073}
1074.listhead h2 { margin-bottom: var(--sp-2); }
1075nav.filters { display: flex; gap: var(--sp-1); }
1076nav.filters a {
1077  color: var(--muted);
1078  font-size: var(--fs-2);
1079  padding: 0.05rem var(--sp-2);
1080  border-radius: var(--r-pill);
1081}
1082nav.filters a:hover { color: var(--fg); background: var(--hover); text-decoration: none; }
1083nav.filters a.active { color: var(--accent); background: color-mix(in srgb, var(--accent) 10%, transparent); }
1084
1085ul.issuelist {
1086  list-style: none;
1087  margin: var(--sp-2) 0;
1088  padding: 0;
1089  background: var(--surface);
1090  border: 1px solid var(--line);
1091  border-radius: var(--r-lg);
1092  overflow: hidden;
1093}
1094ul.issuelist li {
1095  display: flex;
1096  align-items: baseline;
1097  gap: var(--sp-3);
1098  padding: var(--sp-3) var(--sp-4);
1099  border-bottom: 1px solid var(--faint);
1100}
1101ul.issuelist li:last-child { border-bottom: none; }
1102ul.issuelist li:hover { background: var(--hover); }
1103ul.issuelist li.empty { display: block; color: var(--muted); }
1104ul.issuelist .issuemain { flex: 1; min-width: 0; }
1105ul.issuelist p { margin: 0; }
1106ul.issuelist .title a { color: var(--fg); font-weight: 550; }
1107ul.issuelist .title a:hover { color: var(--accent); }
1108
1109/* releases */
1110article.release {
1111  background: var(--surface);
1112  border: 1px solid var(--line);
1113  border-radius: var(--r-lg);
1114  padding: var(--sp-3) var(--sp-4);
1115  margin: var(--sp-4) 0;
1116}
1117.releasehead { background: none; border: 0; padding: 0; }
1118.releasehead h3 { margin: 0 0 var(--sp-1); }
1119.releasehead .meta { margin: 0 0 var(--sp-2); }
1120table.assets { margin-top: var(--sp-2); }
1121table.assets td.size { color: var(--muted); white-space: nowrap; text-align: right; }
1122table.assets td.name { width: 100%; font-family: var(--mono); font-size: var(--fs-1); }
1123
1124/* milestones */
1125ul.milestonelist {
1126  list-style: none;
1127  margin: var(--sp-2) 0;
1128  padding: 0;
1129  border: 1px solid var(--line);
1130  border-radius: var(--r-lg);
1131  overflow: hidden;
1132}
1133ul.milestonelist li { padding: var(--sp-3) var(--sp-4); border-bottom: 1px solid var(--faint); }
1134ul.milestonelist li:last-child { border-bottom: none; }
1135ul.milestonelist li.empty { color: var(--muted); }
1136ul.milestonelist p { margin: 0; }
1137ul.milestonelist .title { font-weight: 550; }
1138.progress {
1139  height: 6px;
1140  background: var(--faint);
1141  border-radius: var(--r-pill);
1142  margin-top: var(--sp-2);
1143  overflow: hidden;
1144  max-width: 24rem;
1145}
1146.progress .bar { height: 100%; background: var(--ok); }
1147.listhead .spacer { flex: 1; }
1148
1149/* issue / MR detail */
1150h2.issuetitle { margin-bottom: var(--sp-1); font-size: var(--fs-5); }
1151.issuenumber { color: var(--muted); font-weight: 400; }
1152.issuemeta { color: var(--muted); font-size: var(--fs-2); margin-top: 0; }
1153.review { font-size: var(--fs-2); }
1154
1155/* comment cards; discussion stays at a readable measure */
1156article.comment, form.commentform {
1157  max-width: 48rem;
1158}
1159article.comment {
1160  border: 1px solid var(--faint);
1161  border-radius: var(--r-lg);
1162  margin: var(--sp-4) 0;
1163}
1164article.comment .commenthead {
1165  background: var(--surface);
1166  border-bottom: 1px solid var(--faint);
1167  border-radius: var(--r-lg) var(--r-lg) 0 0;
1168  padding: var(--sp-2) var(--sp-4);
1169  font-size: var(--fs-2);
1170}
1171article.comment .commenthead .when { color: var(--muted); }
1172article.comment .rendered { padding: 0 var(--sp-4); }
1173article.comment .rendered > :first-child { margin-top: var(--sp-3); }
1174article.comment .rendered > :last-child { margin-bottom: var(--sp-3); }
1175form.commentform { margin: var(--sp-4) 0; }
1176details.editbox { margin: var(--sp-2) 0; max-width: 48rem; }
1177details.editbox summary { cursor: pointer; color: var(--muted); font-size: var(--fs-2); }
1178details.editbox summary:hover { color: var(--accent); }
1179details.editbox input[type="text"] { width: 100%; }
1180.syscomment {
1181  max-width: 48rem;
1182  color: var(--muted);
1183  font-size: var(--fs-2);
1184  margin: var(--sp-3) 0;
1185  padding-left: var(--sp-4);
1186  border-left: 3px solid var(--faint);
1187}
1188.syscomment p { display: inline; margin: 0; }
1189
1190/* review threads under diff lines */
1191.thread {
1192  border: 1px solid var(--faint);
1193  border-left: 3px solid var(--accent);
1194  border-radius: var(--r-md);
1195  padding: var(--sp-2) var(--sp-4);
1196  margin: var(--sp-2) 0 var(--sp-2) var(--sp-6);
1197}
1198.thread.resolved { border-left-color: var(--ok); opacity: 0.75; }
1199/* an unsubmitted comment is the viewer's own note, not the conversation */
1200.thread.pending { border-left-color: var(--neutral); border-left-style: dashed; }
1201.thread.stale { border-left-color: var(--warn); }
1202.thread p.commenthead { font-size: var(--fs-2); margin: var(--sp-2) 0 0; }
1203.thread .when { color: var(--muted); }
1204.thread .rendered p { margin: var(--sp-1) 0; }
1205.thread .threadstate { color: var(--muted); font-size: var(--fs-1); margin: var(--sp-1) 0 0; }
1206.thread.composing { border-left-color: var(--mark); }
1207.thread details.threadreply { margin: var(--sp-2) 0 0; }
1208.thread details.threadreply > summary { color: var(--accent); font-size: var(--fs-1); cursor: pointer; }
1209.thread textarea { width: 100%; }
1210.thread.composing p, .thread details.threadreply p { margin: var(--sp-2) 0 0; }
1211
1212/* forms: every control shares one shell, so a select reads as a sibling
1213   of the text inputs beside it rather than as an OS control dropped in */
1214input, textarea, button, select { font: inherit; color: inherit; }
1215input[type="text"], input[type="search"], input[type="email"],
1216input[type="password"], input:not([type]), textarea, select {
1217  background: var(--bg);
1218  color: var(--fg);
1219  border: 1px solid var(--line);
1220  border-radius: var(--r-md);
1221  padding: var(--sp-2) var(--sp-3);
1222  line-height: 1.4;
1223}
1224textarea { width: 100%; resize: vertical; }
1225textarea[name="content"] { font-family: var(--mono); font-size: var(--fs-1); }
1226/* the native arrow cannot be restyled, so draw our own and match the
1227   inputs exactly; --chev carries the stroke colour per scheme */
1228select {
1229  appearance: none;
1230  padding-right: 1.9rem;
1231  background-image: var(--chev);
1232  background-repeat: no-repeat;
1233  background-position: right 0.6rem center;
1234  background-size: 0.62rem;
1235  cursor: pointer;
1236}
1237input:hover, textarea:hover, select:hover { border-color: var(--muted); }
1238/* the global :focus-visible ring covers these; only the border moves */
1239input:focus, textarea:focus, select:focus { border-color: var(--accent); }
1240input[type="radio"] { accent-color: var(--fill); }
1241label:has(> input[type="radio"]), label:has(> input[type="checkbox"]),
1242label:has(> select) { display: inline-flex; align-items: center; gap: var(--sp-2); }
1243
1244/* A radio keeps the native control: a circle takes one answer, a square
1245   takes any number, and that shape is the cue that survives greyscale,
1246   high contrast and colour blindness. The checkbox is drawn instead,
1247   because at the size the OS renders it it reads as an afterthought
1248   beside the button it shares a row with. The square is kept square —
1249   the size changes, the cue does not. */
1250input[type="checkbox"] {
1251  appearance: none;
1252  flex: none;
1253  /* the height of a text input, a select and a button, so a settings row
1254     ends in one column of controls rather than a button and a speck */
1255  width: 2.5rem;
1256  height: 2.5rem;
1257  /* an empty box has nothing but its border to announce it, so the border
1258     is --muted rather than the --line the sized controls can afford */
1259  border: 1px solid var(--muted);
1260  border-radius: var(--r-sm);
1261  background: var(--bg);
1262  cursor: pointer;
1263}
1264input[type="checkbox"]:not(:checked):hover { border-color: var(--fg); }
1265input[type="checkbox"]:checked {
1266  background: var(--fill);
1267  border-color: var(--fill);
1268  /* the mark sits on the blue ground, so it is white in both schemes */
1269  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");
1270  background-repeat: no-repeat;
1271  background-position: center;
1272  background-size: 1.2rem;
1273}
1274
1275/* a segmented control for a small, mutually exclusive set: real radios
1276   underneath, so arrow keys and screen readers behave as they always did */
1277fieldset.segmented { border: 0; margin: 0 0 var(--sp-4); padding: 0; }
1278fieldset.segmented legend {
1279  padding: 0;
1280  margin-bottom: var(--sp-2);
1281  color: var(--muted);
1282  font-size: var(--fs-2);
1283}
1284fieldset.segmented .options { display: inline-flex; }
1285fieldset.segmented label { gap: 0; }
1286fieldset.segmented input {
1287  position: absolute;
1288  width: 1px;
1289  height: 1px;
1290  opacity: 0;
1291  pointer-events: none;
1292}
1293fieldset.segmented span {
1294  display: block;
1295  padding: var(--sp-2) var(--sp-4);
1296  border: 1px solid var(--line);
1297  font-size: var(--fs-2);
1298  cursor: pointer;
1299}
1300fieldset.segmented label + label span { border-left: 0; }
1301fieldset.segmented label:first-child span { border-radius: var(--r-sm) 0 0 var(--r-sm); }
1302fieldset.segmented label:last-child span { border-radius: 0 var(--r-sm) var(--r-sm) 0; }
1303fieldset.segmented span:hover { border-color: var(--muted); }
1304fieldset.segmented input:checked + span {
1305  background: var(--fill);
1306  border-color: var(--fill);
1307  color: #ffffff;
1308  font-weight: 600;
1309}
1310fieldset.segmented input:focus-visible + span {
1311  outline: 2px solid var(--accent);
1312  outline-offset: 2px;
1313}
1314
1315/* remaining input states */
1316::placeholder { color: var(--muted); opacity: 1; }
1317::selection { background: var(--fill); color: #ffffff; }
1318/* :user-invalid, not :invalid — a required field is invalid the moment
1319   the page loads, and colouring it red before anyone has typed is a
1320   scolding, not a hint */
1321input:user-invalid, textarea:user-invalid {
1322  border-color: var(--bad);
1323}
1324.hint { display: block; color: var(--muted); font-size: var(--fs-1); margin-top: var(--sp-1); }
1325button {
1326  background: var(--fill);
1327  color: #ffffff;
1328  border: 1px solid var(--fill-edge);
1329  border-radius: var(--r-md);
1330  padding: var(--sp-2) var(--sp-4);
1331  font-weight: 600;
1332  cursor: pointer;
1333}
1334button:hover { filter: brightness(1.15); }
1335
1336/* 404 */
1337.notfound { text-align: center; padding: var(--sp-6) 0; }
1338.notfound .bigcode {
1339  font-family: var(--mono);
1340  font-size: var(--fs-7);
1341  color: var(--faint);
1342  -webkit-text-stroke: 1px var(--line);
1343  margin: 0;
1344  line-height: 1;
1345}
1346.notfound h1 { margin-top: var(--sp-3); }
1347
1348/* footer */
1349footer {
1350  border-top: 1px solid var(--faint);
1351  padding: var(--sp-4) var(--sp-5) var(--sp-5);
1352  width: 100%;
1353  color: var(--muted);
1354  font-size: var(--fs-1);
1355}
1356footer p { margin: 0; }
1357footer a { color: var(--muted); text-decoration: underline; }
1358footer a:hover { color: var(--accent); }
1359
1360/* search */
1361form.searchform {
1362  display: flex;
1363  gap: var(--sp-2);
1364  margin: var(--sp-3) 0 var(--sp-4);
1365}
1366form.searchform input[type="search"] { flex: 1; max-width: 32rem; }
1367form.searchform.compact { margin: 0; }
1368form.searchform.compact input[type="search"] {
1369  padding: var(--sp-1) var(--sp-3);
1370  font-size: var(--fs-2);
1371  min-width: 18rem;
1372}
1373ul.matchlist { list-style: none; margin: var(--sp-3) 0; padding: 0; }
1374ul.matchlist li { margin-bottom: var(--sp-3); }
1375ul.matchlist .matchpath { font-family: var(--mono); font-size: var(--fs-1); }
1376pre.matchline {
1377  background: var(--code-bg);
1378  border: 1px solid var(--faint);
1379  border-radius: var(--r-md);
1380  padding: var(--sp-1) var(--sp-3);
1381  margin: var(--sp-1) 0 0;
1382  overflow-x: auto;
1383}
1384pre.matchline mark {
1385  background: color-mix(in srgb, var(--warn) 25%, transparent);
1386  color: inherit;
1387  border-radius: 2px;
1388}
1389
1390/* wiki */
1391.wikilayout { display: flex; gap: var(--sp-5); align-items: flex-start; }
1392.wikipage { flex: 1; min-width: 0; }
1393.wikinav { flex: none; width: 13rem; padding-top: var(--sp-4); }
1394.wikinav ul { list-style: none; margin: var(--sp-1) 0 var(--sp-4); padding: 0; }
1395.wikinav li a { display: block; padding: 0.15rem 0; color: var(--fg); }
1396.wikinav li a:hover { color: var(--accent); text-decoration: none; }
1397.wikinav li a.active { color: var(--accent); font-weight: 550; }
1398@media (max-width: 40rem) {
1399  /* Column direction + the desktop align-items:flex-start lets .wikipage
1400     take its content's intrinsic width, overflowing the viewport (and
1401     triggering iOS font inflation). Stretch pins it to the container. */
1402  .wikilayout { flex-direction: column; align-items: stretch; }
1403  .wikinav { width: auto; padding-top: 0; }
1404}
1405
1406/* blame */
1407.blame {
1408  border: 1px solid var(--line);
1409  border-radius: var(--r-md);
1410  overflow: hidden;
1411}
1412.blamehunk {
1413  display: flex;
1414  gap: var(--sp-3);
1415  border-top: 1px solid var(--line);
1416  align-items: flex-start;
1417}
1418.blamehunk:first-child { border-top: none; }
1419.blameinfo {
1420  flex: none;
1421  width: 17rem;
1422  padding: var(--sp-2) var(--sp-3);
1423  min-width: 0;
1424}
1425.blameinfo .subject {
1426  margin: 0;
1427  font-size: var(--fs-1);
1428  white-space: nowrap;
1429  overflow: hidden;
1430  text-overflow: ellipsis;
1431}
1432.blameinfo .meta { margin: 0; font-size: var(--fs-1); color: var(--muted); }
1433.blamecode {
1434  flex: 1;
1435  margin: 0;
1436  padding: var(--sp-2) var(--sp-3) var(--sp-2) 0;
1437  overflow-x: auto;
1438  border-left: 1px solid var(--line);
1439  align-self: stretch;
1440}
1441.lineno {
1442  display: inline-block;
1443  min-width: 3.5em;
1444  padding-right: 1em;
1445  text-align: right;
1446  color: var(--muted);
1447  user-select: none;
1448}
1449
1450/* mobile */
1451@media (max-width: 40rem) {
1452  .thread { margin-left: var(--sp-3); }
1453  /* the commit subject is the widest thing in the listing and the one
1454     column a phone can do without: the tipbar above already names the
1455     latest commit. */
1456  table.tree td.lastcommit, table.tree th.lastcommit { display: none; }
1457  table.tree td.mode, table.tree th.mode { display: none; }
1458  .clone, .pathbar { overflow-wrap: anywhere; }
1459  .readme, .code { padding: var(--sp-3); }
1460  .blamehunk { flex-direction: column; gap: 0; }
1461  .blameinfo { width: auto; }
1462  .blamecode { border-left: none; padding-left: var(--sp-3); width: 100%; }
1463  .cardhead {
1464    margin: calc(-1 * var(--sp-3)) calc(-1 * var(--sp-3)) var(--sp-3);
1465    padding: var(--sp-2) var(--sp-3);
1466  }
1467}
1468
1469/* tree listing: the last commit is what a file table is scanned for, so
1470   it leads. The file's own facts moved to the blob page. */
1471.tipbar {
1472  display: flex;
1473  align-items: center;
1474  gap: var(--sp-3);
1475  padding: var(--sp-2) var(--sp-4);
1476  background: var(--hover);
1477  border: 1px solid var(--line);
1478  border-bottom: 0;
1479  border-radius: var(--r-lg) var(--r-lg) 0 0;
1480  font-size: var(--fs-2);
1481}
1482.tipbar .who { font-weight: 600; }
1483.tipbar .subject { color: var(--fg); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1484.tipbar .subject:hover { color: var(--accent); }
1485.tipbar .spacer { flex: 1; }
1486.tipbar .age { color: var(--muted); white-space: nowrap; }
1487.tipbar + table.tree { border-radius: 0 0 var(--r-lg) var(--r-lg); }
1488table.tree th.lastcommit, table.tree td.lastcommit { width: 55%; }
1489table.tree td.lastcommit a { color: var(--muted); display: block; overflow: hidden;
1490                             text-overflow: ellipsis; white-space: nowrap; }
1491table.tree td.lastcommit a:hover { color: var(--accent); }
1492table.tree th.age, table.tree td.age {
1493  text-align: right;
1494  white-space: nowrap;
1495  color: var(--muted);
1496  font-variant-numeric: tabular-nums;
1497}
1498table.tree td.name { width: 25%; white-space: nowrap; }
1499table.tree td.name.dir a { color: var(--accent); }
1500p.clone { margin: 0 0 var(--sp-3); }
1501p.filefacts { color: var(--muted); font-size: var(--fs-1); margin: 0 0 var(--sp-3); }
1502.pathbar .actions { font-size: var(--fs-1); color: var(--muted); }
1503.snippetfile { margin-bottom: var(--sp-5); }
1504
1505/* merge request: a two-column split, so state has somewhere to live that
1506   is not a run-on sentence under the title */
1507.withaside {
1508  display: grid;
1509  grid-template-columns: minmax(0, 1fr) 15rem;
1510  gap: var(--sp-5);
1511  align-items: start;
1512}
1513.withaside .mainside { min-width: 0; }
1514@media (max-width: 62rem) { .withaside { grid-template-columns: 1fr; } }
1515
1516.aside { font-size: var(--fs-2); }
1517.aside .grp { border-top: 1px solid var(--line); padding: var(--sp-3) 0; }
1518.aside .grp:first-child { border-top: 0; padding-top: 0; }
1519.aside h2 {
1520  margin: 0 0 var(--sp-2);
1521  font-family: var(--mono);
1522  font-size: var(--fs-0);
1523  font-weight: 500;
1524  letter-spacing: 0.1em;
1525  text-transform: uppercase;
1526  color: var(--muted);
1527}
1528.aside .row { display: flex; align-items: center; gap: var(--sp-2); margin: 0 0 var(--sp-1); flex-wrap: wrap; }
1529.aside .row:last-child { margin-bottom: 0; }
1530.aside .none { color: var(--muted); }
1531/* a row's second line: when it happened, under what it happened to. The
1532   extra gap keeps the pair reading as one entry. */
1533.aside .row .sub { flex-basis: 100%; color: var(--muted); font-size: var(--fs-0); }
1534.aside .row:has(.sub) { margin-bottom: var(--sp-2); }
1535.aside .dot { width: 0.45rem; height: 0.45rem; flex: none; border-radius: 50%; background: var(--muted); }
1536.aside .dot.ok { background: var(--ok); }
1537.aside .dot.bad { background: var(--bad); }
1538.aside .dot.pend { background: var(--mark); }
1539
1540/* sub-navigation within an item; orange marks position as it does above */
1541nav.subtabs {
1542  display: flex;
1543  gap: var(--sp-4);
1544  border-bottom: 1px solid var(--line);
1545  margin: 0 0 var(--sp-4);
1546}
1547nav.subtabs a {
1548  display: inline-flex;
1549  align-items: center;
1550  gap: var(--sp-2);
1551  padding: 0 0 var(--sp-2);
1552  color: var(--muted);
1553  font-size: var(--fs-2);
1554  border-bottom: 2px solid transparent;
1555  margin-bottom: -1px;
1556}
1557nav.subtabs a:hover { color: var(--fg); text-decoration: none; }
1558nav.subtabs a[aria-current] { color: var(--fg); font-weight: 700; border-bottom-color: var(--mark); }
1559nav.subtabs a i {
1560  font-style: normal;
1561  font-family: var(--mono);
1562  font-size: var(--fs-0);
1563  background: var(--hover);
1564  border-radius: var(--r-sm);
1565  padding: 0 0.35rem;
1566}
1567p.diffstat { color: var(--muted); font-size: var(--fs-2); margin: 0 0 var(--sp-3); }
1568p.diffstat .add { color: var(--ok); }
1569p.diffstat .del { color: var(--bad); }
1570
1571/* file edit form */
1572.editform textarea { width: 100%; }
1573.editform input[name="message"] { width: 60%; }