internal/web/static/style.css

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

1533 lines · 52750 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/* line length is a reading constraint, so it applies to prose only —
 767   tables, code blocks and images keep the full column */
 768.rendered > p,
 769.rendered > ul,
 770.rendered > ol,
 771.rendered > blockquote { max-width: 78ch; }
 772.rendered pre {
 773  background: var(--code-bg);
 774  border: 1px solid var(--line);
 775  padding: var(--sp-3) var(--sp-4);
 776  border-radius: var(--r-md);
 777  overflow-x: auto;
 778}
 779.rendered code {
 780  background: var(--code-bg);
 781  border: 1px solid var(--line);
 782  padding: 0.05rem 0.3rem;
 783  border-radius: var(--r-sm);
 784}
 785.rendered pre code { background: transparent; border: none; padding: 0; }
 786.rendered blockquote {
 787  border-left: 3px solid var(--line);
 788  margin-left: 0;
 789  padding-left: var(--sp-4);
 790  color: var(--muted);
 791}
 792.rendered img { max-width: 100%; }
 793.rendered h1, .rendered h2 { border-bottom: 1px solid var(--faint); padding-bottom: var(--sp-1); }
 794.rendered table {
 795  border-collapse: collapse;
 796  margin: var(--sp-3) 0;
 797  display: block;
 798  max-width: 100%;
 799  overflow-x: auto;
 800}
 801.rendered th, .rendered td {
 802  border: 1px solid var(--line);
 803  padding: var(--sp-1) var(--sp-3);
 804  text-align: left;
 805  overflow-wrap: normal; /* scroll the table, don't shatter words */
 806}
 807.rendered th { background: var(--surface); font-weight: 600; }
 808.blobimage { text-align: center; }
 809.blobimage img {
 810  max-width: 100%;
 811  border: 1px solid var(--faint);
 812  border-radius: var(--r-md);
 813  background: var(--surface);
 814}
 815
 816pre.message {
 817  background: var(--code-bg);
 818  border: 1px solid var(--line);
 819  padding: var(--sp-3) var(--sp-4);
 820  border-radius: var(--r-md);
 821  white-space: pre-wrap;
 822  overflow-wrap: anywhere;
 823}
 824
 825/* repo facts: the counts a visitor sizes a project up with */
 826.teambody { padding: var(--sp-3); background: var(--bg); }
 827.teambody .memberchip { margin-right: var(--sp-2); }
 828
 829form.logfilter {
 830  display: flex; align-items: center; gap: var(--sp-2);
 831  margin: 0 0 var(--sp-4); flex-wrap: wrap;
 832}
 833form.logfilter input { flex: 1; min-width: 12rem; max-width: 28rem; }
 834
 835/* account settings */
 836table.keys td { padding: var(--sp-2) var(--sp-4) var(--sp-2) 0; }
 837/* these tables sit flush with the page text, so the header row drops the
 838   left padding the card-style listings use */
 839table.keys tr.cols th { padding-left: 0; background: transparent; }
 840table.keys td.mono, .mono { font-family: var(--mono); font-size: var(--fs-1); overflow-wrap: anywhere; }
 841table.keys td.act { text-align: right; }
 842ul.plain { list-style: none; margin: 0 0 var(--sp-3); padding: 0; }
 843ul.plain li { padding: var(--sp-1) 0; display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
 844form.setform.stack { display: block; }
 845form.setform.stack textarea, form.setform.stack select { width: 100%; max-width: 48rem; display: block; margin-bottom: var(--sp-2); }
 846p.notice {
 847  border-left: 3px solid var(--ok);
 848  background: var(--hover);
 849  padding: var(--sp-2) var(--sp-3);
 850  margin: 0 0 var(--sp-4);
 851}
 852
 853.facts { margin: 0 0 var(--sp-4); }
 854.facts .counts {
 855  display: flex; flex-wrap: wrap; gap: var(--sp-1) var(--sp-4);
 856  margin: 0 0 var(--sp-3); font-size: var(--fs-2);
 857}
 858.facts .counts a { color: var(--fg); text-decoration: none; }
 859.facts .counts a:hover { color: var(--accent); text-decoration: underline; }
 860.facts .counts .fact { color: var(--muted); }
 861.facts .counts strong { font-weight: 600; }
 862
 863.langbar {
 864  display: flex; height: 0.5rem; margin: 0 0 var(--sp-2);
 865  border-radius: var(--r-sm); overflow: hidden; background: var(--faint);
 866}
 867.langbar .lang { display: block; min-width: 2px; }
 868.langs {
 869  display: flex; flex-wrap: wrap; gap: var(--sp-1) var(--sp-4);
 870  margin: 0 0 var(--sp-3); font-size: var(--fs-1);
 871}
 872.langs .lang-name { display: inline-flex; align-items: center; gap: var(--sp-1); }
 873.langs .muted { color: var(--muted); }
 874.langs .dot { width: 0.6rem; height: 0.6rem; border-radius: 50%; display: inline-block; }
 875/* named where it reads as the language's own color, neutral otherwise */
 876.lang { background: var(--neutral); }
 877.lang-go { background: #00add8; }
 878.lang-rust { background: #dea584; }
 879.lang-c, .lang-c- { background: #555555; }
 880.lang-python { background: #3572a5; }
 881.lang-javascript { background: #f1e05a; }
 882.lang-typescript { background: #3178c6; }
 883.lang-shell { background: #89e051; }
 884.lang-ruby { background: #701516; }
 885.lang-java { background: #b07219; }
 886.lang-swift { background: #f05138; }
 887.lang-html { background: #e34c26; }
 888.lang-css { background: #563d7c; }
 889.lang-org { background: #77aa99; }
 890.lang-markdown { background: #083fa1; }
 891.lang-emacs-lisp { background: #c065db; }
 892.lang-nix { background: #7e7eff; }
 893.lang-sql { background: #e38c00; }
 894
 895.contribs {
 896  display: flex; flex-wrap: wrap; align-items: baseline;
 897  gap: var(--sp-1) var(--sp-3); margin: 0; font-size: var(--fs-1);
 898}
 899.contribs .label { color: var(--muted); }
 900
 901/* diffs: one foldable section per file, a table so the line-number
 902   gutters stay put while the code scrolls */
 903details.difffold {
 904  border: 1px solid var(--line);
 905  border-radius: var(--r-md);
 906  margin: 0 0 var(--sp-3);
 907  background: var(--code-bg);
 908}
 909details.difffold summary {
 910  cursor: pointer;
 911  display: flex;
 912  align-items: baseline;
 913  gap: var(--sp-3);
 914  padding: var(--sp-2) var(--sp-3);
 915  list-style: none;
 916  font-size: var(--fs-2);
 917}
 918details.difffold[open] summary { border-bottom: 1px solid var(--line); }
 919details.difffold summary::-webkit-details-marker { display: none; }
 920/* flex summaries lose the native disclosure marker; draw our own */
 921details.difffold summary::before { content: "\25b8"; color: var(--muted); }
 922details.difffold[open] summary::before { content: "\25be"; }
 923details.difffold summary:hover { background: var(--fill-subtle); }
 924summary .fpath { font-family: var(--mono); overflow-wrap: anywhere; }
 925summary .fpath .was { color: var(--muted); }
 926summary .fstat { margin-left: auto; font-family: var(--mono); white-space: nowrap; }
 927details.difffold summary .add { color: var(--ok); }
 928details.difffold summary .del { color: var(--bad); }
 929details.difffold > .none { padding: var(--sp-3); margin: 0; }
 930details.difffold .tablewrap { border-radius: 0 0 var(--r-md) var(--r-md); }
 931
 932table.difftable {
 933  border-collapse: collapse;
 934  width: 100%;
 935  font-family: var(--mono);
 936  font-size: var(--fs-1);
 937  line-height: 1.5;
 938}
 939table.difftable td {
 940  border: 0;
 941  padding: 0;
 942  vertical-align: top;
 943  font-size: inherit;
 944}
 945table.difftable td.ln {
 946  width: 1%;
 947  min-width: 2.5rem;
 948  padding: 0 var(--sp-2);
 949  text-align: right;
 950  color: var(--muted);
 951  user-select: none;
 952  border-right: 1px solid var(--line);
 953  white-space: nowrap;
 954}
 955/* not .code: that class is the blob/code-block style and carries its own
 956   background, which painted over every row tint */
 957table.difftable td.src {
 958  padding: 0 var(--sp-3);
 959  white-space: pre-wrap;
 960  overflow-wrap: anywhere;
 961}
 962/* the marker is decoration, so it lives in CSS and stays out of a copied
 963   selection */
 964table.difftable tr.add td.src::before { content: "+"; color: var(--ok); }
 965table.difftable tr.del td.src::before { content: "\2212"; color: var(--bad); }
 966table.difftable tr.ctx td.src::before { content: " "; }
 967table.difftable tr.add { background: var(--diff-add); }
 968table.difftable tr.del { background: var(--diff-del); }
 969table.difftable tr.hunk td, table.difftable tr.dmeta td {
 970  color: var(--muted);
 971  padding: var(--sp-1) var(--sp-3);
 972  background: var(--fill-subtle);
 973  border-top: 1px solid var(--line);
 974  border-bottom: 1px solid var(--line);
 975}
 976table.difftable tr.hunk td.src { color: var(--accent); }
 977table.difftable tr.threadrow td { padding: var(--sp-2) var(--sp-3); background: var(--bg); }
 978/* a line number is the handle for commenting on that line; it stays a
 979   plain number until pointed at, so the gutter does not read as a wall of
 980   links */
 981table.difftable td.ln a.cmt { color: inherit; text-decoration: none; }
 982table.difftable td.ln a.cmt:hover,
 983table.difftable td.ln a.cmt:focus { color: var(--accent); text-decoration: underline; }
 984
 985/* badges: signature and check states. Semantic colors stay distinct:
 986   green verified/success, amber pending/stale, red bad/failure, muted
 987   unsigned. */
 988.badge, .chip {
 989  --chip: var(--neutral);
 990  display: inline-block;
 991  padding: 0.05rem 0.55rem;
 992  border-radius: var(--r-pill);
 993  font-size: var(--fs-0);
 994  font-weight: 500;
 995  line-height: 1.5;
 996  color: var(--chip);
 997  border: 1px solid color-mix(in srgb, var(--chip) 35%, transparent);
 998  background: color-mix(in srgb, var(--chip) 7%, transparent);
 999  white-space: nowrap;
1000  vertical-align: middle;
1001}
1002.chip-open { --chip: var(--ok); }
1003.chip-closed { --chip: var(--bad); }
1004.chip-merged, .chip-done { --chip: var(--done); }
1005.chip-neutral, .chip-source_gone { --chip: var(--neutral); }
1006.chip-stale { --chip: var(--warn); }
1007.chip.label { font-weight: 500; }
1008.chip.topic { --chip: var(--accent); }
1009.repohead .topics { margin: 0 0 var(--sp-2); }
1010.repohead .mirrorline { margin: 0 0 var(--sp-2); }
1011.mirrorerr { color: var(--bad); }
1012.badge-verified { --chip: var(--ok); }
1013.badge-unsigned { --chip: var(--neutral); }
1014.badge-signed_unknown_key { --chip: var(--warn); }
1015.badge-signed_email_mismatch { --chip: var(--bad); }
1016.badge-signed_key_expired { --chip: var(--warn); }
1017.badge-signed_key_revoked { --chip: var(--bad); }
1018.badge-bad_signature { --chip: var(--bad); }
1019.badge.check-success { --chip: var(--ok); }
1020.badge.check-pending { --chip: var(--warn); }
1021.badge.check-failure, .badge.check-error { --chip: var(--bad); }
1022.badge.check-running, .chip.check-running { --chip: var(--warn); }
1023.chip.check-success { --chip: var(--ok); }
1024.chip.check-pending { --chip: var(--warn); }
1025.chip.check-failure { --chip: var(--bad); }
1026pre.buildlog { max-height: 40rem; overflow: auto; }
1027span.check-success { color: var(--ok); }
1028span.check-pending { color: var(--warn); }
1029span.check-failure, span.check-error { color: var(--bad); }
1030
1031/* issue and MR listings */
1032.listhead {
1033  display: flex;
1034  align-items: baseline;
1035  gap: var(--sp-4);
1036  flex-wrap: wrap;
1037}
1038.listhead h2 { margin-bottom: var(--sp-2); }
1039nav.filters { display: flex; gap: var(--sp-1); }
1040nav.filters a {
1041  color: var(--muted);
1042  font-size: var(--fs-2);
1043  padding: 0.05rem var(--sp-2);
1044  border-radius: var(--r-pill);
1045}
1046nav.filters a:hover { color: var(--fg); background: var(--hover); text-decoration: none; }
1047nav.filters a.active { color: var(--accent); background: color-mix(in srgb, var(--accent) 10%, transparent); }
1048
1049ul.issuelist {
1050  list-style: none;
1051  margin: var(--sp-2) 0;
1052  padding: 0;
1053  background: var(--surface);
1054  border: 1px solid var(--line);
1055  border-radius: var(--r-lg);
1056  overflow: hidden;
1057}
1058ul.issuelist li {
1059  display: flex;
1060  align-items: baseline;
1061  gap: var(--sp-3);
1062  padding: var(--sp-3) var(--sp-4);
1063  border-bottom: 1px solid var(--faint);
1064}
1065ul.issuelist li:last-child { border-bottom: none; }
1066ul.issuelist li:hover { background: var(--hover); }
1067ul.issuelist li.empty { display: block; color: var(--muted); }
1068ul.issuelist .issuemain { flex: 1; min-width: 0; }
1069ul.issuelist p { margin: 0; }
1070ul.issuelist .title a { color: var(--fg); font-weight: 550; }
1071ul.issuelist .title a:hover { color: var(--accent); }
1072
1073/* releases */
1074article.release {
1075  background: var(--surface);
1076  border: 1px solid var(--line);
1077  border-radius: var(--r-lg);
1078  padding: var(--sp-3) var(--sp-4);
1079  margin: var(--sp-4) 0;
1080}
1081.releasehead { background: none; border: 0; padding: 0; }
1082.releasehead h3 { margin: 0 0 var(--sp-1); }
1083.releasehead .meta { margin: 0 0 var(--sp-2); }
1084table.assets { margin-top: var(--sp-2); }
1085table.assets td.size { color: var(--muted); white-space: nowrap; text-align: right; }
1086table.assets td.name { width: 100%; font-family: var(--mono); font-size: var(--fs-1); }
1087
1088/* milestones */
1089ul.milestonelist {
1090  list-style: none;
1091  margin: var(--sp-2) 0;
1092  padding: 0;
1093  border: 1px solid var(--line);
1094  border-radius: var(--r-lg);
1095  overflow: hidden;
1096}
1097ul.milestonelist li { padding: var(--sp-3) var(--sp-4); border-bottom: 1px solid var(--faint); }
1098ul.milestonelist li:last-child { border-bottom: none; }
1099ul.milestonelist li.empty { color: var(--muted); }
1100ul.milestonelist p { margin: 0; }
1101ul.milestonelist .title { font-weight: 550; }
1102.progress {
1103  height: 6px;
1104  background: var(--faint);
1105  border-radius: var(--r-pill);
1106  margin-top: var(--sp-2);
1107  overflow: hidden;
1108  max-width: 24rem;
1109}
1110.progress .bar { height: 100%; background: var(--ok); }
1111.listhead .spacer { flex: 1; }
1112
1113/* issue / MR detail */
1114h2.issuetitle { margin-bottom: var(--sp-1); font-size: var(--fs-5); }
1115.issuenumber { color: var(--muted); font-weight: 400; }
1116.issuemeta { color: var(--muted); font-size: var(--fs-2); margin-top: 0; }
1117.review { font-size: var(--fs-2); }
1118
1119/* comment cards; discussion stays at a readable measure */
1120article.comment, form.commentform {
1121  max-width: 48rem;
1122}
1123article.comment {
1124  border: 1px solid var(--faint);
1125  border-radius: var(--r-lg);
1126  margin: var(--sp-4) 0;
1127}
1128article.comment .commenthead {
1129  background: var(--surface);
1130  border-bottom: 1px solid var(--faint);
1131  border-radius: var(--r-lg) var(--r-lg) 0 0;
1132  padding: var(--sp-2) var(--sp-4);
1133  font-size: var(--fs-2);
1134}
1135article.comment .commenthead .when { color: var(--muted); }
1136article.comment .rendered { padding: 0 var(--sp-4); }
1137article.comment .rendered > :first-child { margin-top: var(--sp-3); }
1138article.comment .rendered > :last-child { margin-bottom: var(--sp-3); }
1139form.commentform { margin: var(--sp-4) 0; }
1140details.editbox { margin: var(--sp-2) 0; max-width: 48rem; }
1141details.editbox summary { cursor: pointer; color: var(--muted); font-size: var(--fs-2); }
1142details.editbox summary:hover { color: var(--accent); }
1143details.editbox input[type="text"] { width: 100%; }
1144.syscomment {
1145  max-width: 48rem;
1146  color: var(--muted);
1147  font-size: var(--fs-2);
1148  margin: var(--sp-3) 0;
1149  padding-left: var(--sp-4);
1150  border-left: 3px solid var(--faint);
1151}
1152.syscomment p { display: inline; margin: 0; }
1153
1154/* review threads under diff lines */
1155.thread {
1156  border: 1px solid var(--faint);
1157  border-left: 3px solid var(--accent);
1158  border-radius: var(--r-md);
1159  padding: var(--sp-2) var(--sp-4);
1160  margin: var(--sp-2) 0 var(--sp-2) var(--sp-6);
1161}
1162.thread.resolved { border-left-color: var(--ok); opacity: 0.75; }
1163.thread.stale { border-left-color: var(--warn); }
1164.thread p.commenthead { font-size: var(--fs-2); margin: var(--sp-2) 0 0; }
1165.thread .when { color: var(--muted); }
1166.thread .rendered p { margin: var(--sp-1) 0; }
1167.thread .threadstate { color: var(--muted); font-size: var(--fs-1); margin: var(--sp-1) 0 0; }
1168.thread.composing { border-left-color: var(--mark); }
1169.thread details.threadreply { margin: var(--sp-2) 0 0; }
1170.thread details.threadreply > summary { color: var(--accent); font-size: var(--fs-1); cursor: pointer; }
1171.thread textarea { width: 100%; }
1172.thread.composing p, .thread details.threadreply p { margin: var(--sp-2) 0 0; }
1173
1174/* forms: every control shares one shell, so a select reads as a sibling
1175   of the text inputs beside it rather than as an OS control dropped in */
1176input, textarea, button, select { font: inherit; color: inherit; }
1177input[type="text"], input[type="search"], input[type="email"],
1178input[type="password"], input:not([type]), textarea, select {
1179  background: var(--bg);
1180  color: var(--fg);
1181  border: 1px solid var(--line);
1182  border-radius: var(--r-md);
1183  padding: var(--sp-2) var(--sp-3);
1184  line-height: 1.4;
1185}
1186textarea { width: 100%; resize: vertical; }
1187textarea[name="content"] { font-family: var(--mono); font-size: var(--fs-1); }
1188/* the native arrow cannot be restyled, so draw our own and match the
1189   inputs exactly; --chev carries the stroke colour per scheme */
1190select {
1191  appearance: none;
1192  padding-right: 1.9rem;
1193  background-image: var(--chev);
1194  background-repeat: no-repeat;
1195  background-position: right 0.6rem center;
1196  background-size: 0.62rem;
1197  cursor: pointer;
1198}
1199input:hover, textarea:hover, select:hover { border-color: var(--muted); }
1200/* the global :focus-visible ring covers these; only the border moves */
1201input:focus, textarea:focus, select:focus { border-color: var(--accent); }
1202input[type="radio"] { accent-color: var(--fill); }
1203label:has(> input[type="radio"]), label:has(> input[type="checkbox"]),
1204label:has(> select) { display: inline-flex; align-items: center; gap: var(--sp-2); }
1205
1206/* A radio keeps the native control: a circle takes one answer, a square
1207   takes any number, and that shape is the cue that survives greyscale,
1208   high contrast and colour blindness. The checkbox is drawn instead,
1209   because at the size the OS renders it it reads as an afterthought
1210   beside the button it shares a row with. The square is kept square —
1211   the size changes, the cue does not. */
1212input[type="checkbox"] {
1213  appearance: none;
1214  flex: none;
1215  /* the height of a text input, a select and a button, so a settings row
1216     ends in one column of controls rather than a button and a speck */
1217  width: 2.5rem;
1218  height: 2.5rem;
1219  /* an empty box has nothing but its border to announce it, so the border
1220     is --muted rather than the --line the sized controls can afford */
1221  border: 1px solid var(--muted);
1222  border-radius: var(--r-sm);
1223  background: var(--bg);
1224  cursor: pointer;
1225}
1226input[type="checkbox"]:not(:checked):hover { border-color: var(--fg); }
1227input[type="checkbox"]:checked {
1228  background: var(--fill);
1229  border-color: var(--fill);
1230  /* the mark sits on the blue ground, so it is white in both schemes */
1231  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");
1232  background-repeat: no-repeat;
1233  background-position: center;
1234  background-size: 1.2rem;
1235}
1236
1237/* a segmented control for a small, mutually exclusive set: real radios
1238   underneath, so arrow keys and screen readers behave as they always did */
1239fieldset.segmented { border: 0; margin: 0 0 var(--sp-4); padding: 0; }
1240fieldset.segmented legend {
1241  padding: 0;
1242  margin-bottom: var(--sp-2);
1243  color: var(--muted);
1244  font-size: var(--fs-2);
1245}
1246fieldset.segmented .options { display: inline-flex; }
1247fieldset.segmented label { gap: 0; }
1248fieldset.segmented input {
1249  position: absolute;
1250  width: 1px;
1251  height: 1px;
1252  opacity: 0;
1253  pointer-events: none;
1254}
1255fieldset.segmented span {
1256  display: block;
1257  padding: var(--sp-2) var(--sp-4);
1258  border: 1px solid var(--line);
1259  font-size: var(--fs-2);
1260  cursor: pointer;
1261}
1262fieldset.segmented label + label span { border-left: 0; }
1263fieldset.segmented label:first-child span { border-radius: var(--r-sm) 0 0 var(--r-sm); }
1264fieldset.segmented label:last-child span { border-radius: 0 var(--r-sm) var(--r-sm) 0; }
1265fieldset.segmented span:hover { border-color: var(--muted); }
1266fieldset.segmented input:checked + span {
1267  background: var(--fill);
1268  border-color: var(--fill);
1269  color: #ffffff;
1270  font-weight: 600;
1271}
1272fieldset.segmented input:focus-visible + span {
1273  outline: 2px solid var(--accent);
1274  outline-offset: 2px;
1275}
1276
1277/* remaining input states */
1278::placeholder { color: var(--muted); opacity: 1; }
1279::selection { background: var(--fill); color: #ffffff; }
1280/* :user-invalid, not :invalid — a required field is invalid the moment
1281   the page loads, and colouring it red before anyone has typed is a
1282   scolding, not a hint */
1283input:user-invalid, textarea:user-invalid {
1284  border-color: var(--bad);
1285}
1286.hint { display: block; color: var(--muted); font-size: var(--fs-1); margin-top: var(--sp-1); }
1287button {
1288  background: var(--fill);
1289  color: #ffffff;
1290  border: none;
1291  border-radius: var(--r-md);
1292  padding: var(--sp-2) var(--sp-4);
1293  font-weight: 600;
1294  cursor: pointer;
1295}
1296button:hover { filter: brightness(1.15); }
1297
1298/* 404 */
1299.notfound { text-align: center; padding: var(--sp-6) 0; }
1300.notfound .bigcode {
1301  font-family: var(--mono);
1302  font-size: 4rem;
1303  color: var(--faint);
1304  -webkit-text-stroke: 1px var(--line);
1305  margin: 0;
1306  line-height: 1;
1307}
1308.notfound h1 { margin-top: var(--sp-3); }
1309
1310/* footer */
1311footer {
1312  border-top: 1px solid var(--faint);
1313  padding: var(--sp-4) var(--sp-5) var(--sp-5);
1314  width: 100%;
1315  color: var(--muted);
1316  font-size: var(--fs-1);
1317}
1318footer p { margin: 0; }
1319footer a { color: var(--muted); text-decoration: underline; }
1320footer a:hover { color: var(--accent); }
1321
1322/* search */
1323form.searchform {
1324  display: flex;
1325  gap: var(--sp-2);
1326  margin: var(--sp-3) 0 var(--sp-4);
1327}
1328form.searchform input[type="search"] { flex: 1; max-width: 32rem; }
1329form.searchform.compact { margin: 0; }
1330form.searchform.compact input[type="search"] {
1331  padding: var(--sp-1) var(--sp-3);
1332  font-size: var(--fs-2);
1333  min-width: 18rem;
1334}
1335ul.matchlist { list-style: none; margin: var(--sp-3) 0; padding: 0; }
1336ul.matchlist li { margin-bottom: var(--sp-3); }
1337ul.matchlist .matchpath { font-family: var(--mono); font-size: var(--fs-1); }
1338pre.matchline {
1339  background: var(--code-bg);
1340  border: 1px solid var(--faint);
1341  border-radius: var(--r-md);
1342  padding: var(--sp-1) var(--sp-3);
1343  margin: var(--sp-1) 0 0;
1344  overflow-x: auto;
1345}
1346pre.matchline mark {
1347  background: color-mix(in srgb, var(--warn) 25%, transparent);
1348  color: inherit;
1349  border-radius: 2px;
1350}
1351
1352/* wiki */
1353.wikilayout { display: flex; gap: var(--sp-5); align-items: flex-start; }
1354.wikipage { flex: 1; min-width: 0; }
1355.wikinav { flex: none; width: 13rem; padding-top: var(--sp-4); }
1356.wikinav ul { list-style: none; margin: var(--sp-1) 0 var(--sp-4); padding: 0; }
1357.wikinav li a { display: block; padding: 0.15rem 0; color: var(--fg); }
1358.wikinav li a:hover { color: var(--accent); text-decoration: none; }
1359.wikinav li a.active { color: var(--accent); font-weight: 550; }
1360@media (max-width: 40rem) {
1361  /* Column direction + the desktop align-items:flex-start lets .wikipage
1362     take its content's intrinsic width, overflowing the viewport (and
1363     triggering iOS font inflation). Stretch pins it to the container. */
1364  .wikilayout { flex-direction: column; align-items: stretch; }
1365  .wikinav { width: auto; padding-top: 0; }
1366}
1367
1368/* blame */
1369.blame {
1370  border: 1px solid var(--line);
1371  border-radius: var(--rad-2, 6px);
1372  overflow: hidden;
1373}
1374.blamehunk {
1375  display: flex;
1376  gap: var(--sp-3);
1377  border-top: 1px solid var(--line);
1378  align-items: flex-start;
1379}
1380.blamehunk:first-child { border-top: none; }
1381.blameinfo {
1382  flex: none;
1383  width: 17rem;
1384  padding: var(--sp-2) var(--sp-3);
1385  min-width: 0;
1386}
1387.blameinfo .subject {
1388  margin: 0;
1389  font-size: var(--fs-1);
1390  white-space: nowrap;
1391  overflow: hidden;
1392  text-overflow: ellipsis;
1393}
1394.blameinfo .meta { margin: 0; font-size: var(--fs-1); color: var(--muted); }
1395.blamecode {
1396  flex: 1;
1397  margin: 0;
1398  padding: var(--sp-2) var(--sp-3) var(--sp-2) 0;
1399  overflow-x: auto;
1400  border-left: 1px solid var(--line);
1401  align-self: stretch;
1402}
1403.lineno {
1404  display: inline-block;
1405  min-width: 3.5em;
1406  padding-right: 1em;
1407  text-align: right;
1408  color: var(--muted);
1409  user-select: none;
1410}
1411
1412/* mobile */
1413@media (max-width: 40rem) {
1414  .container { padding-left: var(--sp-4); padding-right: var(--sp-4); }
1415  main.container { padding-top: var(--sp-2); }
1416  .repogrid { grid-template-columns: 1fr; }
1417  .thread { margin-left: var(--sp-3); }
1418  /* the commit subject is the widest thing in the listing and the one
1419     column a phone can do without: the tipbar above already names the
1420     latest commit. */
1421  table.tree td.lastcommit, table.tree th.lastcommit { display: none; }
1422  table.tree td.mode, table.tree th.mode { display: none; }
1423  .clone, .pathbar { overflow-wrap: anywhere; }
1424  .readme, .code { padding: var(--sp-3); }
1425  .blamehunk { flex-direction: column; gap: 0; }
1426  .blameinfo { width: auto; }
1427  .blamecode { border-left: none; padding-left: var(--sp-3); width: 100%; }
1428  .cardhead {
1429    margin: calc(-1 * var(--sp-3)) calc(-1 * var(--sp-3)) var(--sp-3);
1430    padding: var(--sp-2) var(--sp-3);
1431  }
1432}
1433
1434/* tree listing: the last commit is what a file table is scanned for, so
1435   it leads. The file's own facts moved to the blob page. */
1436.tipbar {
1437  display: flex;
1438  align-items: center;
1439  gap: var(--sp-3);
1440  padding: var(--sp-2) var(--sp-4);
1441  background: var(--hover);
1442  border: 1px solid var(--line);
1443  border-bottom: 0;
1444  border-radius: var(--r-lg) var(--r-lg) 0 0;
1445  font-size: var(--fs-2);
1446}
1447.tipbar .who { font-weight: 600; }
1448.tipbar .subject { color: var(--fg); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1449.tipbar .subject:hover { color: var(--accent); }
1450.tipbar .spacer { flex: 1; }
1451.tipbar .age { color: var(--muted); white-space: nowrap; }
1452.tipbar + table.tree { border-radius: 0 0 var(--r-lg) var(--r-lg); }
1453table.tree th.lastcommit, table.tree td.lastcommit { width: 55%; }
1454table.tree td.lastcommit a { color: var(--muted); display: block; overflow: hidden;
1455                             text-overflow: ellipsis; white-space: nowrap; }
1456table.tree td.lastcommit a:hover { color: var(--accent); }
1457table.tree th.age, table.tree td.age {
1458  text-align: right;
1459  white-space: nowrap;
1460  color: var(--muted);
1461  font-variant-numeric: tabular-nums;
1462}
1463table.tree td.name { width: 25%; white-space: nowrap; }
1464table.tree td.name.dir a { color: var(--accent); }
1465p.clone { margin: 0 0 var(--sp-3); }
1466p.filefacts { color: var(--muted); font-size: var(--fs-1); margin: 0 0 var(--sp-3); }
1467.pathbar .act { font-size: var(--fs-1); }
1468
1469/* merge request: a two-column split, so state has somewhere to live that
1470   is not a run-on sentence under the title */
1471.withaside {
1472  display: grid;
1473  grid-template-columns: minmax(0, 1fr) 15rem;
1474  gap: var(--sp-5);
1475  align-items: start;
1476}
1477.withaside .mainside { min-width: 0; }
1478@media (max-width: 62rem) { .withaside { grid-template-columns: 1fr; } }
1479
1480.aside { font-size: var(--fs-2); }
1481.aside .grp { border-top: 1px solid var(--line); padding: var(--sp-3) 0; }
1482.aside .grp:first-child { border-top: 0; padding-top: 0; }
1483.aside h2 {
1484  margin: 0 0 var(--sp-2);
1485  font-family: var(--mono);
1486  font-size: var(--fs-0);
1487  font-weight: 500;
1488  letter-spacing: 0.1em;
1489  text-transform: uppercase;
1490  color: var(--muted);
1491}
1492.aside .row { display: flex; align-items: center; gap: var(--sp-2); margin: 0 0 var(--sp-1); flex-wrap: wrap; }
1493.aside .row:last-child { margin-bottom: 0; }
1494.aside .none { color: var(--muted); }
1495/* a row's second line: when it happened, under what it happened to. The
1496   extra gap keeps the pair reading as one entry. */
1497.aside .row .sub { flex-basis: 100%; color: var(--muted); font-size: var(--fs-0); }
1498.aside .row:has(.sub) { margin-bottom: var(--sp-2); }
1499.aside .dot { width: 0.45rem; height: 0.45rem; flex: none; border-radius: 50%; background: var(--muted); }
1500.aside .dot.ok { background: var(--ok); }
1501.aside .dot.bad { background: var(--bad); }
1502.aside .dot.pend { background: var(--mark); }
1503
1504/* sub-navigation within an item; orange marks position as it does above */
1505nav.subtabs {
1506  display: flex;
1507  gap: var(--sp-4);
1508  border-bottom: 1px solid var(--line);
1509  margin: 0 0 var(--sp-4);
1510}
1511nav.subtabs a {
1512  display: inline-flex;
1513  align-items: center;
1514  gap: var(--sp-2);
1515  padding: 0 0 var(--sp-2);
1516  color: var(--muted);
1517  font-size: var(--fs-2);
1518  border-bottom: 2px solid transparent;
1519  margin-bottom: -1px;
1520}
1521nav.subtabs a:hover { color: var(--fg); text-decoration: none; }
1522nav.subtabs a[aria-current] { color: var(--fg); font-weight: 700; border-bottom-color: var(--mark); }
1523nav.subtabs a i {
1524  font-style: normal;
1525  font-family: var(--mono);
1526  font-size: var(--fs-0);
1527  background: var(--hover);
1528  border-radius: var(--r-sm);
1529  padding: 0 0.35rem;
1530}
1531p.diffstat { color: var(--muted); font-size: var(--fs-2); margin: 0 0 var(--sp-3); }
1532p.diffstat .add { color: var(--ok); }
1533p.diffstat .del { color: var(--bad); }