1@layer components {
2 /* tables in a card, with a row cursor (j/k) */
3 .list {
4 width: 100%; table-layout: fixed;
5 background: var(--bg-surface); border: 1px solid var(--border); border-radius: var(--r);
6 border-collapse: separate; overflow: hidden;
7 & th {
8 height: 40px; padding: 0 var(--s3); white-space: nowrap;
9 font: 600 var(--fs-xs) / 1 var(--font); text-transform: uppercase; letter-spacing: .05em; color: var(--ink-mute);
10 background: var(--bg-raised); border-bottom: 1px solid var(--border);
11 }
12 & th a { text-decoration: none; }
13 & th a:hover { color: var(--ink-hi); }
14 & th[aria-sort="ascending"], & th[aria-sort="descending"] { color: var(--ink-hi); }
15 & td { height: 40px; padding: 0 var(--s3); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; border-top: 1px solid var(--border); }
16 & tbody tr:first-child td { border-top: 0; }
17 & tbody tr:hover { background: var(--bg-raised); }
18 /* the cursor: the row raised, framed by one even line. The frame is drawn
19 on the cells (a row takes no radius), so it can follow the card's
20 curve when the row sits at a corner. */
21 & tbody tr[data-cursor] > td {
22 background: var(--bg-raised); border-top-color: transparent;
23 box-shadow: inset 0 1px var(--accent), inset 0 -1px var(--accent);
24 }
25 & tbody tr[data-cursor] > td:first-child { box-shadow: inset 1px 0 var(--accent), inset 0 1px var(--accent), inset 0 -1px var(--accent); }
26 & tbody tr[data-cursor] > td:last-child { box-shadow: inset -1px 0 var(--accent), inset 0 1px var(--accent), inset 0 -1px var(--accent); }
27 & > tbody:first-child > tr[data-cursor]:first-child > td:first-child { border-top-left-radius: calc(var(--r) - 1px); }
28 & > tbody:first-child > tr[data-cursor]:first-child > td:last-child { border-top-right-radius: calc(var(--r) - 1px); }
29 & > tbody:last-child > tr[data-cursor]:last-child > td:first-child { border-bottom-left-radius: calc(var(--r) - 1px); }
30 & > tbody:last-child > tr[data-cursor]:last-child > td:last-child { border-bottom-right-radius: calc(var(--r) - 1px); }
31 & tbody tr[hidden] { display: none; }
32 & td > a { text-decoration: none; }
33 & td > a:first-child { color: var(--ink-hi); }
34 & tr:hover td > a:first-child, & tr[data-cursor] td > a:first-child { text-decoration: underline; }
35 & .c-sha, & .c-ref, & .c-kind, & .c-size, & .c-n { font-family: var(--mono); font-size: var(--fs-s); }
36 & .c-sha a { color: var(--ink-dim); }
37 & .c-when, & .c-who { font-size: var(--fs-s); }
38 & .c-sub { color: var(--ink-dim); }
39 }
40
41 /* home: the repositories as rows in a vertical list; the page is a stack of
42 sections, so others can sit beside or below */
43 .home {
44 display: flex; flex-direction: column; gap: var(--s5);
45 & .home-bar { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--s3) var(--s4); margin-bottom: var(--s4); }
46 & .home-bar .filter { flex: 1 1 260px; }
47 & .sort { display: flex; align-items: center; gap: var(--s1); font-size: var(--fs-s); }
48 & .sort > .mute { margin-right: var(--s1); }
49 & .sort a {
50 display: inline-flex; align-items: center; gap: 4px; height: 28px; padding: 0 var(--s2);
51 color: var(--ink-dim); text-decoration: none; border: 1px solid transparent; border-radius: var(--r-s);
52 }
53 & .sort a:hover { color: var(--ink-hi); background: var(--bg-raised); }
54 & .sort a[aria-current="true"] { color: var(--ink-hi); background: var(--bg-raised); border-color: var(--border-strong); }
55 }
56 /* a row: name, description and facts on the left; the state of the latest
57 run at the top right, the newest commits under it. Stacked on a phone. */
58 .repos {
59 display: flex; flex-direction: column; gap: var(--s3);
60 margin: 0; padding: 0; list-style: none;
61 & .repo {
62 display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); grid-template-areas: "main head" "main recent";
63 grid-template-rows: auto 1fr; column-gap: var(--s6); row-gap: var(--s3); min-width: 0;
64 padding: var(--s4); background: var(--bg-surface); border: 1px solid var(--border); border-radius: var(--r);
65 }
66 & .repo:hover { background: var(--bg-raised); border-color: var(--border-strong); }
67 & .repo[data-cursor] { background: var(--bg-raised); border-color: var(--accent); }
68 & .repo[hidden] { display: none; }
69 & .repo-main { grid-area: main; display: flex; flex-direction: column; gap: var(--s2); min-width: 0; }
70 & .repo-name { display: flex; align-items: center; flex-wrap: wrap; gap: var(--s1) var(--s2); font-family: var(--mono); font-size: 17px; line-height: 26px; overflow-wrap: anywhere; }
71 & .repo-name a { color: var(--ink-hi); text-decoration: none; }
72 & .repo:hover .repo-name a:not(.pill), & .repo[data-cursor] .repo-name a:not(.pill) { text-decoration: underline; }
73 & .repo-name .pill { margin-left: 0; }
74 & .repo-name a.pill { display: block; line-height: 18px; min-width: 0; max-width: 18ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: normal; }
75 & .repo-name .repo-release { color: var(--accent); }
76 & .repo-name .repo-license { color: var(--ink-dim); }
77 & .repo-activity { display: inline-flex; align-items: center; flex: none; color: var(--accent); }
78 /* Every card has the same height: two lines of description, one of topics
79 (reserved when there are none), five of commits. */
80 & .repo-desc {
81 color: var(--ink-dim); min-height: calc(2 * var(--lh));
82 display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; overflow: hidden;
83 }
84 & .repo-desc.mute { color: var(--ink-mute); font-style: italic; }
85 /* topics: small quiet chips, as on GitHub, on one line. The chips that do
86 not fit wrap out of sight; ermite.fit (ermite.js) puts a … chip after
87 the last one shown, the hidden topics in its title. */
88 & .repo-topics {
89 display: flex; flex-wrap: wrap; gap: 6px; height: 22px; overflow: hidden;
90 margin: 0; padding: 0; list-style: none;
91 }
92 & .repo-topics li {
93 flex: none; padding: 1px 9px; border: 1px solid var(--border-strong); border-radius: 999px;
94 font-family: var(--mono); font-size: 12px; line-height: 18px; color: var(--teal); background: var(--bg-raised);
95 }
96 & .repo-topics li[hidden] { display: none; }
97 & .repo-topics li.more { padding: 0; }
98 & .repo-topics li.more button {
99 padding: 1px 7px; border: 0; border-radius: 999px; background: none; cursor: pointer;
100 font: inherit; line-height: 18px; color: var(--ink-mute);
101 }
102 & .repo-topics li.more button:hover { color: var(--accent); }
103 & .repo-topics[data-open] { height: auto; }
104 /* the pulse: the commits per day over 90 days, smoothed, under the text */
105 & .repo-pulse { margin: auto 0 0; padding-top: var(--s2); }
106 /* as tall as a drawn pulse: the padding comes on top of the ratio */
107 & div.repo-pulse { aspect-ratio: 600 / 64; box-sizing: content-box; }
108 & .repo-pulse svg { display: block; width: 100%; height: auto; overflow: visible; }
109 & .pulse-grid { fill: none; stroke: var(--border); stroke-width: 1; stroke-dasharray: 2 4; }
110 & .pulse-stop-top { stop-color: var(--teal); stop-opacity: .32; }
111 & .pulse-stop-bottom { stop-color: var(--teal); stop-opacity: 0; }
112 & .pulse-line { fill: none; stroke: var(--teal); stroke-width: 1.75; stroke-linecap: round; stroke-linejoin: round; }
113 & .pulse-dot { fill: var(--teal); stroke: var(--bg-surface); stroke-width: 1.5; }
114 & .repo:hover .pulse-dot, & .repo[data-cursor] .pulse-dot { stroke: var(--bg-raised); }
115 & .repo-pulse:not([data-live]) .pulse-line { stroke: var(--ink-mute); }
116 & .repo-pulse:not([data-live]) .pulse-dot { fill: var(--ink-mute); }
117 & .repo-pulse:not([data-live]) .pulse-stop-top { stop-color: var(--ink-mute); stop-opacity: .18; }
118 /* the right column: figures, then the CI state, over the commits */
119 & .repo-head { grid-area: head; display: flex; align-items: flex-start; justify-content: space-between; gap: var(--s3) var(--s5); min-width: 0; }
120 /* the figures: a small label over a mono value, side by side */
121 & .repo-stats { display: flex; flex-wrap: wrap; gap: var(--s2) var(--s5); margin: 0; min-width: 0; }
122 & .repo-stats div { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
123 & .repo-stats dt { font-size: 11px; line-height: 14px; letter-spacing: .04em; text-transform: uppercase; color: var(--ink-mute); white-space: nowrap; }
124 & .repo-stats dd { margin: 0; font-family: var(--mono); font-size: 15px; line-height: 20px; color: var(--ink-hi); font-variant-numeric: tabular-nums; white-space: nowrap; }
125 & .repo-stats .rs-tag dd { max-width: 18ch; overflow: hidden; text-overflow: ellipsis; }
126 & .repo-stats dd.mute { color: var(--ink-mute); }
127 & .repo-stats dd a { color: inherit; text-decoration: none; }
128 & .repo-stats dd a:hover { text-decoration: underline; }
129 & .repo-ci {
130 flex: none; display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px;
131 font-size: var(--fs-s); color: var(--ink-dim); text-decoration: none; white-space: nowrap;
132 }
133 & .repo-ci:hover { color: var(--ink-hi); text-decoration: underline; }
134 & .repo-ci .ck { margin: 0; }
135 & .repo-commits {
136 grid-area: recent; min-width: 0; margin: 0; padding: 0; list-style: none; font-size: var(--fs-s);
137 min-height: calc(5 * (var(--lh) + 4px)); /* five rows, full or not */
138 }
139 & p.repo-commits { font-style: italic; }
140 & .repo-commits li { display: flex; align-items: baseline; gap: var(--s2); min-width: 0; padding: 2px 0; }
141 & .rc-sha { flex: none; font-family: var(--mono); color: var(--ink-dim); text-decoration: none; }
142 & .rc-sha:hover { color: var(--ink-hi); text-decoration: underline; }
143 & .rc-subject { flex: 1 1 0; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--ink-hi); }
144 & .repo-commits time { flex: none; white-space: nowrap; }
145 /* a full list goes on: its last commit fades */
146 & .repo-commits[data-full] li:last-child { opacity: .65; }
147 @media (max-width: 720px) {
148 & .repo { grid-template-columns: minmax(0, 1fr); grid-template-areas: "main" "head" "recent"; grid-template-rows: none; }
149 & .repo-head { flex-wrap: wrap; }
150 & .repo-commits { min-height: 0; }
151 }
152 }
153 /* Home layouts. The server renders "cards" (the rules above); a script
154 sets .home[data-layout] from the stored choice. The rows carry no markup
155 of their own for a layout, so a row swapped in by a live update fits. */
156 .home .layout-switch { display: flex; align-items: center; gap: 2px; }
157 .home .layout-switch .btn {
158 width: 28px; height: 28px; color: var(--ink-dim); background: none; border-color: transparent; border-radius: var(--r-s);
159 &:hover { color: var(--ink-hi); background: var(--bg-raised); }
160 &[aria-pressed="true"] { color: var(--ink-hi); background: var(--bg-raised); border-color: var(--border-strong); }
161 }
162 .home .home-bar .sort { margin-left: auto; }
163
164 /* split: two denser cards side by side. Name and CI state on top, the
165 description, the pulse as a band from edge to edge, the figures in one
166 row and the two newest commits. One column on a narrow screen. */
167 .home[data-layout="split"] {
168 & .repos { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s4); }
169 & .repo {
170 grid-template-columns: minmax(0, 1fr) auto; column-gap: var(--s3); row-gap: 0;
171 grid-template-areas: "name ci" "desc desc" "topics topics" "pulse pulse" "stats stats" "recent recent";
172 grid-template-rows: auto 1fr auto auto auto auto;
173 overflow: hidden;
174 }
175 & .repo-main, & .repo-head { display: contents; }
176 & .repo-name { grid-area: name; align-self: center; font-size: 16px; }
177 & .repo-ci { grid-area: ci; align-self: center; }
178 & .repo-desc { grid-area: desc; margin-top: var(--s2); }
179 & .repo-topics { grid-area: topics; margin-top: var(--s2); }
180 & .repo-topics:empty { display: none; }
181 & .repo-pulse { grid-area: pulse; margin: var(--s3) calc(-1 * var(--s4)) 0; padding: 0; border-block: 1px solid var(--border); }
182 /* one row while it fits; a narrow card wraps the figures instead of
183 letting them overlap */
184 & .repo-stats { grid-area: stats; flex-wrap: wrap; justify-content: space-between; gap: var(--s2) var(--s3); margin-top: var(--s3); }
185 & .repo-stats div { flex: none; }
186 & .repo-stats dd { font-size: var(--fs); }
187 & .repo-stats .rs-tag dd { max-width: 12ch; }
188 & .repo-commits { grid-area: recent; min-height: calc(2 * (var(--lh) + 4px)); margin-top: var(--s2); }
189 & .repo-commits li:nth-child(n+3) { display: none; }
190 & .repo-commits[data-full] li:last-child { opacity: 1; }
191 & .repo-commits[data-full] li:nth-child(2) { opacity: .65; }
192 }
193 @media (max-width: 900px) {
194 .home[data-layout="split"] .repos { grid-template-columns: minmax(0, 1fr); }
195 }
196
197 /* compact: a line per repository. Name, description, figures, CI mark and
198 a small pulse sit in one grid row; the commits and topics are left out.
199 The rows share the columns of the list (subgrid): the name column is as
200 wide as the longest name, the figures line up, and the description takes
201 what is left. */
202 .home[data-layout="compact"] {
203 & .repos {
204 display: grid; grid-template-columns: fit-content(18rem) minmax(0, 1fr) auto auto 140px;
205 gap: var(--s2) var(--s4);
206 }
207 & .repo {
208 grid-column: 1 / -1; grid-template-columns: subgrid;
209 grid-template-areas: "name desc stats ci pulse";
210 /* normal: the row takes the gaps of the list */
211 grid-template-rows: none; column-gap: normal; row-gap: 0; align-items: center;
212 padding: var(--s2) var(--s4);
213 }
214 & .repo-main, & .repo-head { display: contents; }
215 & .repo-topics, & .repo-commits { display: none; }
216 /* min-width 0: a long name is cut, it does not widen the column past its cap */
217 & .repo-name { grid-area: name; min-width: 0; flex-wrap: nowrap; gap: var(--s2); font-size: var(--fs); line-height: 24px; overflow-wrap: normal; }
218 & .repo-name a { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
219 & .repo-name .pill { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
220 & .repo-name a.pill { max-width: 12ch; }
221 & .repo-desc {
222 grid-area: desc; display: block; min-height: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
223 font-size: var(--fs-s); color: var(--ink-mute);
224 }
225 /* figures inline, the value before its label; the label of the date and
226 of the tag is left to screen readers */
227 & .repo-stats { grid-area: stats; flex-wrap: nowrap; gap: var(--s3); overflow: hidden; }
228 & .repo-stats div { flex-direction: row; align-items: baseline; gap: 4px; flex: none; }
229 & .repo-stats dt { order: 1; font-size: var(--fs-xs); line-height: 16px; letter-spacing: 0; text-transform: none; }
230 & .repo-stats dd { font-size: var(--fs-xs); line-height: 16px; color: var(--ink-dim); }
231 & .repo-stats .rs-active dt, & .repo-stats .rs-tag dt {
232 position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%);
233 }
234 & .repo-stats .rs-tag dd { max-width: 10ch; }
235 /* the CI state is its mark; the word is in the tooltip and for screen readers */
236 & .repo-ci { grid-area: ci; justify-self: center; }
237 /* the pulse: the same curve, small, with a bigger dot to stay seen */
238 & .repo-pulse { grid-area: pulse; width: 100%; height: 100%; min-height: 18px; margin: 0; padding: 0; }
239 & div.repo-pulse { aspect-ratio: auto; box-sizing: border-box; }
240 & .repo-pulse svg { position: relative; top: 50%; transform: translateY(-50%); }
241 & .pulse-grid { display: none; }
242 & .pulse-dot { r: 9px; stroke: none; }
243 }
244 @media (max-width: 1100px) {
245 .home[data-layout="compact"] .repos { grid-template-columns: fit-content(14rem) minmax(0, 1fr) auto auto 96px; column-gap: var(--s3); }
246 .home[data-layout="compact"] .repo-stats .rs-tag { display: none; }
247 }
248 /* on a phone: name and CI state on a line, the description and the figures
249 under it; the pulse is dropped */
250 @media (max-width: 720px) {
251 .home[data-layout="compact"] .repo {
252 grid-template-columns: minmax(0, 1fr) auto; grid-template-areas: "name ci" "desc desc" "stats stats"; column-gap: var(--s3); row-gap: 2px;
253 }
254 .home[data-layout="compact"] .repo-pulse { display: none; }
255 .home[data-layout="compact"] .repo-stats .rs-tag { display: flex; }
256 }
257 .list.passkeys {
258 & .c-name { width: auto; font-weight: 600; }
259 & .c-when { width: 120px; font-size: var(--fs-s); }
260 & .c-sync { width: 80px; font-size: var(--fs-s); }
261 & .c-act { width: 110px; text-align: right; }
262 }
263 .passkey-add {
264 display: flex; flex-wrap: wrap; gap: var(--s2); align-items: center; margin-top: var(--s4);
265 & input {
266 flex: 1; min-width: 12ch; max-width: 320px; height: 32px; padding: 0 var(--s3);
267 background: var(--bg-surface); border: 1px solid var(--border-strong); border-radius: var(--r-s);
268 }
269 & input:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; border-color: transparent; }
270 & input::placeholder { color: var(--ink-mute); opacity: 1; }
271 }
272 .list.commits, .list.refs {
273 & .c-sha { width: 96px; }
274 & .c-who { width: 140px; }
275 & .c-when { width: 100px; }
276 }
277
278 /* filter: a search field above a list */
279 .filter {
280 position: relative; display: flex; max-width: 420px;
281 &::before {
282 content: ""; position: absolute; left: 11px; top: 10px; width: 16px; height: 16px; pointer-events: none;
283 background: var(--ink-mute); mask: url(icons/search.svg) center / contain no-repeat;
284 }
285 &::after {
286 content: "/"; position: absolute; right: 8px; top: 50%; transform: translateY(-50%);
287 font: 600 var(--fs-xs) / 18px var(--mono); color: var(--ink-mute); padding: 0 6px;
288 border: 1px solid var(--border-strong); border-bottom-width: 2px; border-radius: 4px; pointer-events: none;
289 }
290 & input {
291 flex: 1; min-width: 0; height: 36px; padding: 0 36px 0 34px;
292 background: var(--bg-surface); border: 1px solid var(--border-strong); border-radius: var(--r-s);
293 }
294 & input:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; border-color: transparent; }
295 & input::placeholder { color: var(--ink-mute); opacity: 1; }
296 &:focus-within::after { display: none; }
297 }
298
299 /* file finder: the results replace the tree while the field has a query */
300 #finder {
301 & #finder-status { padding: var(--s3) var(--s4) var(--s2); font-size: var(--fs-s); }
302 & #finder-list { padding: 0 var(--s2) var(--s2); }
303 }
304
305 /* the Files view: the finder on top, the tree in a card under it */
306 .tree-find {
307 display: flex; align-items: center; gap: var(--s4); margin-bottom: var(--s3);
308 & .filter { flex: 1; max-width: none; }
309 & .filter::before { top: 14px; left: 14px; }
310 & .filter::after { right: 12px; }
311 & .filter input { height: 44px; padding-left: 42px; font-size: var(--fs); background: var(--bg-surface); }
312 }
313 .tree-keys { flex: none; color: var(--ink-mute); font-size: var(--fs-xs); white-space: nowrap; }
314 .tree-card {
315 background: var(--bg-surface); border: 1px solid var(--border); border-radius: var(--r); overflow: hidden;
316 }
317 .tree-head {
318 display: flex; align-items: center; gap: var(--s3); min-height: 40px; padding: 0 var(--s4);
319 background: var(--bg-raised); border-bottom: 1px solid var(--border); font-size: var(--fs-s);
320 & .tree-path { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font: 700 var(--fs-s) / 1 var(--mono); color: var(--ink-hi); }
321 & .tree-up { flex: none; font: 600 var(--fs-s) / 1 var(--mono); color: var(--ink-dim); text-decoration: none; }
322 & .tree-up:hover { color: var(--ink-hi); text-decoration: underline; }
323 & .tree-count { flex: none; color: var(--ink-mute); }
324 }
325 .tree-card > :is(.lede, .note) { margin: var(--s3) var(--s4); }
326
327 /* The tree: one flat list, a row per entry, each as deep as --depth says
328 (files.js sets it on the rows it opens in place). A row draws, from the
329 left: a vertical guide per level above it, the branch tick to its node,
330 the node, its name; then size, last commit and date, which are secondary. */
331 ul.tree {
332 --indent: 22px; --rowh: 32px; --guide: var(--border-strong);
333 & > li {
334 display: grid; grid-template-columns: minmax(0, 1fr) 72px minmax(0, 1.2fr) 96px; align-items: center;
335 min-height: var(--rowh); padding-right: var(--s4); font-size: var(--fs-s);
336 }
337 & > li:hover, & > li[data-cursor] { background: var(--bg-raised); }
338 & > li[data-cursor] { outline: 1px solid var(--accent); outline-offset: -1px; }
339 & > li:focus-visible { outline-offset: -2px; border-radius: 0; }
340 & .t-main { display: flex; align-items: stretch; min-width: 0; min-height: var(--rowh); padding-left: var(--s3); }
341 & .t-twig {
342 flex: none; width: calc(var(--depth, 0) * var(--indent));
343 background: repeating-linear-gradient(90deg,
344 transparent 0 calc(var(--indent) / 2), var(--guide) calc(var(--indent) / 2) calc(var(--indent) / 2 + 1px),
345 transparent calc(var(--indent) / 2 + 1px) var(--indent));
346 }
347 & .t-node { position: relative; flex: none; width: var(--indent); }
348 & .t-node::before, & .t-node::after { content: ""; position: absolute; }
349 /* the branch tick, from the guide of the directory to this node */
350 & .t-node::before { top: 50%; left: calc(var(--indent) / -2); right: calc(50% + 6px); height: 1px; background: var(--guide); }
351 & > li[data-depth="0"] .t-node::before { display: none; }
352 /* an open directory continues its guide down, to its first entry */
353 & > li[aria-expanded="true"] .t-node {
354 background: linear-gradient(var(--guide), var(--guide)) center bottom / 1px calc(50% - 8px) no-repeat;
355 }
356 /* the node: a ring for a file, a chevron for a directory (it turns down
357 once open), a diamond for a link, a square for a submodule */
358 & .t-node::after {
359 top: 50%; left: 50%; width: 7px; height: 7px; margin: -4px 0 0 -4px;
360 background: var(--bg-surface); border: 1px solid var(--ink-mute); border-radius: 50%;
361 }
362 & > li:hover .t-node::after, & > li[data-cursor] .t-node::after { background: var(--bg-raised); border-color: var(--ink-hi); }
363 & > li[data-kind="dir"] .t-node::after {
364 width: 16px; height: 16px; margin: -8px 0 0 -8px; border: 0; border-radius: 0; background: var(--ink-dim);
365 mask: url(icons/chevron.svg) center / contain no-repeat; transform: rotate(-90deg);
366 }
367 & > li[data-kind="dir"][aria-expanded="true"] .t-node::after { transform: none; background: var(--accent); }
368 & > li[data-kind="link"] .t-node::after { border-radius: 1px; transform: rotate(45deg); }
369 & > li[data-kind="module"] .t-node::after { border-radius: 1px; }
370 & > li[aria-busy="true"] .t-node { opacity: .4; }
371 & .t-name {
372 align-self: center; min-width: 0; padding: 4px var(--s1); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
373 font-family: var(--mono); font-size: var(--fs); color: var(--ink); text-decoration: none; cursor: pointer;
374 }
375 & > li[data-kind="dir"] .t-name { font-weight: 700; color: var(--ink-hi); }
376 & > li[data-kind="link"] .t-name, & > li[data-kind="module"] .t-name { color: var(--ink-dim); cursor: default; }
377 & > li:hover .t-name, & > li[data-cursor] .t-name { text-decoration: underline; }
378 & .t-size { padding-right: var(--s4); text-align: right; font: var(--fs-xs) / 1 var(--mono); color: var(--ink-mute); font-variant-numeric: tabular-nums; }
379 & .t-sub { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--ink-dim); }
380 & .t-sub a { color: inherit; text-decoration: none; }
381 & .t-sub a:hover { color: var(--ink-hi); text-decoration: underline; }
382 & .t-when { text-align: right; white-space: nowrap; color: var(--ink-mute); font-size: var(--fs-xs); }
383 & > li.tree-more { grid-template-columns: minmax(0, 1fr); }
384 & > li.tree-more .t-name { font-size: var(--fs-s); font-weight: 400; color: var(--ink-dim); text-decoration: underline; }
385 & > li.tree-more .t-node::after { display: none; }
386 }
387 @media (max-width: 900px) {
388 ul.tree > li { grid-template-columns: minmax(0, 1fr) 64px 80px; }
389 ul.tree .t-sub { display: none; }
390 }
391 @media (max-width: 720px) {
392 .tree-keys { display: none; }
393 ul.tree { --indent: 18px; }
394 ul.tree > li { grid-template-columns: minmax(0, 1fr) 72px; }
395 ul.tree .t-size { display: none; }
396 }
397
398 /* section headings inside a page */
399 .rule-h {
400 display: flex; align-items: center; gap: var(--s2); margin-top: var(--s5);
401 font-size: var(--fs);
402 & > :first-child { color: var(--ink-hi); }
403 }
404 main > .rule-h:first-child { margin-top: 0; }
405
406 /* empty / 404 / error / sign in: one card in the middle */
407 .state {
408 max-width: 560px; margin: var(--s7) auto 0 !important; padding: var(--s6);
409 background: var(--bg-surface); border: 1px solid var(--border); border-radius: var(--r);
410 & .eyebrow { font: 700 var(--fs-s) / 1 var(--mono); color: var(--ink-mute); text-transform: uppercase; letter-spacing: .08em; margin-bottom: var(--s2); }
411 & h1 { margin-bottom: var(--s3); }
412 & p.dim { color: var(--ink-dim); }
413 & .state-act { margin-top: var(--s5); }
414 & .state-back { margin-top: var(--s5); display: flex; gap: var(--s4); font-size: var(--fs-s); }
415 & .clone { margin-top: var(--s4); }
416 }
417
418 /* copyable shell lines */
419 .clone {
420 display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--s2); min-width: 0;
421 & .clone-line {
422 min-width: 0;
423 display: flex; gap: var(--s2); align-items: center; padding: var(--s1) var(--s1) var(--s1) var(--s3);
424 background: var(--bg-inset); border: 1px solid var(--border); border-radius: var(--r-s);
425 font-size: var(--fs-code);
426 }
427 & .clone-line > .mute { user-select: none; font-family: var(--mono); }
428 & code { flex: 1; min-width: 0; overflow-x: auto; white-space: pre; scrollbar-width: none; }
429 & .copy { height: 26px; padding: 0 var(--s2); font-size: var(--fs-xs); }
430 }
431}