internal/web/static/style.css

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

1551 lines · 53856 bytes

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