Oregami
Repositories/oxedyne/daimond

oxedyne/daimond/www/css/files.css

29.9 KiB, 1 run

created by r2519314175:1097, which is this file's identity for as long as the history lasts, whatever it is later renamed to

download · who wrote it · its history

1/* ── Daimond workspace file browser (WS-F) — docked panel ── */
2.files-card {
3 flex: 1; min-height: 0;
4 display: flex; flex-direction: column;
5 background: var(--bg-secondary);
6 border: 1px solid var(--border);
7 border-radius: 10px;
8 overflow: hidden;
9 font-size: var(--fs-sm);
10}
11
12.files-actions { display: flex; gap: 4px; align-items: center; }
13
14/* Workspace root mode: OPFS sandbox vs an open real folder (FSA). */
15/* The chips total 418px, and the Workspace sheet is 380px wide on a phone, so
16 the row wraps -- and the second row was then clipped by the foot of the sheet.
17 It scrolls sideways below the breakpoint instead: a control on a second row
18 the sheet cuts off is one nobody can reach, where a control reached by a swipe
19 at least exists. 10px is the dock's one body inset. */
20.files-mode {
21 display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
22 padding: 6px 10px;
23 border-bottom: 1px solid var(--border);
24 flex: none;
25}
26@media (max-width: 760px) {
27 .files-mode {
28 flex-wrap: nowrap;
29 overflow-x: auto;
30 scrollbar-width: none;
31 }
32 .files-mode::-webkit-scrollbar { display: none; }
33 .files-mode > * { flex: 0 0 auto; }
34}
35.files-mode-chip {
36 display: inline-flex; align-items: center; gap: 4px;
37 padding: 2px 8px; border-radius: 999px;
38 font-size: var(--fs-2xs); font-weight: 600;
39 border: 1px solid var(--border-2);
40}
41/* The .ic default of 16px belongs to toolbar buttons; on a --fs-2xs chip the
42 glyph must sit at cap height. */
43.files-mode-chip .ic { width: 13px; height: 13px; }
44/* A chip states where things are. Two of them name the workspace root and are
45 mutually exclusive; the third names a residency and is additive -- so the
46 lit one is not "the selected tab", it is simply what is true. */
47.files-mode-chip { color: var(--text-secondary); background: var(--bg-tertiary); }
48.files-mode-chip.active { color: var(--accent-text); background: var(--accent-soft); border-color: var(--accent); }
49.files-mode-chip.cloud { color: var(--accent-text); background: var(--accent-soft); border-color: var(--accent); }
50/* "Nothing in cloud storage yet" -- the chip is quieter because there is nothing
51 behind it, NOT because it is switched off: it keeps `role="button"`,
52 `tabindex="0"` and its click, and its title is a sentence the reader is meant
53 to read. `opacity: 0.55` over the whole chip put those words at 2.22:1 in
54 Light and Lollypop, 2.26 in Mist, 2.84 in Dark -- under even the audit's
55 invisible-text alarm, and nowhere near the 4.5 an 11px label asks for.
56 Opacity cannot be the answer: measured across the eleven palettes it takes
57 0.93 before the words clear 4.5, and 0.93 is not a ghost. So the fade moves to
58 the GLYPH, which carries no words, and the label drops to the app's own quiet
59 rung instead -- 4.51 (Light) to 5.37 (Dusk), and still a rung below the two
60 chips beside it. The chip reads as quiet; the sentence stays readable.
61
62 The `:not()` guard is not decoration: `.ghost` is also put on the Machine chip
63 when no folder can be picked, and that chip can be the LIT one at the same
64 time. The old rule only set an opacity, so the lit accent survived under it; a
65 colour would have taken it away. */
66.files-mode-chip.ghost:not(.active):not(.cloud) { color: var(--text-muted); }
67.files-mode-chip.ghost .ic { opacity: 0.55; }
68.files-mode-chip.act { cursor: pointer; }
69.files-mode-chip.act:hover { background: var(--bg-hover); border-color: var(--accent); }
70.files-mode-chip.act:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
71.files-mode-btn {
72 padding: 2px 8px; border-radius: 6px;
73 font-size: var(--fs-2xs); cursor: pointer;
74 color: var(--text-secondary);
75 background: var(--bg-tertiary); border: 1px solid var(--border-strong);
76}
77.files-mode-btn:hover { background: var(--bg-hover); }
78.files-mode-btn.accent {
79 color: var(--on-fill, #fff); background: var(--accent); border-color: var(--accent);
80}
81.files-mode-btn.accent:hover { background: var(--accent-hover); }
82.files-mode-msg {
83 flex-basis: 100%; font-size: var(--fs-2xs); color: var(--text-secondary);
84}
85.files-mode-msg.err { color: var(--accent-text); }
86
87/* ── Whose files: the workspace, or one Diamond's ──────────────
88 A second row under the mode row, and deliberately not part of it: those chips
89 name a PLACE the files are (the sandbox, a folder on this disk, cloud
90 storage), these name WHOSE files are being shown. Hidden outright when no
91 Diamond is open, because a switch with one position is furniture. */
92.files-scope {
93 display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
94 padding: 6px 10px;
95 border-bottom: 1px solid var(--border);
96 flex: none;
97}
98.files-scope-chip {
99 display: inline-flex; align-items: center; gap: 4px;
100 padding: 2px 8px; border-radius: 999px;
101 font-size: var(--fs-2xs); font-weight: 600;
102 color: var(--text-secondary); background: var(--bg-tertiary);
103 border: 1px solid var(--border-strong); cursor: pointer;
104}
105.files-scope-chip:hover { background: var(--bg-hover); border-color: var(--accent); }
106.files-scope-chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
107/* The lit one is what the tree below is showing, so it has to read as a state
108 and not as an offer -- the two trees can differ by a whole workspace. */
109.files-scope-chip.active {
110 color: var(--accent-text); background: var(--accent-soft); border-color: var(--accent);
111}
112
113/* The word in front of the chips. They pick a TREE and change no permission,
114 and without a label they read as a setting for how much the daimon may
115 touch -- which is exactly how they were read. */
116.files-scope-label, .files-reach-label {
117 font-size: var(--fs-2xs); font-weight: 600; color: var(--text-secondary);
118}
119
120/* ── Where this Diamond's daimon may actually work ─────────────
121 Under the chips because it answers the question the chips were being asked:
122 what may be written, and where a search that names no path will look. Drawn
123 in BOTH trees and built from the same `Files.bounds` that composes the fence,
124 so it cannot come to disagree with what a turn is confined to. Nothing here
125 waits for a hover: a fence nobody can see is a fence nobody trusts. */
126.files-reach {
127 display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
128 padding: 6px 10px;
129 border-bottom: 1px solid var(--border);
130 flex: none;
131}
132/* A place inside the fence. The accent edge is the app's one mark for "this
133 grants something", worn by the workspace rail in the chat footer and by a
134 granted toolchain beside it. */
135.files-reach-chip {
136 display: inline-flex; align-items: center;
137 padding: 2px 8px; border-radius: 999px;
138 font-size: var(--fs-2xs); font-weight: 600;
139 max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
140 color: var(--accent-text); background: var(--accent-soft);
141 border: 1px solid var(--accent);
142}
143/* Consulted, not worked in. It reads as a place and not as a grant, so it drops
144 the accent and keeps the ordinary control edge. */
145.files-reach-chip.ro {
146 color: var(--text-secondary); background: var(--bg-tertiary);
147 border-color: var(--border-strong);
148}
149.files-reach-says {
150 flex: 1 1 100%;
151 font-size: var(--fs-3xs-2xs); color: var(--text-secondary); line-height: 1.5;
152}
153/* The state that most needs saying, and the one a person is most likely to be
154 in without knowing: the daimon can write nowhere of theirs. */
155.files-reach-says.none { color: var(--accent-text); }
156/* One press, from the folder already on screen. A button and not a chip: the
157 row beside it reports, and this changes something. */
158.files-mark-here {
159 display: inline-flex; align-items: center;
160 padding: 2px 9px; border-radius: 999px;
161 font-size: var(--fs-2xs); font-weight: 600;
162 color: var(--text-secondary); background: var(--bg-tertiary);
163 border: 1px dashed var(--border-strong); cursor: pointer;
164}
165.files-mark-here:hover { color: var(--accent-text); border-color: var(--accent); }
166.files-mark-here:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
167
168/* ── What a Diamond may reach that is not a folder ─────────────
169 A toolchain grant. It sits under the scope row because it answers the same
170 question the row above and the tree below answer -- what this Diamond may
171 open -- and it is the one answer that is not a path in the workspace: a
172 compiler lives on the machine, outside anything the user granted as a folder.
173 Off unless the user says so, and a daimon never chooses one. */
174.files-kits {
175 display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
176 padding: 6px 10px;
177 border-bottom: 1px solid var(--border);
178 flex: none;
179}
180.files-kits-label {
181 font-size: var(--fs-2xs); font-weight: 600;
182 color: var(--text-secondary);
183}
184.files-kit-chip {
185 display: inline-flex; align-items: center;
186 padding: 2px 8px; border-radius: 999px;
187 font-size: var(--fs-2xs); font-weight: 600;
188 color: var(--text-secondary); background: var(--bg-tertiary);
189 border: 1px solid var(--border-strong); cursor: pointer;
190}
191.files-kit-chip:hover { background: var(--bg-hover); border-color: var(--accent); }
192.files-kit-chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
193/* A granted toolchain is a PERMISSION in force, so it reads as a state. */
194.files-kit-chip.active {
195 color: var(--accent-text); background: var(--accent-soft); border-color: var(--accent);
196}
197/* Said out loud rather than left to be inferred from four unlit chips: "none"
198 is the ordinary state and the safe one, and a person should be able to see
199 that it is deliberate. */
200.files-kits-none {
201 flex: 1 1 100%;
202 font-size: var(--fs-3xs-2xs); color: var(--text-secondary);
203}
204
205/* The step out of this folder, at the top of the listing where every file
206 manager has put it. Dimmer than a real entry, because it is navigation and
207 not a thing in the folder, and it carries none of the controls a real row
208 does. It replaced a button in the panel's header on 2026-08-24. */
209.files-row.files-up .files-name { color: var(--text-secondary); }
210
211/* A row for something attached from elsewhere in the workspace. It is a
212 pointer, not a copy: the file lives where it always did, and every other
213 Diamond holding it goes on holding it. */
214.files-row.attached .files-name { font-style: italic; }
215.files-badge {
216 flex: none; font-size: var(--fs-3xs-2xs);
217 color: var(--text-secondary);
218 border: 1px solid var(--border-2); border-radius: 999px;
219 padding: 0 5px; line-height: 1.6;
220}
221.files-badge.files-elsewhere { border: none; padding: 0 2px; font-size: var(--fs-2xs); }
222/* Read-only says what a daimon may do with it, so it stays visible: a hover-only
223 badge is a permission nobody reads. */
224.files-badge.files-ro { white-space: nowrap; }
225/* ◈ on a row: put this folder in the open Diamond's workspace, or take it out.
226 Like the pin, the LIT state is standing information and must not hide until
227 the pointer finds it. */
228.files-res.files-hold.on { visibility: visible; color: var(--accent-text); }
229/* The hint under an empty Diamond workspace, which sits below real rows rather
230 than replacing them. */
231.files-dws-hint { text-align: left; font-size: var(--fs-2xs); line-height: 1.5; }
232
233/* A folder row with a Diamond in focus: the paperclip is the control that puts
234 that folder inside the write fence, so it does not wait for a hover. The same
235 rule `.files-hold.on` already follows, for the same reason. */
236.attach-btn.mark { visibility: visible; }
237
238/* The paperclip: "Attach to current focus" (ATTACH_CONTRACT.md §4), on a
239 folder row, a file row, and the Doc header. Its own class rather than a
240 `.files-res` modifier for the reason `.files-pin` already has one -- '📎' is
241 an emoji, drawn at full size in a colour font, so the size bump `.files-res`
242 gives its thin dingbats would only inflate it. Lit and away are standing
243 information, exactly as the pin's `.on` is, and must not hide until the
244 pointer finds them. */
245.attach-btn {
246 background: transparent; border: none; color: var(--text-secondary);
247 cursor: pointer; padding: 0 3px; visibility: hidden; font-size: var(--fs-sm);
248}
249.files-row:hover .attach-btn { visibility: visible; }
250.attach-btn:hover { color: var(--accent-text); }
251.attach-btn.on { visibility: visible; color: var(--accent-text); }
252.attach-btn.away { visibility: visible; color: var(--text-muted); }
253
254/* Touch. The scope chips take the same 24px floor as the mode row's, and the ◈
255 comes out of hiding: there is no hover on a phone, so a control that waits for
256 one cannot be reached at all. */
257@media (max-width: 760px), (pointer: coarse) {
258 .files-scope-chip { min-height: 24px; padding-top: 4px; padding-bottom: 4px; }
259 .files-mark-here { min-height: 24px; padding-top: 4px; padding-bottom: 4px; }
260 .files-row .files-hold { visibility: visible; padding: 4px 6px; }
261 .files-row .attach-btn { visibility: visible; padding: 4px 6px; }
262}
263
264.files-path {
265 padding: 6px 10px; color: var(--text-secondary);
266 font-family: var(--font-mono); font-size: var(--fs-2xs);
267 border-bottom: 1px solid var(--border);
268 flex: none;
269 display: flex; flex-wrap: wrap; align-items: center;
270}
271
272/* One folder in the path line, or the whole trail's root. A plain button reset
273 rather than a link -- there is nowhere to navigate TO, only a tree to relist --
274 so it takes the surrounding text colour and only the current, disabled one
275 (the folder already on screen) reads differently. */
276.path-crumb {
277 background: none; border: none; padding: 0; margin: 0;
278 font: inherit; color: inherit; cursor: pointer;
279 border-radius: 3px;
280}
281.path-crumb:hover:not(:disabled) { color: var(--text-primary); text-decoration: underline; }
282.path-crumb:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
283.path-crumb:disabled { color: var(--text-primary); cursor: default; font-weight: 600; }
284.path-sep { margin: 0 3px; opacity: 0.55; }
285
286/* Residency controls on a row: fetch, pin, free up space. Like .files-del they
287 stay out of the way until the row is under the pointer -- except a pin, which
288 is a standing state and must be visible without hunting for it.
289 The pin is its OWN class rather than a `.files-res` modifier, because the two
290 want different type sizes and a shared class can only carry one. Everything
291 else about them is identical, so the shape is stated once for both. */
292.files-res, .files-pin {
293 background: transparent; border: none; color: var(--text-secondary);
294 cursor: pointer; padding: 0 3px; visibility: hidden;
295}
296/* '⤓', '⤒' and '◈' are dingbats drawn well inside their em box -- the same
297 reason `.files-ren` needs a size of its own. At the 13px they had, they
298 carried visibly less ink than the 15px '×' on the same row and read as specks
299 rather than as controls. A step ABOVE the cross, not level with it, because
300 the cross fills its box and these do not. */
301.files-res { font-size: var(--fs-xl); }
302/* '📌' is an emoji: the platform draws it at full size in a colour font, so it
303 was never the undersized one and the bump above would only have inflated it. */
304.files-pin { font-size: var(--fs-sm); }
305.files-row:hover .files-res,
306.files-row:hover .files-pin { visibility: visible; }
307.files-res:hover, .files-pin:hover { color: var(--accent-text); }
308.files-pin.on { visibility: visible; opacity: 0.9; }
309/* A cloud row is the user's file, safe, and simply not here at the moment --
310 so it is quieter than a held file, never struck through or greyed to junk. */
311.files-row.cloud .files-name { color: var(--text-secondary); }
312.files-row.cloud .files-size { font-style: italic; }
313
314.files-tree { flex: 1; overflow-y: auto; padding: 4px 0; }
315.files-empty { padding: 16px; text-align: center; color: var(--text-secondary); }
316
317.files-row {
318 display: flex; align-items: center; gap: 8px;
319 padding: 5px 10px; cursor: pointer;
320}
321.files-row:hover { background: var(--bg-hover); }
322.files-name { flex: 1; color: var(--text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
323/* A folder is named in the accent, and the accent has TWO tokens because it does
324 two jobs. `--accent` is the FILL and the EDGE -- a button's ground, a border,
325 a focus ring -- and SC 1.4.11 asks 3.0 of it. `--accent-text` is the accent as
326 INK, which SC 1.4.3 asks 4.5 of, and every palette carries it a rung further
327 from its own surfaces for exactly that reason.
328 This rule, and the three above it, reached for `--accent`. On the four dark
329 palettes that is a mid-toned accent on a near-black panel: Midnight measured
330 4.19:1 for a folder name on `--bg-secondary` and Amber 4.35, both under the
331 floor for 13px. Nothing about the token's VALUE was wrong -- 4.19 is a
332 perfectly good ring -- so lifting `--accent` would have paled every accent
333 fill in the app to fix four lines of text in one panel. The wrong token was
334 named, and naming the right one moves nothing outside these four rules.
335 Against all four surfaces of its own palette -- primary, secondary, tertiary
336 and hover -- `--accent-text` reads 6.04-10.56 across the dark band and
337 4.98-9.38 across the light. */
338.files-row.dir .files-name { color: var(--accent-text); }
339.files-size { color: var(--text-secondary); font-size: var(--fs-2xs); font-family: var(--font-mono); }
340.files-del {
341 background: transparent; border: none; color: var(--text-secondary);
342 cursor: pointer; font-size: var(--fs-lg); padding: 0 4px; visibility: hidden;
343}
344.files-row:hover .files-del { visibility: visible; }
345.files-del:hover { color: var(--danger); }
346/* A control that waits for a hover is a control the keyboard cannot see: tabbing
347 to the cross moved the focus to something not on the screen, so the row's
348 controls come out when anything inside the row takes focus, exactly as the
349 touch rule above brings the ◈ out where there is no pointer at all.
350 Appearing is not enough on its own -- `verify_a11y_keyboard` compares the
351 focused element's own ink, and rightly, since a control that merely appears
352 has not said WHICH of the three now holds the focus. So each draws the ring.
353 `:focus`, not `:focus-visible`: these are reached by Tab, but the check
354 focuses them from script, and a ring that depends on the browser's guess at
355 the user's modality is a ring that is sometimes not there. */
356.files-row:focus-within .files-del,
357.files-row:focus-within .files-res,
358.files-row:focus-within .files-pin { visibility: visible; }
359.files-del:focus,
360.files-res:focus,
361.files-pin:focus { outline: 2px solid var(--accent); outline-offset: 1px; }
362/* The row itself is now a control (`sysRowAsButton`), so it says so as well.
363 Inset, because a row is flush with the panel and an outset ring would be
364 clipped by the tree's own scroller -- which the desktop sweep already reports
365 for the Diamond rail.
366 `:focus-visible` here, where the three buttons above keep `:focus`: a row is
367 opened by pointer far more often than by Tab, so a bare `:focus` left a ring
368 standing on the row of every file anyone clicked. Nothing is lost to the
369 keyboard -- `verify_a11y_keyboard` presses Tab before it focuses anything, so
370 modality is established and a script focus matches this too. */
371.files-row:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
372/* The rename pencil borrows `.files-del` for its shape and needs a size of its
373 own: '✎' is drawn well inside its em box where '×' fills it, so at the same
374 nominal 15px the pencil carried about half the ink of the cross beside it and
375 looked like a fleck. It is not a delete either, so it keeps the neutral
376 hover. */
377.files-ren { font-size: var(--fs-2xl); }
378.files-ren:hover { color: var(--text-primary); }
379
380/* The file VIEW now lives in the Doc panel (see app.css, .doc-view): a document
381 you have opened is something you are attending to, and belongs on the stage.
382 The inner .files-view-* rules below still apply -- they style the view itself,
383 wherever it is rendered. */
384.files-view-head {
385 display: flex; align-items: center; justify-content: space-between; gap: 8px;
386 padding: 7px 10px; border-bottom: 1px solid var(--border); flex: none; flex-wrap: wrap;
387}
388.files-view-name {
389 font-family: var(--font-mono); font-size: var(--fs-xs);
390 color: var(--text-primary); word-break: break-all;
391}
392/* A fixed HEIGHT, not padding alone: ◈ below was set in a larger font than its
393 neighbours for legibility, and with only top/bottom padding to hold it that
394 grew the button by the same fraction as the glyph, so the row read as four
395 buttons of three different heights. Centring a variable-size label inside a
396 box of one fixed height is what keeps the row level regardless of which
397 button's font changes next. */
398.files-btn {
399 background: transparent; border: 1px solid var(--border-strong);
400 color: var(--text-secondary); border-radius: 6px;
401 height: 24px; padding: 0 8px; cursor: pointer; font-size: var(--fs-xs);
402 display: inline-flex; align-items: center; line-height: 1;
403}
404/* The paperclip that attaches the open file to the focus on screen. Always
405 visible here -- `.attach-btn`'s hover-hide is for a row in a list, and this
406 is a header button among header buttons, none of which hide. Lit when it is
407 attached, so the button reports the state rather than only offering the
408 action -- it is the only place in the Files panel that says whether this
409 file is part of a pursuit. Drawn with an INSET ring: the strip it sits in
410 scrolls, and a ring outside the border box is one an overflow rule is
411 entitled to slice off. */
412.files-btn[data-act="attach"] { visibility: visible; font-size: var(--fs-sm); padding: 0 7px; }
413.files-btn[data-act="attach"].on {
414 color: var(--accent-text);
415 border-color: var(--accent);
416 box-shadow: inset 0 0 0 1px var(--accent);
417}
418.files-btn[data-act="attach"].away { color: var(--text-muted); }
419.files-btn:hover { color: var(--text-primary); border-color: var(--accent); }
420.files-btn.active { color: var(--accent); border-color: var(--accent); }
421.files-btn:disabled { opacity: 0.6; cursor: default; }
422
423/* Typst compile status line and inline PDF pane. */
424.files-view-msg {
425 flex: none; padding: 7px 10px; border-bottom: 1px solid var(--border);
426 font-family: var(--font-mono); font-size: var(--fs-xs);
427 color: var(--text-secondary); word-break: break-word; white-space: pre-wrap;
428}
429.files-view-msg.err { color: var(--danger, #e5484d); }
430
431/* The box the file VIEWER draws into, and it had no rule at all.
432 Everything inside it is written to stretch -- `.fileview`, `.fv-body` and each
433 tier are all `flex: 1` -- but a flex item with no rule of its own is
434 `flex: 0 1 auto`, so the whole chain collapsed to the content's own height.
435 For a PDF that is an <embed>'s default 150px, which is why a document opened
436 in a full-height panel sat in a letterbox with empty panel under it. */
437.files-view-viewer {
438 flex: 1; min-height: 0;
439 display: flex; flex-direction: column;
440 overflow: hidden;
441}
442
443.files-view-body {
444 flex: 1; margin: 0; padding: 12px 14px; overflow: auto;
445 white-space: pre-wrap; word-break: break-word;
446 font-family: var(--font-mono); font-size: var(--fs-sm);
447 color: var(--text-primary); background: var(--bg-primary);
448}
449
450/* Inline editor: swaps in for the <pre> while editing a file.
451 Wrapped, like the viewer beside it. `pre` meant every prose line ran off the
452 right edge of a ~400px panel and had to be scrolled to be read, one line at a
453 time; you cannot edit what you cannot see. Only the EDITOR wraps -- a
454 read-only <pre> presentation keeps whatever measure its content asks for. */
455.files-edit {
456 flex: 1; margin: 0; padding: 12px 14px; border: none; resize: none; outline: none;
457 white-space: pre-wrap; word-break: break-word; overflow: auto;
458 font-family: var(--font-mono); font-size: var(--fs-sm);
459 color: var(--text-primary); background: var(--bg-primary);
460}
461.files-edit:focus { box-shadow: inset 0 0 0 1px var(--accent); }
462
463/* File viewer line numbers.
464 A hanging indent, so a line that wraps continues past the gutter instead of
465 returning to the margin and reading as a new line. Invisible in a narrow
466 panel where little wrapped; obvious now the document has the stage. The two
467 measures must agree: the gutter is the .ln width plus its margin. */
468.files-view-body.with-lineno { padding-left: 14px; text-indent: 0; }
469/* The hanging indent lives on each line, not on the block: with pre-wrap the
470 whole file is one block, so an indent set there applies to its first line
471 only. The two measures must agree -- the gutter is the .ln width plus its
472 margin. */
473.files-view-body .lnrow { display: block; padding-left: 4em; text-indent: -4em; }
474/* `--text-muted` at full strength, and NOT `--text-secondary` behind an
475 `opacity: 0.55`. The alpha composited the gutter towards the page until it
476 sat at 2.28:1 on Light -- half the 4.5:1 floor, on 13px digits that are the
477 only way to say which line anything is on. Muted is the token for exactly
478 this: the quietest text in the app, and quiet is not the same as faint. */
479.files-view-body .ln {
480 display: inline-block; width: 3.2em; margin-right: 0.8em;
481 text-align: right; color: var(--text-muted);
482 user-select: none; -webkit-user-select: none;
483}
484
485/* ── Cloud storage view (the stage, not a popup) ──────────────
486 What is stored, what it is costing, and what is not on this device. With
487 accounts that are keys and have no address, there is nowhere to send a
488 statement, so this is the only honest place to say it. */
489.cloud-view { white-space: normal; font-family: inherit; }
490.cloud-intro { margin: 0 0 12px; color: var(--text-primary); line-height: 1.5; }
491.cloud-cap { margin: 6px 0 16px; color: var(--text-secondary); font-size: var(--fs-xs); line-height: 1.5; }
492
493.cloud-bar {
494 height: 8px; border-radius: 999px; overflow: hidden;
495 background: var(--bg-tertiary); border: 1px solid var(--border-2);
496}
497.cloud-bar span { display: block; height: 100%; background: var(--accent); }
498.cloud-bar span.hot { background: var(--danger, #e5484d); }
499
500.cloud-row {
501 display: flex; align-items: center; gap: 8px;
502 padding: 5px 0; border-bottom: 1px solid var(--border);
503 font-size: var(--fs-sm);
504}
505.cloud-path {
506 flex: 1; font-family: var(--font-mono); font-size: var(--fs-xs);
507 color: var(--text-primary); word-break: break-all;
508}
509.cloud-row.away .cloud-path { color: var(--text-secondary); }
510.cloud-size { color: var(--text-secondary); font-family: var(--font-mono); font-size: var(--fs-2xs); }
511.cloud-pin { font-size: var(--fs-2xs); }
512
513/* ── The rest of the folder, at the foot of the tree ─────────────
514 One shut row instead of a filter: the tree used to drop every dotfile
515 silently, which is a file its owner cannot see he has. The clutter it was
516 avoiding is answered by DESIGN — his own files stay at the top level, and
517 what he did not put there himself sits under one row with a count on it, so
518 he knows before pressing whether it costs a line or a screenful.
519 It borrows the System section's disclosure triangle deliberately: both rows
520 say the same thing about the same kind of contents, and two pictures for one
521 idea is how a panel stops being read. */
522.files-rest { border-top: 1px solid var(--border); margin-top: 8px; padding-top: 4px; }
523.files-rest-head {
524 display: flex; align-items: baseline; gap: 8px; width: 100%; text-align: left;
525 background: none; border: 0; cursor: pointer; padding: 6px 12px;
526 font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: 0.04em;
527 color: var(--text-muted);
528}
529.files-rest-head::before { content: '\25B8'; display: inline-block; }
530.files-rest-head[aria-expanded="true"]::before { content: '\25BE'; }
531.files-rest-label { flex: 1; }
532/* The count is a number about size, so it is set like every other one in this
533 panel — mono, quiet, and not shouted by the row's own uppercase. */
534.files-rest-count {
535 font-family: var(--font-mono); font-size: var(--fs-3xs);
536 text-transform: none; letter-spacing: 0;
537}
538.files-rest-head:hover { color: var(--text-primary); }
539.files-rest-head:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
540/* Indented, so an entry inside the row is plainly the row's and not the tree's
541 — the only difference between the two; the controls on it are the same ones. */
542.files-rest-body .files-row { padding-left: 26px; }
543
544/* ── The System section: Daimond's own store ─────────────────────
545 Its own section under the tree, not part of it. The two resolve against
546 different roots — the tree against whatever workspace is active, this always
547 against the browser's own store — and a single list quietly spanning both is
548 the confusion dev/ROOT_SEPARATION.md exists to prevent. Collapsed by default:
549 it answers a question ("where is DAIMOND.md?") rather than being where the
550 work is. */
551.sys-sec { border-top: 1px solid var(--border); margin-top: 8px; padding-top: 4px; }
552.sys-head {
553 display: block; width: 100%; text-align: left; background: none; border: 0;
554 cursor: pointer; padding: 6px 12px; font-size: var(--fs-2xs);
555 text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-muted);
556}
557/* A disclosure triangle the section draws itself, from `aria-expanded` -- the
558 one place the state is written, so the picture cannot disagree with what a
559 screen reader is told. */
560.sys-head::before { content: '\25B8 '; display: inline-block; }
561.sys-head[aria-expanded="true"]::before { content: '\25BE '; }
562.sys-head:hover { color: var(--text-primary); }
563.sys-head:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
564.sys-note { padding: 0 12px 6px; font-size: var(--fs-2xs); color: var(--text-muted); line-height: 1.5; }
565.sys-path {
566 padding: 0 12px 4px; font-family: var(--font-mono); font-size: var(--fs-3xs);
567 color: var(--text-muted);
568 display: flex; flex-wrap: wrap; align-items: center;
569}
570/* No × and no rename: this section is for reading and editing, never for
571 destroying. A row here is a Diamond's whole directory as often as not. */
572.sys-row .files-res, .sys-row .files-pin, .sys-row .files-del { display: none; }
573/* A row that can take focus has to SHOW it. These became focusable the hour they
574 were written -- they are `role="button"` -- and a focus ring is the other half
575 of that: a keyboard user who cannot see where they are is not being offered a
576 keyboard route, they are being offered a guess. */
577.sys-row:focus {
578 outline: 2px solid var(--accent); outline-offset: -2px;
579 background: var(--bg-secondary);
580}