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