internal/web/static/style.css
1572 lines · 55079 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-skipped, .chip.check-skipped { --chip: var(--neutral); }
1057.badge.check-running, .chip.check-running { --chip: var(--warn); }
1058.chip.check-success { --chip: var(--ok); }
1059.chip.check-pending { --chip: var(--warn); }
1060.chip.check-failure { --chip: var(--bad); }
1061pre.buildlog { max-height: 40rem; overflow: auto; }
1062span.check-success { color: var(--ok); }
1063span.check-pending { color: var(--warn); }
1064span.check-failure, span.check-error { color: var(--bad); }
1065
1066/* issue and MR listings */
1067.listhead {
1068 display: flex;
1069 align-items: baseline;
1070 gap: var(--sp-4);
1071 flex-wrap: wrap;
1072}
1073.listhead h2 { margin-bottom: var(--sp-2); }
1074nav.filters { display: flex; gap: var(--sp-1); }
1075nav.filters a {
1076 color: var(--muted);
1077 font-size: var(--fs-2);
1078 padding: 0.05rem var(--sp-2);
1079 border-radius: var(--r-pill);
1080}
1081nav.filters a:hover { color: var(--fg); background: var(--hover); text-decoration: none; }
1082nav.filters a.active { color: var(--accent); background: color-mix(in srgb, var(--accent) 10%, transparent); }
1083
1084ul.issuelist {
1085 list-style: none;
1086 margin: var(--sp-2) 0;
1087 padding: 0;
1088 background: var(--surface);
1089 border: 1px solid var(--line);
1090 border-radius: var(--r-lg);
1091 overflow: hidden;
1092}
1093ul.issuelist li {
1094 display: flex;
1095 align-items: baseline;
1096 gap: var(--sp-3);
1097 padding: var(--sp-3) var(--sp-4);
1098 border-bottom: 1px solid var(--faint);
1099}
1100ul.issuelist li:last-child { border-bottom: none; }
1101ul.issuelist li:hover { background: var(--hover); }
1102ul.issuelist li.empty { display: block; color: var(--muted); }
1103ul.issuelist .issuemain { flex: 1; min-width: 0; }
1104ul.issuelist p { margin: 0; }
1105ul.issuelist .title a { color: var(--fg); font-weight: 550; }
1106ul.issuelist .title a:hover { color: var(--accent); }
1107
1108/* releases */
1109article.release {
1110 background: var(--surface);
1111 border: 1px solid var(--line);
1112 border-radius: var(--r-lg);
1113 padding: var(--sp-3) var(--sp-4);
1114 margin: var(--sp-4) 0;
1115}
1116.releasehead { background: none; border: 0; padding: 0; }
1117.releasehead h3 { margin: 0 0 var(--sp-1); }
1118.releasehead .meta { margin: 0 0 var(--sp-2); }
1119table.assets { margin-top: var(--sp-2); }
1120table.assets td.size { color: var(--muted); white-space: nowrap; text-align: right; }
1121table.assets td.name { width: 100%; font-family: var(--mono); font-size: var(--fs-1); }
1122
1123/* milestones */
1124ul.milestonelist {
1125 list-style: none;
1126 margin: var(--sp-2) 0;
1127 padding: 0;
1128 border: 1px solid var(--line);
1129 border-radius: var(--r-lg);
1130 overflow: hidden;
1131}
1132ul.milestonelist li { padding: var(--sp-3) var(--sp-4); border-bottom: 1px solid var(--faint); }
1133ul.milestonelist li:last-child { border-bottom: none; }
1134ul.milestonelist li.empty { color: var(--muted); }
1135ul.milestonelist p { margin: 0; }
1136ul.milestonelist .title { font-weight: 550; }
1137.progress {
1138 height: 6px;
1139 background: var(--faint);
1140 border-radius: var(--r-pill);
1141 margin-top: var(--sp-2);
1142 overflow: hidden;
1143 max-width: 24rem;
1144}
1145.progress .bar { height: 100%; background: var(--ok); }
1146.listhead .spacer { flex: 1; }
1147
1148/* issue / MR detail */
1149h2.issuetitle { margin-bottom: var(--sp-1); font-size: var(--fs-5); }
1150.issuenumber { color: var(--muted); font-weight: 400; }
1151.issuemeta { color: var(--muted); font-size: var(--fs-2); margin-top: 0; }
1152.review { font-size: var(--fs-2); }
1153
1154/* comment cards; discussion stays at a readable measure */
1155article.comment, form.commentform {
1156 max-width: 48rem;
1157}
1158article.comment {
1159 border: 1px solid var(--faint);
1160 border-radius: var(--r-lg);
1161 margin: var(--sp-4) 0;
1162}
1163article.comment .commenthead {
1164 background: var(--surface);
1165 border-bottom: 1px solid var(--faint);
1166 border-radius: var(--r-lg) var(--r-lg) 0 0;
1167 padding: var(--sp-2) var(--sp-4);
1168 font-size: var(--fs-2);
1169}
1170article.comment .commenthead .when { color: var(--muted); }
1171article.comment .rendered { padding: 0 var(--sp-4); }
1172article.comment .rendered > :first-child { margin-top: var(--sp-3); }
1173article.comment .rendered > :last-child { margin-bottom: var(--sp-3); }
1174form.commentform { margin: var(--sp-4) 0; }
1175details.editbox { margin: var(--sp-2) 0; max-width: 48rem; }
1176details.editbox summary { cursor: pointer; color: var(--muted); font-size: var(--fs-2); }
1177details.editbox summary:hover { color: var(--accent); }
1178details.editbox input[type="text"] { width: 100%; }
1179.syscomment {
1180 max-width: 48rem;
1181 color: var(--muted);
1182 font-size: var(--fs-2);
1183 margin: var(--sp-3) 0;
1184 padding-left: var(--sp-4);
1185 border-left: 3px solid var(--faint);
1186}
1187.syscomment p { display: inline; margin: 0; }
1188
1189/* review threads under diff lines */
1190.thread {
1191 border: 1px solid var(--faint);
1192 border-left: 3px solid var(--accent);
1193 border-radius: var(--r-md);
1194 padding: var(--sp-2) var(--sp-4);
1195 margin: var(--sp-2) 0 var(--sp-2) var(--sp-6);
1196}
1197.thread.resolved { border-left-color: var(--ok); opacity: 0.75; }
1198/* an unsubmitted comment is the viewer's own note, not the conversation */
1199.thread.pending { border-left-color: var(--neutral); border-left-style: dashed; }
1200.thread.stale { border-left-color: var(--warn); }
1201.thread p.commenthead { font-size: var(--fs-2); margin: var(--sp-2) 0 0; }
1202.thread .when { color: var(--muted); }
1203.thread .rendered p { margin: var(--sp-1) 0; }
1204.thread .threadstate { color: var(--muted); font-size: var(--fs-1); margin: var(--sp-1) 0 0; }
1205.thread.composing { border-left-color: var(--mark); }
1206.thread details.threadreply { margin: var(--sp-2) 0 0; }
1207.thread details.threadreply > summary { color: var(--accent); font-size: var(--fs-1); cursor: pointer; }
1208.thread textarea { width: 100%; }
1209.thread.composing p, .thread details.threadreply p { margin: var(--sp-2) 0 0; }
1210
1211/* forms: every control shares one shell, so a select reads as a sibling
1212 of the text inputs beside it rather than as an OS control dropped in */
1213input, textarea, button, select { font: inherit; color: inherit; }
1214input[type="text"], input[type="search"], input[type="email"],
1215input[type="password"], input:not([type]), textarea, select {
1216 background: var(--bg);
1217 color: var(--fg);
1218 border: 1px solid var(--line);
1219 border-radius: var(--r-md);
1220 padding: var(--sp-2) var(--sp-3);
1221 line-height: 1.4;
1222}
1223textarea { width: 100%; resize: vertical; }
1224textarea[name="content"] { font-family: var(--mono); font-size: var(--fs-1); }
1225/* the native arrow cannot be restyled, so draw our own and match the
1226 inputs exactly; --chev carries the stroke colour per scheme */
1227select {
1228 appearance: none;
1229 padding-right: 1.9rem;
1230 background-image: var(--chev);
1231 background-repeat: no-repeat;
1232 background-position: right 0.6rem center;
1233 background-size: 0.62rem;
1234 cursor: pointer;
1235}
1236input:hover, textarea:hover, select:hover { border-color: var(--muted); }
1237/* the global :focus-visible ring covers these; only the border moves */
1238input:focus, textarea:focus, select:focus { border-color: var(--accent); }
1239input[type="radio"] { accent-color: var(--fill); }
1240label:has(> input[type="radio"]), label:has(> input[type="checkbox"]),
1241label:has(> select) { display: inline-flex; align-items: center; gap: var(--sp-2); }
1242
1243/* A radio keeps the native control: a circle takes one answer, a square
1244 takes any number, and that shape is the cue that survives greyscale,
1245 high contrast and colour blindness. The checkbox is drawn instead,
1246 because at the size the OS renders it it reads as an afterthought
1247 beside the button it shares a row with. The square is kept square —
1248 the size changes, the cue does not. */
1249input[type="checkbox"] {
1250 appearance: none;
1251 flex: none;
1252 /* the height of a text input, a select and a button, so a settings row
1253 ends in one column of controls rather than a button and a speck */
1254 width: 2.5rem;
1255 height: 2.5rem;
1256 /* an empty box has nothing but its border to announce it, so the border
1257 is --muted rather than the --line the sized controls can afford */
1258 border: 1px solid var(--muted);
1259 border-radius: var(--r-sm);
1260 background: var(--bg);
1261 cursor: pointer;
1262}
1263input[type="checkbox"]:not(:checked):hover { border-color: var(--fg); }
1264input[type="checkbox"]:checked {
1265 background: var(--fill);
1266 border-color: var(--fill);
1267 /* the mark sits on the blue ground, so it is white in both schemes */
1268 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");
1269 background-repeat: no-repeat;
1270 background-position: center;
1271 background-size: 1.2rem;
1272}
1273
1274/* a segmented control for a small, mutually exclusive set: real radios
1275 underneath, so arrow keys and screen readers behave as they always did */
1276fieldset.segmented { border: 0; margin: 0 0 var(--sp-4); padding: 0; }
1277fieldset.segmented legend {
1278 padding: 0;
1279 margin-bottom: var(--sp-2);
1280 color: var(--muted);
1281 font-size: var(--fs-2);
1282}
1283fieldset.segmented .options { display: inline-flex; }
1284fieldset.segmented label { gap: 0; }
1285fieldset.segmented input {
1286 position: absolute;
1287 width: 1px;
1288 height: 1px;
1289 opacity: 0;
1290 pointer-events: none;
1291}
1292fieldset.segmented span {
1293 display: block;
1294 padding: var(--sp-2) var(--sp-4);
1295 border: 1px solid var(--line);
1296 font-size: var(--fs-2);
1297 cursor: pointer;
1298}
1299fieldset.segmented label + label span { border-left: 0; }
1300fieldset.segmented label:first-child span { border-radius: var(--r-sm) 0 0 var(--r-sm); }
1301fieldset.segmented label:last-child span { border-radius: 0 var(--r-sm) var(--r-sm) 0; }
1302fieldset.segmented span:hover { border-color: var(--muted); }
1303fieldset.segmented input:checked + span {
1304 background: var(--fill);
1305 border-color: var(--fill);
1306 color: #ffffff;
1307 font-weight: 600;
1308}
1309fieldset.segmented input:focus-visible + span {
1310 outline: 2px solid var(--accent);
1311 outline-offset: 2px;
1312}
1313
1314/* remaining input states */
1315::placeholder { color: var(--muted); opacity: 1; }
1316::selection { background: var(--fill); color: #ffffff; }
1317/* :user-invalid, not :invalid — a required field is invalid the moment
1318 the page loads, and colouring it red before anyone has typed is a
1319 scolding, not a hint */
1320input:user-invalid, textarea:user-invalid {
1321 border-color: var(--bad);
1322}
1323.hint { display: block; color: var(--muted); font-size: var(--fs-1); margin-top: var(--sp-1); }
1324button {
1325 background: var(--fill);
1326 color: #ffffff;
1327 border: 1px solid var(--fill-edge);
1328 border-radius: var(--r-md);
1329 padding: var(--sp-2) var(--sp-4);
1330 font-weight: 600;
1331 cursor: pointer;
1332}
1333button:hover { filter: brightness(1.15); }
1334
1335/* 404 */
1336.notfound { text-align: center; padding: var(--sp-6) 0; }
1337.notfound .bigcode {
1338 font-family: var(--mono);
1339 font-size: var(--fs-7);
1340 color: var(--faint);
1341 -webkit-text-stroke: 1px var(--line);
1342 margin: 0;
1343 line-height: 1;
1344}
1345.notfound h1 { margin-top: var(--sp-3); }
1346
1347/* footer */
1348footer {
1349 border-top: 1px solid var(--faint);
1350 padding: var(--sp-4) var(--sp-5) var(--sp-5);
1351 width: 100%;
1352 color: var(--muted);
1353 font-size: var(--fs-1);
1354}
1355footer p { margin: 0; }
1356footer a { color: var(--muted); text-decoration: underline; }
1357footer a:hover { color: var(--accent); }
1358
1359/* search */
1360form.searchform {
1361 display: flex;
1362 gap: var(--sp-2);
1363 margin: var(--sp-3) 0 var(--sp-4);
1364}
1365form.searchform input[type="search"] { flex: 1; max-width: 32rem; }
1366form.searchform.compact { margin: 0; }
1367form.searchform.compact input[type="search"] {
1368 padding: var(--sp-1) var(--sp-3);
1369 font-size: var(--fs-2);
1370 min-width: 18rem;
1371}
1372ul.matchlist { list-style: none; margin: var(--sp-3) 0; padding: 0; }
1373ul.matchlist li { margin-bottom: var(--sp-3); }
1374ul.matchlist .matchpath { font-family: var(--mono); font-size: var(--fs-1); }
1375pre.matchline {
1376 background: var(--code-bg);
1377 border: 1px solid var(--faint);
1378 border-radius: var(--r-md);
1379 padding: var(--sp-1) var(--sp-3);
1380 margin: var(--sp-1) 0 0;
1381 overflow-x: auto;
1382}
1383pre.matchline mark {
1384 background: color-mix(in srgb, var(--warn) 25%, transparent);
1385 color: inherit;
1386 border-radius: 2px;
1387}
1388
1389/* wiki */
1390.wikilayout { display: flex; gap: var(--sp-5); align-items: flex-start; }
1391.wikipage { flex: 1; min-width: 0; }
1392.wikinav { flex: none; width: 13rem; padding-top: var(--sp-4); }
1393.wikinav ul { list-style: none; margin: var(--sp-1) 0 var(--sp-4); padding: 0; }
1394.wikinav li a { display: block; padding: 0.15rem 0; color: var(--fg); }
1395.wikinav li a:hover { color: var(--accent); text-decoration: none; }
1396.wikinav li a.active { color: var(--accent); font-weight: 550; }
1397@media (max-width: 40rem) {
1398 /* Column direction + the desktop align-items:flex-start lets .wikipage
1399 take its content's intrinsic width, overflowing the viewport (and
1400 triggering iOS font inflation). Stretch pins it to the container. */
1401 .wikilayout { flex-direction: column; align-items: stretch; }
1402 .wikinav { width: auto; padding-top: 0; }
1403}
1404
1405/* blame */
1406.blame {
1407 border: 1px solid var(--line);
1408 border-radius: var(--r-md);
1409 overflow: hidden;
1410}
1411.blamehunk {
1412 display: flex;
1413 gap: var(--sp-3);
1414 border-top: 1px solid var(--line);
1415 align-items: flex-start;
1416}
1417.blamehunk:first-child { border-top: none; }
1418.blameinfo {
1419 flex: none;
1420 width: 17rem;
1421 padding: var(--sp-2) var(--sp-3);
1422 min-width: 0;
1423}
1424.blameinfo .subject {
1425 margin: 0;
1426 font-size: var(--fs-1);
1427 white-space: nowrap;
1428 overflow: hidden;
1429 text-overflow: ellipsis;
1430}
1431.blameinfo .meta { margin: 0; font-size: var(--fs-1); color: var(--muted); }
1432.blamecode {
1433 flex: 1;
1434 margin: 0;
1435 padding: var(--sp-2) var(--sp-3) var(--sp-2) 0;
1436 overflow-x: auto;
1437 border-left: 1px solid var(--line);
1438 align-self: stretch;
1439}
1440.lineno {
1441 display: inline-block;
1442 min-width: 3.5em;
1443 padding-right: 1em;
1444 text-align: right;
1445 color: var(--muted);
1446 user-select: none;
1447}
1448
1449/* mobile */
1450@media (max-width: 40rem) {
1451 .thread { margin-left: var(--sp-3); }
1452 /* the commit subject is the widest thing in the listing and the one
1453 column a phone can do without: the tipbar above already names the
1454 latest commit. */
1455 table.tree td.lastcommit, table.tree th.lastcommit { display: none; }
1456 table.tree td.mode, table.tree th.mode { display: none; }
1457 .clone, .pathbar { overflow-wrap: anywhere; }
1458 .readme, .code { padding: var(--sp-3); }
1459 .blamehunk { flex-direction: column; gap: 0; }
1460 .blameinfo { width: auto; }
1461 .blamecode { border-left: none; padding-left: var(--sp-3); width: 100%; }
1462 .cardhead {
1463 margin: calc(-1 * var(--sp-3)) calc(-1 * var(--sp-3)) var(--sp-3);
1464 padding: var(--sp-2) var(--sp-3);
1465 }
1466}
1467
1468/* tree listing: the last commit is what a file table is scanned for, so
1469 it leads. The file's own facts moved to the blob page. */
1470.tipbar {
1471 display: flex;
1472 align-items: center;
1473 gap: var(--sp-3);
1474 padding: var(--sp-2) var(--sp-4);
1475 background: var(--hover);
1476 border: 1px solid var(--line);
1477 border-bottom: 0;
1478 border-radius: var(--r-lg) var(--r-lg) 0 0;
1479 font-size: var(--fs-2);
1480}
1481.tipbar .who { font-weight: 600; }
1482.tipbar .subject { color: var(--fg); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1483.tipbar .subject:hover { color: var(--accent); }
1484.tipbar .spacer { flex: 1; }
1485.tipbar .age { color: var(--muted); white-space: nowrap; }
1486.tipbar + table.tree { border-radius: 0 0 var(--r-lg) var(--r-lg); }
1487table.tree th.lastcommit, table.tree td.lastcommit { width: 55%; }
1488table.tree td.lastcommit a { color: var(--muted); display: block; overflow: hidden;
1489 text-overflow: ellipsis; white-space: nowrap; }
1490table.tree td.lastcommit a:hover { color: var(--accent); }
1491table.tree th.age, table.tree td.age {
1492 text-align: right;
1493 white-space: nowrap;
1494 color: var(--muted);
1495 font-variant-numeric: tabular-nums;
1496}
1497table.tree td.name { width: 25%; white-space: nowrap; }
1498table.tree td.name.dir a { color: var(--accent); }
1499p.clone { margin: 0 0 var(--sp-3); }
1500p.filefacts { color: var(--muted); font-size: var(--fs-1); margin: 0 0 var(--sp-3); }
1501.pathbar .actions { font-size: var(--fs-1); color: var(--muted); }
1502.snippetfile { margin-bottom: 1.5rem }
1503
1504/* merge request: a two-column split, so state has somewhere to live that
1505 is not a run-on sentence under the title */
1506.withaside {
1507 display: grid;
1508 grid-template-columns: minmax(0, 1fr) 15rem;
1509 gap: var(--sp-5);
1510 align-items: start;
1511}
1512.withaside .mainside { min-width: 0; }
1513@media (max-width: 62rem) { .withaside { grid-template-columns: 1fr; } }
1514
1515.aside { font-size: var(--fs-2); }
1516.aside .grp { border-top: 1px solid var(--line); padding: var(--sp-3) 0; }
1517.aside .grp:first-child { border-top: 0; padding-top: 0; }
1518.aside h2 {
1519 margin: 0 0 var(--sp-2);
1520 font-family: var(--mono);
1521 font-size: var(--fs-0);
1522 font-weight: 500;
1523 letter-spacing: 0.1em;
1524 text-transform: uppercase;
1525 color: var(--muted);
1526}
1527.aside .row { display: flex; align-items: center; gap: var(--sp-2); margin: 0 0 var(--sp-1); flex-wrap: wrap; }
1528.aside .row:last-child { margin-bottom: 0; }
1529.aside .none { color: var(--muted); }
1530/* a row's second line: when it happened, under what it happened to. The
1531 extra gap keeps the pair reading as one entry. */
1532.aside .row .sub { flex-basis: 100%; color: var(--muted); font-size: var(--fs-0); }
1533.aside .row:has(.sub) { margin-bottom: var(--sp-2); }
1534.aside .dot { width: 0.45rem; height: 0.45rem; flex: none; border-radius: 50%; background: var(--muted); }
1535.aside .dot.ok { background: var(--ok); }
1536.aside .dot.bad { background: var(--bad); }
1537.aside .dot.pend { background: var(--mark); }
1538
1539/* sub-navigation within an item; orange marks position as it does above */
1540nav.subtabs {
1541 display: flex;
1542 gap: var(--sp-4);
1543 border-bottom: 1px solid var(--line);
1544 margin: 0 0 var(--sp-4);
1545}
1546nav.subtabs a {
1547 display: inline-flex;
1548 align-items: center;
1549 gap: var(--sp-2);
1550 padding: 0 0 var(--sp-2);
1551 color: var(--muted);
1552 font-size: var(--fs-2);
1553 border-bottom: 2px solid transparent;
1554 margin-bottom: -1px;
1555}
1556nav.subtabs a:hover { color: var(--fg); text-decoration: none; }
1557nav.subtabs a[aria-current] { color: var(--fg); font-weight: 700; border-bottom-color: var(--mark); }
1558nav.subtabs a i {
1559 font-style: normal;
1560 font-family: var(--mono);
1561 font-size: var(--fs-0);
1562 background: var(--hover);
1563 border-radius: var(--r-sm);
1564 padding: 0 0.35rem;
1565}
1566p.diffstat { color: var(--muted); font-size: var(--fs-2); margin: 0 0 var(--sp-3); }
1567p.diffstat .add { color: var(--ok); }
1568p.diffstat .del { color: var(--bad); }
1569
1570/* file edit form */
1571.editform textarea { width: 100%; }
1572.editform input[name="message"] { width: 60%; }