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