internal/web/static/style.css

1945 lines · 80380 bytes

   1/* gitbay stylesheet. Tokens first, on :root and again in the dark media
   2   query; components use tokens only. Two accents with separate jobs:
   3   blue is what you can do (links, primary buttons, focus, pressed
   4   toggles), orange is where you are and what wants you (current tab,
   5   rail counts, pending state, unverified signatures). Orange is never a
   6   button ground. internal/web/tokens_test.go measures every token;
   7   internal/web/classes_test.go checks every template class has a rule.
   8   No external requests: fonts are served from this origin. */
   9
  10@font-face {
  11  font-family: "Atkinson Hyperlegible Next";
  12  font-style: normal;
  13  font-weight: 400 700;
  14  font-display: swap;
  15  src: url(/static/fonts/atkinson-next-roman.woff2) format("woff2");
  16}
  17@font-face {
  18  font-family: "Atkinson Hyperlegible Next";
  19  font-style: italic;
  20  font-weight: 400 700;
  21  font-display: swap;
  22  src: url(/static/fonts/atkinson-next-italic.woff2) format("woff2");
  23}
  24@font-face {
  25  font-family: "Atkinson Hyperlegible Mono";
  26  font-style: normal;
  27  font-weight: 400 600;
  28  font-display: swap;
  29  src: url(/static/fonts/atkinson-mono-roman.woff2) format("woff2");
  30}
  31
  32:root {
  33  /* declare native light+dark support: correct form controls and
  34     scrollbars per scheme, and opt out of browser auto-darkening */
  35  color-scheme: light dark;
  36
  37  /* grounds: canvas, surface and inset are three steps, not one */
  38  --canvas: #ffffff;
  39  --surface: #ebedf1;
  40  --inset: #ffffff;
  41  --hover: #e1e4ea;
  42  --line: #8d939c;           /* 3.09:1 on canvas — a border you can see */
  43  --faint: #d8dce2;
  44
  45  /* ink */
  46  --fg: #1c1f24;
  47  --muted: #5a6270;
  48  --neutral: var(--muted);
  49
  50  /* blue: what you can do. --link is text, --fill is a ground; one blue
  51     cannot clear 4.5:1 as text and 3:1 as a ground in both schemes. */
  52  --link: #0033d6;
  53  --fill: #0000f0;
  54  --fill-fg: #ffffff;
  55  --focus: var(--link);
  56
  57  /* orange: where you are, what wants you. Two cuts because the floor
  58     differs by size: --mark for bars, --warn at text size. */
  59  --mark: #e4572e;
  60  --warn: #b8400f;
  61
  62  /* state */
  63  --ok: #0a7536;
  64  --bad: #c62828;
  65  --done: #6b21a8;
  66
  67  /* diff row tints: enough to scan, light enough that the syntax
  68     palette stays legible on them (TestSyntaxPaletteContrast) */
  69  --diff-add: #e4f6ea;
  70  --diff-del: #fdeaea;
  71
  72  /* the rail is black in both schemes: the one brand constant */
  73  --shell-bg: #000000;
  74  --shell-fg: #f2f2f2;
  75  --shell-muted: #8f8f8f;
  76  --shell-line: #1c1c1c;
  77  --shell-hover: #141414;
  78  --shell-mark: #ff6b3d;
  79
  80  /* the languages' own colours; the same in both schemes */
  81  --lang-go: #00add8;
  82  --lang-rust: #dea584;
  83  --lang-c: #555555;
  84  --lang-python: #3572a5;
  85  --lang-javascript: #f1e05a;
  86  --lang-typescript: #3178c6;
  87  --lang-shell: #89e051;
  88  --lang-ruby: #701516;
  89  --lang-java: #b07219;
  90  --lang-swift: #f05138;
  91  --lang-html: #e34c26;
  92  --lang-css: #563d7c;
  93  --lang-org: #77aa99;
  94  --lang-markdown: #083fa1;
  95  --lang-emacs-lisp: #c065db;
  96  --lang-nix: #7e7eff;
  97  --lang-sql: #e38c00;
  98
  99  --sans: "Atkinson Hyperlegible Next", -apple-system, BlinkMacSystemFont,
 100          "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
 101  --mono: "Atkinson Hyperlegible Mono", ui-monospace, "SF Mono",
 102          SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
 103
 104  /* 12, 13, 14, 16, 20, 24, 32 */
 105  --fs-0: 0.75rem;
 106  --fs-1: 0.8125rem;
 107  --fs-2: 0.875rem;
 108  --fs-3: 1rem;
 109  --fs-4: 1.25rem;
 110  --fs-5: 1.5rem;
 111  --fs-6: 2rem;
 112
 113  /* 4, 8, 12, 16, 24, 32, 48 */
 114  --sp-1: 4px;
 115  --sp-2: 8px;
 116  --sp-3: 12px;
 117  --sp-4: 16px;
 118  --sp-5: 24px;
 119  --sp-6: 32px;
 120  --sp-7: 48px;
 121
 122  /* the rail: a column the width of one hit target, a row one tall */
 123  --rail-hit: 44px;
 124  --rail-strip-h: 48px;
 125  --rail-mark: 24px;
 126  --rail-mark-box: 32px;
 127
 128  /* the one page container: header content, main and footer align on it */
 129  --container: 100rem;
 130
 131  --r-ctl: 4px;   /* controls and chips */
 132  --r-card: 6px;  /* cards, lists, code blocks */
 133  --shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
 134
 135  /* the native select arrow cannot be restyled, so draw our own;
 136     --chev carries the stroke colour per scheme */
 137  --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='%235a6270' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
 138}
 139@media (prefers-color-scheme: dark) {
 140  :root {
 141    /* the scheme in force, not just the tokens: light-dark() reads it,
 142       and themedCSS repeats this block for a page stamped dark */
 143    color-scheme: dark;
 144
 145    --canvas: #101114;
 146    --surface: #212429;
 147    --inset: #0b0c0e;
 148    --hover: #2b2f36;
 149    --line: #61666f;
 150    --faint: #2f333a;
 151
 152    --fg: #e9eaec;
 153    --muted: #a3a9b3;
 154    --neutral: var(--muted);
 155
 156    --link: #8b9bff;
 157    --fill: #4d61f0;
 158    --fill-fg: #ffffff;
 159    --focus: var(--link);
 160
 161    --mark: #ff6b3d;
 162    --warn: #ff8a63;
 163
 164    --ok: #3fce7a;
 165    --bad: #ff6b6b;
 166    --done: #c084fc;
 167
 168    --diff-add: #0d2a18;
 169    --diff-del: #2c1113;
 170
 171    --shell-bg: #000000;
 172    --shell-fg: #f2f2f2;
 173    --shell-muted: #8f8f8f;
 174    --shell-line: #1c1c1c;
 175    --shell-hover: #141414;
 176    --shell-mark: #ff6b3d;
 177
 178    /* the languages' own colours; the same in both schemes */
 179    --lang-go: #00add8;
 180    --lang-rust: #dea584;
 181    --lang-c: #555555;
 182    --lang-python: #3572a5;
 183    --lang-javascript: #f1e05a;
 184    --lang-typescript: #3178c6;
 185    --lang-shell: #89e051;
 186    --lang-ruby: #701516;
 187    --lang-java: #b07219;
 188    --lang-swift: #f05138;
 189    --lang-html: #e34c26;
 190    --lang-css: #563d7c;
 191    --lang-org: #77aa99;
 192    --lang-markdown: #083fa1;
 193    --lang-emacs-lisp: #c065db;
 194    --lang-nix: #7e7eff;
 195    --lang-sql: #e38c00;
 196
 197    --shadow: 0 4px 16px rgba(0, 0, 0, 0.5);
 198    --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='%23a3a9b3' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
 199  }
 200}
 201
 202/* a page stamped light keeps the light scheme under a dark OS: the token
 203   block above is guarded for it, and this is the other half of that */
 204:root[data-theme="light"] { color-scheme: light; }
 205
 206/* ---- base ---- */
 207* { box-sizing: border-box; }
 208html { font-size: 16px; -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
 209body {
 210  margin: 0;
 211  background: var(--canvas);
 212  color: var(--fg);
 213  font: var(--fs-3)/1.5 var(--sans);
 214  min-height: 100vh;
 215}
 216h1, h2, h3 { line-height: 1.25; font-weight: 600; }
 217h1 { font-size: var(--fs-5); margin: 0 0 var(--sp-3); text-wrap: balance; }
 218h2 { font-size: var(--fs-4); margin: var(--sp-6) 0 var(--sp-3); }
 219h3 { font-size: var(--fs-3); margin: var(--sp-5) 0 var(--sp-2); }
 220p { margin: 0 0 var(--sp-3); }
 221a { color: var(--link); text-decoration: none; }
 222a:hover { text-decoration: underline; }
 223/* a link inside running text is told apart by its underline: link and
 224   muted text are 1.07:1 apart in dark, and colour alone is not a cue
 225   (#232). Navigation, chips and buttons keep position as their cue. */
 226p a, .syscomment a { text-decoration: underline; }
 227/* a title or a path is the whole line: position is the cue */
 228p a.button, p a.btn, p a.chip, .repotitle a, p.title a, .pathbar a, .pager a { text-decoration: none; }
 229/* where a link shares its line with other text the underline is the cue
 230   again, path bar or not: a crumb beside the file name, a heading that
 231   names an owner, an empty-state note (#226) */
 232.crumbs a, h1 a, li.empty a { text-decoration: underline; }
 233a.xref { color: var(--link); text-decoration: none; }
 234
 235code, pre, .mono, .code, td.mode, td.size {
 236  font-family: var(--mono);
 237  font-size: var(--fs-1);
 238}
 239.mono { overflow-wrap: anywhere; }
 240
 241/* ---- focus: one authored ring, so keyboard position is never the
 242   browser's guess ---- */
 243:focus-visible {
 244  outline: 2px solid var(--focus);
 245  outline-offset: 2px;
 246  border-radius: 1px;
 247}
 248/* the rail is black in both schemes and --focus is a dark blue in the
 249   light one — a ring nobody could see there */
 250.rail :focus-visible { outline-color: var(--shell-mark); }
 251
 252/* visually hidden, still read aloud */
 253.vh {
 254  position: absolute;
 255  width: 1px; height: 1px;
 256  margin: -1px; padding: 0; border: 0;
 257  clip-path: inset(50%);
 258  overflow: hidden;
 259  white-space: nowrap;
 260}
 261
 262/* ---- skip link: the rail runs ahead of the content in source order ---- */
 263a.skip {
 264  position: absolute;
 265  left: var(--sp-3); top: -3rem;
 266  z-index: 50;
 267  display: flex;
 268  align-items: center;
 269  min-height: var(--rail-hit);
 270  background: var(--fill);
 271  color: var(--fill-fg);
 272  padding: var(--sp-2) var(--sp-4);
 273  font-weight: 600;
 274}
 275a.skip:focus { top: var(--sp-3); text-decoration: none; }
 276
 277/* ---- rail: one row of glyphs across the top, no labels ----
 278   One strip the height of a hit target, at every width: fixed squares,
 279   no wrapping and no scrolling. Every item is a square link carrying an
 280   inline svg, its name in an aria-label and in a visually hidden span, so
 281   the nav reads the same to a screen reader as the old text list did. */
 282.rail {
 283  height: var(--rail-strip-h);
 284  background: var(--shell-bg);
 285  color: var(--shell-fg);
 286  border-bottom: 1px solid var(--shell-line);
 287  display: flex;
 288  flex-direction: row;
 289  align-items: center;
 290  padding: 0 var(--sp-1);
 291}
 292a.brand {
 293  display: flex;
 294  align-items: center;
 295  justify-content: center;
 296  flex: none;
 297  width: var(--rail-mark-box);
 298  height: var(--rail-strip-h);
 299  color: var(--shell-fg);
 300}
 301a.brand:hover { text-decoration: none; }
 302a.brand svg.mark { display: block; flex: none; width: var(--rail-mark); height: var(--rail-mark); }
 303
 304ul.raillist {
 305  list-style: none;
 306  margin: 0;
 307  padding: 0;
 308  display: flex;
 309  align-items: center;
 310  min-width: 0;
 311}
 312/* the spacer that holds settings, admin and the account cell at the far
 313   end of the row */
 314.railgap { flex: 1; }
 315.railicon {
 316  position: relative;
 317  display: flex;
 318  align-items: center;
 319  justify-content: center;
 320  flex: none;
 321  width: var(--rail-hit);
 322  height: var(--rail-strip-h);
 323  padding: 0;
 324  border: 0;
 325  background: none;
 326  color: var(--shell-fg);
 327  cursor: pointer;
 328}
 329.railicon:hover { background: var(--shell-hover); text-decoration: none; }
 330/* orange marks position along the bottom edge — the rail's only accent */
 331.railicon[aria-current] {
 332  background: var(--shell-hover);
 333  box-shadow: inset 0 -2px 0 var(--shell-mark);
 334}
 335.count {
 336  font-size: var(--fs-0);
 337  font-weight: 600;
 338  letter-spacing: 0;
 339  background: var(--shell-mark);
 340  color: var(--shell-bg);
 341  padding: 0 5px;
 342  border-radius: var(--r-ctl);
 343  margin-left: auto;
 344}
 345/* the unread count is a badge on the bell's corner, not a trailing number */
 346.railicon .count {
 347  position: absolute;
 348  top: 2px;
 349  right: 2px;
 350  margin: 0;
 351  padding: 0 3px;
 352  line-height: 1.35;
 353}
 354.railfoot {
 355  flex: none;
 356  display: flex;
 357  align-items: center;
 358  min-width: 0;
 359}
 360.railfoot a { color: var(--shell-fg); }
 361.railfoot form { margin: 0; }
 362a.railuser {
 363  display: flex;
 364  align-items: center;
 365  justify-content: center;
 366  flex: none;
 367  width: var(--rail-hit);
 368  height: var(--rail-strip-h);
 369}
 370a.railuser:hover { text-decoration: none; }
 371/* the phone rail's overflow: every square .railopt hides, and log out,
 372   as a labelled list under the rail. Shown only below 34rem. */
 373.railmore { display: none; position: relative; }
 374.railmore > summary { list-style: none; cursor: pointer; }
 375.railmore > summary::-webkit-details-marker { display: none; }
 376.railmore[open] > summary { background: var(--shell-hover); }
 377.raildrop {
 378  position: absolute;
 379  top: 100%;
 380  right: 0;
 381  z-index: 20;
 382  min-width: 12rem;
 383  padding: var(--sp-1) 0;
 384  background: var(--shell-bg);
 385  border: 1px solid var(--shell-line);
 386  border-radius: var(--r-card);
 387  box-shadow: 0 6px 24px rgb(0 0 0 / 0.35);
 388}
 389.raildrop a, .raildrop button {
 390  display: flex;
 391  align-items: center;
 392  /* a button's own default is centred; the rows read as one list */
 393  justify-content: flex-start;
 394  gap: var(--sp-3);
 395  width: 100%;
 396  min-height: var(--rail-hit);
 397  padding: 0 var(--sp-4);
 398  border: 0;
 399  background: none;
 400  font: inherit;
 401  font-size: var(--fs-2);
 402  text-align: left;
 403  color: var(--shell-fg);
 404  cursor: pointer;
 405}
 406.raildrop a:hover, .raildrop button:hover { background: var(--shell-hover); text-decoration: none; }
 407.raildrop form { margin: 0; }
 408.raildrop svg { flex: none; }
 409a.railuser .avatar { width: var(--rail-mark); height: var(--rail-mark); }
 410.avatar {
 411  display: inline-grid;
 412  place-items: center;
 413  width: 22px; height: 22px;
 414  flex: none;
 415  border-radius: 50%;
 416  background: var(--fill);
 417  color: var(--fill-fg);
 418  font-size: var(--fs-0);
 419  font-weight: 600;
 420}
 421
 422/* ---- shell: the rail above, then the pane, which fills the rest of the
 423   viewport so the footer sits at the bottom of a short page ---- */
 424.shell { display: flex; flex-direction: column; min-height: 100vh; }
 425.pane { flex: 1; min-width: 0; display: flex; flex-direction: column; }
 426main.content {
 427  flex: 1;
 428  width: 100%;
 429  padding: var(--sp-5) var(--sp-6) var(--sp-7);
 430}
 431/* the container is the default width and what `wide` names; reading and
 432   bounded follow, so equal specificity gives them the cascade */
 433main.content, main.wide, footer { max-width: calc(var(--container) + 2 * var(--sp-6)); margin: 0 auto; }
 434main.reading { max-width: calc(72rem + 2 * var(--sp-6)); margin: 0 auto; }
 435main.bounded { max-width: calc(48rem + 2 * var(--sp-6)); margin: 0 auto; }
 436footer {
 437  width: 100%;
 438  padding: var(--sp-4) var(--sp-6);
 439  border-top: 1px solid var(--faint);
 440  color: var(--muted);
 441  font-size: var(--fs-1);
 442}
 443footer p { margin: 0; }
 444footer a { color: var(--muted); text-decoration: underline; }
 445footer a:hover { color: var(--link); }
 446
 447/* ---- repository header ---- */
 448.repohead {
 449  padding: var(--sp-4) 0 0;
 450  border-bottom: 1px solid var(--line);
 451}
 452.repohead .wrap { max-width: calc(var(--container) + 2 * var(--sp-6)); margin: 0 auto; padding: 0 var(--sp-6); }
 453.identity { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
 454.grow { flex: 1; }
 455/* h1 on the repo home, p elsewhere so each page keeps its own h1 — but
 456   identical in size, because the tabs sit below and navigation must not
 457   move when you use it */
 458.repotitle {
 459  margin: 0;
 460  font-size: var(--fs-4);
 461  font-weight: 600;
 462  line-height: 1.25;
 463}
 464.repotitle a { color: var(--fg); }
 465.repotitle a.owner { color: var(--muted); font-weight: 400; }
 466.repotitle a:hover { color: var(--link); text-decoration: none; }
 467.repotitle .sep { color: var(--muted); margin: 0 2px; font-weight: 400; }
 468.repodesc {
 469  margin: 0 0 0 var(--sp-2);
 470  color: var(--muted);
 471  font-size: var(--fs-2);
 472  display: inline-flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap;
 473  min-width: 0;
 474}
 475.repodesc a.site { color: var(--muted); }
 476.repodesc a.site:hover { color: var(--link); }
 477.repometa { margin: var(--sp-1) 0 0; color: var(--muted); font-size: var(--fs-1); }
 478
 479nav.tabs {
 480  display: flex;
 481  gap: var(--sp-1);
 482  margin-top: var(--sp-3);
 483  margin-bottom: var(--sp-4);
 484  overflow-x: auto;
 485  /* with overflow-y left visible the browser computes it to auto, which
 486     is a vertical drag on touch; the strip scrolls sideways only */
 487  overflow-y: hidden;
 488  scrollbar-width: none;
 489}
 490nav.tabs::-webkit-scrollbar { display: none; }
 491nav.tabs a {
 492  display: inline-flex;
 493  align-items: center;
 494  gap: var(--sp-1);
 495  padding: var(--sp-2) var(--sp-3) 10px;
 496  color: var(--muted);
 497  font-size: var(--fs-2);
 498  white-space: nowrap;
 499  border-bottom: 2px solid transparent;
 500  margin-bottom: -1px;
 501}
 502nav.tabs a:hover { color: var(--fg); text-decoration: none; }
 503nav.tabs a[aria-current] { color: var(--fg); font-weight: 600; border-bottom-color: var(--mark); }
 504/* the profile strip draws its unlit tabs, so the underline reads as the
 505   control rather than appearing only on the one you are already on.
 506   --line, not --faint: the track has to be visible to be a track, and
 507   at 2px --faint disappears into the canvas in both schemes. */
 508nav.tabs.profiletabs a { border-bottom-color: var(--line); }
 509nav.tabs.profiletabs a[aria-current] { border-bottom-color: var(--mark); }
 510/* a count in a tab is plain metadata, not a badge */
 511nav.tabs a i { font-style: normal; color: var(--muted); margin-left: 4px; }
 512
 513/* ---- page heads ---- */
 514.pagehead, .listhead, .headrow {
 515  display: flex;
 516  align-items: center;
 517  gap: var(--sp-3);
 518  flex-wrap: wrap;
 519  margin-bottom: var(--sp-5);
 520}
 521/* every child sheds its block margins, or a p.meta beside an h1 centres
 522   its text inside a box the margins made taller than the heading's */
 523.pagehead h1, .listhead h1, .headrow h1, .listhead h2, .headrow h2 { margin: 0; }
 524.pagehead > p, .listhead > p, .headrow > p { margin: 0; }
 525.pagehead .grow, .listhead .spacer, .headrow .spacer { flex: 1; }
 526.issuetitle { margin: 0 0 var(--sp-2); font-size: var(--fs-5); text-wrap: balance; }
 527.issuenumber { color: var(--muted); font-weight: 400; }
 528.issuemeta { color: var(--muted); font-size: var(--fs-2); margin: 0 0 var(--sp-4); }
 529.commithead p { margin: var(--sp-1) 0; }
 530.commithead h2 { margin-top: 0; margin-bottom: var(--sp-1); }
 531
 532nav.filters { display: inline-flex; gap: var(--sp-2); flex-wrap: wrap; }
 533nav.filters a {
 534  display: inline-flex;
 535  align-items: center;
 536  height: 32px;
 537  padding: 0 var(--sp-3);
 538  background: var(--canvas);
 539  border: 1px solid var(--line);
 540  border-radius: var(--r-ctl);
 541  color: var(--fg);
 542  font-size: var(--fs-2);
 543  font-weight: 500;
 544}
 545nav.filters a:hover { background: var(--hover); text-decoration: none; }
 546nav.filters a.active { background: var(--surface); border-color: var(--link); color: var(--link); }
 547
 548form.searchform, form.logfilter, form.compareform {
 549  display: flex;
 550  align-items: center;
 551  gap: var(--sp-2);
 552  flex-wrap: wrap;
 553  margin: 0 0 var(--sp-4);
 554}
 555form.searchform input[type="search"], form.searchform input[type="text"], form.logfilter input { flex: 1; min-width: 12rem; max-width: 32rem; }
 556form.searchform.compact { margin: 0; }
 557form.searchform.compact input[type="search"], form.searchform.compact input[type="text"] { min-width: 18rem; }
 558form.compareform label { display: inline-flex; align-items: center; gap: var(--sp-2); margin: 0; }
 559
 560/* ---- buttons: one family, 32px tall, so a row of them lines up ---- */
 561button, .button {
 562  display: inline-flex;
 563  align-items: center;
 564  justify-content: center;
 565  gap: 6px;
 566  height: 32px;
 567  padding: 0 var(--sp-3);
 568  font: inherit;
 569  font-size: var(--fs-2);
 570  font-weight: 500;
 571  border: 1px solid transparent;
 572  border-radius: var(--r-ctl);
 573  cursor: pointer;
 574  text-decoration: none;
 575  white-space: nowrap;
 576  /* a bare <button> is the primary action of its form group */
 577  background: var(--fill);
 578  color: var(--fill-fg);
 579}
 580button:hover, .button.primary:hover { filter: brightness(1.12); text-decoration: none; }
 581button.btn, .button.btn {
 582  background: var(--canvas);
 583  color: var(--fg);
 584  border-color: var(--line);
 585  filter: none;
 586}
 587button.btn:hover, .button.btn:hover { background: var(--hover); filter: none; text-decoration: none; }
 588button.btn[aria-pressed="true"] { background: var(--surface); color: var(--link); border-color: var(--link); }
 589button.linklike {
 590  background: none;
 591  border: 0;
 592  padding: 0;
 593  height: auto;
 594  color: var(--link);
 595  font-weight: 400;
 596  filter: none;
 597}
 598button.linklike:hover { text-decoration: underline; filter: none; }
 599button.danger, .button.danger {
 600  background: var(--canvas);
 601  color: var(--bad);
 602  border-color: var(--bad);
 603  filter: none;
 604}
 605/* --canvas, not --fill-fg: white on the dark scheme's --bad is 2.3:1 */
 606button.danger:hover, .button.danger:hover { background: var(--bad); color: var(--canvas); filter: none; }
 607button:disabled { opacity: 0.55; cursor: default; filter: none; }
 608.btngroup { display: inline-flex; gap: var(--sp-2); flex-wrap: wrap; }
 609/* a destructive button holds the way out at arm's length: 8px puts the
 610   cancel's underline against the button's border */
 611.btngroup:has(> button.danger) { gap: var(--sp-4); }
 612form.inline { display: inline; }
 613.act { text-align: right; }
 614
 615/* ---- forms ---- */
 616/* not button: it sets its own colour above, and this rule comes later at
 617   the same specificity */
 618input, textarea, select { font: inherit; color: inherit; }
 619input[type="text"], input[type="search"], input[type="email"],
 620input[type="password"], input[type="number"], input:not([type]), textarea, select {
 621  background: var(--inset);
 622  color: var(--fg);
 623  border: 1px solid var(--line);
 624  border-radius: var(--r-ctl);
 625  padding: 0 10px;
 626  height: 32px;
 627  font-size: var(--fs-2);
 628  line-height: 1.4;
 629}
 630textarea {
 631  width: 100%;
 632  height: auto;
 633  padding: 8px 10px;
 634  resize: vertical;
 635  line-height: 1.5;
 636}
 637textarea[name="content"] { font-family: var(--mono); font-size: var(--fs-1); }
 638select {
 639  appearance: none;
 640  padding-right: 1.9rem;
 641  background-image: var(--chev);
 642  background-repeat: no-repeat;
 643  background-position: right 0.6rem center;
 644  background-size: 0.62rem;
 645  cursor: pointer;
 646}
 647input:hover, textarea:hover, select:hover { border-color: var(--muted); }
 648input[type="radio"] { accent-color: var(--fill); }
 649label { display: block; font-weight: 500; font-size: var(--fs-2); margin-bottom: 4px; }
 650label:has(> input[type="radio"]), label:has(> input[type="checkbox"]),
 651label:has(> select) { display: inline-flex; align-items: center; gap: var(--sp-2); }
 652.fieldname { display: block; font-size: var(--fs-2); font-weight: 500; }
 653.hint { display: block; color: var(--muted); font-size: var(--fs-1); font-weight: 400; margin: 0 0 6px; }
 654.field { margin-bottom: var(--sp-4); }
 655.field input[type="text"], .field input[type="email"], .field select { width: 100%; }
 656.check { display: flex; align-items: center; gap: var(--sp-2); font-weight: 400; }
 657::placeholder { color: var(--muted); opacity: 1; }
 658::selection { background: var(--fill); color: var(--fill-fg); }
 659/* :user-invalid, not :invalid — a required field is invalid the moment
 660   the page loads, and colouring it red before anyone has typed is a
 661   scolding, not a hint */
 662input:user-invalid, textarea:user-invalid { border-color: var(--bad); }
 663input[name="confirm"] { width: auto; margin-right: var(--sp-2); }
 664
 665/* A radio keeps the native control: a circle takes one answer, a square
 666   takes any number, and that shape survives greyscale and colour
 667   blindness. The checkbox is drawn so it matches the rest of the family. */
 668input[type="checkbox"] {
 669  appearance: none;
 670  flex: none;
 671  width: 16px;
 672  height: 16px;
 673  margin: 0;
 674  /* an empty box has nothing but its border to announce it */
 675  border: 1px solid var(--muted);
 676  border-radius: var(--r-ctl);
 677  background: var(--inset);
 678  cursor: pointer;
 679}
 680input[type="checkbox"]:not(:checked):hover { border-color: var(--fg); }
 681input[type="checkbox"]:checked {
 682  background: var(--fill);
 683  border-color: var(--fill);
 684  /* the mark sits on the blue ground, so it is white in both schemes */
 685  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");
 686  background-repeat: no-repeat;
 687  background-position: center;
 688  background-size: 11px;
 689}
 690
 691/* a segmented control for a small, mutually exclusive set: real radios
 692   underneath, so arrow keys and screen readers behave as they always did */
 693fieldset.segmented { border: 0; margin: 0 0 var(--sp-4); padding: 0; }
 694fieldset.segmented legend { padding: 0; margin-bottom: var(--sp-2); color: var(--muted); font-size: var(--fs-2); }
 695fieldset.segmented .options { display: inline-flex; }
 696fieldset.segmented label { gap: 0; margin: 0; }
 697fieldset.segmented input { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
 698fieldset.segmented span {
 699  display: flex;
 700  align-items: center;
 701  height: 32px;
 702  padding: 0 var(--sp-4);
 703  border: 1px solid var(--line);
 704  font-size: var(--fs-2);
 705  cursor: pointer;
 706}
 707fieldset.segmented label + label span { border-left: 0; }
 708fieldset.segmented label:first-child span { border-radius: var(--r-ctl) 0 0 var(--r-ctl); }
 709fieldset.segmented label:last-child span { border-radius: 0 var(--r-ctl) var(--r-ctl) 0; }
 710fieldset.segmented span:hover { border-color: var(--muted); }
 711fieldset.segmented input:checked + span {
 712  background: var(--fill);
 713  border-color: var(--fill);
 714  color: var(--fill-fg);
 715  font-weight: 600;
 716}
 717fieldset.segmented input:focus-visible + span { outline: 2px solid var(--focus); outline-offset: 2px; }
 718
 719p.branchpick { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2); }
 720p.branchpick label { color: var(--muted); font-size: var(--fs-2); margin: 0; }
 721p.branchpick select { flex: 1 1 12rem; }
 722
 723form.signupform, form.editform { max-width: 40rem; }
 724form.signupform label, form.editform label { margin-bottom: 4px; }
 725.editform textarea { width: 100%; }
 726.editform input[name="message"] { width: 100%; }
 727
 728/* A previewed draft, above the textarea it came from. The orange edge is
 729   "where you are and what wants you": this is not saved yet. */
 730.preview {
 731  margin: var(--sp-4) 0;
 732  max-width: 48rem;
 733  background: var(--surface);
 734  border: 1px solid var(--line);
 735  border-left: 3px solid var(--mark);
 736  border-radius: var(--r-card);
 737}
 738.preview > .previewmark {
 739  margin: 0;
 740  padding: var(--sp-2) var(--sp-4);
 741  border-bottom: 1px solid var(--line);
 742  color: var(--warn);
 743  font-size: var(--fs-1);
 744}
 745.preview > .rendered { padding: var(--sp-3) var(--sp-4); }
 746.preview > .rendered > :first-child { margin-top: 0; }
 747.preview > .rendered > :last-child { margin-bottom: 0; }
 748.preview > .rendered:empty::before { content: "nothing to preview"; color: var(--muted); }
 749
 750form.commentform { margin: var(--sp-4) 0; max-width: 48rem; }
 751form.commentform p:last-child { display: flex; align-items: center; gap: var(--sp-3); margin-bottom: 0; }
 752
 753details.editbox { margin: var(--sp-2) 0; max-width: 48rem; }
 754details.editbox > summary {
 755  display: inline-flex;
 756  align-items: center;
 757  height: 32px;
 758  padding: 0 var(--sp-3);
 759  background: var(--canvas);
 760  border: 1px solid var(--line);
 761  border-radius: var(--r-ctl);
 762  color: var(--fg);
 763  font-size: var(--fs-2);
 764  font-weight: 500;
 765  cursor: pointer;
 766  list-style: none;
 767}
 768details.editbox > summary::-webkit-details-marker { display: none; }
 769details.editbox > summary:hover { background: var(--hover); }
 770details.editbox[open] > summary { margin-bottom: var(--sp-2); }
 771details.editbox input[type="text"] { width: 100%; }
 772
 773/* ---- settings: one row per control, label then input then its own
 774   button. The name stays setform because the templates carry it.
 775   The label column takes the slack, so the control and the button ride
 776   the right edge: a checkbox, a number and a full-width text input all
 777   end on the same rule instead of the narrow ones stranding left. Its
 778   14rem floor is what keeps a hint from wrapping a word to a line once
 779   the control column claims its 28rem. ---- */
 780form.setform {
 781  display: grid;
 782  grid-template-columns: minmax(14rem, 1fr) minmax(0, 28rem) auto;
 783  gap: var(--sp-4);
 784  align-items: start;
 785  padding: var(--sp-3) 0;
 786  border-bottom: 1px solid var(--faint);
 787}
 788form.setform label { margin-top: 6px; }
 789form.setform .hint { margin: 2px 0 0; }
 790form.setform input[type="text"], form.setform input[type="password"], form.setform select { width: 100%; }
 791/* a control narrower than its column is pushed to the column's end,
 792   which is where a full-width input's own right edge lands */
 793form.setform .check { justify-content: flex-end; }
 794form.setform .num { text-align: right; }
 795form.setform .num input[type="number"] { width: auto; }
 796/* the third column takes the leftover width; the button keeps its own */
 797form.setform > button, form.setform > .btngroup { justify-self: start; }
 798form.setform.stack { grid-template-columns: 1fr; }
 799form.setform.stack textarea, form.setform.stack select, form.setform.stack input[type="text"], form.setform.stack input[type="password"] { width: 100%; max-width: 48rem; }
 800ul.protlist { list-style: none; margin: var(--sp-2) 0; padding: 0; }
 801ul.protlist li {
 802  display: flex;
 803  align-items: center;
 804  gap: var(--sp-3);
 805  padding: var(--sp-2) 0;
 806  border-bottom: 1px solid var(--faint);
 807}
 808ul.protlist li:last-child { border-bottom: 0; }
 809ul.protlist form { margin-left: auto; }
 810ul.plain { list-style: none; margin: 0 0 var(--sp-3); padding: 0; }
 811ul.plain li { padding: var(--sp-1) 0; display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
 812/* the email list: address and badges read left, the controls sit right,
 813   so the buttons line up down the list however long an address is */
 814ul.plain li > form:first-of-type { margin-left: auto; }
 815
 816/* ---- messages: .error and .notice are one family. The names stay
 817   because e2e tests match class="error". ---- */
 818.error, .notice {
 819  border-left: 3px solid var(--bad);
 820  background: color-mix(in srgb, var(--bad) 6%, transparent);
 821  color: var(--fg);
 822  border-radius: 0 var(--r-ctl) var(--r-ctl) 0;
 823  padding: var(--sp-2) var(--sp-3);
 824  margin: 0 0 var(--sp-4);
 825  font-size: var(--fs-2);
 826}
 827.notice { border-left-color: var(--ok); background: color-mix(in srgb, var(--ok) 6%, transparent); }
 828.empty-note, .none, .empty, li.empty, td.empty { color: var(--muted); }
 829
 830/* ---- containers: one recipe for cards, lists and tables ---- */
 831.card, ul.list, ul.loglist, ul.repolist, ul.issuelist,
 832ul.milestonelist, .readme, .code, article.release, .blame, .teambody {
 833  background: var(--surface);
 834  border: 1px solid var(--line);
 835  border-radius: var(--r-card);
 836  overflow: hidden;
 837}
 838.cardhead {
 839  display: flex;
 840  align-items: center;
 841  gap: var(--sp-3);
 842  padding: var(--sp-2) var(--sp-4);
 843  border-bottom: 1px solid var(--line);
 844  font-size: var(--fs-1);
 845  color: var(--muted);
 846}
 847.cardhead b { color: var(--fg); font-weight: 500; }
 848.cardbody { padding: var(--sp-4) var(--sp-5); }
 849ul.list { list-style: none; margin: 0 0 var(--sp-4); padding: 0; }
 850ul.list li { padding: var(--sp-3) var(--sp-4); border-bottom: 1px solid var(--faint); }
 851ul.list li:last-child { border-bottom: 0; }
 852ul.list li:hover { background: var(--hover); }
 853
 854table { border-collapse: collapse; width: 100%; font-size: var(--fs-2); }
 855th {
 856  text-align: left;
 857  font-weight: 500;
 858  font-size: var(--fs-1);
 859  color: var(--muted);
 860  padding: 6px var(--sp-4);
 861  border-bottom: 1px solid var(--line);
 862}
 863td { padding: 7px var(--sp-4); border-bottom: 1px solid var(--faint); vertical-align: top; }
 864tr:last-child td { border-bottom: 0; }
 865tbody tr:hover td { background: var(--hover); }
 866/* every header row a template writes, by the class it or its table
 867   carries: TestHeaderRowsAreLeftAligned reads these selectors */
 868tr.cols th, table.tree th, table.refs th, table.keys th, table.assets th { text-align: left; }
 869tr.cols th { background: var(--canvas); }
 870/* right-aligned columns keep their header with their values */
 871tr.cols th.age, tr.cols th.size { text-align: right; }
 872/* wide listings scroll inside their own box; the page never does. The
 873   wrapper is the scroller only — the table inside carries the card. */
 874.tablewrap { overflow-x: auto; max-width: 100%; }
 875
 876table.tree, table.refs {
 877  background: var(--surface);
 878  border: 1px solid var(--line);
 879  border-radius: var(--r-card);
 880  /* collapsed borders defeat a radius on the table itself */
 881  border-collapse: separate;
 882  border-spacing: 0;
 883  overflow: hidden;
 884  margin-bottom: var(--sp-4);
 885}
 886table.tree tr:last-child td, table.refs tr:last-child td { border-bottom: 0; }
 887table.tree tr:hover td, table.refs tr:hover td { background: var(--hover); }
 888td.name a { color: var(--fg); }
 889td.name a:hover { color: var(--link); }
 890table.tree td.name { width: 25%; white-space: nowrap; }
 891table.tree td.name.dir a { color: var(--link); }
 892/* the glyph stays muted in both rows: it is a shape cue, and colour here
 893   goes on carrying what it already carries */
 894table.tree td.name svg { color: var(--muted); vertical-align: -0.125em; margin-right: var(--sp-2); }
 895table.tree th.lastcommit, table.tree td.lastcommit { width: 55%; }
 896table.tree td.lastcommit {
 897  max-width: 0;
 898  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
 899}
 900table.tree td.lastcommit a {
 901  color: var(--muted);
 902  display: block;
 903  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
 904}
 905table.tree td.lastcommit a:hover { color: var(--link); }
 906table.tree th.age, table.tree td.age { width: 20%; }
 907td.age, th.age { text-align: right; white-space: nowrap; color: var(--muted); font-variant-numeric: tabular-nums; }
 908td.size, td.mode { color: var(--muted); white-space: nowrap; font-variant-numeric: tabular-nums; }
 909table.tree th.size, table.tree td.size { text-align: right; }
 910table.refs td.name { width: 100%; }
 911table.refs td.sha, td.sha { color: var(--muted); }
 912
 913/* these tables sit flush with the page text: no outer border, and the
 914   header row drops the ground and the left padding */
 915table.keys, table.assets { background: none; border: 0; border-radius: 0; }
 916table.keys th, table.assets th, table.keys tr.cols th { background: transparent; padding-left: 0; }
 917table.keys td { padding-left: 0; }
 918table.keys td.act, table.assets td.size { text-align: right; }
 919/* a fingerprint or an account row is read across, not down: the cells
 920   keep one line each and .tablewrap scrolls what does not fit */
 921table.keys.nowrap th, table.keys.nowrap td { white-space: nowrap; }
 922/* The account list types a username to confirm, which is longer than
 923   the key prefixes the confirm field is sized for elsewhere. 14rem, not
 924   11rem, because "type <name> to confirm" was cut mid-word for a name of
 925   ordinary length; a long enough name still truncates and the field's
 926   aria-label carries the instruction in full either way.
 927
 928   The cell is two fixed slots, each right-aligned. A form cannot span
 929   table cells, so one column per control — which would let the table
 930   align them — is out without form="" plumbing, and the forms hold
 931   different numbers of controls: the enable form and the disabled
 932   row's Promote have no confirm field. Sizing the slot instead
 933   of the form is what puts every button in one column and every field
 934   in another; right-aligning the cell alone left the disabled row's
 935   Promote where other rows keep their field. A slot is a field, a gap
 936   and a button wide, so a form that is only a button still ends on the
 937   same rule. No element stands in for the controls a row lacks. */
 938table.accounts { --acct-field: 14rem; --acct-btn: 6rem; }
 939table.accounts td.acts { display: flex; justify-content: flex-end; align-items: center; gap: var(--sp-2); }
 940table.accounts input[name="confirm"] { width: var(--acct-field); }
 941table.accounts form.actions {
 942  flex: none;
 943  display: inline-flex;
 944  /* the generic form.actions wraps and grows its button to fill; both
 945     have to go, or the slot takes the button to a second line and the
 946     field sits above it */
 947  flex-wrap: nowrap;
 948  justify-content: flex-end;
 949  align-items: center;
 950  gap: var(--sp-2);
 951  width: calc(var(--acct-field) + var(--acct-btn) + var(--sp-2));
 952  margin: 0;
 953}
 954/* a set width, not a minimum: it makes the slot's arithmetic exact, and
 955   6rem clears the widest of Promote, Demote, Disable and Enable */
 956table.accounts form.actions button { flex: none; width: var(--acct-btn); justify-content: center; }
 957table.assets td.name { width: 100%; font-family: var(--mono); font-size: var(--fs-1); }
 958table.assets { margin-top: var(--sp-2); }
 959
 960/* the tip commit is the first row of the file table */
 961tr.tipbar td {
 962  background: var(--surface);
 963  padding: var(--sp-3) var(--sp-4);
 964  border-bottom: 1px solid var(--line);
 965  max-width: 0;
 966}
 967.tipbar .tip {
 968  display: flex;
 969  align-items: center;
 970  gap: var(--sp-2);
 971  flex-wrap: wrap;
 972  min-width: 0;
 973  font-size: var(--fs-2);
 974}
 975.tipbar .who { font-weight: 600; }
 976.tipbar .subject { color: var(--fg); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
 977.tipbar .subject:hover { color: var(--link); }
 978.tipbar .spacer { flex: 1; }
 979.tipbar .age { color: var(--muted); white-space: nowrap; }
 980
 981.readme { margin-top: var(--sp-5); }
 982.readme .cardbody { max-width: 78ch; }
 983/* the overview README caps as a card, so prose and code share an edge */
 984.overview .readme { max-width: 88ch; }
 985/* until every card wraps its body in .cardbody, the rendered block is
 986   the body */
 987.readme > .rendered { padding: var(--sp-4) var(--sp-5); }
 988.code { background: var(--inset); padding: var(--sp-3) var(--sp-4); overflow-x: auto; }
 989.code pre { margin: 0; background: transparent !important; border: 0; padding: 0; }
 990pre.bigcode, .bigcode {
 991  font-family: var(--mono);
 992  /* the 404 numeral is a picture of a number, not body type */
 993  font-size: 4rem;
 994  color: var(--faint);
 995  -webkit-text-stroke: 1px var(--line);
 996  margin: 0;
 997  line-height: 1;
 998}
 999.notfound { text-align: center; padding: var(--sp-6) 0; }
1000.notfound h1 { margin-top: var(--sp-3); }
1001
1002ul.loglist { list-style: none; margin: var(--sp-3) 0; padding: 0; }
1003ul.loglist li {
1004  display: flex;
1005  align-items: baseline;
1006  gap: var(--sp-3);
1007  padding: var(--sp-3) var(--sp-4);
1008  border-bottom: 1px solid var(--faint);
1009}
1010ul.loglist li:last-child { border-bottom: 0; }
1011ul.loglist li:hover { background: var(--hover); }
1012ul.loglist p { margin: 0; }
1013ul.loglist .commitmain { flex: 1 1 auto; min-width: 0; }
1014ul.loglist .subject a { color: var(--fg); font-weight: 500; }
1015ul.loglist .subject a:hover { color: var(--link); }
1016ul.loglist .commitside { flex: none; display: flex; gap: var(--sp-2); align-items: center; white-space: nowrap; }
1017
1018ul.loglist.rows li { align-items: center; padding: var(--sp-2) var(--sp-4); }
1019ul.loglist.rows .commitmain { display: flex; align-items: baseline; gap: var(--sp-3); min-width: 0; }
1020ul.loglist.rows .meta { flex: none; color: var(--muted); font-size: var(--fs-1); white-space: nowrap; }
1021
1022ul.repolist { list-style: none; margin: var(--sp-3) 0 var(--sp-5); padding: 0; }
1023ul.repolist li { padding: var(--sp-3) var(--sp-4); border-bottom: 1px solid var(--faint); }
1024ul.repolist li:last-child { border-bottom: 0; }
1025ul.repolist li:hover { background: var(--hover); }
1026ul.repolist p { margin: 0; }
1027ul.repolist .reponame { font-size: var(--fs-3); }
1028ul.repolist .reponame a { color: var(--fg); }
1029ul.repolist .reponame a strong { color: var(--link); font-weight: 600; }
1030ul.repolist .reponame .sep { color: var(--muted); margin: 0 2px; }
1031ul.repolist .desc { margin-top: 2px; }
1032ul.repolist .topics, ul.repolist .meta { margin-top: var(--sp-1); }
1033
1034ul.repolist.rows li { display: flex; align-items: baseline; gap: var(--sp-3); padding: var(--sp-2) var(--sp-4); }
1035ul.repolist.rows .reponame { flex: none; font-size: var(--fs-2); }
1036ul.repolist.rows .desc { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--muted); font-size: var(--fs-2); margin: 0; }
1037ul.repolist.rows .topics, ul.repolist.rows .meta { flex: none; margin: 0; white-space: nowrap; }
1038ul.repolist.rows .meta { font-size: var(--fs-1); }
1039
1040/* pinned repos: a compact wrap of names, no description or meta */
1041ul.pinlist { list-style: none; margin: var(--sp-3) 0 var(--sp-5); padding: 0; display: flex; flex-wrap: wrap; gap: var(--sp-2); }
1042ul.pinlist li {
1043  background: var(--surface);
1044  border: 1px solid var(--line);
1045  border-radius: var(--r-ctl);
1046  padding: var(--sp-1) var(--sp-3);
1047}
1048ul.pinlist li:hover { background: var(--hover); }
1049ul.pinlist li a { color: var(--fg); }
1050ul.pinlist li a:hover { color: var(--link); text-decoration: none; }
1051ul.pinlist .sep { color: var(--muted); margin: 0 2px; }
1052
1053ul.issuelist { list-style: none; margin: var(--sp-3) 0; padding: 0; }
1054ul.issuelist li {
1055  display: flex;
1056  align-items: baseline;
1057  gap: var(--sp-3);
1058  padding: var(--sp-3) var(--sp-4);
1059  border-bottom: 1px solid var(--faint);
1060}
1061ul.issuelist li:last-child { border-bottom: 0; }
1062ul.issuelist li:hover { background: var(--hover); }
1063ul.issuelist li.empty { display: block; }
1064ul.issuelist p { margin: 0; }
1065ul.issuelist .issuemain { flex: 1 1 auto; min-width: 0; }
1066ul.issuelist .issueside { display: flex; gap: var(--sp-2); align-items: center; flex: none; }
1067ul.issuelist .title a { color: var(--fg); font-weight: 500; }
1068ul.issuelist .title a:hover { color: var(--link); }
1069
1070/* one-line rows: title, labels, then the meta pushed right. Above 64rem
1071   a list is a table, not prose (desktop layout spec). */
1072ul.issuelist.rows li { align-items: center; padding: var(--sp-2) var(--sp-4); }
1073ul.issuelist.rows .issuemain { display: flex; align-items: baseline; gap: var(--sp-3); min-width: 0; }
1074ul.issuelist.rows .title { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1075ul.issuelist.rows .title .chip { margin-left: var(--sp-1); }
1076ul.issuelist.rows .meta { flex: none; color: var(--muted); font-size: var(--fs-1); white-space: nowrap; }
1077ul.issuelist.rows .meta .repo { color: var(--fg); }
1078
1079ul.milestonelist { list-style: none; margin: var(--sp-3) 0; padding: 0; }
1080ul.milestonelist li { padding: var(--sp-3) var(--sp-4); border-bottom: 1px solid var(--faint); }
1081ul.milestonelist li:last-child { border-bottom: 0; }
1082ul.milestonelist li:hover { background: var(--hover); }
1083ul.milestonelist p { margin: 0; }
1084ul.milestonelist .msmain { min-width: 0; }
1085ul.milestonelist .title { font-weight: 500; }
1086.progress {
1087  height: 6px;
1088  max-width: 24rem;
1089  margin-top: var(--sp-2);
1090  background: var(--faint);
1091  border-radius: var(--r-ctl);
1092  overflow: hidden;
1093}
1094.progress .bar { height: 100%; background: var(--ok); }
1095
1096article.release { padding: var(--sp-4); margin: var(--sp-4) 0; }
1097.releasehead { background: none; border: 0; padding: 0; }
1098.releasehead h2 { margin: 0 0 var(--sp-1); font-size: var(--fs-3); }
1099.releasehead .meta { margin: 0 0 var(--sp-2); }
1100
1101ul.matchlist { list-style: none; margin: var(--sp-3) 0; padding: 0; }
1102ul.matchlist li { margin-bottom: var(--sp-3); }
1103.matchpath { font-family: var(--mono); font-size: var(--fs-1); }
1104/* a name in highlighted source that links to its definition keeps its
1105   token colour; the dotted underline is what marks it as a link */
1106.chroma a.sym { color: inherit; text-decoration: underline dotted; text-underline-offset: 2px; }
1107.chroma a.sym:hover { text-decoration-style: solid; }
1108details.symlist { margin: 0 0 var(--sp-3); font-size: var(--fs-1); }
1109details.symlist summary { cursor: pointer; color: var(--muted); }
1110details.symlist ul { list-style: none; margin: var(--sp-2) 0 0; padding: 0; columns: 16rem; }
1111details.symlist li { break-inside: avoid; padding: 2px 0; }
1112pre.matchline {
1113  background: var(--inset);
1114  border: 1px solid var(--line);
1115  border-radius: var(--r-card);
1116  padding: var(--sp-1) var(--sp-3);
1117  margin: var(--sp-1) 0 0;
1118  overflow-x: auto;
1119}
1120pre.matchline mark { background: color-mix(in srgb, var(--mark) 25%, transparent); color: inherit; border-radius: 2px; }
1121p.pager { margin-top: var(--sp-4); }
1122.snippetfile { margin-bottom: var(--sp-5); }
1123p.filefacts { color: var(--muted); font-size: var(--fs-1); margin: 0 0 var(--sp-3); }
1124pre.buildlog { max-height: 40rem; overflow: auto; white-space: pre-wrap; overflow-wrap: anywhere; }
1125details.buildstep pre.buildlog { margin: 0; border: 0; border-radius: 0; }
1126details.buildstep summary code { overflow-wrap: anywhere; }
1127
1128/* ---- rendered prose ---- */
1129pre {
1130  background: var(--inset);
1131  border: 1px solid var(--line);
1132  border-radius: var(--r-card);
1133  padding: var(--sp-3) var(--sp-4);
1134  margin: 0 0 var(--sp-4);
1135  overflow-x: auto;
1136  font-size: var(--fs-2);
1137  line-height: 1.6;
1138}
1139code {
1140  background: var(--inset);
1141  border: 1px solid var(--faint);
1142  border-radius: var(--r-ctl);
1143  padding: 0 0.3em;
1144}
1145pre code { background: none; border: 0; padding: 0; font-size: inherit; }
1146code.fullsha { color: var(--muted); overflow-wrap: anywhere; }
1147pre.message {
1148  white-space: pre-wrap;
1149  overflow-wrap: anywhere;
1150}
1151.rendered { max-width: 100%; overflow-wrap: anywhere; }
1152.rendered a { text-decoration: underline; }
1153.prose, .rendered > p, .rendered > ul, .rendered > ol, .rendered > blockquote { max-width: 78ch; }
1154.rendered blockquote { border-left: 3px solid var(--line); margin-left: 0; padding-left: var(--sp-4); color: var(--muted); }
1155.rendered img { max-width: 100%; }
1156.rendered h1, .rendered h2 { border-bottom: 1px solid var(--faint); padding-bottom: var(--sp-1); }
1157.rendered table { border-collapse: collapse; margin: var(--sp-3) 0; display: block; max-width: 100%; overflow-x: auto; }
1158.rendered th, .rendered td {
1159  border: 1px solid var(--line);
1160  padding: var(--sp-1) var(--sp-3);
1161  text-align: left;
1162  overflow-wrap: normal; /* scroll the table, don't shatter words */
1163}
1164.rendered th { background: var(--surface); font-weight: 600; }
1165.rendered tr:last-child td { border-bottom: 1px solid var(--line); }
1166/* GFM task lists: the box sits in the line, sized to the prose */
1167.rendered li input[type="checkbox"] { width: 0.9em; height: 0.9em; margin: 0 0.35em 0 0; vertical-align: -0.05em; background-size: 0.7em; }
1168.rendered li:has(> input[type="checkbox"]) { list-style: none; margin-left: -1.2em; }
1169.blobimage { text-align: center; }
1170.blobimage img { max-width: 100%; border: 1px solid var(--line); border-radius: var(--r-card); background: var(--surface); }
1171
1172/* ---- dashboard: pinned column, tiles and queues, feed ---- */
1173.dashgrid { display: grid; grid-template-columns: 15rem minmax(0, 1fr) 20rem; gap: var(--sp-6); align-items: start; }
1174.dashgrid h1 { margin-top: 0; }
1175.dashgrid > aside { position: sticky; top: var(--sp-5); }
1176.dashgrid .dashmain h2 { margin-top: var(--sp-5); }
1177.dashgrid .dashmain .tiles + h2 { margin-top: 0; }
1178.colhead {
1179  margin: 0 0 var(--sp-2);
1180  font-size: var(--fs-0);
1181  font-weight: 500;
1182  letter-spacing: 0.08em;
1183  text-transform: uppercase;
1184  color: var(--muted);
1185}
1186.pins { list-style: none; margin: 0; padding: 0; font-size: var(--fs-2); }
1187.pins li { display: flex; align-items: center; gap: var(--sp-2); padding: 6px 0; border-bottom: 1px solid var(--faint); }
1188.pins li:last-child { border-bottom: 0; }
1189.pins a { color: var(--fg); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1190.pins a:hover { color: var(--link); }
1191.pins .owner { color: var(--muted); }
1192.pins .n { margin-left: auto; flex: none; display: flex; align-items: center; gap: var(--sp-2); font-size: var(--fs-1); color: var(--muted); font-variant-numeric: tabular-nums; }
1193.pins .n b { font-weight: 600; color: var(--fg); }
1194.pins .n b.wants { color: var(--warn); }
1195.pins .dot { margin: 0; }
1196/* count tiles: the queues' sizes in one strip; a non-zero count is orange,
1197   what wants you */
1198.tiles { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--sp-3); margin: var(--sp-4) 0 var(--sp-5); }
1199.tile {
1200  display: block;
1201  background: var(--surface);
1202  border: 1px solid var(--line);
1203  border-radius: var(--r-card);
1204  padding: var(--sp-3) var(--sp-4);
1205  color: var(--fg);
1206}
1207a.tile:hover { background: var(--hover); text-decoration: none; }
1208.tile b { display: block; font-size: var(--fs-5); font-weight: 600; line-height: 1.2; }
1209.tile.wants b { color: var(--warn); }
1210.tile span { font-size: var(--fs-1); color: var(--muted); }
1211.feedcol .feedline { font-size: var(--fs-2); margin-bottom: var(--sp-2); }
1212.feedcol .feedline .none { font-size: var(--fs-1); }
1213
1214/* ---- two-column: conversation and aside ---- */
1215.withaside { display: grid; grid-template-columns: minmax(0, 1fr) 20rem; gap: var(--sp-6); align-items: start; }
1216.withaside .mainside { min-width: 0; }
1217aside.aside { font-size: var(--fs-2); }
1218.aside .grp { border-top: 1px solid var(--line); padding: var(--sp-3) 0; }
1219.aside .grp:first-child { border-top: 0; padding-top: 0; }
1220.aside h2 {
1221  margin: 0 0 var(--sp-2);
1222  font-size: var(--fs-0);
1223  font-weight: 500;
1224  letter-spacing: 0.08em;
1225  text-transform: uppercase;
1226  color: var(--muted);
1227}
1228.aside .row { display: flex; align-items: center; gap: 6px; margin: 0 0 var(--sp-1); flex-wrap: wrap; }
1229.aside .row:last-child { margin-bottom: 0; }
1230/* a row's second line: when it happened, under what it happened to */
1231.aside .row .sub { flex-basis: 100%; padding-left: 14px; color: var(--muted); font-size: var(--fs-1); }
1232.aside .row:has(.sub) { margin-bottom: var(--sp-2); }
1233.aside .none { color: var(--muted); }
1234.dot { display: inline-block; width: 8px; height: 8px; flex: none; border-radius: 50%; background: var(--neutral); margin-right: 2px; vertical-align: middle; }
1235.dot.ok { background: var(--ok); }
1236.dot.bad { background: var(--bad); }
1237.dot.pend { background: var(--mark); }
1238.ok { color: var(--ok); }
1239.bad { color: var(--bad); }
1240form.actions { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin-top: var(--sp-2); }
1241form.actions button { flex: 1 1 auto; }
1242form.actions select, form.actions textarea { width: 100%; }
1243.feedline { margin-bottom: var(--sp-2); }
1244
1245/* ---- comments and threads ---- */
1246article.comment {
1247  max-width: 48rem;
1248  border: 1px solid var(--line);
1249  border-radius: var(--r-card);
1250  margin: var(--sp-4) 0;
1251  overflow: hidden;
1252}
1253article.comment .commenthead {
1254  display: flex;
1255  gap: var(--sp-2);
1256  flex-wrap: wrap;
1257  background: var(--surface);
1258  border-bottom: 1px solid var(--line);
1259  padding: var(--sp-2) var(--sp-4);
1260  margin: 0;
1261  font-size: var(--fs-1);
1262  color: var(--muted);
1263}
1264article.comment .commenthead b { color: var(--fg); font-weight: 500; }
1265article.comment .commenthead .when { color: var(--muted); }
1266article.comment .rendered { padding: var(--sp-3) var(--sp-4); }
1267article.comment .rendered > :first-child { margin-top: 0; }
1268article.comment .rendered > :last-child { margin-bottom: 0; }
1269a.authorlink { color: var(--fg); font-weight: 500; }
1270a.authorlink:hover { color: var(--link); }
1271
1272.syscomment {
1273  max-width: 48rem;
1274  color: var(--muted);
1275  font-size: var(--fs-1);
1276  margin: 0 0 var(--sp-4) var(--sp-3);
1277  padding: var(--sp-2) 0 var(--sp-2) var(--sp-4);
1278  border-left: 2px solid var(--faint);
1279}
1280.syscomment p { display: inline; margin: 0; }
1281
1282.thread {
1283  border: 1px solid var(--faint);
1284  border-left: 3px solid var(--link);
1285  border-radius: var(--r-ctl);
1286  padding: var(--sp-2) var(--sp-4);
1287  margin: var(--sp-2) 0 var(--sp-2) var(--sp-6);
1288}
1289.thread.resolved { border-left-color: var(--ok); opacity: 0.75; }
1290/* an unsubmitted comment is the viewer's own note, not the conversation */
1291.thread.pending { border-left-color: var(--neutral); border-left-style: dashed; }
1292.thread.stale { border-left-color: var(--warn); }
1293.thread.composing { border-left-color: var(--mark); }
1294.thread:target { outline: 2px solid var(--mark); outline-offset: 2px; }
1295.thread p.commenthead { font-size: var(--fs-2); margin: var(--sp-2) 0 0; }
1296.thread .when { color: var(--muted); }
1297.thread .rendered p { margin: var(--sp-1) 0; }
1298.thread .threadstate { color: var(--muted); font-size: var(--fs-1); margin: var(--sp-1) 0 0; }
1299.thread details.threadreply { margin: var(--sp-2) 0 0; }
1300.thread details.threadreply > summary { color: var(--link); font-size: var(--fs-1); cursor: pointer; }
1301.thread textarea { width: 100%; }
1302.thread.composing p, .thread details.threadreply p { margin: var(--sp-2) 0 0; }
1303form.threadact { margin: var(--sp-1) 0 0; padding: 0 var(--sp-3) var(--sp-2); }
1304.thread .suggestion { margin: var(--sp-2) 0 0; }
1305.thread .suggestion table.difftable { margin: var(--sp-1) 0 0; }
1306.thread .suggestion table.difftable tr.add td,
1307.thread .suggestion table.difftable tr.del td { display: table-cell; }
1308
1309nav.subtabs {
1310  display: flex;
1311  gap: var(--sp-4);
1312  border-bottom: 1px solid var(--line);
1313  margin: 0 0 var(--sp-4);
1314}
1315nav.subtabs a {
1316  display: inline-flex;
1317  align-items: center;
1318  gap: var(--sp-1);
1319  padding: var(--sp-2) 0 10px;
1320  color: var(--muted);
1321  font-size: var(--fs-2);
1322  border-bottom: 2px solid transparent;
1323  margin-bottom: -1px;
1324}
1325nav.subtabs a:hover { color: var(--fg); text-decoration: none; }
1326nav.subtabs a[aria-current] { color: var(--fg); font-weight: 600; border-bottom-color: var(--mark); }
1327nav.subtabs a i { font-style: normal; color: var(--muted); margin-left: 4px; }
1328
1329/* ---- diffs and code ---- */
1330p.diffstat { color: var(--muted); font-size: var(--fs-2); margin: 0 0 var(--sp-3); }
1331p.diffstat .add { color: var(--ok); }
1332p.diffstat .del { color: var(--bad); }
1333
1334details.difffold {
1335  border: 1px solid var(--line);
1336  border-radius: var(--r-card);
1337  margin: 0 0 var(--sp-3);
1338  background: var(--inset);
1339  overflow: hidden;
1340}
1341details.difffold summary {
1342  display: flex;
1343  align-items: baseline;
1344  gap: var(--sp-3);
1345  padding: var(--sp-2) var(--sp-3);
1346  background: var(--surface);
1347  font-size: var(--fs-2);
1348  cursor: pointer;
1349  list-style: none;
1350}
1351details.difffold[open] summary { border-bottom: 1px solid var(--line); }
1352details.difffold summary::-webkit-details-marker { display: none; }
1353/* flex summaries lose the native disclosure marker; draw our own */
1354details.difffold summary::before { content: "\25b8"; color: var(--muted); }
1355details.difffold[open] summary::before { content: "\25be"; }
1356details.difffold summary:hover { background: var(--hover); }
1357details.difffold summary .fpath { font-family: var(--mono); overflow-wrap: anywhere; }
1358details.difffold summary .fpath .was { color: var(--muted); }
1359details.difffold summary .fstat { margin-left: auto; font-family: var(--mono); white-space: nowrap; }
1360details.difffold summary .add { color: var(--ok); }
1361details.difffold summary .del { color: var(--bad); }
1362details.difffold > .none { padding: var(--sp-3); margin: 0; }
1363details.difffold .tablewrap { background: none; border: 0; border-radius: 0; }
1364
1365table.difftable {
1366  border-collapse: collapse;
1367  width: 100%;
1368  font-family: var(--mono);
1369  font-size: var(--fs-1);
1370  line-height: 1.5;
1371}
1372table.difftable td { border: 0; padding: 0; vertical-align: top; font-size: inherit; }
1373table.difftable tbody tr:hover td { background: none; }
1374table.difftable td.ln {
1375  width: 1%;
1376  min-width: 2.5rem;
1377  padding: 0 var(--sp-2);
1378  text-align: right;
1379  color: var(--muted);
1380  user-select: none;
1381  border-right: 1px solid var(--line);
1382  white-space: nowrap;
1383}
1384/* not .code: that class carries its own ground, which painted over the
1385   row tints */
1386table.difftable td.src { padding: 0 var(--sp-3); white-space: pre-wrap; overflow-wrap: anywhere; }
1387/* the marker is decoration, so it lives in CSS and stays out of a copied
1388   selection */
1389table.difftable tr.add td.src::before { content: "+"; color: var(--ok); }
1390table.difftable tr.del td.src::before { content: "\2212"; color: var(--bad); }
1391table.difftable tr.ctx td.src::before { content: " "; }
1392table.difftable tr.add { background: var(--diff-add); }
1393table.difftable tr.del { background: var(--diff-del); }
1394table.difftable tr.hunk td, table.difftable tr.dmeta td {
1395  color: var(--muted);
1396  padding: var(--sp-1) var(--sp-3);
1397  background: var(--surface);
1398  border-top: 1px solid var(--line);
1399  border-bottom: 1px solid var(--line);
1400}
1401table.difftable tr.hunk td.src { color: var(--link); }
1402table.difftable tr.threadrow td { padding: var(--sp-2) var(--sp-3); background: var(--canvas); }
1403/* a line number is the handle for commenting on that line; it stays a
1404   plain number until pointed at, so the gutter is not a wall of links */
1405table.difftable td.ln a.cmt { color: inherit; text-decoration: none; }
1406table.difftable td.ln a.cmt:hover,
1407table.difftable td.ln a.cmt:focus { color: var(--link); text-decoration: underline; }
1408/* split layout: old and new side by side in one table. Where two
1409   columns of code do not fit, each row becomes a two-column grid and the
1410   old cells stack over the new, which reads as a unified diff. */
1411table.difftable.split { table-layout: fixed; }
1412table.difftable.split col.cln { width: 3.5rem; }
1413table.difftable.split td.ln.ctx, table.difftable.split td.ln.none { border-right: 1px solid var(--line); }
1414table.difftable.split td.ln + td.src + td.ln { border-left: 1px solid var(--line); }
1415table.difftable.split td.ln { width: auto; }
1416table.difftable.split td.del { background: var(--diff-del); }
1417table.difftable.split td.add { background: var(--diff-add); }
1418table.difftable.split td.none { background: var(--surface); }
1419table.difftable.split td.src.add::before { content: "+"; color: var(--ok); }
1420table.difftable.split td.src.del::before { content: "\2212"; color: var(--bad); }
1421table.difftable.split td.src.ctx::before { content: " "; }
1422table.difftable.split tr.threadrow td { overflow-wrap: anywhere; }
1423table.difftable.split td.ln:target,
1424table.difftable.split td.ln:target + td { background: color-mix(in srgb, var(--mark) 18%, transparent); }
1425table.difftable.split td.ln:target { border-left: 3px solid var(--mark); }
1426table.difftable.split .nonl { display: block; color: var(--muted); font-style: italic; }
1427p.layoutpick { color: var(--muted); font-size: var(--fs-2); margin: 0 0 var(--sp-3); }
1428p.layoutpick strong { color: var(--fg); font-weight: 600; }
1429@media (max-width: 64rem) {
1430  p.layoutpick { display: none; }
1431  table.difftable.split, table.difftable.split tbody { display: block; }
1432  table.difftable.split colgroup { display: none; }
1433  table.difftable.split tr.pair { display: grid; grid-template-columns: auto 1fr; }
1434  table.difftable.split tr.pair td.none, table.difftable.split tr.pair td.ctx.old { display: none; }
1435  table.difftable.split td.ln + td.src + td.ln { border-left: 0; }
1436  table.difftable.split tr.pair td.ln { min-width: 3rem; }
1437  table.difftable.split tr.hunk, table.difftable.split tr.dmeta,
1438  table.difftable.split tr.threadrow { display: block; }
1439  table.difftable.split tr.hunk td.ln, table.difftable.split tr.dmeta td.ln { display: none; }
1440  table.difftable.split tr.hunk td, table.difftable.split tr.dmeta td,
1441  table.difftable.split tr.threadrow td { display: block; }
1442}
1443/* A linked line: f2-n17 on a diff, L17 on a file (see issue 118). */
1444.difftable tr:target td, .code .line:has(a:target) { background: color-mix(in srgb, var(--mark) 18%, transparent); }
1445.difftable tr:target td.ln, .code .line:has(a:target) .ln { border-left: 3px solid var(--mark); }
1446/* chroma writes the highlighted spans; the container only supplies the
1447   ground and the face */
1448.chroma { font-family: var(--mono); background: none; }
1449/* chroma's own LineLink rule is dropped from the served palette: it set
1450   outline: none and hid the focus ring. This is that rule without it, on
1451   a 24px box so a line number is a target and not a glyph (#232) */
1452.chroma .lnlinks { display: inline-block; min-width: 24px; min-height: 24px; line-height: 24px; text-decoration: none; color: inherit; }
1453.chroma .line { line-height: 24px; }
1454
1455.blamehunk { display: flex; gap: var(--sp-3); border-top: 1px solid var(--line); align-items: flex-start; }
1456.blamehunk:first-child { border-top: 0; }
1457.blameinfo { flex: none; width: 17rem; min-width: 0; padding: var(--sp-2) var(--sp-3); }
1458.blameinfo .subject { margin: 0; font-size: var(--fs-1); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
1459.blameinfo .meta { margin: 0; font-size: var(--fs-1); color: var(--muted); }
1460.blamecode {
1461  flex: 1;
1462  margin: 0;
1463  padding: var(--sp-2) var(--sp-3) var(--sp-2) 0;
1464  background: var(--inset);
1465  border: 0;
1466  border-left: 1px solid var(--line);
1467  border-radius: 0;
1468  overflow-x: auto;
1469  align-self: stretch;
1470}
1471.lineno {
1472  display: inline-block;
1473  min-width: 3.5em;
1474  padding-right: 1em;
1475  text-align: right;
1476  color: var(--muted);
1477  user-select: none;
1478}
1479
1480/* ---- chips and badges ---- */
1481.chip, .badge {
1482  --chip: var(--neutral);
1483  display: inline-block;
1484  padding: 1px 8px;
1485  border-radius: var(--r-ctl);
1486  font-size: var(--fs-0);
1487  font-weight: 500;
1488  line-height: 1.5;
1489  color: var(--chip);
1490  border: 1px solid color-mix(in srgb, var(--chip) 40%, transparent);
1491  background: color-mix(in srgb, var(--chip) 10%, var(--canvas));
1492  white-space: nowrap;
1493  vertical-align: middle;
1494}
1495.chip-open { --chip: var(--ok); }
1496.chip-closed { --chip: var(--bad); }
1497.chip-merged, .chip-done { --chip: var(--done); }
1498.chip-neutral, .chip-source_gone { --chip: var(--neutral); }
1499.chip-stale { --chip: var(--warn); }
1500.chip.topic { --chip: var(--link); }
1501.chip.topic:hover { text-decoration: none; background: color-mix(in srgb, var(--chip) 18%, transparent); }
1502/* A label carries its own colour inline, from the label's hex, as one
1503   tone per scheme: chip text is 12px, so it needs 4.5:1 against its own
1504   ground, and no single colour clears that on both canvases (#226). An
1505   old browser without light-dark() drops the declaration and the chip
1506   falls back to the neutral above. */
1507.chip.label {
1508  --chip: light-dark(var(--chip-l, var(--neutral)), var(--chip-d, var(--neutral)));
1509  font-weight: 500;
1510}
1511.badge-verified { --chip: var(--ok); }
1512.badge-unsigned { --chip: var(--neutral); }
1513.badge-signed_unknown_key, .badge-signed_key_expired { --chip: var(--warn); }
1514.badge-signed_email_mismatch, .badge-signed_key_revoked, .badge-bad_signature { --chip: var(--bad); }
1515.badge.check-success, .chip.check-success { --chip: var(--ok); }
1516.badge.check-pending, .chip.check-pending,
1517.badge.check-running, .chip.check-running { --chip: var(--warn); }
1518.badge.check-failure, .chip.check-failure,
1519.badge.check-error, .chip.check-error { --chip: var(--bad); }
1520.badge.check-cancelled, .chip.check-cancelled,
1521.badge.check-skipped, .chip.check-skipped { --chip: var(--neutral); }
1522span.check-success { color: var(--ok); }
1523span.check-pending, span.check-running { color: var(--warn); }
1524span.check-failure, span.check-error { color: var(--bad); }
1525
1526a.memberchip {
1527  display: inline-block;
1528  border: 1px solid var(--line);
1529  border-radius: var(--r-ctl);
1530  padding: 1px 8px;
1531  margin: 0 2px 2px 0;
1532  color: var(--fg);
1533  font-size: var(--fs-1);
1534}
1535a.memberchip:hover { text-decoration: none; border-color: var(--link); color: var(--link); }
1536a.memberchip .role { color: var(--muted); }
1537
1538.pathbar { display: flex; align-items: center; gap: var(--sp-2) var(--sp-3); margin: 0 0 var(--sp-3); flex-wrap: wrap; font-size: var(--fs-2); overflow-wrap: anywhere; }
1539.pathbar .spacer { flex: 1; }
1540.pathbar .actions { display: flex; align-items: center; gap: var(--sp-2); }
1541/* history · blame · raw were 21px tall and "raw" 22px wide (#232) */
1542.pathbar .actions a { display: inline-block; padding: 6px 4px; }
1543.pathbar .acts { display: flex; gap: var(--sp-2); }
1544.crumbs { color: var(--muted); }
1545.crumbs a { color: var(--link); }
1546.crumbs strong { color: var(--fg); }
1547
1548/* ---- file navigator: the directory beside a file ---- */
1549.blobgrid { display: grid; grid-template-columns: 15rem minmax(0, 1fr); gap: var(--sp-6); align-items: start; }
1550.blobmain { min-width: 0; }
1551.filenav { position: sticky; top: var(--sp-4); font-size: var(--fs-2); }
1552.filenav ul { list-style: none; margin: 0; padding: 0; }
1553.filenav li a {
1554  display: block;
1555  padding: 3px var(--sp-2);
1556  border-radius: var(--r-ctl);
1557  color: var(--fg);
1558  font-family: var(--mono);
1559  font-size: var(--fs-1);
1560  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
1561}
1562.filenav li a:hover { background: var(--hover); text-decoration: none; }
1563.filenav li a[aria-current] { background: var(--surface); box-shadow: inset 2px 0 0 var(--mark); font-weight: 600; }
1564.filenav li a.dir { color: var(--link); }
1565.filenav li a.up { color: var(--muted); }
1566
1567/* ---- side column: facets or sections beside a list or a form ---- */
1568.withcol { display: grid; grid-template-columns: 15rem minmax(0, 1fr); gap: var(--sp-6); align-items: start; }
1569.withcol.narrow { grid-template-columns: 15rem minmax(0, 56rem); }
1570.colmain { min-width: 0; }
1571.sidecol { position: sticky; top: var(--sp-4); font-size: var(--fs-2); }
1572.sidecol .grp { margin-bottom: var(--sp-4); }
1573.sidecol ul { list-style: none; margin: 0; padding: 0; }
1574.sidecol li a {
1575  display: flex; align-items: baseline; gap: var(--sp-2);
1576  padding: 4px var(--sp-2);
1577  border-radius: var(--r-ctl);
1578  color: var(--fg);
1579}
1580.sidecol li a:hover { background: var(--hover); text-decoration: none; }
1581.sidecol li a[aria-current] { background: var(--surface); box-shadow: inset 2px 0 0 var(--mark); font-weight: 500; }
1582.sidecol li a i { margin-left: auto; font-style: normal; color: var(--muted); font-size: var(--fs-1); font-variant-numeric: tabular-nums; }
1583.sidecol form.searchform { margin-top: var(--sp-2); }
1584.sidecol form.searchform input[type="text"] { min-width: 0; width: 100%; }
1585
1586/* The column's groups sit in a disclosure. Below 62rem that is all the
1587   reader sees of the column: one closed line before the list, because a
1588   repository with two dozen labels stacked the whole vocabulary under
1589   the content it filters. Above 62rem the summary is hidden and the
1590   content shown whatever the open state, so the column is what it
1591   always was. The @supports guard is the fail-safe: an engine without
1592   ::details-content keeps a working toggle at every width instead of
1593   losing the column (#237). */
1594.sidedrop > summary {
1595  display: inline-flex;
1596  align-items: center;
1597  gap: var(--sp-2);
1598  height: 32px;
1599  padding: 0 var(--sp-3);
1600  background: var(--canvas);
1601  border: 1px solid var(--line);
1602  border-radius: var(--r-ctl);
1603  color: var(--fg);
1604  font-size: var(--fs-2);
1605  cursor: pointer;
1606  list-style: none;
1607}
1608.sidedrop[open] > summary { margin-bottom: var(--sp-3); }
1609.sidedrop > summary::-webkit-details-marker { display: none; }
1610.sidedrop > summary:hover { background: var(--hover); }
1611.sidedrop > summary::after { content: "\25be"; color: var(--muted); }
1612.sidedrop[open] > summary::after { content: "\25b4"; }
1613.sideword { font-weight: 600; }
1614/* the filters in force: where you are */
1615.sideon { color: var(--warn); }
1616/* every group came out empty, so there is nothing to open */
1617.sidedrop:not(:has(.grp)) { display: none; }
1618
1619@media (min-width: 62.01rem) {
1620  @supports selector(::details-content) {
1621    .sidedrop > summary { display: none; }
1622    .sidedrop::details-content { content-visibility: visible; }
1623  }
1624}
1625
1626/* a sticky column taller than the viewport scrolls inside itself, or its
1627   tail is unreachable once the page scrolls. Above 62rem only: below it
1628   .filenav is hidden and the other two are static. .dashpins stops being
1629   a column at 80rem and resets both properties there. */
1630@media (min-width: 62.01rem) {
1631  .filenav, .sidecol, .dashgrid > aside { max-height: calc(100vh - 2 * var(--sp-4)); overflow-y: auto; }
1632}
1633
1634.refchip {
1635  display: inline-flex;
1636  align-items: center;
1637  gap: 6px;
1638  height: 32px;
1639  padding: 0 10px;
1640  background: var(--canvas);
1641  border: 1px solid var(--line);
1642  border-radius: var(--r-ctl);
1643  font-family: var(--mono);
1644  font-size: var(--fs-2);
1645  font-weight: 500;
1646  color: var(--fg);
1647}
1648details.refmenu { position: relative; }
1649details.refmenu summary { cursor: pointer; list-style: none; }
1650details.refmenu summary::-webkit-details-marker { display: none; }
1651details.refmenu summary::after { content: " \25be"; color: var(--muted); }
1652details.refmenu .refdrop {
1653  position: absolute;
1654  z-index: 10;
1655  top: calc(100% + 4px);
1656  left: 0;
1657  min-width: 12rem;
1658  max-height: 20rem;
1659  overflow-y: auto;
1660  overscroll-behavior: contain;
1661  background: var(--canvas);
1662  border: 1px solid var(--line);
1663  border-radius: var(--r-card);
1664  box-shadow: var(--shadow);
1665  padding: var(--sp-1) 0;
1666}
1667details.refmenu .refdrop a {
1668  display: block;
1669  padding: var(--sp-1) var(--sp-3);
1670  color: var(--fg);
1671  font-family: var(--mono);
1672  font-size: var(--fs-1);
1673}
1674details.refmenu .refdrop a:hover { background: var(--hover); text-decoration: none; }
1675details.refmenu .refdrop a.allrefs {
1676  color: var(--link);
1677  font-family: var(--sans);
1678  border-top: 1px solid var(--faint);
1679  margin-top: var(--sp-1);
1680}
1681
1682/* ---- repository overview layout ---- */
1683.overview { display: block; }
1684.overviewmain { min-width: 0; }
1685/* the side column exists only above 62rem; below it .facts keeps its
1686   own two-up layout and the 40rem factgrid rule applies untouched */
1687@media (min-width: 62.01rem) {
1688  .overview.withfacts { display: grid; grid-template-columns: minmax(0, 1fr) 20rem; gap: var(--sp-6); align-items: start; }
1689  .overview.withfacts .facts { display: block; margin-top: 0; max-width: none; }
1690  .overview.withfacts .facts .factgrid { grid-template-columns: repeat(2, auto); }
1691  .overview.withfacts .clone pre { white-space: pre-wrap; word-break: break-all; }
1692}
1693
1694/* ---- repository facts ---- */
1695.facts { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-5); margin: var(--sp-5) 0 var(--sp-4); max-width: 72rem; }
1696.facts h2 {
1697  margin: 0 0 var(--sp-2);
1698  font-size: var(--fs-1);
1699  font-weight: 500;
1700  letter-spacing: 0.08em;
1701  text-transform: uppercase;
1702  color: var(--muted);
1703}
1704.clone { margin: 0 0 var(--sp-3); }
1705.facts .about { min-width: 0; margin-bottom: var(--sp-5); }
1706.clone .fieldname { font-size: var(--fs-1); color: var(--muted); font-weight: 500; margin-bottom: 2px; }
1707.clone pre { margin-bottom: var(--sp-3); user-select: all; }
1708.factgrid { display: grid; grid-template-columns: repeat(4, auto); gap: var(--sp-2) var(--sp-4); font-size: var(--fs-2); margin-bottom: var(--sp-3); }
1709.factgrid b { font-weight: 600; }
1710.factgrid a { color: var(--fg); }
1711.factgrid a:hover { color: var(--link); }
1712.counts { display: flex; flex-wrap: wrap; gap: var(--sp-1) var(--sp-4); margin: 0 0 var(--sp-3); font-size: var(--fs-2); }
1713.counts a { color: var(--fg); }
1714.counts a:hover { color: var(--link); }
1715.counts strong { font-weight: 600; }
1716.fact { color: var(--muted); }
1717.langbar { display: flex; height: 8px; margin: 0 0 var(--sp-2); border-radius: var(--r-ctl); overflow: hidden; gap: 1px; background: var(--faint); }
1718.langbar .lang { display: block; height: 100%; min-width: 2px; }
1719.langs { display: flex; flex-wrap: wrap; gap: var(--sp-1) var(--sp-3); margin: 0 0 var(--sp-3); font-size: var(--fs-1); color: var(--muted); }
1720.langs .lang-name { display: inline-flex; align-items: center; gap: var(--sp-1); }
1721.langs .dot { width: 8px; height: 8px; }
1722/* named where it reads as the language's own colour, neutral otherwise */
1723.lang { background: var(--neutral); }
1724.lang-go { background: var(--lang-go); }
1725.lang-rust { background: var(--lang-rust); }
1726.lang-c, .lang-c- { background: var(--lang-c); }
1727.lang-python { background: var(--lang-python); }
1728.lang-javascript { background: var(--lang-javascript); }
1729.lang-typescript { background: var(--lang-typescript); }
1730.lang-shell { background: var(--lang-shell); }
1731.lang-ruby { background: var(--lang-ruby); }
1732.lang-java { background: var(--lang-java); }
1733.lang-swift { background: var(--lang-swift); }
1734.lang-html { background: var(--lang-html); }
1735.lang-css { background: var(--lang-css); }
1736.lang-org { background: var(--lang-org); }
1737.lang-markdown { background: var(--lang-markdown); }
1738.lang-emacs-lisp { background: var(--lang-emacs-lisp); }
1739.lang-nix { background: var(--lang-nix); }
1740.lang-sql { background: var(--lang-sql); }
1741.contribs { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--sp-1) var(--sp-3); margin: 0; font-size: var(--fs-1); }
1742.contribs .label { color: var(--muted); }
1743.teambody { padding: var(--sp-3); }
1744.teambody .memberchip { margin-right: var(--sp-2); }
1745
1746/* ---- landing ---- */
1747/* the width class on main carries this too; the cap here keeps the page
1748   readable on the templates that do not set one yet */
1749.landing { max-width: 48rem; padding: var(--sp-6) 0; }
1750.landing h1 { font-size: var(--fs-6); display: flex; align-items: center; gap: var(--sp-3); margin-bottom: var(--sp-3); }
1751.landing h1 .mark { width: 32px; height: 32px; }
1752.lede { font-size: var(--fs-4); color: var(--muted); line-height: 1.5; max-width: 40rem; margin: 0 0 var(--sp-5); }
1753pre.quickstart { margin: 0 0 var(--sp-4); }
1754.shot { border: 1px solid var(--line); border-radius: var(--r-card); overflow: hidden; margin: var(--sp-5) 0; }
1755.shot video { display: block; width: 100%; height: auto; }
1756.shot figcaption {
1757  border-top: 1px solid var(--line);
1758  background: var(--surface);
1759  padding: var(--sp-2) var(--sp-3);
1760  color: var(--muted);
1761  font-size: var(--fs-1);
1762}
1763.facets { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--sp-5); margin: var(--sp-5) 0; }
1764.facets h2 { font-size: var(--fs-3); margin: 0 0 var(--sp-1); }
1765.facets p { color: var(--muted); font-size: var(--fs-2); margin: 0; }
1766.routes { display: flex; gap: var(--sp-3); align-items: center; margin: var(--sp-4) 0 0; }
1767
1768/* ---- profile ---- */
1769.profilehead { border-bottom: 1px solid var(--faint); padding-bottom: var(--sp-4); margin-bottom: var(--sp-4); }
1770.profilehead h1 { margin: 0 0 var(--sp-1); }
1771.profilehead .lede { font-size: var(--fs-3); margin: 0 0 var(--sp-1); }
1772.profilehead .meta { margin: var(--sp-1) 0 0; }
1773.activity { margin-bottom: var(--sp-5); }
1774h2 .count { background: none; color: var(--muted); font-weight: 400; font-size: var(--fs-2); margin-left: var(--sp-2); padding: 0; }
1775/* 53 week columns x 7 day rows, tinted with the link blue */
1776.actgraph-scroll { overflow-x: auto; padding-bottom: var(--sp-1); }
1777.actgraph { display: flex; gap: 3px; width: max-content; padding-top: 18px; }
1778.actweek { display: flex; flex-direction: column; gap: 3px; position: relative; }
1779.actmonth { position: absolute; top: -16px; left: 0; font-size: var(--fs-0); color: var(--muted); }
1780.actday { width: 11px; height: 11px; border-radius: 2px; background: var(--faint); }
1781.actday.pad { background: transparent; }
1782.actday.l0 { background: var(--faint); }
1783.actday.l1 { background: color-mix(in srgb, var(--link) 25%, var(--canvas)); }
1784.actday.l2 { background: color-mix(in srgb, var(--link) 50%, var(--canvas)); }
1785.actday.l3 { background: color-mix(in srgb, var(--link) 75%, var(--canvas)); }
1786.actday.l4 { background: var(--link); }
1787.actlegend { display: flex; align-items: center; gap: 3px; margin-top: var(--sp-2); font-size: var(--fs-0); color: var(--muted); }
1788.actlegend span:first-child { margin-right: var(--sp-1); }
1789.actlegend span:last-child { margin-left: var(--sp-1); }
1790/* the activity log under the graph: the newest events, not the year */
1791.logsub { margin: var(--sp-5) 0 var(--sp-2); font-size: var(--fs-2); color: var(--muted); text-transform: uppercase; letter-spacing: 0.04em; }
1792.ownerlog .feedline { font-size: var(--fs-2); }
1793.ownerlog .feedline .none { font-size: var(--fs-1); }
1794
1795/* ---- wiki ---- */
1796.wikilayout { display: flex; gap: var(--sp-5); align-items: flex-start; }
1797.wikipage { flex: 1; min-width: 0; }
1798.wikinav { flex: none; width: 15rem; }
1799.wikinav ul { list-style: none; margin: var(--sp-1) 0 var(--sp-4); padding: 0; }
1800.wikinav .wikidir { margin-bottom: 0; }
1801.wikinav li a {
1802  display: block;
1803  padding: 5px var(--sp-2);
1804  border-radius: var(--r-ctl);
1805  color: var(--fg);
1806  font-size: var(--fs-2);
1807}
1808.wikinav li a:hover { background: var(--hover); color: var(--fg); text-decoration: none; }
1809.wikinav li a.active { background: var(--surface); color: var(--link); font-weight: 500; }
1810
1811/* ---- misc ---- */
1812.meta { color: var(--muted); font-size: var(--fs-1); }
1813.muted { color: var(--muted); }
1814.desc, td.desc { color: var(--muted); }
1815.spacer { flex: 1; }
1816.compact { margin: 0; }
1817.name { min-width: 0; }
1818svg.icon { vertical-align: -0.125em; }
1819.sep { color: var(--muted); }
1820.owner { color: var(--muted); }
1821.title { margin: 0; }
1822.subject { min-width: 0; }
1823.sha { font-family: var(--mono); }
1824.when { color: var(--muted); }
1825.who { font-weight: 500; }
1826.was { color: var(--muted); }
1827.role { color: var(--muted); }
1828.age { color: var(--muted); }
1829.size { color: var(--muted); }
1830.review { font-size: var(--fs-2); }
1831
1832/* ---- breakpoints ---- */
1833@media (max-width: 80rem) {
1834  .dashgrid { grid-template-columns: minmax(0, 1fr) 20rem; }
1835  /* not a column any more, so it neither sticks nor scrolls in itself */
1836  .dashgrid > .dashpins { grid-column: 1 / -1; position: static; max-height: none; overflow: visible; }
1837  /* a pinned block that is not a column is the chip row (spec rule 7) */
1838  .pins { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
1839  /* the shorthand loses to .pins li:last-child, which drops the bottom
1840     border of the last chip -- and so of the second row */
1841  .pins li, .pins li:last-child { border: 1px solid var(--line); border-radius: var(--r-ctl); padding: var(--sp-1) var(--sp-3); }
1842  .pins .n, .dashpins .meta { display: none; }
1843}
1844
1845@media (max-width: 62rem) {
1846  /* one column, the aside after the thread: a phone shows the
1847     description first, checks and reviewers after it (#232) */
1848  .withaside { grid-template-columns: 1fr; }
1849
1850  /* rows go back to two lines where one does not fit */
1851  ul.issuelist.rows .issuemain, ul.repolist.rows li, ul.loglist.rows .commitmain { display: block; }
1852  ul.issuelist.rows .title, ul.repolist.rows .desc { white-space: normal; }
1853  /* a nowrap meta or topic line on one of these rows forces horizontal
1854     page scroll on a phone: a long branch name, a long owner/repo#N */
1855  ul.issuelist.rows .meta, ul.loglist.rows .meta,
1856  ul.repolist.rows .meta, ul.repolist.rows .topics { white-space: normal; }
1857
1858  .dashgrid { grid-template-columns: 1fr; }
1859  .dashgrid > aside { position: static; }
1860  .tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); }
1861
1862  /* the tree page is the navigator on a phone */
1863  .blobgrid { grid-template-columns: 1fr; }
1864  .filenav { display: none; }
1865
1866  /* the column is a closed disclosure before the content: a filter you
1867     reach without scrolling the list, and nothing to scroll past when
1868     you do not want it. It leads rather than follows because it is what
1869     the list is narrowed by, not a note on one row (#237) */
1870  .withcol, .withcol.narrow { grid-template-columns: 1fr; }
1871  .sidecol { position: static; margin-bottom: var(--sp-3); }
1872  .sidecol .grp { display: inline-block; vertical-align: top; margin-right: var(--sp-5); }
1873}
1874
1875@media (max-width: 52rem) {
1876  main.content { padding: var(--sp-4) var(--sp-4) var(--sp-5); }
1877  footer { padding: var(--sp-4); }
1878  .repohead { padding: var(--sp-3) 0 0; }
1879  .repohead .wrap { padding: 0 var(--sp-4); }
1880  /* seven tabs do not fit a phone in one row; a hidden scrollbar made the
1881     ones past the edge look absent. Two rows show all of them. */
1882  nav.tabs { flex-wrap: wrap; overflow: visible; }
1883  .facts { grid-template-columns: 1fr; }
1884  /* a phone shows the description under the name, not beside it */
1885  .repodesc { flex-basis: 100%; margin-left: 0; }
1886}
1887
1888@media (max-width: 40rem) {
1889  .facets { grid-template-columns: 1fr; gap: var(--sp-4); }
1890  .wikilayout { flex-direction: column; align-items: stretch; }
1891  .wikinav { width: auto; }
1892  form.setform { grid-template-columns: 1fr; gap: var(--sp-2); }
1893  form.setform label { margin-top: 0; }
1894  /* the commit subject is the widest thing in a file table and the one
1895     column a phone can do without: the tipbar above already names the
1896     latest commit */
1897  table.tree td.lastcommit, table.tree th.lastcommit { display: none; }
1898  table.tree td.mode, table.tree th.mode { display: none; }
1899  /* at 320px a nowrap name plus the age column overran the card by the
1900     cell padding and the card clipped the age; a long name wraps (#232) */
1901  table.tree td.name { white-space: normal; overflow-wrap: anywhere; }
1902  .clone pre { white-space: pre-wrap; word-break: break-all; }
1903  .readme .cardbody, .code { padding: var(--sp-3); }
1904  .thread { margin-left: var(--sp-3); }
1905  .blamehunk { flex-direction: column; gap: 0; }
1906  .blameinfo { width: auto; }
1907  .blamecode { border-left: 0; padding-left: var(--sp-3); width: 100%; }
1908  .factgrid { grid-template-columns: repeat(2, auto); }
1909  ul.loglist li { flex-wrap: wrap; }
1910  ul.loglist .commitside { width: 100%; justify-content: flex-start; }
1911}
1912
1913/* Nine 44px squares and the mark are 440px, so below 34rem the rail
1914   keeps six — dashboard, explore, search, notifications, More, your
1915   avatar — and the More menu holds the rest. Seven squares at 44px are
1916   308px, inside a 320px phone. Explore is one of the six because the
1917   More menu only exists for a signed-in viewer: dropping it left a
1918   signed-out phone with no route to the listing at all. Shrinking the
1919   squares instead is what this replaces: 32px was under the target
1920   every phone guideline asks for, and the count only grows. */
1921@media (max-width: 34rem) {
1922  .railopt { display: none; }
1923  .railmore { display: block; }
1924  .railicon svg { width: 20px; height: 20px; }
1925  /* the mark is a link home too, so it is a target like the rest */
1926  a.brand { width: var(--rail-hit); }
1927}
1928
1929/* ---- reactions ---- */
1930.reactions { display: flex; flex-wrap: wrap; gap: var(--sp-1); margin: 0; padding: var(--sp-2) var(--sp-4); }
1931p.reactions { color: var(--muted); font-size: var(--fs-1); }
1932article.comment .reactions { border-top: 1px solid var(--faint); }
1933button.react, span.react {
1934  font: inherit;
1935  font-size: var(--fs-1);
1936  padding: 0 var(--sp-2);
1937  min-height: 24px;
1938  border: 1px solid var(--line);
1939  border-radius: var(--r-sm);
1940  background: transparent;
1941  color: var(--muted);
1942}
1943button.react { cursor: pointer; }
1944button.react:hover { background: var(--surface); }
1945button.react.mine { border-color: var(--accent); background: var(--surface); color: var(--fg); }