internal/web/static/style.css

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

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