internal/web/static/style.css

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

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