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