internal/web/static/style.css

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

1491 lines · 50590 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}
 594form.setform label { min-width: 12rem; color: var(--muted); font-size: var(--fs-2); }
 595form.setform input[type="text"], form.setform select { flex: 1 1 16rem; }
 596form.setform input[type="number"] { width: 5rem; }
 597ul.protlist { list-style: none; padding: 0; margin: var(--sp-2) 0; }
 598ul.protlist li { padding: var(--sp-1) 0; display: flex; gap: var(--sp-3); align-items: baseline; }
 599
 600/* branch picker on the new merge request form */
 601p.branchpick {
 602  display: flex;
 603  flex-wrap: wrap;
 604  align-items: center;
 605  gap: var(--sp-2);
 606}
 607p.branchpick label { color: var(--muted); font-size: var(--fs-2); }
 608p.branchpick select { flex: 1 1 12rem; }
 609
 610/* merge request actions in the aside: stacked controls, full width */
 611.aside form.actions {
 612  display: flex;
 613  flex-wrap: wrap;
 614  gap: var(--sp-2);
 615  margin-top: var(--sp-2);
 616}
 617.aside form.actions button { flex: 1 1 auto; }
 618.aside form.actions select { width: 100%; }
 619form.threadact { margin: var(--sp-1) 0 0; padding: 0 var(--sp-3) var(--sp-2); }
 620
 621.meta { color: var(--muted); font-size: var(--fs-1); }
 622svg.icon { vertical-align: -0.125em; }
 623.lede { font-size: var(--fs-3); margin: var(--sp-2) 0; }
 624
 625/* repo listings (explore, owner pages, dashboard): one row per repo */
 626ul.repolist {
 627  list-style: none;
 628  margin: var(--sp-3) 0 var(--sp-5);
 629  padding: 0;
 630  background: var(--surface);
 631  border: 1px solid var(--line);
 632  border-radius: var(--r-lg);
 633  overflow: hidden;
 634}
 635ul.repolist li { padding: var(--sp-3) var(--sp-4); border-bottom: 1px solid var(--faint); }
 636/* pinned repos: a compact wrap of name pills, no description or meta */
 637ul.pinlist {
 638  list-style: none;
 639  margin: var(--sp-3) 0 var(--sp-5);
 640  padding: 0;
 641  display: flex;
 642  flex-wrap: wrap;
 643  gap: var(--sp-2);
 644}
 645ul.pinlist li {
 646  background: var(--surface);
 647  border: 1px solid var(--line);
 648  border-radius: var(--r-md);
 649  padding: var(--sp-1) var(--sp-3);
 650}
 651ul.pinlist li:hover { background: var(--hover); }
 652ul.pinlist li a { color: var(--fg); }
 653ul.pinlist li a:hover { color: var(--accent); text-decoration: none; }
 654ul.pinlist .sep { color: var(--muted); margin: 0 0.15em; }
 655ul.repolist li:last-child { border-bottom: none; }
 656ul.repolist li:hover { background: var(--hover); }
 657ul.repolist li.empty { color: var(--muted); }
 658ul.repolist p { margin: 0; }
 659ul.repolist .reponame { font-size: var(--fs-3); }
 660ul.repolist .reponame a { color: var(--fg); }
 661ul.repolist .reponame a strong { color: var(--accent); font-weight: 600; }
 662ul.repolist .reponame .sep { color: var(--muted); margin: 0 0.1em; }
 663ul.repolist .desc { margin-top: 0.1rem; }
 664ul.repolist .topics { margin-top: var(--sp-1); }
 665ul.repolist .meta { margin-top: var(--sp-1); }
 666
 667/* owner profile header */
 668.profilehead {
 669  border-bottom: 1px solid var(--faint);
 670  padding-bottom: var(--sp-4);
 671  margin-bottom: var(--sp-4);
 672}
 673.profilehead h1 { margin: var(--sp-4) 0 var(--sp-1); }
 674.profilehead .lede { margin: 0 0 var(--sp-1); }
 675.profilehead .meta { margin: var(--sp-1) 0 0; }
 676h2 .count { color: var(--muted); font-weight: 400; font-size: var(--fs-2); }
 677
 678/* activity graph: 53 week columns x 7 day rows, accent-tinted levels */
 679.actgraph-scroll { overflow-x: auto; padding-bottom: var(--sp-1); }
 680.actgraph { display: flex; gap: 3px; width: max-content; }
 681.actweek { display: flex; flex-direction: column; gap: 3px; }
 682.actday {
 683  width: 11px;
 684  height: 11px;
 685  border-radius: 2px;
 686  background: var(--faint);
 687}
 688.actday.pad { background: transparent; }
 689.actday.l1 { background: color-mix(in srgb, var(--accent) 25%, var(--bg)); }
 690.actday.l2 { background: color-mix(in srgb, var(--accent) 50%, var(--bg)); }
 691.actday.l3 { background: color-mix(in srgb, var(--accent) 75%, var(--bg)); }
 692.actday.l4 { background: var(--accent); }
 693
 694/* heading row with actions on the right */
 695.headrow {
 696  display: flex;
 697  align-items: baseline;
 698  gap: var(--sp-4);
 699  flex-wrap: wrap;
 700}
 701.headrow .spacer { flex: 1; }
 702
 703/* member and org chips on owner pages */
 704a.memberchip {
 705  display: inline-block;
 706  border: 1px solid var(--line);
 707  border-radius: var(--r-pill);
 708  padding: 0.05rem 0.6rem;
 709  color: var(--fg);
 710  font-size: var(--fs-1);
 711  margin: 0 0.15rem 0.15rem 0;
 712}
 713a.memberchip:hover { text-decoration: none; border-color: var(--accent); color: var(--accent); }
 714a.memberchip .role { color: var(--muted); }
 715
 716/* landing page */
 717.landing { max-width: 44rem; padding: var(--sp-6) 0; }
 718.landing h1 { font-size: 1.8rem; margin-bottom: var(--sp-3); }
 719.landing .lede { font-size: var(--fs-4); color: var(--muted); line-height: 1.7; margin: 0 0 var(--sp-4); }
 720pre.quickstart {
 721  background: var(--code-bg);
 722  border: 1px solid var(--line);
 723  border-radius: var(--r-md);
 724  padding: var(--sp-3) var(--sp-4);
 725  overflow-x: auto;
 726  margin: 0 0 var(--sp-4);
 727}
 728.landing .explorelink { font-weight: 550; }
 729
 730/* index toolbar */
 731p.toolbar { color: var(--muted); font-size: var(--fs-2); margin-top: var(--sp-4); }
 732form.inline { display: inline; }
 733button.linklike {
 734  background: none;
 735  border: none;
 736  color: var(--accent);
 737  padding: 0;
 738  font: inherit;
 739  font-size: var(--fs-2);
 740  cursor: pointer;
 741}
 742button.linklike:hover { text-decoration: underline; filter: none; }
 743
 744/* cards: README, comments */
 745.readme, .code {
 746  background: var(--surface);
 747  border: 1px solid var(--line);
 748  border-radius: var(--r-lg);
 749  padding: var(--sp-4) var(--sp-5);
 750  margin-top: var(--sp-4);
 751  overflow-x: auto;
 752}
 753/* Highlighting is class-based with a palette per scheme, so code blocks
 754   follow the site's own colors. */
 755.code { background: var(--code-bg); color: var(--fg); padding: var(--sp-3) var(--sp-4); border-color: var(--line); }
 756.code pre { margin: 0; background: transparent !important; }
 757@media (prefers-color-scheme: dark) {
 758  .code { border-color: var(--line); }
 759}
 760
 761/* rendered markdown / org / html */
 762.rendered { max-width: 100%; overflow-wrap: anywhere; }
 763/* line length is a reading constraint, so it applies to prose only —
 764   tables, code blocks and images keep the full column */
 765.rendered > p,
 766.rendered > ul,
 767.rendered > ol,
 768.rendered > blockquote { max-width: 78ch; }
 769.rendered pre {
 770  background: var(--code-bg);
 771  border: 1px solid var(--line);
 772  padding: var(--sp-3) var(--sp-4);
 773  border-radius: var(--r-md);
 774  overflow-x: auto;
 775}
 776.rendered code {
 777  background: var(--code-bg);
 778  border: 1px solid var(--line);
 779  padding: 0.05rem 0.3rem;
 780  border-radius: var(--r-sm);
 781}
 782.rendered pre code { background: transparent; border: none; padding: 0; }
 783.rendered blockquote {
 784  border-left: 3px solid var(--line);
 785  margin-left: 0;
 786  padding-left: var(--sp-4);
 787  color: var(--muted);
 788}
 789.rendered img { max-width: 100%; }
 790.rendered h1, .rendered h2 { border-bottom: 1px solid var(--faint); padding-bottom: var(--sp-1); }
 791.rendered table {
 792  border-collapse: collapse;
 793  margin: var(--sp-3) 0;
 794  display: block;
 795  max-width: 100%;
 796  overflow-x: auto;
 797}
 798.rendered th, .rendered td {
 799  border: 1px solid var(--line);
 800  padding: var(--sp-1) var(--sp-3);
 801  text-align: left;
 802  overflow-wrap: normal; /* scroll the table, don't shatter words */
 803}
 804.rendered th { background: var(--surface); font-weight: 600; }
 805.blobimage { text-align: center; }
 806.blobimage img {
 807  max-width: 100%;
 808  border: 1px solid var(--faint);
 809  border-radius: var(--r-md);
 810  background: var(--surface);
 811}
 812
 813pre.message {
 814  background: var(--code-bg);
 815  border: 1px solid var(--line);
 816  padding: var(--sp-3) var(--sp-4);
 817  border-radius: var(--r-md);
 818  white-space: pre-wrap;
 819  overflow-wrap: anywhere;
 820}
 821
 822/* repo facts: the counts a visitor sizes a project up with */
 823.teambody { padding: var(--sp-3); background: var(--bg); }
 824.teambody .memberchip { margin-right: var(--sp-2); }
 825
 826form.logfilter {
 827  display: flex; align-items: center; gap: var(--sp-2);
 828  margin: 0 0 var(--sp-4); flex-wrap: wrap;
 829}
 830form.logfilter input { flex: 1; min-width: 12rem; max-width: 28rem; }
 831
 832/* account settings */
 833table.keys td { padding: var(--sp-2) var(--sp-4) var(--sp-2) 0; }
 834/* these tables sit flush with the page text, so the header row drops the
 835   left padding the card-style listings use */
 836table.keys tr.cols th { padding-left: 0; background: transparent; }
 837table.keys td.mono, .mono { font-family: var(--mono); font-size: var(--fs-1); overflow-wrap: anywhere; }
 838table.keys td.act { text-align: right; }
 839ul.plain { list-style: none; margin: 0 0 var(--sp-3); padding: 0; }
 840ul.plain li { padding: var(--sp-1) 0; display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
 841form.setform.stack { display: block; }
 842form.setform.stack textarea, form.setform.stack select { width: 100%; max-width: 48rem; display: block; margin-bottom: var(--sp-2); }
 843p.notice {
 844  border-left: 3px solid var(--ok);
 845  background: var(--hover);
 846  padding: var(--sp-2) var(--sp-3);
 847  margin: 0 0 var(--sp-4);
 848}
 849
 850.facts { margin: 0 0 var(--sp-4); }
 851.facts .counts {
 852  display: flex; flex-wrap: wrap; gap: var(--sp-1) var(--sp-4);
 853  margin: 0 0 var(--sp-3); font-size: var(--fs-2);
 854}
 855.facts .counts a { color: var(--fg); text-decoration: none; }
 856.facts .counts a:hover { color: var(--accent); text-decoration: underline; }
 857.facts .counts .fact { color: var(--muted); }
 858.facts .counts strong { font-weight: 600; }
 859
 860.langbar {
 861  display: flex; height: 0.5rem; margin: 0 0 var(--sp-2);
 862  border-radius: var(--r-sm); overflow: hidden; background: var(--faint);
 863}
 864.langbar .lang { display: block; min-width: 2px; }
 865.langs {
 866  display: flex; flex-wrap: wrap; gap: var(--sp-1) var(--sp-4);
 867  margin: 0 0 var(--sp-3); font-size: var(--fs-1);
 868}
 869.langs .lang-name { display: inline-flex; align-items: center; gap: var(--sp-1); }
 870.langs .muted { color: var(--muted); }
 871.langs .dot { width: 0.6rem; height: 0.6rem; border-radius: 50%; display: inline-block; }
 872/* named where it reads as the language's own color, neutral otherwise */
 873.lang { background: var(--neutral); }
 874.lang-go { background: #00add8; }
 875.lang-rust { background: #dea584; }
 876.lang-c, .lang-c- { background: #555555; }
 877.lang-python { background: #3572a5; }
 878.lang-javascript { background: #f1e05a; }
 879.lang-typescript { background: #3178c6; }
 880.lang-shell { background: #89e051; }
 881.lang-ruby { background: #701516; }
 882.lang-java { background: #b07219; }
 883.lang-swift { background: #f05138; }
 884.lang-html { background: #e34c26; }
 885.lang-css { background: #563d7c; }
 886.lang-org { background: #77aa99; }
 887.lang-markdown { background: #083fa1; }
 888.lang-emacs-lisp { background: #c065db; }
 889.lang-nix { background: #7e7eff; }
 890.lang-sql { background: #e38c00; }
 891
 892.contribs {
 893  display: flex; flex-wrap: wrap; align-items: baseline;
 894  gap: var(--sp-1) var(--sp-3); margin: 0; font-size: var(--fs-1);
 895}
 896.contribs .label { color: var(--muted); }
 897
 898/* diffs: one foldable section per file, a table so the line-number
 899   gutters stay put while the code scrolls */
 900details.difffold {
 901  border: 1px solid var(--line);
 902  border-radius: var(--r-md);
 903  margin: 0 0 var(--sp-3);
 904  background: var(--code-bg);
 905}
 906details.difffold summary {
 907  cursor: pointer;
 908  display: flex;
 909  align-items: baseline;
 910  gap: var(--sp-3);
 911  padding: var(--sp-2) var(--sp-3);
 912  list-style: none;
 913  font-size: var(--fs-2);
 914}
 915details.difffold[open] summary { border-bottom: 1px solid var(--line); }
 916details.difffold summary::-webkit-details-marker { display: none; }
 917/* flex summaries lose the native disclosure marker; draw our own */
 918details.difffold summary::before { content: "\25b8"; color: var(--muted); }
 919details.difffold[open] summary::before { content: "\25be"; }
 920details.difffold summary:hover { background: var(--fill-subtle); }
 921summary .fpath { font-family: var(--mono); overflow-wrap: anywhere; }
 922summary .fpath .was { color: var(--muted); }
 923summary .fstat { margin-left: auto; font-family: var(--mono); white-space: nowrap; }
 924details.difffold summary .add { color: var(--ok); }
 925details.difffold summary .del { color: var(--bad); }
 926details.difffold > .none { padding: var(--sp-3); margin: 0; }
 927details.difffold .tablewrap { border-radius: 0 0 var(--r-md) var(--r-md); }
 928
 929table.difftable {
 930  border-collapse: collapse;
 931  width: 100%;
 932  font-family: var(--mono);
 933  font-size: var(--fs-1);
 934  line-height: 1.5;
 935}
 936table.difftable td {
 937  border: 0;
 938  padding: 0;
 939  vertical-align: top;
 940  font-size: inherit;
 941}
 942table.difftable td.ln {
 943  width: 1%;
 944  min-width: 2.5rem;
 945  padding: 0 var(--sp-2);
 946  text-align: right;
 947  color: var(--muted);
 948  user-select: none;
 949  border-right: 1px solid var(--line);
 950  white-space: nowrap;
 951}
 952/* not .code: that class is the blob/code-block style and carries its own
 953   background, which painted over every row tint */
 954table.difftable td.src {
 955  padding: 0 var(--sp-3);
 956  white-space: pre-wrap;
 957  overflow-wrap: anywhere;
 958}
 959/* the marker is decoration, so it lives in CSS and stays out of a copied
 960   selection */
 961table.difftable tr.add td.src::before { content: "+"; color: var(--ok); }
 962table.difftable tr.del td.src::before { content: "\2212"; color: var(--bad); }
 963table.difftable tr.ctx td.src::before { content: " "; }
 964table.difftable tr.add { background: var(--diff-add); }
 965table.difftable tr.del { background: var(--diff-del); }
 966table.difftable tr.hunk td, table.difftable tr.dmeta td {
 967  color: var(--muted);
 968  padding: var(--sp-1) var(--sp-3);
 969  background: var(--fill-subtle);
 970  border-top: 1px solid var(--line);
 971  border-bottom: 1px solid var(--line);
 972}
 973table.difftable tr.hunk td.src { color: var(--accent); }
 974table.difftable tr.threadrow td { padding: var(--sp-2) var(--sp-3); background: var(--bg); }
 975
 976/* badges: signature and check states. Semantic colors stay distinct:
 977   green verified/success, amber pending/stale, red bad/failure, muted
 978   unsigned. */
 979.badge, .chip {
 980  --chip: var(--neutral);
 981  display: inline-block;
 982  padding: 0.05rem 0.55rem;
 983  border-radius: var(--r-pill);
 984  font-size: var(--fs-0);
 985  font-weight: 500;
 986  line-height: 1.5;
 987  color: var(--chip);
 988  border: 1px solid color-mix(in srgb, var(--chip) 35%, transparent);
 989  background: color-mix(in srgb, var(--chip) 7%, transparent);
 990  white-space: nowrap;
 991  vertical-align: middle;
 992}
 993.chip-open { --chip: var(--ok); }
 994.chip-closed { --chip: var(--bad); }
 995.chip-merged, .chip-done { --chip: var(--done); }
 996.chip-neutral, .chip-source_gone { --chip: var(--neutral); }
 997.chip-stale { --chip: var(--warn); }
 998.chip.label { font-weight: 500; }
 999.chip.topic { --chip: var(--accent); }
1000.repohead .topics { margin: 0 0 var(--sp-2); }
1001.repohead .mirrorline { margin: 0 0 var(--sp-2); }
1002.mirrorerr { color: var(--bad); }
1003.badge-verified { --chip: var(--ok); }
1004.badge-unsigned { --chip: var(--neutral); }
1005.badge-signed_unknown_key { --chip: var(--warn); }
1006.badge-signed_email_mismatch { --chip: var(--bad); }
1007.badge-signed_key_expired { --chip: var(--warn); }
1008.badge-signed_key_revoked { --chip: var(--bad); }
1009.badge-bad_signature { --chip: var(--bad); }
1010.badge.check-success { --chip: var(--ok); }
1011.badge.check-pending { --chip: var(--warn); }
1012.badge.check-failure, .badge.check-error { --chip: var(--bad); }
1013.badge.check-running, .chip.check-running { --chip: var(--warn); }
1014.chip.check-success { --chip: var(--ok); }
1015.chip.check-pending { --chip: var(--warn); }
1016.chip.check-failure { --chip: var(--bad); }
1017pre.buildlog { max-height: 40rem; overflow: auto; }
1018span.check-success { color: var(--ok); }
1019span.check-pending { color: var(--warn); }
1020span.check-failure, span.check-error { color: var(--bad); }
1021
1022/* issue and MR listings */
1023.listhead {
1024  display: flex;
1025  align-items: baseline;
1026  gap: var(--sp-4);
1027  flex-wrap: wrap;
1028}
1029.listhead h2 { margin-bottom: var(--sp-2); }
1030nav.filters { display: flex; gap: var(--sp-1); }
1031nav.filters a {
1032  color: var(--muted);
1033  font-size: var(--fs-2);
1034  padding: 0.05rem var(--sp-2);
1035  border-radius: var(--r-pill);
1036}
1037nav.filters a:hover { color: var(--fg); background: var(--hover); text-decoration: none; }
1038nav.filters a.active { color: var(--accent); background: color-mix(in srgb, var(--accent) 10%, transparent); }
1039
1040ul.issuelist {
1041  list-style: none;
1042  margin: var(--sp-2) 0;
1043  padding: 0;
1044  background: var(--surface);
1045  border: 1px solid var(--line);
1046  border-radius: var(--r-lg);
1047  overflow: hidden;
1048}
1049ul.issuelist li {
1050  display: flex;
1051  align-items: baseline;
1052  gap: var(--sp-3);
1053  padding: var(--sp-3) var(--sp-4);
1054  border-bottom: 1px solid var(--faint);
1055}
1056ul.issuelist li:last-child { border-bottom: none; }
1057ul.issuelist li:hover { background: var(--hover); }
1058ul.issuelist li.empty { display: block; color: var(--muted); }
1059ul.issuelist .issuemain { flex: 1; min-width: 0; }
1060ul.issuelist p { margin: 0; }
1061ul.issuelist .title a { color: var(--fg); font-weight: 550; }
1062ul.issuelist .title a:hover { color: var(--accent); }
1063
1064/* releases */
1065article.release {
1066  background: var(--surface);
1067  border: 1px solid var(--line);
1068  border-radius: var(--r-lg);
1069  padding: var(--sp-3) var(--sp-4);
1070  margin: var(--sp-4) 0;
1071}
1072.releasehead { background: none; border: 0; padding: 0; }
1073.releasehead h3 { margin: 0 0 var(--sp-1); }
1074.releasehead .meta { margin: 0 0 var(--sp-2); }
1075table.assets { margin-top: var(--sp-2); }
1076table.assets td.size { color: var(--muted); white-space: nowrap; text-align: right; }
1077table.assets td.name { width: 100%; font-family: var(--mono); font-size: var(--fs-1); }
1078
1079/* milestones */
1080ul.milestonelist {
1081  list-style: none;
1082  margin: var(--sp-2) 0;
1083  padding: 0;
1084  border: 1px solid var(--line);
1085  border-radius: var(--r-lg);
1086  overflow: hidden;
1087}
1088ul.milestonelist li { padding: var(--sp-3) var(--sp-4); border-bottom: 1px solid var(--faint); }
1089ul.milestonelist li:last-child { border-bottom: none; }
1090ul.milestonelist li.empty { color: var(--muted); }
1091ul.milestonelist p { margin: 0; }
1092ul.milestonelist .title { font-weight: 550; }
1093.progress {
1094  height: 6px;
1095  background: var(--faint);
1096  border-radius: var(--r-pill);
1097  margin-top: var(--sp-2);
1098  overflow: hidden;
1099  max-width: 24rem;
1100}
1101.progress .bar { height: 100%; background: var(--ok); }
1102.listhead .spacer { flex: 1; }
1103
1104/* issue / MR detail */
1105h2.issuetitle { margin-bottom: var(--sp-1); font-size: var(--fs-5); }
1106.issuenumber { color: var(--muted); font-weight: 400; }
1107.issuemeta { color: var(--muted); font-size: var(--fs-2); margin-top: 0; }
1108.review { font-size: var(--fs-2); }
1109
1110/* comment cards; discussion stays at a readable measure */
1111article.comment, form.commentform {
1112  max-width: 48rem;
1113}
1114article.comment {
1115  border: 1px solid var(--faint);
1116  border-radius: var(--r-lg);
1117  margin: var(--sp-4) 0;
1118}
1119article.comment .commenthead {
1120  background: var(--surface);
1121  border-bottom: 1px solid var(--faint);
1122  border-radius: var(--r-lg) var(--r-lg) 0 0;
1123  padding: var(--sp-2) var(--sp-4);
1124  font-size: var(--fs-2);
1125}
1126article.comment .commenthead .when { color: var(--muted); }
1127article.comment .rendered { padding: 0 var(--sp-4); }
1128article.comment .rendered > :first-child { margin-top: var(--sp-3); }
1129article.comment .rendered > :last-child { margin-bottom: var(--sp-3); }
1130form.commentform { margin: var(--sp-4) 0; }
1131details.editbox { margin: var(--sp-2) 0; max-width: 48rem; }
1132details.editbox summary { cursor: pointer; color: var(--muted); font-size: var(--fs-2); }
1133details.editbox summary:hover { color: var(--accent); }
1134details.editbox input[type="text"] { width: 100%; }
1135.syscomment {
1136  max-width: 48rem;
1137  color: var(--muted);
1138  font-size: var(--fs-2);
1139  margin: var(--sp-3) 0;
1140  padding-left: var(--sp-4);
1141  border-left: 3px solid var(--faint);
1142}
1143.syscomment p { display: inline; margin: 0; }
1144
1145/* review threads under diff lines */
1146.thread {
1147  border: 1px solid var(--faint);
1148  border-left: 3px solid var(--accent);
1149  border-radius: var(--r-md);
1150  padding: var(--sp-2) var(--sp-4);
1151  margin: var(--sp-2) 0 var(--sp-2) var(--sp-6);
1152}
1153.thread.resolved { border-left-color: var(--ok); opacity: 0.75; }
1154.thread.stale { border-left-color: var(--warn); }
1155.thread p.commenthead { font-size: var(--fs-2); margin: var(--sp-2) 0 0; }
1156.thread .when { color: var(--muted); }
1157.thread .rendered p { margin: var(--sp-1) 0; }
1158.thread .threadstate { color: var(--muted); font-size: var(--fs-1); margin: var(--sp-1) 0 0; }
1159
1160/* forms: every control shares one shell, so a select reads as a sibling
1161   of the text inputs beside it rather than as an OS control dropped in */
1162input, textarea, button, select { font: inherit; color: inherit; }
1163input[type="text"], input[type="search"], input[type="email"],
1164input[type="password"], input:not([type]), textarea, select {
1165  background: var(--bg);
1166  color: var(--fg);
1167  border: 1px solid var(--line);
1168  border-radius: var(--r-md);
1169  padding: var(--sp-2) var(--sp-3);
1170  line-height: 1.4;
1171}
1172textarea { width: 100%; resize: vertical; }
1173textarea[name="content"] { font-family: var(--mono); font-size: var(--fs-1); }
1174/* the native arrow cannot be restyled, so draw our own and match the
1175   inputs exactly; --chev carries the stroke colour per scheme */
1176select {
1177  appearance: none;
1178  padding-right: 1.9rem;
1179  background-image: var(--chev);
1180  background-repeat: no-repeat;
1181  background-position: right 0.6rem center;
1182  background-size: 0.62rem;
1183  cursor: pointer;
1184}
1185input:hover, textarea:hover, select:hover { border-color: var(--muted); }
1186/* the global :focus-visible ring covers these; only the border moves */
1187input:focus, textarea:focus, select:focus { border-color: var(--accent); }
1188input[type="radio"], input[type="checkbox"] { accent-color: var(--fill); }
1189label:has(> input[type="radio"]), label:has(> input[type="checkbox"]),
1190label:has(> select) { display: inline-flex; align-items: center; gap: var(--sp-2); }
1191
1192/* Shape carries the meaning on these two and nothing else does: a circle
1193   takes one answer, a square takes any number. It survives colour being
1194   removed — greyscale, high contrast, colour blindness — so the native
1195   shapes stay and only the colour joins the system. Drawing them by hand
1196   would align the corners at the cost of the one cue that always works. */
1197input[type="radio"], input[type="checkbox"] { accent-color: var(--fill); }
1198
1199/* a segmented control for a small, mutually exclusive set: real radios
1200   underneath, so arrow keys and screen readers behave as they always did */
1201fieldset.segmented { border: 0; margin: 0 0 var(--sp-4); padding: 0; }
1202fieldset.segmented legend {
1203  padding: 0;
1204  margin-bottom: var(--sp-2);
1205  color: var(--muted);
1206  font-size: var(--fs-2);
1207}
1208fieldset.segmented .options { display: inline-flex; }
1209fieldset.segmented label { gap: 0; }
1210fieldset.segmented input {
1211  position: absolute;
1212  width: 1px;
1213  height: 1px;
1214  opacity: 0;
1215  pointer-events: none;
1216}
1217fieldset.segmented span {
1218  display: block;
1219  padding: var(--sp-2) var(--sp-4);
1220  border: 1px solid var(--line);
1221  font-size: var(--fs-2);
1222  cursor: pointer;
1223}
1224fieldset.segmented label + label span { border-left: 0; }
1225fieldset.segmented label:first-child span { border-radius: var(--r-sm) 0 0 var(--r-sm); }
1226fieldset.segmented label:last-child span { border-radius: 0 var(--r-sm) var(--r-sm) 0; }
1227fieldset.segmented span:hover { border-color: var(--muted); }
1228fieldset.segmented input:checked + span {
1229  background: var(--fill);
1230  border-color: var(--fill);
1231  color: #ffffff;
1232  font-weight: 600;
1233}
1234fieldset.segmented input:focus-visible + span {
1235  outline: 2px solid var(--accent);
1236  outline-offset: 2px;
1237}
1238
1239/* remaining input states */
1240::placeholder { color: var(--muted); opacity: 1; }
1241::selection { background: var(--fill); color: #ffffff; }
1242/* :user-invalid, not :invalid — a required field is invalid the moment
1243   the page loads, and colouring it red before anyone has typed is a
1244   scolding, not a hint */
1245input:user-invalid, textarea:user-invalid {
1246  border-color: var(--bad);
1247}
1248.hint { display: block; color: var(--muted); font-size: var(--fs-1); margin-top: var(--sp-1); }
1249button {
1250  background: var(--fill);
1251  color: #ffffff;
1252  border: none;
1253  border-radius: var(--r-md);
1254  padding: var(--sp-2) var(--sp-4);
1255  font-weight: 600;
1256  cursor: pointer;
1257}
1258button:hover { filter: brightness(1.15); }
1259
1260/* 404 */
1261.notfound { text-align: center; padding: var(--sp-6) 0; }
1262.notfound .bigcode {
1263  font-family: var(--mono);
1264  font-size: 4rem;
1265  color: var(--faint);
1266  -webkit-text-stroke: 1px var(--line);
1267  margin: 0;
1268  line-height: 1;
1269}
1270.notfound h1 { margin-top: var(--sp-3); }
1271
1272/* footer */
1273footer {
1274  border-top: 1px solid var(--faint);
1275  padding: var(--sp-4) var(--sp-5) var(--sp-5);
1276  width: 100%;
1277  color: var(--muted);
1278  font-size: var(--fs-1);
1279}
1280footer p { margin: 0; }
1281footer a { color: var(--muted); text-decoration: underline; }
1282footer a:hover { color: var(--accent); }
1283
1284/* search */
1285form.searchform {
1286  display: flex;
1287  gap: var(--sp-2);
1288  margin: var(--sp-3) 0 var(--sp-4);
1289}
1290form.searchform input[type="search"] { flex: 1; max-width: 32rem; }
1291form.searchform.compact { margin: 0; }
1292form.searchform.compact input[type="search"] {
1293  padding: var(--sp-1) var(--sp-3);
1294  font-size: var(--fs-2);
1295  min-width: 18rem;
1296}
1297ul.matchlist { list-style: none; margin: var(--sp-3) 0; padding: 0; }
1298ul.matchlist li { margin-bottom: var(--sp-3); }
1299ul.matchlist .matchpath { font-family: var(--mono); font-size: var(--fs-1); }
1300pre.matchline {
1301  background: var(--code-bg);
1302  border: 1px solid var(--faint);
1303  border-radius: var(--r-md);
1304  padding: var(--sp-1) var(--sp-3);
1305  margin: var(--sp-1) 0 0;
1306  overflow-x: auto;
1307}
1308pre.matchline mark {
1309  background: color-mix(in srgb, var(--warn) 25%, transparent);
1310  color: inherit;
1311  border-radius: 2px;
1312}
1313
1314/* wiki */
1315.wikilayout { display: flex; gap: var(--sp-5); align-items: flex-start; }
1316.wikipage { flex: 1; min-width: 0; }
1317.wikinav { flex: none; width: 13rem; padding-top: var(--sp-4); }
1318.wikinav ul { list-style: none; margin: var(--sp-1) 0 var(--sp-4); padding: 0; }
1319.wikinav li a { display: block; padding: 0.15rem 0; color: var(--fg); }
1320.wikinav li a:hover { color: var(--accent); text-decoration: none; }
1321.wikinav li a.active { color: var(--accent); font-weight: 550; }
1322@media (max-width: 40rem) {
1323  /* Column direction + the desktop align-items:flex-start lets .wikipage
1324     take its content's intrinsic width, overflowing the viewport (and
1325     triggering iOS font inflation). Stretch pins it to the container. */
1326  .wikilayout { flex-direction: column; align-items: stretch; }
1327  .wikinav { width: auto; padding-top: 0; }
1328}
1329
1330/* blame */
1331.blame {
1332  border: 1px solid var(--line);
1333  border-radius: var(--rad-2, 6px);
1334  overflow: hidden;
1335}
1336.blamehunk {
1337  display: flex;
1338  gap: var(--sp-3);
1339  border-top: 1px solid var(--line);
1340  align-items: flex-start;
1341}
1342.blamehunk:first-child { border-top: none; }
1343.blameinfo {
1344  flex: none;
1345  width: 17rem;
1346  padding: var(--sp-2) var(--sp-3);
1347  min-width: 0;
1348}
1349.blameinfo .subject {
1350  margin: 0;
1351  font-size: var(--fs-1);
1352  white-space: nowrap;
1353  overflow: hidden;
1354  text-overflow: ellipsis;
1355}
1356.blameinfo .meta { margin: 0; font-size: var(--fs-1); color: var(--muted); }
1357.blamecode {
1358  flex: 1;
1359  margin: 0;
1360  padding: var(--sp-2) var(--sp-3) var(--sp-2) 0;
1361  overflow-x: auto;
1362  border-left: 1px solid var(--line);
1363  align-self: stretch;
1364}
1365.lineno {
1366  display: inline-block;
1367  min-width: 3.5em;
1368  padding-right: 1em;
1369  text-align: right;
1370  color: var(--muted);
1371  user-select: none;
1372}
1373
1374/* mobile */
1375@media (max-width: 40rem) {
1376  .container { padding-left: var(--sp-4); padding-right: var(--sp-4); }
1377  main.container { padding-top: var(--sp-2); }
1378  .repogrid { grid-template-columns: 1fr; }
1379  .thread { margin-left: var(--sp-3); }
1380  /* the commit subject is the widest thing in the listing and the one
1381     column a phone can do without: the tipbar above already names the
1382     latest commit. */
1383  table.tree td.lastcommit, table.tree th.lastcommit { display: none; }
1384  table.tree td.mode, table.tree th.mode { display: none; }
1385  .clone, .pathbar { overflow-wrap: anywhere; }
1386  .readme, .code { padding: var(--sp-3); }
1387  .blamehunk { flex-direction: column; gap: 0; }
1388  .blameinfo { width: auto; }
1389  .blamecode { border-left: none; padding-left: var(--sp-3); width: 100%; }
1390  .cardhead {
1391    margin: calc(-1 * var(--sp-3)) calc(-1 * var(--sp-3)) var(--sp-3);
1392    padding: var(--sp-2) var(--sp-3);
1393  }
1394}
1395
1396/* tree listing: the last commit is what a file table is scanned for, so
1397   it leads. The file's own facts moved to the blob page. */
1398.tipbar {
1399  display: flex;
1400  align-items: center;
1401  gap: var(--sp-3);
1402  padding: var(--sp-2) var(--sp-4);
1403  background: var(--hover);
1404  border: 1px solid var(--line);
1405  border-bottom: 0;
1406  border-radius: var(--r-lg) var(--r-lg) 0 0;
1407  font-size: var(--fs-2);
1408}
1409.tipbar .who { font-weight: 600; }
1410.tipbar .subject { color: var(--fg); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1411.tipbar .subject:hover { color: var(--accent); }
1412.tipbar .spacer { flex: 1; }
1413.tipbar .age { color: var(--muted); white-space: nowrap; }
1414.tipbar + table.tree { border-radius: 0 0 var(--r-lg) var(--r-lg); }
1415table.tree th.lastcommit, table.tree td.lastcommit { width: 55%; }
1416table.tree td.lastcommit a { color: var(--muted); display: block; overflow: hidden;
1417                             text-overflow: ellipsis; white-space: nowrap; }
1418table.tree td.lastcommit a:hover { color: var(--accent); }
1419table.tree th.age, table.tree td.age {
1420  text-align: right;
1421  white-space: nowrap;
1422  color: var(--muted);
1423  font-variant-numeric: tabular-nums;
1424}
1425table.tree td.name { width: 25%; white-space: nowrap; }
1426table.tree td.name.dir a { color: var(--accent); }
1427p.clone { margin: 0 0 var(--sp-3); }
1428p.filefacts { color: var(--muted); font-size: var(--fs-1); margin: 0 0 var(--sp-3); }
1429.pathbar .act { font-size: var(--fs-1); }
1430
1431/* merge request: a two-column split, so state has somewhere to live that
1432   is not a run-on sentence under the title */
1433.withaside {
1434  display: grid;
1435  grid-template-columns: minmax(0, 1fr) 15rem;
1436  gap: var(--sp-5);
1437  align-items: start;
1438}
1439.withaside .mainside { min-width: 0; }
1440@media (max-width: 62rem) { .withaside { grid-template-columns: 1fr; } }
1441
1442.aside { font-size: var(--fs-2); }
1443.aside .grp { border-top: 1px solid var(--line); padding: var(--sp-3) 0; }
1444.aside .grp:first-child { border-top: 0; padding-top: 0; }
1445.aside h2 {
1446  margin: 0 0 var(--sp-2);
1447  font-family: var(--mono);
1448  font-size: var(--fs-0);
1449  font-weight: 500;
1450  letter-spacing: 0.1em;
1451  text-transform: uppercase;
1452  color: var(--muted);
1453}
1454.aside .row { display: flex; align-items: center; gap: var(--sp-2); margin: 0 0 var(--sp-1); flex-wrap: wrap; }
1455.aside .row:last-child { margin-bottom: 0; }
1456.aside .none { color: var(--muted); }
1457.aside .dot { width: 0.45rem; height: 0.45rem; flex: none; border-radius: 50%; background: var(--muted); }
1458.aside .dot.ok { background: var(--ok); }
1459.aside .dot.bad { background: var(--bad); }
1460.aside .dot.pend { background: var(--mark); }
1461
1462/* sub-navigation within an item; orange marks position as it does above */
1463nav.subtabs {
1464  display: flex;
1465  gap: var(--sp-4);
1466  border-bottom: 1px solid var(--line);
1467  margin: 0 0 var(--sp-4);
1468}
1469nav.subtabs a {
1470  display: inline-flex;
1471  align-items: center;
1472  gap: var(--sp-2);
1473  padding: 0 0 var(--sp-2);
1474  color: var(--muted);
1475  font-size: var(--fs-2);
1476  border-bottom: 2px solid transparent;
1477  margin-bottom: -1px;
1478}
1479nav.subtabs a:hover { color: var(--fg); text-decoration: none; }
1480nav.subtabs a[aria-current] { color: var(--fg); font-weight: 700; border-bottom-color: var(--mark); }
1481nav.subtabs a i {
1482  font-style: normal;
1483  font-family: var(--mono);
1484  font-size: var(--fs-0);
1485  background: var(--hover);
1486  border-radius: var(--r-sm);
1487  padding: 0 0.35rem;
1488}
1489p.diffstat { color: var(--muted); font-size: var(--fs-2); margin: 0 0 var(--sp-3); }
1490p.diffstat .add { color: var(--ok); }
1491p.diffstat .del { color: var(--bad); }