Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/vole.git

Write over SSH with a registered key:

git clone ssh://git@rodent.software:2222/vole.git

A free and open-source forge that lives in your Git repo.

Blame · server/web/static/vole.css

Line attribution on main · 2413 line(s) · showing the first 1 MiB

796ac8c0
Keenan Nemetz
11d ago
1 /* Vole's web surface. Token-driven: a light theme is another block of variables,
2 and a real identity is a change to the top block only. Everforest is the
3 permanent theme in static/everforest.css; this sheet supplies the layout and
4 the fallback tokens the theme sheet overrides.
c717d4eb
Keenan Nemetz
13d ago
5
6 Sections, in file order. Rules cascade in this order too, so a later section
7 can override an earlier one and a section cannot be moved without checking
8 what it was quietly overriding:
9
10 tokens custom properties, dark and light (:root)
6c03e6d3
Keenan Nemetz
9d ago
11 chrome document shell, global bar, account menu,
12 command palette
c717d4eb
Keenan Nemetz
13d ago
13 repository header name, visibility, tab row
14 panels and tables the bordered surfaces lists and pages sit on
15 controls buttons, inputs, selects, form layout
16 labels, flashes, empty pages
17 auth sign-in, registration, invite, account
18 repository home clone button, readme, the home row layout
19 diffs and markdown code/diff tables, hunks, comment cells, .md
20 shared responsive rules the few rules that apply to every surface
21 repository overview summary page: main column and sidebar
22 code tab bar and crumbs the bar above the open file
23 code browser and file tree the persistent tree beside the pane
306f36fc
Keenan Nemetz
12d ago
24 syntax highlighting prism token classes
c717d4eb
Keenan Nemetz
13d ago
25 repository overview sidebar compact commit and run rows
c765ae2e
Keenan Nemetz
13d ago
26 run detail summary cards, step timing and lazy logs
c717d4eb
Keenan Nemetz
13d ago
27 issues and changes list pages, thread, forms, metadata sidebar
28 icons inline SVG sizing
29 activity feed event rows for one repository and the instance
30 accessibility skip link, focus rings, reduced motion
31 light theme the second token block
32 pager previous/next between list pages
33 code search per-file result blocks
34 blame per-line commit column
6ce635a8
Keenan Nemetz
1d ago
35 landing page introduction card, repository grid, feed rail
c717d4eb
Keenan Nemetz
13d ago
36 status page instance storage, runners, configuration
37 language colours file-icon and language-dot palette
38
39 Note the two pairs that are easy to confuse: "repository home" is the clone
ed03b3d1
Keenan Nemetz
6h ago
40 button and readme on /<name>, while "landing page" is the instance home at
c717d4eb
Keenan Nemetz
13d ago
41 /; "repository overview" is the summary page, while "repository overview
42 sidebar" is the small commit and run rows it uses. */
f09ef39e
Keenan Nemetz
15d ago
43
44 :root {
45 color-scheme: dark;
46
47 /* surfaces */
48 --bg: #0e0f11;
49 --bg-panel: #141517;
50 --bg-raised: #191a1e;
51 --bg-hover: #1f2024;
52 --bg-active: #24262b;
53 --border: #26282d;
54 --border-strong: #35383f;
55
56 /* text */
57 --fg: #e7e8ea;
58 --fg-muted: #8b909a;
59 --fg-faint: #696e78;
60
61 /* accent and status */
62 --accent: #7c86ff;
63 --accent-fg: #ffffff;
64 --accent-soft: rgba(124, 134, 255, 0.14);
3516aef3
Keenan Nemetz
10d ago
65 /* A run that is still going gets its own token rather than --accent: a theme
66 whose accent is green (Everforest) would otherwise paint "running" and
67 "success" the same colour, and the two must not be confused at a glance. */
68 --running: var(--accent);
f09ef39e
Keenan Nemetz
15d ago
69 --good: #4cb782;
70 --good-soft: rgba(76, 183, 130, 0.12);
71 --bad: #eb5757;
72 --bad-soft: rgba(235, 87, 87, 0.12);
73 --warn: #f2c94c;
f6f4b1db
Keenan Nemetz
15d ago
74 --warn-soft: rgba(242, 201, 76, 0.14);
d69be4df
Keenan Nemetz
11d ago
75 /* Status buttons are filled, so their label uses the page ground colour. It is
76 dark in the dark themes and light in the light ones, which keeps it legible
77 against every theme's good/bad/warn hue. */
78 --status-fg: var(--bg);
0e35a6a0
Keenan Nemetz
11d ago
79 /* A merged request has landed, which is neither open nor merely closed. It
80 gets its own hue so a merged row cannot be read as still being reviewed. */
81 --merged: #b083f0;
82 --merged-soft: rgba(176, 131, 240, 0.14);
f09ef39e
Keenan Nemetz
15d ago
83
84 /* shape and type */
85 --radius: 6px;
86 --radius-lg: 8px;
55615c7a
Keenan Nemetz
11d ago
87 /* One width for every search input: the global bar and a page's filter row
88 should not resize the field when the reader moves between them. */
89 --search-w: 18rem;
6c03e6d3
Keenan Nemetz
9d ago
90 /* One measure for the page column and the global bar. The bar pads itself out
91 to the page's content edges, so the brand and the account menu sit on the
92 same vertical line as the content below them. */
93 --page-max: 1440px;
94 --page-gutter: 24px;
796ac8c0
Keenan Nemetz
11d ago
95 --font: "Lexend", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Noto Sans", "Liberation Sans", Arial, sans-serif;
96 --mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
97 --fs: 15.4px;
98 --lh: 1.55;
99 /* Every explicit size is calc(<px> * var(--font-scale)) so the Account page's
100 Text size preset reaches labels and code, not just body copy. */
101 --font-scale: 1;
102 }
103
104 /* Text-size presets. The account page stores the choice in this browser and
105 static/theme.js applies the attribute before the first paint. */
106 :root[data-font-scale="small"] { --font-scale: 0.9; }
107 :root[data-font-scale="medium"] { --font-scale: 1; }
108 :root[data-font-scale="large"] { --font-scale: 1.1; }
109
110 /* Self-hosted, OFL fonts. The Latin/Latin-ext subsets keep the binary small;
111 anything outside them falls through to the system stack in --font/--mono. */
112 @font-face {
113 font-family: "Lexend";
114 font-style: normal;
115 font-weight: 100 900;
116 font-display: swap;
ed03b3d1
Keenan Nemetz
6h ago
117 src: url("/-/static/fonts/lexend-latin-ext.woff2") format("woff2");
796ac8c0
Keenan Nemetz
11d ago
118 unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
119 }
120 @font-face {
121 font-family: "Lexend";
122 font-style: normal;
123 font-weight: 100 900;
124 font-display: swap;
ed03b3d1
Keenan Nemetz
6h ago
125 src: url("/-/static/fonts/lexend-latin.woff2") format("woff2");
796ac8c0
Keenan Nemetz
11d ago
126 unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
127 }
128 @font-face {
129 font-family: "JetBrains Mono";
130 font-style: normal;
131 font-weight: 100 800;
132 font-display: swap;
ed03b3d1
Keenan Nemetz
6h ago
133 src: url("/-/static/fonts/jetbrains-mono-latin-ext.woff2") format("woff2");
796ac8c0
Keenan Nemetz
11d ago
134 unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
135 }
136 @font-face {
137 font-family: "JetBrains Mono";
138 font-style: normal;
139 font-weight: 100 800;
140 font-display: swap;
ed03b3d1
Keenan Nemetz
6h ago
141 src: url("/-/static/fonts/jetbrains-mono-latin.woff2") format("woff2");
796ac8c0
Keenan Nemetz
11d ago
142 unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
143 }
144 @font-face {
145 font-family: "JetBrains Mono";
146 font-style: italic;
147 font-weight: 100 800;
148 font-display: swap;
ed03b3d1
Keenan Nemetz
6h ago
149 src: url("/-/static/fonts/jetbrains-mono-italic-latin-ext.woff2") format("woff2");
796ac8c0
Keenan Nemetz
11d ago
150 unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
151 }
152 @font-face {
153 font-family: "JetBrains Mono";
154 font-style: italic;
155 font-weight: 100 800;
156 font-display: swap;
ed03b3d1
Keenan Nemetz
6h ago
157 src: url("/-/static/fonts/jetbrains-mono-italic-latin.woff2") format("woff2");
796ac8c0
Keenan Nemetz
11d ago
158 unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
f09ef39e
Keenan Nemetz
15d ago
159 }
160
161 * { box-sizing: border-box; }
162 * { scrollbar-width: thin; scrollbar-color: var(--border-strong) transparent; }
163 ::-webkit-scrollbar { width: 10px; height: 10px; }
164 ::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: 5px; }
165 ::-webkit-scrollbar-track { background: transparent; }
166
167 html, body { height: 100%; }
168
169 body {
170 margin: 0;
171 background: var(--bg);
172 color: var(--fg);
796ac8c0
Keenan Nemetz
11d ago
173 font-family: var(--font);
174 font-size: calc(var(--fs) * var(--font-scale));
175 line-height: var(--lh);
176 -webkit-font-smoothing: auto;
177 font-kerning: normal;
f09ef39e
Keenan Nemetz
15d ago
178 }
179
180 a { color: var(--accent); text-decoration: none; }
181 a:hover { text-decoration: underline; }
182
183 h1, h2, h3 { margin: 0; font-weight: 600; letter-spacing: -0.005em; }
796ac8c0
Keenan Nemetz
11d ago
184 h1 { font-size: calc(19.8px * var(--font-scale)); line-height: 1.35; margin: 2px 0 12px; }
185 h2 { font-size: calc(14.3px * var(--font-scale)); color: var(--fg-muted); margin: 24px 0 8px; }
186 h3 { font-size: calc(14.3px * var(--font-scale)); }
f09ef39e
Keenan Nemetz
15d ago
187 p { margin: 0 0 10px; }
188 ul, ol { margin: 0 0 10px; padding-left: 20px; }
189 hr { border: 0; border-top: 1px solid var(--border); margin: 20px 0; }
190 img { max-width: 100%; }
191
192 .muted { color: var(--fg-muted); }
796ac8c0
Keenan Nemetz
11d ago
193 .sha { font-family: var(--mono); font-size: calc(13.2px * var(--font-scale)); color: var(--fg-muted); }
f09ef39e
Keenan Nemetz
15d ago
194 .nowrap { white-space: nowrap; }
195 .adds { color: var(--good); }
196 .dels { color: var(--bad); }
197 .conflict, .error { color: var(--bad); }
198 .bad { color: var(--bad); }
199
200 /* ---------- chrome ---------- */
201
202 .topbar {
203 display: flex;
204 align-items: center;
205 gap: 20px;
206 height: 46px;
6c03e6d3
Keenan Nemetz
9d ago
207 /* The bar spans the window; its items stay inside the page column. */
208 padding-block: 0;
209 padding-inline: max(var(--page-gutter), calc((100% - var(--page-max)) / 2 + var(--page-gutter)));
f09ef39e
Keenan Nemetz
15d ago
210 background: color-mix(in srgb, var(--bg-panel) 92%, transparent);
211 border-bottom: 1px solid var(--border);
796ac8c0
Keenan Nemetz
11d ago
212 position: relative;
f09ef39e
Keenan Nemetz
15d ago
213 z-index: 20;
214 backdrop-filter: blur(8px);
215 }
216
b0fd812c
Keenan Nemetz
13d ago
217 /* The brand is the emblem alone. The link keeps an accessible name, so a mark with
218 no text beside it is still "Vole home" to a screen reader. */
f09ef39e
Keenan Nemetz
15d ago
219 .brand {
220 display: inline-flex;
221 align-items: center;
222 color: var(--fg);
223 }
224 .brand:hover { text-decoration: none; }
8911ab5f
Keenan Nemetz
1d ago
225
e3b9c83c
Keenan Nemetz
1d ago
226 /* The mark is a mask in the theme's ink. A light theme draws it in the page
227 colour with a dark outline instead, because the same muted ink on a pale bar
228 reads as a smudge. A custom light theme can opt in by setting these tokens. */
8911ab5f
Keenan Nemetz
1d ago
229 :root {
e3b9c83c
Keenan Nemetz
1d ago
230 --mark-fill: currentColor;
231 --mark-outline: transparent;
8911ab5f
Keenan Nemetz
1d ago
232 }
233 :root[data-theme^="everforest-light"],
234 :root[data-theme="catppuccin-latte"],
235 :root[data-theme="solarized-light"],
236 :root[data-theme="rose-pine-dawn"],
237 :root[data-theme="ayu-light"] {
e3b9c83c
Keenan Nemetz
1d ago
238 --mark-fill: var(--bg);
239 --mark-outline: var(--fg);
8911ab5f
Keenan Nemetz
1d ago
240 }
f09ef39e
Keenan Nemetz
15d ago
241 .brand .mark {
6416cab1
Keenan Nemetz
13d ago
242 display: block;
8911ab5f
Keenan Nemetz
1d ago
243 position: relative;
6416cab1
Keenan Nemetz
13d ago
244 flex: none;
8911ab5f
Keenan Nemetz
1d ago
245 width: 30px;
246 height: 30px;
247 }
ed667a0e
Keenan Nemetz
1d ago
248 .brand .mark::before,
249 .brand .mark::after {
8911ab5f
Keenan Nemetz
1d ago
250 content: "";
251 position: absolute;
e3b9c83c
Keenan Nemetz
1d ago
252 inset: 0;
ed667a0e
Keenan Nemetz
1d ago
253 -webkit-mask-position: center;
254 -webkit-mask-size: contain;
255 -webkit-mask-repeat: no-repeat;
256 mask-position: center;
257 mask-size: contain;
258 mask-repeat: no-repeat;
259 }
8f0f3e8d
Keenan Nemetz
1d ago
260 /* The outline layer is a slightly expanded solid silhouette behind the light
261 head. It draws the outside edge and shows through the eye, mouth, and
262 inner-ear holes; the whisker gaps still show the bar. */
ed667a0e
Keenan Nemetz
1d ago
263 .brand .mark::before {
264 background-color: var(--mark-outline);
ed03b3d1
Keenan Nemetz
6h ago
265 -webkit-mask-image: url("/-/static/vole-head-solid.png");
266 mask-image: url("/-/static/vole-head-solid.png");
ed667a0e
Keenan Nemetz
1d ago
267 }
268 .brand .mark::after {
e3b9c83c
Keenan Nemetz
1d ago
269 background-color: var(--mark-fill);
ed03b3d1
Keenan Nemetz
6h ago
270 -webkit-mask-image: url("/-/static/vole-head.png");
271 mask-image: url("/-/static/vole-head.png");
f09ef39e
Keenan Nemetz
15d ago
272 }
273
796ac8c0
Keenan Nemetz
11d ago
274 .topnav { display: flex; gap: 6px; }
074ae160
Keenan Nemetz
14d ago
275 .topnav a {
276 display: inline-flex;
277 align-items: center;
796ac8c0
Keenan Nemetz
11d ago
278 gap: 6px;
279 padding: 5px 11px;
280 border: 1px solid transparent;
074ae160
Keenan Nemetz
14d ago
281 border-radius: var(--radius);
796ac8c0
Keenan Nemetz
11d ago
282 color: var(--fg);
283 font-size: calc(13.75px * var(--font-scale));
284 font-weight: 500;
285 white-space: nowrap;
286 }
287 .topnav a:hover {
288 background: var(--bg-hover);
289 border-color: var(--border);
290 color: var(--fg);
291 text-decoration: none;
292 }
293 .topnav a.active {
294 background: var(--accent-soft);
295 border-color: color-mix(in srgb, var(--accent) 45%, var(--border));
296 color: var(--accent);
074ae160
Keenan Nemetz
14d ago
297 }
298
6c03e6d3
Keenan Nemetz
9d ago
299 .topsearch {
300 flex: 1 1 auto;
301 min-width: 8rem;
302 max-width: var(--search-w);
303 margin-left: auto;
304 }
305 /* The reader is opening the palette, not editing a field, so this is a button
306 shaped like the search input it replaces. The label stands in for the
307 placeholder, and the last item names the shortcut the browser will hear. */
308 .topsearch-box {
309 display: flex;
310 align-items: center;
311 gap: 8px;
312 width: 100%;
313 min-width: 0;
314 min-height: 30px;
315 padding: 5px 8px;
316 border: 1px solid var(--border-strong);
317 border-radius: var(--radius);
318 background: var(--bg);
319 color: var(--fg-muted);
320 font: inherit;
321 font-size: calc(13.75px * var(--font-scale));
322 text-align: left;
323 cursor: pointer;
324 }
325 .topsearch-box:hover {
326 border-color: color-mix(in srgb, var(--accent) 45%, var(--border));
327 color: var(--fg);
328 }
329 .topsearch-box .icon { width: 13px; height: 13px; color: var(--fg-faint); }
330 .topsearch-label {
331 flex: 1 1 auto;
332 min-width: 0;
333 overflow: hidden;
334 text-overflow: ellipsis;
335 white-space: nowrap;
336 }
337 .topsearch-key {
338 flex: 0 0 auto;
339 padding: 0 4px;
340 border: 1px solid var(--border);
341 border-radius: 4px;
342 background: var(--bg-panel);
343 color: var(--fg-faint);
344 font-size: calc(11.55px * var(--font-scale));
345 }
f09ef39e
Keenan Nemetz
15d ago
346
347 .topright {
348 display: flex;
349 align-items: center;
350 gap: 12px;
796ac8c0
Keenan Nemetz
11d ago
351 font-size: calc(13.75px * var(--font-scale));
f09ef39e
Keenan Nemetz
15d ago
352 color: var(--fg-muted);
353 }
074ae160
Keenan Nemetz
14d ago
354 .topright > a { display: inline-flex; align-items: center; gap: 5px; color: var(--fg-muted); }
355 .topright > a:hover { color: var(--fg); text-decoration: none; }
356
357 .account-menu { position: relative; }
358 .account-menu > summary {
359 display: flex;
360 align-items: center;
361 gap: 6px;
362 padding: 4px 8px;
363 border-radius: var(--radius);
364 color: var(--fg-muted);
365 white-space: nowrap;
366 list-style: none;
367 cursor: pointer;
368 }
369 .account-menu > summary::-webkit-details-marker { display: none; }
370 .account-menu > summary:hover { background: var(--bg-hover); color: var(--fg); }
371 .account-menu[open] > summary { background: var(--bg-raised); color: var(--fg); }
372 .account-name { max-width: 12rem; overflow: hidden; text-overflow: ellipsis; }
373 .account-caret { display: inline-flex; color: var(--fg-faint); }
374 .account-caret .icon { width: 12px; height: 12px; transform: rotate(90deg); transition: transform 0.12s ease; }
375 .account-menu[open] .account-caret .icon { transform: rotate(-90deg); }
376 .account-pop {
377 position: absolute;
378 top: calc(100% + 6px);
379 right: 0;
380 z-index: 30;
381 width: 13rem;
382 padding: 4px;
383 background: var(--bg-raised);
384 border: 1px solid var(--border-strong);
385 border-radius: var(--radius-lg);
386 box-shadow: 0 12px 32px rgba(0, 0, 0, 0.4);
387 }
388 .account-pop a,
389 .account-pop button {
390 display: flex;
391 align-items: center;
392 justify-content: flex-start;
393 gap: 8px;
394 width: 100%;
395 padding: 6px 8px;
396 border: 0;
397 border-radius: var(--radius);
398 background: none;
399 color: var(--fg);
400 font: inherit;
796ac8c0
Keenan Nemetz
11d ago
401 font-size: calc(13.75px * var(--font-scale));
074ae160
Keenan Nemetz
14d ago
402 text-align: left;
403 cursor: pointer;
404 }
405 .account-pop a:hover,
406 .account-pop button:hover { background: var(--bg-hover); color: var(--accent); text-decoration: none; }
407 .account-pop form { margin: 0; }
408 .account-sep { border-top: 1px solid var(--border); margin: 4px 0; }
f09ef39e
Keenan Nemetz
15d ago
409
6c03e6d3
Keenan Nemetz
9d ago
410 /* ---------- command palette ---------- */
411
412 /* The global bar's search opens this: one field over every issue, change and
413 comment the reader can see. A row is a link, so Enter and a click take the
414 same route; the stylesheet only has to make the selected row visible. */
415 .palette {
416 width: min(40rem, calc(100vw - 2 * var(--page-gutter)));
417 max-height: min(80vh, 44rem);
418 margin: 10vh auto auto;
419 padding: 0;
420 border: 1px solid var(--border-strong);
421 border-radius: var(--radius-lg);
422 background: var(--bg-raised);
423 color: var(--fg);
424 box-shadow: 0 24px 64px rgba(0, 0, 0, 0.45);
425 overflow: hidden;
426 }
427 .palette::backdrop { background: rgba(0, 0, 0, 0.5); }
428 .palette-search {
429 display: flex;
430 align-items: center;
431 gap: 10px;
432 padding: 10px 12px;
433 border-bottom: 1px solid var(--border);
434 }
435 .palette-search .icon { width: 16px; height: 16px; color: var(--fg-muted); }
436 .palette-search input[type="search"] {
437 flex: 1 1 auto;
438 width: auto;
439 max-width: none;
440 min-height: 0;
441 padding: 2px 0;
442 border: 0;
443 background: transparent;
444 font-size: calc(15.4px * var(--font-scale));
445 }
446 /* The field is the dialog's heading, not a control with a box of its own. */
447 .palette-search input[type="search"]:focus-visible { outline: 0; }
448 .palette-close {
449 display: inline-flex;
450 align-items: center;
451 justify-content: center;
452 padding: 4px;
453 border: 0;
454 border-radius: var(--radius);
455 background: none;
456 color: var(--fg-faint);
457 cursor: pointer;
458 }
459 .palette-close:hover { background: var(--bg-hover); color: var(--fg); }
460 .palette-results { max-height: min(60vh, 30rem); overflow-y: auto; padding: 6px; }
461 .palette-row {
462 display: block;
463 padding: 7px 9px;
464 border-radius: var(--radius);
465 color: var(--fg);
466 }
467 .palette-row:hover { background: var(--bg-hover); text-decoration: none; }
468 .palette-row.is-selected { background: var(--accent-soft); }
469 .palette-row-title {
470 display: block;
471 font-weight: 500;
472 overflow: hidden;
473 text-overflow: ellipsis;
474 white-space: nowrap;
475 }
476 .palette-row-where {
477 display: block;
478 color: var(--fg-faint);
479 font-size: calc(12.65px * var(--font-scale));
480 }
481 /* A palette row is one line high per part; the editable page can afford the
482 wrapped excerpt, the dialog cannot. */
483 .palette-row .search-snippet {
484 margin: 0;
485 overflow: hidden;
486 text-overflow: ellipsis;
487 white-space: nowrap;
488 }
489 .palette-empty {
490 margin: 0;
491 padding: 16px 12px;
492 color: var(--fg-muted);
493 font-size: calc(13.75px * var(--font-scale));
494 }
495 .palette-foot {
496 display: flex;
497 gap: 14px;
498 padding: 7px 12px;
499 border-top: 1px solid var(--border);
500 color: var(--fg-faint);
501 font-size: calc(12.65px * var(--font-scale));
502 }
503 .palette-foot kbd {
504 padding: 0 4px;
505 border: 1px solid var(--border);
506 border-radius: 4px;
507 background: var(--bg-panel);
508 color: var(--fg-muted);
509 font-size: calc(11.55px * var(--font-scale));
510 }
511
f09ef39e
Keenan Nemetz
15d ago
512 .page {
6c03e6d3
Keenan Nemetz
9d ago
513 width: min(100%, var(--page-max));
f09ef39e
Keenan Nemetz
15d ago
514 margin: 0 auto;
6c03e6d3
Keenan Nemetz
9d ago
515 padding: 20px var(--page-gutter) 72px;
f09ef39e
Keenan Nemetz
15d ago
516 }
517
518 @media (max-width: 720px) {
519 .tabs { mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent); }
6c03e6d3
Keenan Nemetz
9d ago
520 :root { --page-gutter: 12px; }
074ae160
Keenan Nemetz
14d ago
521 .topbar {
522 height: auto;
523 flex-wrap: wrap;
6c03e6d3
Keenan Nemetz
9d ago
524 padding-block: 8px;
074ae160
Keenan Nemetz
14d ago
525 gap: 8px 12px;
526 }
6c03e6d3
Keenan Nemetz
9d ago
527 .topsearch { order: 3; flex: 1 1 100%; max-width: none; margin-left: 0; }
528 .topsearch-box { width: 100%; }
529 /* A keyboard shortcut is not part of a phone's vocabulary. */
530 .topsearch-key { display: none; }
074ae160
Keenan Nemetz
14d ago
531 .topright { margin-left: auto; }
796ac8c0
Keenan Nemetz
11d ago
532 .topnav { gap: 4px; }
533 .topnav a { padding: 4px 8px; }
074ae160
Keenan Nemetz
14d ago
534 /* The icon is enough on a phone; the menu keeps the full name. */
535 .account-name { display: none; }
6c03e6d3
Keenan Nemetz
9d ago
536 .page { padding: 16px var(--page-gutter) 56px; }
537 .palette { margin-top: 6vh; }
f09ef39e
Keenan Nemetz
15d ago
538 }
539
540 /* ---------- repository header ---------- */
541
542 .repohead {
543 margin-bottom: 16px;
544 border-bottom: 1px solid var(--border);
545 }
074ae160
Keenan Nemetz
14d ago
546 .repo-title { padding: 26px 0 12px; }
816cc543
Keenan Nemetz
11d ago
547 /* The repository actions sit on the title line, at the far right of the header. */
548 .repo-title-row {
549 display: flex;
550 align-items: center;
551 gap: 16px;
552 }
553 .repo-title-row .repo-name { flex: 1 1 auto; min-width: 0; }
554 .repo-title-actions {
555 display: flex;
556 align-items: center;
557 gap: 8px;
558 flex: 0 0 auto;
559 }
560 .repo-title-row .clone-pop { left: auto; right: 0; }
074ae160
Keenan Nemetz
14d ago
561 .repo-name {
562 display: flex;
563 align-items: center;
564 gap: 8px;
565 min-width: 0;
566 color: var(--fg);
796ac8c0
Keenan Nemetz
11d ago
567 font-size: calc(28.6px * var(--font-scale));
074ae160
Keenan Nemetz
14d ago
568 line-height: 1.2;
569 font-weight: 600;
570 letter-spacing: -0.025em;
571 }
572 .repo-name .repo-vis .icon { width: 16px; height: 16px; }
573 .repo-desc {
574 margin: 6px 0 0;
575 color: var(--fg-muted);
796ac8c0
Keenan Nemetz
11d ago
576 font-size: calc(14.3px * var(--font-scale));
074ae160
Keenan Nemetz
14d ago
577 overflow: hidden;
578 text-overflow: ellipsis;
579 white-space: nowrap;
580 }
581
582 /* The tab pages are a level below the repository identity: their own headers
ab326d3a
Keenan Nemetz
14d ago
583 stay small. Top-level pages (Runners, Activity, Accounts, Account, Status) keep
9579c1f9
Keenan Nemetz
14d ago
584 the home-page header scale. */
585 .repohead + .page-head {
074ae160
Keenan Nemetz
14d ago
586 align-items: flex-start;
587 gap: 16px;
588 padding: 0;
589 margin-bottom: 12px;
590 border-bottom: 0;
591 }
9579c1f9
Keenan Nemetz
14d ago
592 .repohead + .page-head h1 {
796ac8c0
Keenan Nemetz
11d ago
593 font-size: calc(19.8px * var(--font-scale));
074ae160
Keenan Nemetz
14d ago
594 line-height: 1.35;
595 letter-spacing: -0.005em;
596 margin: 0 0 3px;
597 }
2e024cc9
Keenan Nemetz
14d ago
598 .repohead + .page-head h1:last-child { margin-bottom: 0; }
796ac8c0
Keenan Nemetz
11d ago
599 .repohead + .page-head .page-sub { font-size: calc(13.75px * var(--font-scale)); }
074ae160
Keenan Nemetz
14d ago
600
601 @media (max-width: 720px) {
602 .repo-title { padding: 18px 0 10px; }
796ac8c0
Keenan Nemetz
11d ago
603 .repo-name { font-size: calc(24.2px * var(--font-scale)); }
074ae160
Keenan Nemetz
14d ago
604 .repo-name .repo-vis .icon { width: 14px; height: 14px; }
605 }
f09ef39e
Keenan Nemetz
15d ago
606
607 .tabs {
608 display: flex;
609 gap: 2px;
610 overflow-x: auto;
611 scrollbar-width: none;
074ae160
Keenan Nemetz
14d ago
612 margin-left: -10px;
f09ef39e
Keenan Nemetz
15d ago
613 }
614 .tabs::-webkit-scrollbar { display: none; }
615 .tabs a {
616 color: var(--fg-muted);
617 padding: 8px 10px 9px;
618 border-bottom: 2px solid transparent;
619 white-space: nowrap;
796ac8c0
Keenan Nemetz
11d ago
620 font-size: calc(13.75px * var(--font-scale));
f09ef39e
Keenan Nemetz
15d ago
621 }
622 .tabs a:hover { color: var(--fg); text-decoration: none; }
623 .tabs a.active { color: var(--fg); border-bottom-color: var(--accent); }
a196e404
Keenan Nemetz
11d ago
624 .tabs .chip-count { margin-left: 0; }
625 .tabs a.active .chip-count { color: var(--fg-muted); }
f09ef39e
Keenan Nemetz
15d ago
626
973fb809
Keenan Nemetz
1d ago
627 /* The rule under a tab row belongs to a wrapper, not to the strip that carries
628 the labels: `.tabs` pulls itself 10px left so the first label's text sits on
629 the content edge, and a border on that element would hang 10px past it. The
630 repository header keeps its rule on `.repohead`; a page's tabs and a change's
631 sections wrap the same strip in this class, so every row starts and ends on
632 the content edges together. */
633 .tabrow { margin-bottom: 14px; border-bottom: 1px solid var(--border); }
e189a23e
Keenan Nemetz
1d ago
634
a2ec9561
Keenan Nemetz
9d ago
635 /* A count that moved while the reader was looking elsewhere keeps one short
636 ring, so the change is noticed; the number itself is the answer. app.js adds
637 the class when a poll changes a badge and drops it when the animation ends. */
638 .tabs .chip-count.count-changed {
639 border-radius: 999px;
640 animation: count-changed 1.8s ease-out;
641 }
642 @keyframes count-changed {
643 from { color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
644 to { box-shadow: 0 0 0 3px transparent; }
645 }
646
f09ef39e
Keenan Nemetz
15d ago
647 /* ---------- panels and tables ---------- */
648
649 .panel {
650 border: 1px solid var(--border);
651 border-radius: var(--radius-lg);
652 overflow: hidden;
653 margin: 12px 0;
654 background: var(--bg-panel);
655 }
656
657 .card {
658 background: var(--bg-panel);
659 border: 1px solid var(--border);
660 border-radius: var(--radius-lg);
661 padding: 14px;
662 margin: 12px 0;
663 }
664
665 table {
666 width: 100%;
667 border-collapse: collapse;
796ac8c0
Keenan Nemetz
11d ago
668 font-size: calc(13.75px * var(--font-scale));
f09ef39e
Keenan Nemetz
15d ago
669 }
670 .panel > table { margin: 0; }
671 th, td {
672 text-align: left;
673 padding: 8px 12px;
674 border-bottom: 1px solid var(--border);
675 vertical-align: top;
676 }
677 tr:last-child > td { border-bottom: 0; }
678 th {
679 color: var(--fg-faint);
680 font-weight: 500;
796ac8c0
Keenan Nemetz
11d ago
681 font-size: calc(12.1px * var(--font-scale));
f09ef39e
Keenan Nemetz
15d ago
682 text-transform: uppercase;
683 letter-spacing: 0.04em;
684 background: var(--bg-raised);
685 padding-top: 7px;
686 padding-bottom: 7px;
687 }
688 tbody tr:hover { background: var(--bg-hover); }
689
690 table.commits td { padding-top: 7px; padding-bottom: 7px; }
691 table.commits td:first-child { width: 9ch; }
692
693 /* ---------- controls ---------- */
694
695 button, .button {
696 display: inline-flex;
697 align-items: center;
698 justify-content: center;
699 gap: 6px;
700 background: var(--accent);
701 color: var(--accent-fg);
702 border: 1px solid transparent;
703 border-radius: var(--radius);
704 padding: 5px 11px;
796ac8c0
Keenan Nemetz
11d ago
705 font-family: var(--font);
706 font-size: calc(13.75px * var(--font-scale));
707 font-weight: 500;
708 line-height: 1.4;
f09ef39e
Keenan Nemetz
15d ago
709 cursor: pointer;
710 white-space: nowrap;
711 }
712 button:hover, .button:hover { filter: brightness(1.08); text-decoration: none; }
713 button:disabled { opacity: 0.5; cursor: default; }
3a068983
Keenan Nemetz
11d ago
714
715 /* A POST form that is still in flight keeps its button in place: it is disabled
716 and, after a short delay added by app.js, its icon becomes a spinner. */
717 button[data-loading] { cursor: progress; }
718 button.spinning > .icon { display: none; }
719 .spinner {
720 width: 14px;
721 height: 14px;
722 flex: 0 0 auto;
723 border: 2px solid currentColor;
724 border-right-color: transparent;
725 border-radius: 50%;
726 animation: spin 0.7s linear infinite;
727 }
728 @keyframes spin { to { transform: rotate(1turn); } }
729
f09ef39e
Keenan Nemetz
15d ago
730 .button.secondary, button.secondary {
731 background: var(--bg-raised);
732 color: var(--fg);
733 border-color: var(--border-strong);
734 }
735 .button.secondary:hover { background: var(--bg-hover); }
736
d69be4df
Keenan Nemetz
11d ago
737 /* Action colours use the same filled shape as the primary control: the
738 background carries the meaning, and hover brightens it like every other
739 button. */
f6f4b1db
Keenan Nemetz
15d ago
740 .button.success, button.success {
d69be4df
Keenan Nemetz
11d ago
741 background: var(--good);
742 color: var(--status-fg);
743 border-color: transparent;
f6f4b1db
Keenan Nemetz
15d ago
744 }
745 .button.warn, button.warn {
d69be4df
Keenan Nemetz
11d ago
746 background: var(--warn);
747 color: var(--status-fg);
748 border-color: transparent;
f6f4b1db
Keenan Nemetz
15d ago
749 }
750 .button.danger, button.danger {
d69be4df
Keenan Nemetz
11d ago
751 background: var(--bad);
752 color: var(--status-fg);
753 border-color: transparent;
f6f4b1db
Keenan Nemetz
15d ago
754 }
755
f09ef39e
Keenan Nemetz
15d ago
756 button.link, .link {
757 background: none;
758 border: 0;
759 color: var(--accent);
760 padding: 0;
761 font: inherit;
762 font-weight: 400;
763 }
764 button.link:hover, .link:hover { text-decoration: underline; }
765 form.inline { display: inline; }
766
767 select, input[type="text"], input[type="password"], input[type="search"], textarea {
768 background: var(--bg);
769 color: var(--fg);
770 border: 1px solid var(--border-strong);
771 border-radius: var(--radius);
772 padding: 5px 8px;
773 font: inherit;
796ac8c0
Keenan Nemetz
11d ago
774 font-size: calc(13.75px * var(--font-scale));
2e024cc9
Keenan Nemetz
14d ago
775 min-height: 30px;
f09ef39e
Keenan Nemetz
15d ago
776 }
777 select:focus, input:focus, textarea:focus {
778 outline: 2px solid var(--accent-soft);
779 outline-offset: 1px;
780 border-color: var(--accent);
781 }
782 input[type="text"], input[type="password"], input[type="search"], textarea {
783 width: 100%;
784 max-width: 34rem;
785 }
786 textarea { min-height: 7rem; resize: vertical; line-height: 1.5; }
796ac8c0
Keenan Nemetz
11d ago
787 label { display: block; margin: 10px 0; color: var(--fg-muted); font-size: calc(13.75px * var(--font-scale)); }
f09ef39e
Keenan Nemetz
15d ago
788 /* The label text sits above the control. Without this, a max-width input shares
789 the line with its label and the form reads as a row of pairs. */
790 label > input, label > textarea, label > select {
791 display: block;
792 margin-top: 4px;
793 color: var(--fg);
794 }
795 label > select { width: 100%; max-width: 34rem; }
6ce635a8
Keenan Nemetz
1d ago
796 label > .field-hint {
797 display: block;
798 margin-top: 4px;
799 color: var(--fg-faint);
800 font-size: calc(12.1px * var(--font-scale));
801 }
f09ef39e
Keenan Nemetz
15d ago
802
55615c7a
Keenan Nemetz
11d ago
803 /* Every filter/search row is one component: controls in a wrapping row, a
804 visible label on every select, selects that apply on change, and a Search
805 button for the text field. Search inputs share --search-w on every surface. */
f09ef39e
Keenan Nemetz
15d ago
806 .filters {
807 display: flex;
808 flex-wrap: wrap;
809 align-items: center;
810 gap: 8px;
2e024cc9
Keenan Nemetz
14d ago
811 margin: 10px 0 12px;
f09ef39e
Keenan Nemetz
15d ago
812 }
55615c7a
Keenan Nemetz
11d ago
813 /* The code page needs a form per ref picker (two refs in one form would submit
814 twice), so the row can contain forms as well as be one. */
815 .filters form {
816 display: inline-flex;
817 flex-wrap: wrap;
818 align-items: center;
819 gap: 8px;
820 }
6c98a271
Keenan Nemetz
9d ago
821 /* Every row reads left to right: filters, then search. The search group takes
822 the leftover space, so it sits against the right edge whether the row is one
823 page's chips and two selects or another page's ref pickers. A row with no
824 search puts its clear action in the same slot. */
825 .filters-search {
826 display: flex;
827 align-items: center;
828 gap: 8px;
829 margin-left: auto;
830 }
831 /* Inside the row the chips are one group, not a row with its own margins. */
832 .filters .chips { margin: 0; }
2e024cc9
Keenan Nemetz
14d ago
833 .filters input[type="text"],
55615c7a
Keenan Nemetz
11d ago
834 .filters input[type="search"] { width: var(--search-w); max-width: 100%; }
2e024cc9
Keenan Nemetz
14d ago
835 /* A label and its control stay on one line when the filter row wraps. */
836 .filter-field {
837 display: inline-flex;
838 align-items: center;
839 gap: 6px;
840 margin: 0;
841 }
842 .filter-field > input,
843 .filter-field > select {
844 width: auto;
845 max-width: none;
846 margin-top: 0;
847 }
848 .chips + .filters { margin-top: 0; }
55615c7a
Keenan Nemetz
11d ago
849 .page-head + .filters { margin-top: 0; }
850
851 @media (max-width: 720px) {
852 .filters { flex-direction: column; align-items: stretch; }
853 .filters form { flex-direction: column; align-items: stretch; }
854 .filters input[type="text"],
855 .filters input[type="search"] { width: 100%; }
6c98a271
Keenan Nemetz
9d ago
856 .filters-search { margin-left: 0; width: 100%; }
857 /* The search stays one line on a phone: a full-width field and its button,
858 not the stacked label/control shape the ref pickers need. */
859 .filters-search form { flex-direction: row; align-items: center; }
860 .filters-search input[type="text"],
861 .filters-search input[type="search"] { flex: 1 1 auto; width: auto; }
55615c7a
Keenan Nemetz
11d ago
862 /* A phone stacks the label above its control: a label and an 18rem select
863 cannot share a 20rem-wide row without the control overflowing it. */
864 .filter-field { flex-direction: column; align-items: stretch; gap: 4px; }
865 .filters .filter-field > select { width: 100%; }
866 }
f09ef39e
Keenan Nemetz
15d ago
867
868 /* ---------- labels, flashes, empty pages ---------- */
869
870 .label {
871 display: inline-block;
872 margin-left: 6px;
873 padding: 1px 6px;
874 border: 1px solid var(--border-strong);
875 border-radius: 10px;
796ac8c0
Keenan Nemetz
11d ago
876 font-size: calc(12.1px * var(--font-scale));
f09ef39e
Keenan Nemetz
15d ago
877 line-height: 1.5;
878 color: var(--fg-muted);
879 vertical-align: 1px;
880 }
881 a.label:hover { color: var(--accent); border-color: var(--accent); text-decoration: none; }
882
883 .flash {
884 border: 1px solid var(--border-strong);
885 background: var(--bg-panel);
886 border-radius: var(--radius);
887 padding: 8px 12px;
888 margin-bottom: 14px;
796ac8c0
Keenan Nemetz
11d ago
889 font-size: calc(13.75px * var(--font-scale));
f09ef39e
Keenan Nemetz
15d ago
890 }
891 .flash.bad { border-color: color-mix(in srgb, var(--bad) 50%, var(--border)); background: var(--bad-soft); }
1948358f
Keenan Nemetz
15d ago
892 .flash.good { border-color: color-mix(in srgb, var(--good) 50%, var(--border)); background: var(--good-soft); }
4506a4d3
Keenan Nemetz
14d ago
893 .flash.warn { border-color: color-mix(in srgb, var(--warn) 50%, var(--border)); background: var(--warn-soft); }
f09ef39e
Keenan Nemetz
15d ago
894
895 .empty-page {
896 max-width: 34rem;
897 margin: 12vh auto 0;
898 text-align: center;
899 }
900 .empty-page .eyebrow {
901 color: var(--fg-faint);
796ac8c0
Keenan Nemetz
11d ago
902 font-family: var(--mono);
903 font-size: calc(12.1px * var(--font-scale));
904 font-weight: 600;
905 line-height: 1;
f09ef39e
Keenan Nemetz
15d ago
906 letter-spacing: 0.1em;
907 text-transform: uppercase;
908 margin-bottom: 10px;
909 }
796ac8c0
Keenan Nemetz
11d ago
910 .empty-page h1 { font-size: calc(26.4px * var(--font-scale)); margin-bottom: 8px; }
f09ef39e
Keenan Nemetz
15d ago
911 .empty-page p { margin-bottom: 18px; }
912
913 /* ---------- auth ---------- */
914
915 .auth { max-width: 34rem; margin-top: 8vh; }
916 .auth h1 { margin-bottom: 6px; }
917
c717d4eb
Keenan Nemetz
13d ago
918 /* ---------- repository home ---------- */
f09ef39e
Keenan Nemetz
15d ago
919
920 details.clone { position: relative; }
921 details.clone > summary {
922 list-style: none;
923 cursor: pointer;
924 display: inline-flex;
925 align-items: center;
926 gap: 6px;
927 background: var(--bg-raised);
928 color: var(--fg);
929 border: 1px solid var(--border-strong);
930 border-radius: var(--radius);
931 padding: 5px 11px;
796ac8c0
Keenan Nemetz
11d ago
932 font-size: calc(13.75px * var(--font-scale));
f09ef39e
Keenan Nemetz
15d ago
933 }
934 details.clone > summary::-webkit-details-marker { display: none; }
935 details.clone > summary:hover { background: var(--bg-hover); }
936 details.clone[open] > summary { border-color: var(--accent); }
937 .clone-pop {
938 position: absolute;
939 z-index: 10;
940 top: calc(100% + 6px);
941 left: 0;
942 width: min(32rem, 80vw);
943 background: var(--bg-raised);
944 border: 1px solid var(--border-strong);
945 border-radius: var(--radius-lg);
946 padding: 12px;
947 box-shadow: 0 12px 32px rgba(0, 0, 0, 0.4);
948 }
949 .clone-pop p { margin: 8px 0 2px; }
950 .clone-pop code { display: block; }
816cc543
Keenan Nemetz
11d ago
951 /* Each advertised transport is one row: the command takes the width, the copy
952 button stays put at its end. */
953 .clone-field {
954 display: flex;
955 align-items: center;
956 gap: 6px;
957 }
958 .clone-field code {
959 flex: 1 1 auto;
960 min-width: 0;
961 overflow-x: auto;
962 white-space: nowrap;
963 }
964 .clone-field button { flex: 0 0 auto; padding: 5px 8px; }
f09ef39e
Keenan Nemetz
15d ago
965
966 .row { display: flex; gap: 16px; flex-wrap: wrap; }
967 .row > div { flex: 1 1 18rem; min-width: 0; }
968
969 .readme { margin: 16px 0; }
970
c717d4eb
Keenan Nemetz
13d ago
971 /* ---------- diffs and markdown ---------- */
f09ef39e
Keenan Nemetz
15d ago
972
973 code, pre, kbd {
974 font-family: var(--mono);
975 }
976 code {
977 background: var(--bg-raised);
978 border: 1px solid var(--border);
979 border-radius: 4px;
980 padding: 1px 5px;
796ac8c0
Keenan Nemetz
11d ago
981 font-size: calc(12.65px * var(--font-scale));
f09ef39e
Keenan Nemetz
15d ago
982 }
983 pre {
984 background: var(--bg-panel);
985 border: 1px solid var(--border);
986 border-radius: var(--radius);
987 padding: 12px 14px;
988 overflow: auto;
796ac8c0
Keenan Nemetz
11d ago
989 font-size: calc(13.2px * var(--font-scale));
f09ef39e
Keenan Nemetz
15d ago
990 line-height: 1.55;
991 margin: 10px 0;
992 }
993 pre code { background: none; border: 0; padding: 0; font-size: inherit; }
994
796ac8c0
Keenan Nemetz
11d ago
995 .crumbs { color: var(--fg-muted); font-size: calc(13.75px * var(--font-scale)); margin: -4px 0 12px; }
f09ef39e
Keenan Nemetz
15d ago
996 .crumbs a { color: var(--fg-muted); }
997 .crumbs a:hover { color: var(--accent); }
998 .dir { color: var(--accent); }
999
1000 .md { line-height: 1.65; max-width: 90ch; }
1001 .md > :first-child { margin-top: 0; }
1002 .md > :last-child { margin-bottom: 0; }
1003 .md h1, .md h2, .md h3, .md h4 {
1004 color: var(--fg);
1005 margin: 20px 0 8px;
1006 letter-spacing: -0.01em;
1007 text-transform: none;
1008 }
796ac8c0
Keenan Nemetz
11d ago
1009 .md h1 { font-size: calc(22px * var(--font-scale)); }
1010 .md h2 { font-size: calc(17.6px * var(--font-scale)); }
1011 .md h3 { font-size: calc(15.4px * var(--font-scale)); }
1012 .md h4 { font-size: calc(14.3px * var(--font-scale)); }
f09ef39e
Keenan Nemetz
15d ago
1013 .md p { margin: 9px 0; }
1014 .md ul, .md ol { padding-left: 22px; margin: 9px 0; }
1015 .md blockquote {
1016 margin: 10px 0;
1017 padding: 2px 0 2px 12px;
1018 border-left: 3px solid var(--border-strong);
1019 color: var(--fg-muted);
1020 }
1021 .md pre { background: var(--bg-panel); }
1022 .md table { width: auto; }
1023 .md img { border-radius: var(--radius); }
1024
796ac8c0
Keenan Nemetz
11d ago
1025 .diffsum { font-size: calc(13.2px * var(--font-scale)); margin: 10px 0; }
f09ef39e
Keenan Nemetz
15d ago
1026
30de4a44
Keenan Nemetz
8d ago
1027 /* A diff is read with the browser's find bar as much as with the eyes, so the
1028 card is not a scrolling box: it grows to the full height of the patch and the
1029 page scrolls instead. `clip` rounds the corners like `hidden` did, without
1030 making the element a scroll container that find has to scroll separately. */
f09ef39e
Keenan Nemetz
15d ago
1031 .diff {
1032 border: 1px solid var(--border);
1033 border-radius: var(--radius-lg);
1034 margin: 12px 0;
30de4a44
Keenan Nemetz
8d ago
1035 overflow: clip;
f09ef39e
Keenan Nemetz
15d ago
1036 background: var(--bg-panel);
1037 }
1038 .diff h3 {
628d4523
Keenan Nemetz
11d ago
1039 display: flex;
1040 align-items: center;
1041 gap: 8px;
f09ef39e
Keenan Nemetz
15d ago
1042 margin: 0;
1043 padding: 8px 12px;
1044 background: var(--bg-raised);
1045 border-bottom: 1px solid var(--border);
796ac8c0
Keenan Nemetz
11d ago
1046 font-size: calc(13.2px * var(--font-scale));
f09ef39e
Keenan Nemetz
15d ago
1047 font-weight: 550;
1048 }
628d4523
Keenan Nemetz
11d ago
1049 /* The name gives way before the counts do: the path can be long enough to cut,
1050 and two short numbers at the end of the row are the reason the row exists. */
1051 .diff h3 .path {
1052 flex: 1 1 auto;
1053 min-width: 0;
1054 overflow: hidden;
1055 text-overflow: ellipsis;
1056 white-space: nowrap;
1057 font-family: var(--mono);
1058 }
1059 /* One file's counts, in the same row as its name and at the other end of it. */
1060 .diffstat {
1061 display: inline-flex;
1062 align-items: center;
1063 gap: 8px;
1064 flex: 0 0 auto;
1065 font-family: var(--mono);
1066 font-size: calc(12.1px * var(--font-scale));
1067 font-weight: 400;
1068 }
1069 /* The row's own gap spaces the badges; the label's standalone margin was for
1070 prose, where nothing else spaced it. */
1071 .diff h3 .label { margin-left: 0; }
f09ef39e
Keenan Nemetz
15d ago
1072 .diff table {
1073 font-family: var(--mono);
796ac8c0
Keenan Nemetz
11d ago
1074 font-size: calc(12.65px * var(--font-scale));
f09ef39e
Keenan Nemetz
15d ago
1075 border-collapse: collapse;
f6f4b1db
Keenan Nemetz
15d ago
1076 table-layout: fixed;
f09ef39e
Keenan Nemetz
15d ago
1077 }
f6f4b1db
Keenan Nemetz
15d ago
1078 .diff col.comment-col { width: 2.25rem; }
1079 .diff col.ln-col { width: 3.5rem; }
f09ef39e
Keenan Nemetz
15d ago
1080 .diff td {
1081 border: 0;
1082 padding: 1px 10px;
1083 vertical-align: top;
1084 line-height: 19px;
1085 }
1086 .diff tbody tr:hover { background: none; }
1087 .diff td.ln {
1088 text-align: right;
1089 color: var(--fg-faint);
1090 user-select: none;
1091 border-right: 1px solid var(--border);
1092 padding: 1px 8px;
564acd4b
Keenan Nemetz
15d ago
1093 white-space: nowrap;
f09ef39e
Keenan Nemetz
15d ago
1094 }
564acd4b
Keenan Nemetz
15d ago
1095 /* Only the code column keeps spaces and wraps. The gutter cells hold the
1096 template's own newlines and indentation; with pre-wrap those became blank
1097 line boxes and every diff row looked padded. */
1098 .diff td.code { padding-left: 12px; white-space: pre-wrap; word-break: break-word; }
f09ef39e
Keenan Nemetz
15d ago
1099 .diff tr.add { background: var(--good-soft); }
1100 .diff tr.add td.ln { color: var(--good); }
1101 .diff tr.del { background: var(--bad-soft); }
1102 .diff tr.del td.ln { color: var(--bad); }
1103 .diff tr.hunk td {
1104 color: var(--fg-muted);
1105 background: var(--bg-raised);
1106 padding: 5px 12px;
796ac8c0
Keenan Nemetz
11d ago
1107 font-size: calc(12.1px * var(--font-scale));
f09ef39e
Keenan Nemetz
15d ago
1108 border-top: 1px solid var(--border);
1109 border-bottom: 1px solid var(--border);
1110 }
1111 .diff tr.note td {
f6f4b1db
Keenan Nemetz
15d ago
1112 background: var(--bg-panel);
1113 padding: 8px 12px;
796ac8c0
Keenan Nemetz
11d ago
1114 font-size: calc(13.2px * var(--font-scale));
f6f4b1db
Keenan Nemetz
15d ago
1115 }
1116 .inline-note {
1117 padding: 8px 10px;
1118 border: 1px solid var(--border);
1119 border-radius: var(--radius);
1120 background: var(--bg-raised);
796ac8c0
Keenan Nemetz
11d ago
1121 scroll-margin-top: 16px;
f6f4b1db
Keenan Nemetz
15d ago
1122 }
1123 .inline-note + .inline-note { margin-top: 8px; }
1124 .inline-note:target { border-color: var(--accent); }
1125 .inline-note:target .inline-note-head .comment-author { color: var(--accent); }
1126 .inline-note-head {
1127 display: flex;
1128 align-items: center;
1129 gap: 6px;
1130 margin-bottom: 5px;
1131 color: var(--fg-muted);
796ac8c0
Keenan Nemetz
11d ago
1132 font-size: calc(12.65px * var(--font-scale));
f6f4b1db
Keenan Nemetz
15d ago
1133 }
796ac8c0
Keenan Nemetz
11d ago
1134 .inline-note-head .comment-author { color: var(--fg); font-size: calc(13.2px * var(--font-scale)); }
f6f4b1db
Keenan Nemetz
15d ago
1135 .inline-note .comment-body {
1136 padding: 0;
1137 max-width: none;
796ac8c0
Keenan Nemetz
11d ago
1138 font-size: calc(13.75px * var(--font-scale));
f6f4b1db
Keenan Nemetz
15d ago
1139 line-height: 1.55;
1140 }
1141 .inline-note .comment-body p { margin: 4px 0; }
1142 .diff tr.inline-comment td {
1143 background: var(--bg-panel);
1144 padding: 8px 12px;
1145 border-top: 1px solid var(--border);
1146 }
1147 .inline-comment-form {
1148 display: flex;
1149 flex-direction: column;
1150 gap: 8px;
1151 max-width: 52rem;
1152 }
1153 .inline-comment-card {
1154 display: flex;
1155 flex-direction: column;
1156 gap: 8px;
1157 padding: 8px 10px;
1158 border: 1px solid var(--border);
1159 border-radius: var(--radius);
1160 background: var(--bg-raised);
1161 }
1162 .inline-comment-head {
1163 display: flex;
1164 align-items: center;
1165 justify-content: space-between;
1166 gap: 12px;
1167 color: var(--fg-muted);
796ac8c0
Keenan Nemetz
11d ago
1168 font-size: calc(13.2px * var(--font-scale));
f09ef39e
Keenan Nemetz
15d ago
1169 }
f6f4b1db
Keenan Nemetz
15d ago
1170 .inline-comment-head code { color: var(--fg); }
1171 .inline-comment-form textarea { min-height: 5.5rem; max-width: none; }
1172 .inline-comment-form .form-actions { margin-top: 0; }
796ac8c0
Keenan Nemetz
11d ago
1173 .diff .binary { padding: 10px 12px; color: var(--fg-muted); font-size: calc(13.2px * var(--font-scale)); margin: 0; }
f09ef39e
Keenan Nemetz
15d ago
1174
c717d4eb
Keenan Nemetz
13d ago
1175 /* ---------- shared responsive rules ---------- */
f09ef39e
Keenan Nemetz
15d ago
1176
1177 @media (max-width: 720px) {
796ac8c0
Keenan Nemetz
11d ago
1178 h1 { font-size: calc(17.6px * var(--font-scale)); }
f09ef39e
Keenan Nemetz
15d ago
1179 .repohead { gap: 0 10px; }
1180 th, td { padding: 7px 9px; }
1181 .nowrap { white-space: normal; }
1182 }
1183
c717d4eb
Keenan Nemetz
13d ago
1184 /* ---------- repository overview ---------- */
f09ef39e
Keenan Nemetz
15d ago
1185
1186 .overview {
1187 display: grid;
1188 grid-template-columns: minmax(0, 1fr) 340px;
1189 gap: 28px;
1190 align-items: start;
1191 }
1192 .overview-main { min-width: 0; }
1193 .overview-side section { padding: 12px 0 14px; border-bottom: 1px solid var(--border); }
1194 .overview-side section:first-child { padding-top: 0; }
1195 .overview-side section:last-child { border-bottom: 0; }
1196 .overview-side h2 { margin: 0 0 8px; }
1197
1198 .with-more { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
796ac8c0
Keenan Nemetz
11d ago
1199 .with-more .more { font-size: calc(13.2px * var(--font-scale)); font-weight: 400; color: var(--fg-muted); }
f09ef39e
Keenan Nemetz
15d ago
1200 .with-more .more:hover { color: var(--accent); }
1201
1202 .side-list { display: flex; flex-direction: column; gap: 6px; }
796ac8c0
Keenan Nemetz
11d ago
1203 .side-row { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; font-size: calc(13.75px * var(--font-scale)); }
1204 .side-row .muted { font-size: calc(12.65px * var(--font-scale)); white-space: nowrap; }
f09ef39e
Keenan Nemetz
15d ago
1205
1206 .contributors { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 6px; }
a32a859a
Keenan Nemetz
5d ago
1207 .contributors li { display: flex; align-items: center; flex-wrap: wrap; gap: 2px 8px; min-width: 0; }
796ac8c0
Keenan Nemetz
11d ago
1208 .contributors .cname { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: calc(13.75px * var(--font-scale)); }
1209 .contributors .muted { font-size: calc(12.65px * var(--font-scale)); }
a32a859a
Keenan Nemetz
5d ago
1210 .contributors .contributor-stats { display: inline-flex; align-items: center; gap: 8px; color: var(--fg-muted); font-size: calc(12.65px * var(--font-scale)); }
1211 .contributors .contrib-stat { position: relative; display: inline-flex; align-items: center; gap: 3px; cursor: help; }
1212 .contributors .contrib-stat .icon { width: 13px; height: 13px; color: var(--fg-faint); }
1213 .contributors .contrib-stat:hover,
1214 .contributors .contrib-stat:focus-visible { color: var(--fg); outline: none; }
1215 .contributors .contrib-stat:hover .icon,
1216 .contributors .contrib-stat:focus-visible .icon { color: var(--accent); }
f09ef39e
Keenan Nemetz
15d ago
1217
796ac8c0
Keenan Nemetz
11d ago
1218 .lang { display: grid; grid-template-columns: 1fr auto; gap: 2px 8px; margin-bottom: 8px; font-size: calc(13.2px * var(--font-scale)); }
1219 .lang .muted { font-size: calc(12.65px * var(--font-scale)); }
f09ef39e
Keenan Nemetz
15d ago
1220 .bar {
1221 grid-column: 1 / -1;
1222 appearance: none;
1223 width: 100%;
1224 height: 5px;
1225 border: 0;
1226 border-radius: 3px;
1227 background: var(--bg-active);
1228 overflow: hidden;
1229 }
1230 .bar::-webkit-progress-bar { background: var(--bg-active); border-radius: 3px; }
1231 .bar::-webkit-progress-value { background: var(--accent); border-radius: 3px; }
1232 .bar::-moz-progress-bar { background: var(--accent); border-radius: 3px; }
1233
1234 .filehead {
1235 display: flex;
1236 align-items: center;
1237 justify-content: space-between;
1238 gap: 12px;
1239 padding: 7px 12px;
1240 background: var(--bg-raised);
1241 border: 1px solid var(--border);
1242 border-bottom: 0;
1243 border-radius: var(--radius-lg) var(--radius-lg) 0 0;
1244 font-family: var(--mono);
796ac8c0
Keenan Nemetz
11d ago
1245 font-size: calc(13.2px * var(--font-scale));
f09ef39e
Keenan Nemetz
15d ago
1246 }
a464f892
Keenan Nemetz
15d ago
1247 .filehead .icon { width: 13px; height: 13px; margin-right: 6px; vertical-align: -2px; }
796ac8c0
Keenan Nemetz
11d ago
1248 .filehead a { font-family: var(--font); font-size: calc(12.65px * var(--font-scale)); color: var(--fg-muted); }
f09ef39e
Keenan Nemetz
15d ago
1249 .filehead a:hover { color: var(--accent); }
1250 .readme {
1251 background: var(--bg-panel);
1252 border: 1px solid var(--border);
1253 border-radius: 0 0 var(--radius-lg) var(--radius-lg);
1254 padding: 18px 20px;
1255 margin: 0 0 20px;
1256 max-width: none;
1257 }
1258 .readme > :first-child { margin-top: 0; }
1259
1260 .latest {
1261 display: flex;
1262 flex-wrap: wrap;
1263 align-items: baseline;
1264 gap: 4px 10px;
1265 padding: 7px 12px;
1266 margin: 0 0 16px;
1267 background: var(--bg-panel);
1268 border: 1px solid var(--border);
1269 border-radius: var(--radius);
796ac8c0
Keenan Nemetz
11d ago
1270 font-size: calc(13.75px * var(--font-scale));
f09ef39e
Keenan Nemetz
15d ago
1271 }
796ac8c0
Keenan Nemetz
11d ago
1272 .latest .muted { font-size: calc(12.65px * var(--font-scale)); }
f09ef39e
Keenan Nemetz
15d ago
1273 .latest a.sha { margin-left: auto; }
1274
1275 .status {
1276 display: inline-flex;
1277 align-items: center;
1278 gap: 5px;
796ac8c0
Keenan Nemetz
11d ago
1279 font-size: calc(12.1px * var(--font-scale));
f09ef39e
Keenan Nemetz
15d ago
1280 font-weight: 500;
1281 line-height: 1.4;
1282 text-transform: uppercase;
1283 letter-spacing: 0.04em;
1284 white-space: nowrap;
1285 color: var(--fg-muted);
1286 }
1287 .status::before {
1288 content: "";
1289 width: 6px;
1290 height: 6px;
1291 border-radius: 50%;
1292 background: currentColor;
1293 }
1294 .status.success { color: var(--good); }
1295 .status.failed { color: var(--bad); }
3516aef3
Keenan Nemetz
10d ago
1296 .status.running { color: var(--running); }
f09ef39e
Keenan Nemetz
15d ago
1297 .status.queued { color: var(--warn); }
70c9ad4e
Keenan Nemetz
4h ago
1298 .status.error { color: var(--warn); }
f09ef39e
Keenan Nemetz
15d ago
1299 .status.canceled { color: var(--fg-faint); }
1300
1301 .chip {
1302 display: inline-flex;
1303 align-items: center;
074ae160
Keenan Nemetz
14d ago
1304 gap: 5px;
f09ef39e
Keenan Nemetz
15d ago
1305 padding: 3px 9px;
1306 border: 1px solid var(--border-strong);
1307 border-radius: 12px;
796ac8c0
Keenan Nemetz
11d ago
1308 font-size: calc(12.65px * var(--font-scale));
f09ef39e
Keenan Nemetz
15d ago
1309 color: var(--fg-muted);
1310 background: var(--bg-panel);
1311 }
074ae160
Keenan Nemetz
14d ago
1312 .chip .icon { width: 12px; height: 12px; }
f09ef39e
Keenan Nemetz
15d ago
1313 .chip:hover { color: var(--fg); border-color: var(--fg-faint); text-decoration: none; }
1314 .chip.active { color: var(--accent); border-color: var(--accent); background: var(--accent-soft); }
1315
1316 td.fit { width: 1%; white-space: nowrap; }
1317
1318 @media (max-width: 1040px) {
1319 .overview { grid-template-columns: 1fr; }
1320 .overview-side { display: grid; grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr)); gap: 0 24px; }
1321 .overview-side section:first-child { padding-top: 12px; }
1322 }
1323
c717d4eb
Keenan Nemetz
13d ago
1324 /* ---------- code tab bar and crumbs ---------- */
f09ef39e
Keenan Nemetz
15d ago
1325
a464f892
Keenan Nemetz
15d ago
1326 /* The tab bar and the tree header share a height so their content starts on the
1327 same line. The bar is the file's identity, not a separate title block. */
1328 .code-tabbar {
1329 display: flex;
1330 align-items: center;
1331 gap: 8px;
1332 height: 36px;
1333 min-width: 0;
1334 padding: 0 10px;
1335 background: var(--bg-raised);
1336 border: 1px solid var(--border);
1337 border-radius: var(--radius-lg) var(--radius-lg) 0 0;
1338 color: var(--fg-muted);
f09ef39e
Keenan Nemetz
15d ago
1339 font-family: var(--mono);
796ac8c0
Keenan Nemetz
11d ago
1340 font-size: calc(12.65px * var(--font-scale));
a464f892
Keenan Nemetz
15d ago
1341 }
1342 .code-tab-icon { display: inline-flex; flex: 0 0 auto; color: var(--fg-faint); }
1343 .code-tab-icon .icon { width: 13px; height: 13px; }
1344 .code-crumbs {
1345 display: flex;
1346 align-items: center;
1347 gap: 6px;
1348 min-width: 0;
1349 overflow: hidden;
1350 white-space: nowrap;
1351 }
1352 .code-crumbs a { color: var(--fg-muted); }
1353 .code-crumbs a:hover { color: var(--accent); }
1354 .code-crumbs .sep { color: var(--fg-faint); }
1355 .code-crumbs .current { color: var(--fg); overflow: hidden; text-overflow: ellipsis; }
1356 .code-tab-actions {
1357 display: flex;
1358 flex: 0 0 auto;
1359 align-items: center;
1360 gap: 10px;
1361 margin-left: auto;
1362 font-family: var(--font);
796ac8c0
Keenan Nemetz
11d ago
1363 font-size: calc(12.65px * var(--font-scale));
a464f892
Keenan Nemetz
15d ago
1364 }
1365 .code-tab-actions a { color: var(--fg-muted); }
1366 .code-tab-actions a:hover,
1367 .code-tab-actions a.active { color: var(--accent); }
796ac8c0
Keenan Nemetz
11d ago
1368 .code-tab-actions .muted { font-size: calc(12.65px * var(--font-scale)); }
a464f892
Keenan Nemetz
15d ago
1369 .code-tabbar + .panel,
1370 .code-tabbar + .filebody,
1371 .code-tabbar + .card,
1372 .code-tabbar + .readme {
1373 margin-top: 0;
1374 border-top: 0;
1375 border-top-left-radius: 0;
1376 border-top-right-radius: 0;
1377 }
1378
c717d4eb
Keenan Nemetz
13d ago
1379 /* ---------- code browser and file tree ---------- */
a464f892
Keenan Nemetz
15d ago
1380
1381 .code-browser {
1382 display: grid;
1383 grid-template-columns: 260px minmax(0, 1fr);
1384 gap: 18px;
1385 align-items: start;
1386 }
796ac8c0
Keenan Nemetz
11d ago
1387 .code-main { min-width: 0; scroll-margin-top: 16px; }
a464f892
Keenan Nemetz
15d ago
1388
1389 .file-tree {
1390 display: flex;
1391 flex-direction: column;
1392 position: sticky;
796ac8c0
Keenan Nemetz
11d ago
1393 top: 16px;
1394 max-height: calc(100vh - 32px);
a464f892
Keenan Nemetz
15d ago
1395 overflow: hidden;
1396 background: var(--bg-panel);
1397 border: 1px solid var(--border);
1398 border-radius: var(--radius-lg);
1399 }
1400 .tree-head {
1401 display: flex;
1402 align-items: center;
1403 justify-content: space-between;
1404 gap: 8px;
1405 height: 36px;
1406 padding: 0 10px;
1407 background: var(--bg-raised);
1408 border-bottom: 1px solid var(--border);
1409 color: var(--fg-muted);
796ac8c0
Keenan Nemetz
11d ago
1410 font-size: calc(12.1px * var(--font-scale));
a464f892
Keenan Nemetz
15d ago
1411 letter-spacing: 0.05em;
1412 text-transform: uppercase;
1413 }
1414 .tree-head .icon { width: 13px; height: 13px; margin-right: 5px; vertical-align: -2px; }
796ac8c0
Keenan Nemetz
11d ago
1415 .tree-head .muted { font-size: calc(11.55px * var(--font-scale)); letter-spacing: 0; text-transform: none; }
a464f892
Keenan Nemetz
15d ago
1416 .tree-scroll { flex: 1 1 auto; min-height: 0; overflow: auto; padding: 6px 4px 8px; }
1417
1418 .tree-list { list-style: none; margin: 0; padding: 0; }
1419 .tree-children { margin-left: 9px; padding-left: 5px; border-left: 1px solid var(--border); }
1420 .tree-row {
1421 display: flex;
1422 align-items: center;
1423 gap: 6px;
1424 min-height: 24px;
1425 padding: 2px 7px;
1426 border-radius: 4px;
1427 color: var(--fg-muted);
796ac8c0
Keenan Nemetz
11d ago
1428 font-size: calc(13.2px * var(--font-scale));
a464f892
Keenan Nemetz
15d ago
1429 line-height: 1.45;
1430 }
1431 summary.tree-row { list-style: none; cursor: pointer; }
1432 summary.tree-row::-webkit-details-marker { display: none; }
1433 .tree-row:hover { background: var(--bg-hover); color: var(--fg); text-decoration: none; }
1434 .tree-row.active,
1435 .tree-dir.active > summary.tree-row { background: var(--accent-soft); color: var(--accent); }
1436 .tree-icon { display: inline-flex; flex: 0 0 auto; color: var(--fg-faint); }
1437 .tree-row.active .tree-icon { color: var(--accent); }
1438 .tree-icon .icon { width: 13px; height: 13px; }
1439 .tree-chevron { display: inline-flex; flex: 0 0 auto; color: var(--fg-faint); transition: transform 0.12s ease; }
1440 .tree-chevron .icon { width: 12px; height: 12px; }
1441 details[open] > summary .tree-chevron { transform: rotate(90deg); }
1442 .tree-file { padding-left: 25px; }
1443 .tree-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
796ac8c0
Keenan Nemetz
11d ago
1444 .tree-note, .tree-empty { margin: 0; padding: 8px 10px; font-size: calc(12.1px * var(--font-scale)); }
a464f892
Keenan Nemetz
15d ago
1445 .tree-note { border-top: 1px solid var(--border); }
1446
f6f4b1db
Keenan Nemetz
15d ago
1447 /* Review files: the changed-file tree reuses the code browser's shape, with a
1448 status mark in place of the file icon. */
1449 .review-tree .tree-file { padding-left: 7px; gap: 7px; }
1450 .review-mark {
1451 display: inline-grid;
1452 place-items: center;
1453 flex: 0 0 auto;
1454 width: 16px;
1455 height: 16px;
1456 border-radius: 3px;
1457 background: var(--bg-active);
1458 color: var(--fg-muted);
796ac8c0
Keenan Nemetz
11d ago
1459 font-family: var(--mono);
1460 font-size: calc(9.9px * var(--font-scale));
1461 font-weight: 600;
1462 line-height: 1;
f6f4b1db
Keenan Nemetz
15d ago
1463 }
1464 .review-mark[data-status="A"] { color: var(--good); background: var(--good-soft); }
1465 .review-mark[data-status="D"] { color: var(--bad); background: var(--bad-soft); }
1466 .review-mark[data-status="R"] { color: var(--accent); background: var(--accent-soft); }
1467 .review-mark[data-status="M"] { color: var(--warn); background: var(--warn-soft); }
796ac8c0
Keenan Nemetz
11d ago
1468 .review-main { min-width: 0; scroll-margin-top: 16px; }
628d4523
Keenan Nemetz
11d ago
1469 /* A single file is the whole pane, so its header is the pane's top bar: the file
1470 at one end and its counts at the other, as tall as the tree header so both
1471 columns start on the same line. The extra pixel is the section's own top
1472 border, which the height must not add to. */
1473 .review-main .diff { margin-top: 0; }
1474 .review-main .diff h3 { height: 35px; padding: 0 10px; }
f6f4b1db
Keenan Nemetz
15d ago
1475
a464f892
Keenan Nemetz
15d ago
1476 .code-main .filebody { max-width: none; }
1477 .code-main .readme > *,
1478 .code-main .filebody > * { max-width: none; }
1479 .empty-code { margin-top: 0; }
1480
1481 .dirlist td { padding: 2px 10px; }
1482 .dirlist a { display: inline-flex; align-items: center; gap: 7px; }
1483 .dirlist .icon { width: 13px; height: 13px; color: var(--fg-faint); }
1484 .dirlist a.dir .icon { color: var(--accent); }
1485 .dirlist a.parent { color: var(--fg-muted); }
1486
1487 @media (max-width: 980px) {
1488 .code-browser { grid-template-columns: 220px minmax(0, 1fr); }
1489 }
1490 @media (max-width: 760px) {
1491 .code-browser { grid-template-columns: 1fr; }
1492 .file-tree { position: static; max-height: 260px; }
f09ef39e
Keenan Nemetz
15d ago
1493 }
1494
1495 .readme > *,
1496 .filebody > * { max-width: 84ch; }
1497 .readme > pre,
1498 .readme > table,
1499 .filebody > pre,
1500 .filebody > table { max-width: 100%; }
1501
1502 .filebody {
1503 background: var(--bg-panel);
1504 border: 1px solid var(--border);
1505 border-radius: var(--radius-lg);
1506 padding: 18px 20px;
1507 margin: 12px 0 20px;
1508 }
1509 .filebody > :first-child { margin-top: 0; }
1510
30de4a44
Keenan Nemetz
8d ago
1511 /* The source card grows to the file's full height for the same reason: find has
1512 one scroll area, the page. Only a line too wide for the pane scrolls here. */
f09ef39e
Keenan Nemetz
15d ago
1513 .blob {
30de4a44
Keenan Nemetz
8d ago
1514 overflow-x: auto;
f09ef39e
Keenan Nemetz
15d ago
1515 }
1516 .blob table.code {
1517 font-family: var(--mono);
796ac8c0
Keenan Nemetz
11d ago
1518 font-size: calc(12.65px * var(--font-scale));
f09ef39e
Keenan Nemetz
15d ago
1519 border-collapse: collapse;
1520 }
1521 .blob td {
1522 border: 0;
1523 padding: 0 12px;
1524 line-height: 19px;
1525 white-space: pre;
1526 vertical-align: top;
1527 }
1528 .blob td.ln {
1529 width: 3.5rem;
1530 text-align: right;
1531 color: var(--fg-faint);
1532 user-select: none;
1533 border-right: 1px solid var(--border);
1534 padding: 0 8px;
1535 }
1536 .blob td.code { padding-left: 12px; }
1537 .blob tr:hover { background: none; }
1538
1539 /* ---------- syntax highlighting ---------- */
1540
e7982ac5
Keenan Nemetz
12d ago
1541 /* static/prism-worker.js writes Prism token classes into blob and diff cells
1542 after the page paints. The palette is the same one the rest of the surface
1543 uses, so a token is a colour, never a font switch except for comments and
1544 italics. */
1545
1546 :is(.blob, .diff) .token.comment,
1547 :is(.blob, .diff) .token.prolog,
1548 :is(.blob, .diff) .token.doctype,
1549 :is(.blob, .diff) .token.cdata { color: #6f7682; font-style: italic; }
1550 :is(.blob, .diff) .token.punctuation { color: #a6accd; }
1551 :is(.blob, .diff) .token.namespace { color: #a6accd; }
1552 :is(.blob, .diff) .token.operator,
1553 :is(.blob, .diff) .token.entity,
1554 :is(.blob, .diff) .token.url { color: #7fd3e8; }
1555 :is(.blob, .diff) .token.boolean,
1556 :is(.blob, .diff) .token.number,
1557 :is(.blob, .diff) .token.constant,
1558 :is(.blob, .diff) .token.symbol { color: #f2a06b; }
1559 :is(.blob, .diff) .token.string,
1560 :is(.blob, .diff) .token.char,
1561 :is(.blob, .diff) .token.attr-value,
1562 :is(.blob, .diff) .token.regex { color: #a8d98a; }
1563 :is(.blob, .diff) .token.atrule,
1564 :is(.blob, .diff) .token.keyword,
1565 :is(.blob, .diff) .token.tag,
1566 :is(.blob, .diff) .token.selector { color: #c792ea; }
1567 :is(.blob, .diff) .token.function,
1568 :is(.blob, .diff) .token.function-name,
1569 :is(.blob, .diff) .token.method { color: #83aaff; }
1570 :is(.blob, .diff) .token.class-name,
1571 :is(.blob, .diff) .token.builtin,
1572 :is(.blob, .diff) .token.attr-name,
1573 :is(.blob, .diff) .token.property,
1574 :is(.blob, .diff) .token.title { color: #f0c674; }
1575 :is(.blob, .diff) .token.variable { color: #e7e8ea; }
1576 :is(.blob, .diff) .token.deleted { color: var(--bad); }
1577 :is(.blob, .diff) .token.inserted { color: var(--good); }
1578 :is(.blob, .diff) .token.important,
1579 :is(.blob, .diff) .token.bold { font-weight: 600; }
1580 :is(.blob, .diff) .token.italic { font-style: italic; }
f09ef39e
Keenan Nemetz
15d ago
1581
c717d4eb
Keenan Nemetz
13d ago
1582 /* ---------- repository overview sidebar ---------- */
1583
1584 /* Compact commit and run rows beside the overview. */
f09ef39e
Keenan Nemetz
15d ago
1585
1586 .side-commit,
a196e404
Keenan Nemetz
11d ago
1587 .side-run,
1588 .side-issue,
1589 .side-change {
f09ef39e
Keenan Nemetz
15d ago
1590 display: flex;
1591 flex-direction: column;
1592 gap: 1px;
1593 padding: 4px 0;
1594 color: var(--fg);
1595 min-width: 0;
1596 }
1597 .side-commit:hover,
a196e404
Keenan Nemetz
11d ago
1598 .side-run:hover,
1599 .side-issue:hover,
1600 .side-change:hover { color: var(--accent); text-decoration: none; }
f09ef39e
Keenan Nemetz
15d ago
1601 .side-subject {
796ac8c0
Keenan Nemetz
11d ago
1602 font-size: calc(13.75px * var(--font-scale));
f09ef39e
Keenan Nemetz
15d ago
1603 overflow: hidden;
1604 text-overflow: ellipsis;
1605 white-space: nowrap;
1606 }
a196e404
Keenan Nemetz
11d ago
1607 .side-num { color: var(--fg-faint); font-variant-numeric: tabular-nums; margin-right: 4px; }
f09ef39e
Keenan Nemetz
15d ago
1608 .side-meta {
1609 display: flex;
1610 gap: 8px;
796ac8c0
Keenan Nemetz
11d ago
1611 font-size: calc(12.1px * var(--font-scale));
f09ef39e
Keenan Nemetz
15d ago
1612 color: var(--fg-faint);
1613 white-space: nowrap;
1614 }
1615 .side-run-top { display: flex; align-items: baseline; gap: 6px; min-width: 0; }
1616 .side-run-top .status { flex: 0 0 auto; }
1617
267feec2
Keenan Nemetz
10d ago
1618 /* The Issues section of a change lists its linked issues in the overview's compact
cad93cfe
Keenan Nemetz
10d ago
1619 shape, with the remove control beside the row and the add field beneath. */
1620 .request-issue { display: flex; align-items: flex-start; gap: 8px; min-width: 0; }
1621 .request-issue .side-issue { flex: 1; min-width: 0; }
86ddbd14
Keenan Nemetz
10d ago
1622 .thread-side .request-issue form { flex: 0 0 auto; margin: 0; }
267feec2
Keenan Nemetz
10d ago
1623 .request-issue form button { color: var(--fg-faint); }
cad93cfe
Keenan Nemetz
10d ago
1624 .request-issue form button:hover { color: var(--bad); }
267feec2
Keenan Nemetz
10d ago
1625 .request-unlink { display: inline-grid; place-items: center; width: 22px; min-height: 22px; border-radius: var(--radius); }
1626 .request-unlink:hover { background: var(--bg-hover); text-decoration: none; }
707dfd10
Keenan Nemetz
10d ago
1627 #request-combined[hidden], #request-merge-note[hidden] { display: none; }
bb541fde
Keenan Nemetz
10d ago
1628 .request-issue-picker { position: relative; margin-top: 8px; }
1629 .thread-side .request-issue-picker form { margin: 0; }
838f282e
Keenan Nemetz
10d ago
1630 .request-issue-search input { width: 100%; font-size: calc(12.65px * var(--font-scale)); }
bb541fde
Keenan Nemetz
10d ago
1631 .request-issue-results { position: absolute; top: calc(100% + 4px); left: 0; right: 0; z-index: 40; }
1632 .request-issue-popover {
1633 max-height: 18rem;
1634 overflow-y: auto;
1635 padding: 4px;
1636 border: 1px solid var(--border-strong);
1637 border-radius: var(--radius-lg);
1638 background: var(--bg-raised);
1639 box-shadow: 0 12px 32px rgba(0, 0, 0, 0.4);
1640 }
1641 .request-issue-popover p { margin: 0; padding: 5px 6px; font-size: calc(12.65px * var(--font-scale)); }
451332d1
Keenan Nemetz
10d ago
1642 .request-issue-matches { display: flex; flex-direction: column; gap: 2px; }
838f282e
Keenan Nemetz
10d ago
1643 .thread-side .request-issue-match { margin: 0; }
1644 .request-issue-match button {
1645 display: flex;
1646 flex-direction: column;
451332d1
Keenan Nemetz
10d ago
1647 align-items: stretch;
1648 justify-content: flex-start;
838f282e
Keenan Nemetz
10d ago
1649 gap: 1px;
1650 width: 100%;
1651 min-height: 0;
1652 padding: 4px 6px;
1653 border: 0;
1654 border-radius: var(--radius);
1655 background: none;
1656 color: var(--fg);
451332d1
Keenan Nemetz
10d ago
1657 font-weight: 400;
838f282e
Keenan Nemetz
10d ago
1658 text-align: left;
451332d1
Keenan Nemetz
10d ago
1659 white-space: normal;
838f282e
Keenan Nemetz
10d ago
1660 }
1661 .request-issue-match button:hover { background: var(--bg-hover); color: var(--accent); }
cad93cfe
Keenan Nemetz
10d ago
1662
c765ae2e
Keenan Nemetz
13d ago
1663 /* ---------- run detail ---------- */
1664
57dc79af
Keenan Nemetz
6d ago
1665 /* A run's summary is a row of facts, not a paragraph: how long it took, how
1666 long it waited, what it ran on, which commit it tested, and the workflow
1667 definition it ran. */
c765ae2e
Keenan Nemetz
13d ago
1668 .run-summary {
1669 display: grid;
1670 grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
1671 gap: 10px;
1672 margin: 16px 0;
1673 }
1674 .run-summary .card {
1675 margin: 0;
1676 display: flex;
1677 flex-direction: column;
1678 gap: 3px;
1679 min-width: 0;
1680 }
1681 .run-summary-label {
1682 display: flex;
1683 align-items: center;
1684 gap: 5px;
1685 color: var(--fg-faint);
796ac8c0
Keenan Nemetz
11d ago
1686 font-size: calc(11.55px * var(--font-scale));
c765ae2e
Keenan Nemetz
13d ago
1687 font-weight: 500;
1688 letter-spacing: 0.04em;
1689 text-transform: uppercase;
1690 }
1691 .run-summary-label .icon { width: 12px; height: 12px; }
1692 .run-summary strong {
796ac8c0
Keenan Nemetz
11d ago
1693 font-size: calc(17.6px * var(--font-scale));
c765ae2e
Keenan Nemetz
13d ago
1694 font-weight: 600;
1695 font-variant-numeric: tabular-nums;
1696 overflow-wrap: anywhere;
1697 }
1698 .run-summary .status-note { margin: 0; }
796ac8c0
Keenan Nemetz
11d ago
1699 .run-summary code { font-size: calc(11.55px * var(--font-scale)); overflow-wrap: anywhere; }
c765ae2e
Keenan Nemetz
13d ago
1700
1701 /* Step logs are loaded on demand. The details element is the trigger; the pre is
1702 the target, and htmx uses textContent so a log can never become markup. */
1703 .run-log {
1704 margin: 4px 0;
1705 background: var(--bg-panel);
1706 border: 1px solid var(--border);
1707 border-radius: var(--radius);
1708 overflow: hidden;
1709 }
1710 .run-log > summary {
1711 display: flex;
1712 align-items: center;
1713 gap: 6px;
1714 padding: 6px 10px;
1715 color: var(--fg-muted);
796ac8c0
Keenan Nemetz
11d ago
1716 font-size: calc(13.2px * var(--font-scale));
c765ae2e
Keenan Nemetz
13d ago
1717 cursor: pointer;
1718 list-style: none;
1719 }
1720 .run-log > summary::-webkit-details-marker { display: none; }
1721 .run-log > summary:hover { color: var(--fg); background: var(--bg-hover); }
1722 .run-log > summary .icon { width: 12px; height: 12px; color: var(--fg-faint); }
1723 .run-log[open] > summary {
1724 color: var(--fg);
1725 border-bottom: 1px solid var(--border);
1726 }
34d6fd7a
Keenan Nemetz
10d ago
1727 .run-log-card {
1728 padding: 0;
1729 overflow: hidden;
1730 }
1731 .run-log-card > .run-log {
1732 margin: 0;
1733 border: 0;
1734 border-bottom: 1px solid var(--border);
1735 border-radius: 0;
1736 }
1737 .run-log-card > .run-log:last-child { border-bottom: 0; }
1738 .run-log-title { flex: 1; color: var(--fg); font-weight: 500; }
1739 .run-log-meta {
1740 margin-left: auto;
1741 color: var(--fg-faint);
1742 font-size: calc(12.1px * var(--font-scale));
1743 font-variant-numeric: tabular-nums;
1744 }
1745 .run-step-num {
1746 color: var(--fg-faint);
1747 font-variant-numeric: tabular-nums;
1748 min-width: 2.5ch;
1749 }
1750 .run-step-name {
1751 flex: 1;
1752 color: var(--fg);
1753 overflow-wrap: anywhere;
1754 }
1755 .run-step-meta {
1756 margin-left: 8px;
1757 color: var(--fg-faint);
1758 font-size: calc(12.1px * var(--font-scale));
1759 font-variant-numeric: tabular-nums;
1760 white-space: nowrap;
1761 }
1762 .run-log-empty {
1763 margin: 0;
1764 padding: 10px 12px;
1765 color: var(--fg-muted);
1766 font-size: calc(13.2px * var(--font-scale));
1767 }
c765ae2e
Keenan Nemetz
13d ago
1768 .run-log-text {
1769 margin: 0;
1770 padding: 10px 12px;
1771 border: 0;
1772 border-radius: 0;
1773 background: var(--bg);
1774 color: var(--fg);
796ac8c0
Keenan Nemetz
11d ago
1775 font-size: calc(13.2px * var(--font-scale));
c765ae2e
Keenan Nemetz
13d ago
1776 line-height: 1.5;
1777 max-height: 34rem;
1778 min-height: 2.5rem;
1779 overflow: auto;
1780 white-space: pre;
1781 }
1782 .run-logbar {
1783 display: flex;
1784 flex-wrap: wrap;
1785 align-items: center;
1786 gap: 10px;
1787 margin: 8px 0;
796ac8c0
Keenan Nemetz
11d ago
1788 font-size: calc(13.2px * var(--font-scale));
c765ae2e
Keenan Nemetz
13d ago
1789 }
1790
f09ef39e
Keenan Nemetz
15d ago
1791 /* ---------- issues and changes ---------- */
1792
1793 .page-head {
1794 display: flex;
074ae160
Keenan Nemetz
14d ago
1795 align-items: flex-end;
f09ef39e
Keenan Nemetz
15d ago
1796 justify-content: space-between;
074ae160
Keenan Nemetz
14d ago
1797 gap: 24px;
1798 padding: 26px 0 20px;
1799 margin-bottom: 20px;
1800 border-bottom: 1px solid var(--border);
1801 }
1802 .page-head h1 {
796ac8c0
Keenan Nemetz
11d ago
1803 font-size: calc(28.6px * var(--font-scale));
074ae160
Keenan Nemetz
14d ago
1804 line-height: 1.2;
1805 letter-spacing: -0.025em;
1806 margin: 0 0 4px;
f09ef39e
Keenan Nemetz
15d ago
1807 }
a464f892
Keenan Nemetz
15d ago
1808 .page-head-main { min-width: 0; }
de84c32b
Keenan Nemetz
6d ago
1809 /* The `#42` alias leads a page heading, the way it leads a row in a list: the
1810 number is what a reader quotes, and the title is what they are looking for. */
1811 .page-num {
1812 color: var(--fg-faint);
1813 font-variant-numeric: tabular-nums;
1814 margin-right: 7px;
1815 }
a464f892
Keenan Nemetz
15d ago
1816 .page-head-actions {
1817 display: flex;
1818 flex-wrap: wrap;
1819 flex: 0 0 auto;
1820 align-items: center;
1821 justify-content: flex-end;
1822 gap: 8px;
1823 margin-left: auto;
1824 }
2e024cc9
Keenan Nemetz
14d ago
1825 /* Search and branch pickers are filters, not inline prose: a row layout keeps the
1826 input, its icon button, and the select on the same baseline. */
1827 .page-head-actions form.inline {
1828 display: inline-flex;
1829 align-items: center;
1830 gap: 6px;
1831 }
796ac8c0
Keenan Nemetz
11d ago
1832 .page-sub { color: var(--fg-muted); font-size: calc(14.3px * var(--font-scale)); margin: 0; max-width: 80ch; }
074ae160
Keenan Nemetz
14d ago
1833
1834 @media (max-width: 720px) {
1835 .page-head { padding: 18px 0 16px; }
796ac8c0
Keenan Nemetz
11d ago
1836 .page-head h1 { font-size: calc(24.2px * var(--font-scale)); }
074ae160
Keenan Nemetz
14d ago
1837 }
f09ef39e
Keenan Nemetz
15d ago
1838
973fb809
Keenan Nemetz
1d ago
1839 /* Secondary navigation inside a change: conversation and the review files. It is
1840 the same `.tabrow` and `.tabs` as every other row, pulled up against the
1841 request header. */
1842 .subtabs { margin-top: -2px; }
f6f4b1db
Keenan Nemetz
15d ago
1843
f09ef39e
Keenan Nemetz
15d ago
1844 .state {
1845 display: inline-flex;
1846 align-items: center;
1847 gap: 5px;
1848 padding: 1px 7px;
1849 border: 1px solid var(--border-strong);
1850 border-radius: 10px;
796ac8c0
Keenan Nemetz
11d ago
1851 font-size: calc(11.55px * var(--font-scale));
f09ef39e
Keenan Nemetz
15d ago
1852 line-height: 1.6;
1853 font-weight: 500;
1854 text-transform: uppercase;
1855 letter-spacing: 0.04em;
1856 color: var(--fg-muted);
1857 white-space: nowrap;
1858 }
1859 .state::before {
1860 content: "";
1861 width: 6px;
1862 height: 6px;
1863 border-radius: 50%;
1864 background: currentColor;
1865 }
1866 .state.open {
1867 color: var(--good);
1868 border-color: color-mix(in srgb, var(--good) 45%, var(--border));
1869 background: var(--good-soft);
1870 }
1871 .state.closed { color: var(--fg-faint); }
1872 .state.merged {
0e35a6a0
Keenan Nemetz
11d ago
1873 color: var(--merged);
1874 border-color: color-mix(in srgb, var(--merged) 45%, var(--border));
1875 background: var(--merged-soft);
f09ef39e
Keenan Nemetz
15d ago
1876 }
1877
1878 .chips { display: flex; flex-wrap: wrap; gap: 6px; margin: 10px 0; }
1879 .chip-count { margin-left: 5px; color: var(--fg-faint); font-variant-numeric: tabular-nums; }
1880 .chip.active .chip-count { color: var(--accent); }
1881 .filter-label {
1882 color: var(--fg-faint);
796ac8c0
Keenan Nemetz
11d ago
1883 font-size: calc(12.1px * var(--font-scale));
f09ef39e
Keenan Nemetz
15d ago
1884 text-transform: uppercase;
1885 letter-spacing: 0.04em;
1886 }
1887
1888 .row-title { font-weight: 550; color: var(--fg); }
1889 .row-title:hover { color: var(--accent); text-decoration: none; }
1890 .row-meta {
1891 display: flex;
1892 flex-wrap: wrap;
1893 align-items: center;
1894 gap: 4px 8px;
1895 margin-top: 2px;
796ac8c0
Keenan Nemetz
11d ago
1896 font-size: calc(12.65px * var(--font-scale));
f09ef39e
Keenan Nemetz
15d ago
1897 color: var(--fg-faint);
1898 }
1899
6813339b
Keenan Nemetz
4d ago
1900 /* The one word a list says about where a row is: a change's waiting state, or an
1901 issue's open/closed. The icon carries the colour, the label the meaning, and,
1902 where there is one, the title attribute the reason. */
1903 .row-status {
7cc36ba0
Keenan Nemetz
5d ago
1904 display: inline-flex;
1905 align-items: center;
1906 gap: 5px;
1907 font-size: calc(12.65px * var(--font-scale));
1908 color: var(--fg-muted);
1909 white-space: nowrap;
1910 }
6813339b
Keenan Nemetz
4d ago
1911 .row-status .icon { width: 13px; height: 13px; flex: 0 0 auto; }
1912 .row-status.open,
1913 .row-status.ready { color: var(--good); }
1914 .row-status.merged { color: var(--merged); }
1915 .row-status.failed,
1916 .row-status.conflict { color: var(--bad); }
1917 .row-status.waiting,
1918 .row-status.review { color: var(--warn); }
1919 .row-status.closed { color: var(--fg-faint); }
7cc36ba0
Keenan Nemetz
5d ago
1920
72f22292
Keenan Nemetz
10d ago
1921 /* A search snippet is the sentence the query matched, with the matched words
1922 marked. It sits under the row title, quieter than the title and smaller than
1923 the body it came from. */
1924 .search-snippet {
1925 margin: 3px 0 0;
1926 font-size: calc(12.65px * var(--font-scale));
1927 color: var(--fg-faint);
1928 }
1929 .search-snippet mark {
1930 background: var(--accent-soft);
1931 color: var(--fg);
1932 border-radius: 2px;
1933 padding: 0 1px;
1934 }
1935
c7b9094f
Keenan Nemetz
1d ago
1936 /* An empty state stands in for a list or a pane of content that has nothing to
1937 show. Use it for a whole region, not for one quiet line inside a card, a
1938 sidebar, a tree, or a table cell: those stay .muted so a page does not fill
1939 with dashed boxes. */
f09ef39e
Keenan Nemetz
15d ago
1940 .empty-state {
1941 border: 1px dashed var(--border-strong);
1942 border-radius: var(--radius-lg);
1943 padding: 20px;
1944 margin: 14px 0;
1945 color: var(--fg-muted);
1946 }
1947 .empty-state p { margin: 0 0 8px; }
1948 .empty-state p:last-child { margin-bottom: 0; }
1949 .empty-state pre { margin: 8px 0; }
1950
1951 /* Thread layout: conversation in the main column, action and metadata beside it. */
1952 .thread {
1953 display: grid;
1954 grid-template-columns: minmax(0, 1fr) 280px;
1955 gap: 24px;
1956 align-items: start;
1957 }
1958 .thread-main { min-width: 0; }
1959 .thread-side section { padding: 12px 0 14px; border-bottom: 1px solid var(--border); }
707dfd10
Keenan Nemetz
10d ago
1960 .thread-side #request-sidebar > #request-review { padding-top: 0; }
f6f4b1db
Keenan Nemetz
15d ago
1961 .thread-side > section:last-child,
1962 .thread-side #request-sidebar > section:last-child { border-bottom: 0; }
f09ef39e
Keenan Nemetz
15d ago
1963 .thread-side h2 { margin: 0 0 8px; }
1964 .thread-side form { margin: 8px 0 0; }
1965 .thread-side .meta-list { margin-bottom: 0; }
1966
d3dcf54d
Keenan Nemetz
11d ago
1967 /* The run rows beside a change use the same compact shape as the recent runs
1968 on the overview: status and job, then the context under them. */
1969 .thread-side .side-meta { flex-wrap: wrap; white-space: normal; }
1970 .thread-side .side-meta > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
1971
1972 /* A `?` next to a section heading: the explanation is a tooltip rather than a
1973 paragraph, so the panel keeps only the state it is about. The tooltip opens
1974 to the left because the sidebar sits at the right edge of the page. */
1975 .help {
1976 position: relative;
1977 display: inline-grid;
1978 place-items: center;
1979 width: 15px;
1980 height: 15px;
1981 margin-left: 6px;
1982 border: 1px solid var(--border-strong);
1983 border-radius: 50%;
1984 color: var(--fg-faint);
1985 font-family: var(--font);
1986 font-size: calc(10px * var(--font-scale));
1987 font-weight: 600;
1988 line-height: 1;
1989 vertical-align: 1px;
1990 cursor: help;
1991 user-select: none;
1992 }
1993 .help:hover,
1994 .help:focus-visible { color: var(--accent); border-color: var(--accent); outline: none; }
a32a859a
Keenan Nemetz
5d ago
1995 .help::after,
1996 .contributors .contrib-stat::after {
d3dcf54d
Keenan Nemetz
11d ago
1997 content: attr(data-tip);
1998 position: absolute;
1999 top: 50%;
2000 right: calc(100% + 8px);
2001 z-index: 20;
2002 width: max-content;
2003 max-width: 210px;
2004 padding: 7px 9px;
2005 border: 1px solid var(--border-strong);
2006 border-radius: var(--radius);
2007 background: var(--bg-raised);
2008 color: var(--fg);
2009 font-size: calc(12.1px * var(--font-scale));
2010 font-weight: 400;
2011 line-height: 1.45;
2012 letter-spacing: normal;
2013 text-align: left;
2014 white-space: normal;
2015 transform: translateY(-50%);
2016 opacity: 0;
2017 visibility: hidden;
2018 pointer-events: none;
2019 box-shadow: 0 6px 20px rgba(0, 0, 0, 0.35);
2020 }
2021 .help:hover::after,
2022 .help:focus::after,
a32a859a
Keenan Nemetz
5d ago
2023 .help:focus-visible::after,
2024 .contributors .contrib-stat:hover::after,
2025 .contributors .contrib-stat:focus::after,
2026 .contributors .contrib-stat:focus-visible::after { opacity: 1; visibility: visible; }
d3dcf54d
Keenan Nemetz
11d ago
2027 @media (prefers-reduced-motion: no-preference) {
2028 .help::after { transition: opacity 120ms ease; }
2029 }
2030
cfd43dcd
Keenan Nemetz
12d ago
2031 /* Scope the card to the article that renders it. Prism calls a syntax token
2032 "comment" too, and an unscoped .comment rule put a rounded panel behind every
2033 comment-coloured line in a blob or diff. */
2034 article.comment {
f09ef39e
Keenan Nemetz
15d ago
2035 background: var(--bg-panel);
2036 border: 1px solid var(--border);
2037 border-radius: var(--radius-lg);
2038 margin: 10px 0;
f6f4b1db
Keenan Nemetz
15d ago
2039 overflow: visible;
796ac8c0
Keenan Nemetz
11d ago
2040 scroll-margin-top: 16px;
f6f4b1db
Keenan Nemetz
15d ago
2041 }
cfd43dcd
Keenan Nemetz
12d ago
2042 article.comment:target {
f6f4b1db
Keenan Nemetz
15d ago
2043 border-color: var(--accent);
2044 box-shadow: 0 0 0 2px var(--accent-soft);
f09ef39e
Keenan Nemetz
15d ago
2045 }
cfd43dcd
Keenan Nemetz
12d ago
2046 article.comment:target .comment-head { background: var(--accent-soft); }
f09ef39e
Keenan Nemetz
15d ago
2047 .comment-head {
2048 display: flex;
2049 flex-wrap: wrap;
2050 align-items: center;
2051 gap: 6px 8px;
2052 padding: 7px 12px;
2053 background: var(--bg-raised);
2054 border-bottom: 1px solid var(--border);
f6f4b1db
Keenan Nemetz
15d ago
2055 border-top-left-radius: calc(var(--radius-lg) - 1px);
2056 border-top-right-radius: calc(var(--radius-lg) - 1px);
796ac8c0
Keenan Nemetz
11d ago
2057 font-size: calc(13.2px * var(--font-scale));
f09ef39e
Keenan Nemetz
15d ago
2058 }
2059 .comment-author { font-weight: 550; }
796ac8c0
Keenan Nemetz
11d ago
2060 .comment-head .muted { font-size: calc(12.65px * var(--font-scale)); }
2061 .reply-context { color: var(--fg-muted); font-size: calc(12.65px * var(--font-scale)); }
f6f4b1db
Keenan Nemetz
15d ago
2062 .reply-context a { color: var(--fg-muted); }
2063 .reply-context a:hover { color: var(--accent); }
f09ef39e
Keenan Nemetz
15d ago
2064 .comment-body { padding: 12px 14px; }
2065 .comment-body > :first-child { margin-top: 0; }
2066 .comment-body > :last-child { margin-bottom: 0; }
2067
cfd43dcd
Keenan Nemetz
12d ago
2068 article.comment.deleted {
f6f4b1db
Keenan Nemetz
15d ago
2069 border-style: dashed;
2070 background: transparent;
2071 }
2072 .comment-deleted {
2073 display: flex;
2074 align-items: center;
2075 gap: 6px;
2076 padding: 10px 14px;
2077 color: var(--fg-faint);
796ac8c0
Keenan Nemetz
11d ago
2078 font-size: calc(13.2px * var(--font-scale));
f6f4b1db
Keenan Nemetz
15d ago
2079 font-style: italic;
2080 }
2081 .comment-deleted .icon { width: 13px; height: 13px; }
2082
2083 /* Replies are displayed one level deep: a root comment leads, all its
2084 descendants sit in an indented rail, and the reply composer joins the thread. */
2085 .comment-replies {
2086 display: flex;
2087 flex-direction: column;
2088 gap: 8px;
2089 margin: 0 0 12px 26px;
2090 padding-left: 12px;
2091 border-left: 2px solid var(--border);
2092 }
cfd43dcd
Keenan Nemetz
12d ago
2093 .comment-replies article.comment {
f6f4b1db
Keenan Nemetz
15d ago
2094 margin: 0;
2095 background: var(--bg-raised);
2096 }
2097 .comment-menu { position: relative; margin-left: auto; }
2098 .comment-menu-toggle {
2099 display: inline-flex;
2100 align-items: center;
2101 justify-content: center;
2102 width: 22px;
2103 min-height: 0;
2104 padding: 0;
2105 border: 0;
2106 border-radius: 4px;
2107 background: none;
2108 color: var(--fg-muted);
796ac8c0
Keenan Nemetz
11d ago
2109 font-family: var(--font);
2110 font-size: calc(17.6px * var(--font-scale));
2111 font-weight: 600;
2112 line-height: 1;
f6f4b1db
Keenan Nemetz
15d ago
2113 cursor: pointer;
2114 }
2115 .comment-menu-toggle:hover { background: var(--bg-hover); color: var(--fg); }
2116 .comment-menu-list {
2117 position: absolute;
2118 z-index: 60;
2119 top: calc(100% + 4px);
2120 right: 0;
2121 min-width: 10rem;
2122 padding: 4px;
2123 border: 1px solid var(--border-strong);
2124 border-radius: var(--radius);
2125 background: var(--bg-raised);
2126 box-shadow: 0 12px 32px rgba(0, 0, 0, 0.4);
2127 }
2128 .comment-menu-list[hidden] { display: none; }
2129 .comment-menu-list button {
2130 display: block;
2131 width: 100%;
2132 min-height: 0;
2133 padding: 6px 9px;
2134 border: 0;
2135 border-radius: 4px;
2136 background: none;
2137 color: var(--fg);
2138 text-align: left;
796ac8c0
Keenan Nemetz
11d ago
2139 font-family: var(--font);
2140 font-size: calc(13.2px * var(--font-scale));
2141 font-weight: 400;
2142 line-height: 1.4;
f6f4b1db
Keenan Nemetz
15d ago
2143 cursor: pointer;
2144 }
2145 .comment-menu-list button:hover { background: var(--bg-hover); }
2146 .comment-menu-list form { margin: 0; }
2147 .comment-menu-list .menu-danger { color: var(--bad); }
2148 .comment-menu-list .menu-danger:hover { background: var(--bad-soft); }
802e0e52
Keenan Nemetz
5d ago
2149 .comment-edit-form, .details-edit-form { padding: 10px 14px 12px; }
2150 .comment-edit-form[hidden], .details-edit-form[hidden] { display: none; }
2151 .comment-edit-form textarea, .details-edit-form textarea { min-height: 7rem; max-width: none; }
2152 .comment-edit-form .form-actions, .details-edit-form .form-actions { margin-top: 8px; }
cfd43dcd
Keenan Nemetz
12d ago
2153 article.comment.editing > .comment-body,
2154 article.comment.editing > .comment-snippet { display: none; }
f6f4b1db
Keenan Nemetz
15d ago
2155
802e0e52
Keenan Nemetz
5d ago
2156 /* The heading is one row: the number stays put, the title sits beside it, and
2157 the pencil opens a title-only form that takes the title's place. */
2158 .details-title { display: flex; align-items: baseline; flex-wrap: wrap; gap: 7px; margin: 0 0 4px; }
2159 .details-title .page-num { flex: 0 0 auto; margin-right: 0; }
2160 .details-title-view { min-width: 0; margin: 0; }
2161 .details-title-view[hidden], .title-edit-form[hidden], .title-edit-toggle[hidden] { display: none; }
2162 .title-edit-toggle {
2163 display: inline-grid;
2164 place-items: center;
2165 flex: 0 0 auto;
2166 width: 22px;
2167 height: 22px;
2168 border-radius: var(--radius);
2169 color: var(--fg-faint);
2170 }
2171 .title-edit-toggle:hover { background: var(--bg-hover); color: var(--accent); text-decoration: none; }
2172 .title-edit-toggle .icon { width: 14px; height: 14px; }
2173 .title-edit-form { display: flex; align-items: center; flex: 1 1 18rem; gap: 6px; min-width: 0; }
2174 .title-edit-form .details-title-input { flex: 1 1 auto; min-width: 8rem; }
2175 .title-edit-form button { flex: 0 0 auto; }
2176 .details-title-input {
2177 width: 100%;
2178 max-width: none;
2179 font-family: var(--font);
2180 font-size: calc(19.8px * var(--font-scale));
2181 font-weight: 600;
2182 line-height: 1.35;
2183 letter-spacing: -0.005em;
2184 }
2185
f6f4b1db
Keenan Nemetz
15d ago
2186 /* A thread keeps its root visible and lets the replies fold away. */
2187 .thread-replies { margin: 0 0 10px 26px; }
2188 .thread-replies > summary {
2189 display: inline-flex;
2190 align-items: center;
2191 gap: 6px;
2192 width: max-content;
2193 padding: 2px 0;
2194 color: var(--fg-muted);
796ac8c0
Keenan Nemetz
11d ago
2195 font-size: calc(12.65px * var(--font-scale));
f6f4b1db
Keenan Nemetz
15d ago
2196 cursor: pointer;
2197 list-style: none;
2198 }
2199 .thread-replies > summary::-webkit-details-marker { display: none; }
2200 .thread-replies > summary:hover { color: var(--accent); }
2201 .thread-replies[open] > summary { margin-bottom: 6px; }
2202 .thread-chevron { display: inline-flex; color: var(--fg-faint); transition: transform 0.12s ease; }
2203 .thread-chevron .icon { width: 12px; height: 12px; }
2204 .thread-replies[open] .thread-chevron { transform: rotate(90deg); }
2205 .thread-replies .comment-replies { margin: 6px 0 0; }
2206
2207 .discussion-composer { margin: 16px 0 0; }
2208 .discussion-composer textarea {
2209 width: 100%;
2210 max-width: none;
2211 min-height: 7rem;
2212 padding: 12px 14px;
2213 background: var(--bg-panel);
2214 border-color: var(--border);
2215 border-radius: var(--radius-lg);
2216 line-height: 1.55;
2217 }
2218 .discussion-composer .form-actions { margin-top: 8px; }
2219 .reply-target {
2220 display: flex;
2221 align-items: center;
2222 justify-content: space-between;
2223 gap: 8px;
2224 margin: 0 0 8px;
2225 padding: 5px 8px;
2226 border: 1px solid var(--accent);
2227 border-radius: var(--radius);
2228 background: var(--accent-soft);
2229 color: var(--fg-muted);
796ac8c0
Keenan Nemetz
11d ago
2230 font-size: calc(12.65px * var(--font-scale));
f6f4b1db
Keenan Nemetz
15d ago
2231 }
2232 .reply-target[hidden] { display: none; }
2233 .reply-target strong { color: var(--fg); }
2234
2235 /* An anchored comment can show the lines it was written about. The details
306f36fc
Keenan Nemetz
12d ago
2236 element keeps the conversation compact. */
f6f4b1db
Keenan Nemetz
15d ago
2237 .comment-snippet {
2238 margin: 0 14px 12px;
2239 border: 1px solid var(--border);
2240 border-radius: var(--radius);
2241 background: var(--bg-raised);
2242 overflow: hidden;
2243 }
2244 .comment-snippet > summary {
2245 display: flex;
2246 align-items: center;
2247 flex-wrap: wrap;
2248 gap: 4px 8px;
2249 padding: 7px 10px;
2250 color: var(--fg-muted);
796ac8c0
Keenan Nemetz
11d ago
2251 font-size: calc(12.65px * var(--font-scale));
f6f4b1db
Keenan Nemetz
15d ago
2252 cursor: pointer;
2253 list-style: none;
2254 }
2255 .comment-snippet > summary::-webkit-details-marker { display: none; }
2256 .comment-snippet > summary:hover { background: var(--bg-hover); }
2257 .comment-snippet code { color: var(--fg); font-family: var(--mono); }
2258 .comment-snippet[open] > summary { border-bottom: 1px solid var(--border); }
2259 .snippet-chevron { display: inline-flex; color: var(--fg-faint); transition: transform 0.12s ease; }
2260 .snippet-chevron .icon { width: 12px; height: 12px; }
2261 .comment-snippet[open] .snippet-chevron { transform: rotate(90deg); }
2262 .snippet-code {
2263 overflow-x: auto;
2264 background: var(--bg-panel);
796ac8c0
Keenan Nemetz
11d ago
2265 font-family: var(--mono);
2266 font-size: calc(12.65px * var(--font-scale));
2267 line-height: 1.55;
f6f4b1db
Keenan Nemetz
15d ago
2268 }
2269 .snippet-line { display: flex; min-width: max-content; }
2270 .snippet-line.anchor { background: var(--accent-soft); }
2271 .snippet-no {
2272 flex: 0 0 3.2rem;
2273 padding-right: 8px;
2274 text-align: right;
2275 color: var(--fg-faint);
2276 user-select: none;
2277 border-right: 1px solid var(--border);
2278 }
2279 .snippet-text { padding: 0 10px; white-space: pre; color: var(--fg); }
2280 .snippet-actions {
2281 padding: 6px 10px;
2282 border-top: 1px solid var(--border);
796ac8c0
Keenan Nemetz
11d ago
2283 font-size: calc(12.65px * var(--font-scale));
f6f4b1db
Keenan Nemetz
15d ago
2284 }
2285
f09ef39e
Keenan Nemetz
15d ago
2286 .meta-list {
2287 display: grid;
2288 grid-template-columns: auto minmax(0, 1fr);
2289 gap: 4px 10px;
2290 margin: 0;
796ac8c0
Keenan Nemetz
11d ago
2291 font-size: calc(13.2px * var(--font-scale));
f09ef39e
Keenan Nemetz
15d ago
2292 }
2293 .meta-list dt { color: var(--fg-faint); }
2294 .meta-list dd { margin: 0; min-width: 0; overflow-wrap: anywhere; }
41d1fee9
Keenan Nemetz
13d ago
2295
b74cb029
Keenan Nemetz
11d ago
2296 /* The request target is two stacked fields, not one crowded row: the sidebar is
2297 narrow, so each branch name gets the full width and a visible label. */
41d1fee9
Keenan Nemetz
13d ago
2298 .request-target { margin: 2px 0 0; }
f2360c20
Keenan Nemetz
11d ago
2299 .target-fields { display: grid; gap: 4px; }
33e4e54f
Keenan Nemetz
1d ago
2300 .target-field { display: grid; gap: 3px; min-width: 0; margin: 0; }
59c0625b
Keenan Nemetz
11d ago
2301 .target-label { color: var(--fg-muted); font-size: calc(12.1px * var(--font-scale)); }
41d1fee9
Keenan Nemetz
13d ago
2302 .request-target select {
2303 min-width: 0;
2304 width: 100%;
b74cb029
Keenan Nemetz
11d ago
2305 min-height: 30px;
2306 padding: 4px 7px;
796ac8c0
Keenan Nemetz
11d ago
2307 font-size: calc(13.2px * var(--font-scale));
41d1fee9
Keenan Nemetz
13d ago
2308 }
b74cb029
Keenan Nemetz
11d ago
2309 .target-actions {
2310 display: flex;
2311 flex-wrap: wrap;
2312 align-items: center;
2313 gap: 6px 10px;
33e4e54f
Keenan Nemetz
1d ago
2314 margin-top: 6px;
b74cb029
Keenan Nemetz
11d ago
2315 }
2316 .target-actions button {
41d1fee9
Keenan Nemetz
13d ago
2317 display: inline-flex;
2318 align-items: center;
2319 justify-content: center;
2320 min-height: 28px;
b74cb029
Keenan Nemetz
11d ago
2321 margin-left: auto;
2322 padding: 3px 9px;
41d1fee9
Keenan Nemetz
13d ago
2323 }
b74cb029
Keenan Nemetz
11d ago
2324 .request-target p { margin: 0; font-size: calc(12.65px * var(--font-scale)); }
41d1fee9
Keenan Nemetz
13d ago
2325 .target-summary {
2326 display: flex;
2327 flex-wrap: wrap;
2328 align-items: center;
2329 gap: 5px;
2330 margin: 0;
796ac8c0
Keenan Nemetz
11d ago
2331 font-size: calc(13.2px * var(--font-scale));
41d1fee9
Keenan Nemetz
13d ago
2332 }
f09ef39e
Keenan Nemetz
15d ago
2333 .label-row { display: inline-flex; flex-wrap: wrap; gap: 4px; }
2334 .label-row .label { margin-left: 0; }
2335
d3dcf54d
Keenan Nemetz
11d ago
2336 .approval-list {
f09ef39e
Keenan Nemetz
15d ago
2337 list-style: none;
2338 padding: 0;
2339 margin: 0;
2340 display: flex;
2341 flex-direction: column;
2342 gap: 6px;
796ac8c0
Keenan Nemetz
11d ago
2343 font-size: calc(13.2px * var(--font-scale));
f09ef39e
Keenan Nemetz
15d ago
2344 }
d3dcf54d
Keenan Nemetz
11d ago
2345 .approval-list li { display: flex; align-items: center; gap: 7px; min-width: 0; }
2346 .approval-list .cname {
f09ef39e
Keenan Nemetz
15d ago
2347 flex: 1;
2348 min-width: 0;
2349 overflow: hidden;
2350 text-overflow: ellipsis;
2351 white-space: nowrap;
2352 }
796ac8c0
Keenan Nemetz
11d ago
2353 .approval-list .muted { font-size: calc(12.1px * var(--font-scale)); }
f09ef39e
Keenan Nemetz
15d ago
2354
2355 @media (max-width: 900px) {
2356 .thread { grid-template-columns: 1fr; }
d3dcf54d
Keenan Nemetz
11d ago
2357 .thread-side { display: block; }
2358 .thread-side #request-aside { display: grid; grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr)); gap: 0 24px; }
2359 .thread-side #request-sidebar { display: contents; }
f09ef39e
Keenan Nemetz
15d ago
2360 .thread-side section:first-child { padding-top: 12px; }
a32a859a
Keenan Nemetz
5d ago
2361 .help::after,
2362 .contributors .contrib-stat::after { top: calc(100% + 7px); right: auto; left: 0; transform: none; }
f09ef39e
Keenan Nemetz
15d ago
2363 .page-head { flex-wrap: wrap; }
a464f892
Keenan Nemetz
15d ago
2364 .page-head-actions { width: 100%; justify-content: flex-start; }
f09ef39e
Keenan Nemetz
15d ago
2365 }
2366
2367 /* Form cards: fields live on a card, actions sit below it and align right.
2368 Every primary action uses the same height and horizontal padding. */
2369 .form-block { margin: 12px 0; }
9579c1f9
Keenan Nemetz
14d ago
2370 .danger-form + .danger-form { margin-top: 28px; }
2973f727
Keenan Nemetz
15d ago
2371 .acl-check { display: flex; align-items: center; gap: 6px; }
2372 .acl-check input[type="checkbox"] { width: auto; margin: 0; }
2373 table.acl select { min-width: 9rem; }
5b814e99
Keenan Nemetz
14d ago
2374 .role-options { border: 0; padding: 0; margin: 0; min-width: 0; }
2375 .role-options legend {
2376 padding: 0;
2377 margin: 0 0 8px;
2378 color: var(--fg);
796ac8c0
Keenan Nemetz
11d ago
2379 font-size: calc(14.3px * var(--font-scale));
5b814e99
Keenan Nemetz
14d ago
2380 font-weight: 600;
2381 }
2382 .role-options > p { margin: 0 0 10px; }
2383 .role-option {
2384 display: flex;
2385 align-items: baseline;
2386 gap: 7px;
2387 margin: 5px 0;
2388 padding: 7px 10px;
2389 border: 1px solid var(--border);
2390 border-radius: var(--radius);
2391 color: var(--fg);
796ac8c0
Keenan Nemetz
11d ago
2392 font-size: calc(14.3px * var(--font-scale));
5b814e99
Keenan Nemetz
14d ago
2393 cursor: pointer;
2394 }
2395 .role-option:hover { border-color: var(--border-strong); background: var(--bg-raised); }
2396 .role-option:has(input[type="radio"]:checked) {
2397 border-color: var(--accent);
2398 background: var(--accent-soft);
2399 }
2400 .role-option input[type="radio"] {
2401 display: block;
2402 flex: 0 0 auto;
2403 width: auto;
2404 margin: 0;
2405 accent-color: var(--accent);
2406 }
2407 .role-option .role-name { font-weight: 600; }
796ac8c0
Keenan Nemetz
11d ago
2408 .role-option .role-desc { color: var(--fg-muted); font-size: calc(13.2px * var(--font-scale)); line-height: 1.45; }
5b814e99
Keenan Nemetz
14d ago
2409 .role-options.compact { display: flex; flex-wrap: wrap; gap: 4px 12px; }
2410 .role-options.compact legend { display: none; }
2411 .role-options.compact .role-option {
2412 display: inline-flex;
2413 align-items: center;