Oregami
Repositories/oxedyne/daimond

oxedyne/daimond/www/css/improve.css

29.2 KiB, 44 runs

created by r2519314175:1105, 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/* improve.css — the Social panel.
2
3 Named for the file that draws most of it. The panel is `social` and holds
4 four chips; Notes and Proposals are everything below the "Writing a note"
5 heading, and Messages and People are the two shells at the top.
6
7 Everything here is stated in the palette's own variables, never in a fixed
8 colour, so the panel is legible on all eleven palettes and follows a reader
9 who changes theirs while it is on screen.
10
11 Two rules carried over from the panels beside it rather than reinvented:
12
13 A CONTROL BOUNDARY IS `--border-strong`, and a region boundary is
14 `--border-2`. `verify_theme` keeps that rule and its named failure is "a
15 new control written by copying an old one", which is exactly what the vote
16 buttons and the two acts under the box are.
17
18 A FACT THE USER IS MEANT TO ACT ON MAY NOT BE ELLIPSISED. A label may be
19 shortened, because its full text is in its `title`. The line saying what
20 goes with a note is the whole of what will leave this device, so it wraps
21 and is read in full, in a dock column of any width. trash.css records the
22 screenshot that taught this. */
23
24/* ── The head's two chips ───────────────────────────────────── */
25
26/* The same control the chat head carries (Guarded, Concise, Steps), so a chip
27 in this head reads as the chip it is rather than as a fourth kind of tab. */
28.imp-chips { display: flex; align-items: center; gap: 4px; margin-left: auto; }
29
30/* ── THE ONE PILL, stated once for the whole panel ────────────
31 Every button on this surface is one shape: a transparent 999px
32 pill on a --border-strong outline, secondary ink, going to the
33 accent on hover. It was eight near-identical declarations that
34 had drifted apart on padding (8/9/10/11px) and line-height
35 (1.6/1.7/1.8) for a control the eye reads as one -- exactly the
36 "new control written by copying an old one" that verify_theme's
37 --border-strong rule is written against. A control that wants
38 more presence takes `.on` (filled) or the primary size below,
39 never its own padding.
40
41 `#panel-social .pair-btn` joins the list so the People view --
42 and the trust flows drawn into it -- wears the panel's pill and
43 not pairing.js's coral modal button. The pairing/trust MODALS,
44 which draw outside this panel, keep their own button and are out
45 of this scope. */
46.imp-chip, .post-btn, .ref-act, .imp-note-send, .imp-note-copy,
47.imp-keep, .imp-send, .imp-vote, .shr-btn, #panel-social .pair-btn {
48 background: none; border: 1px solid var(--border-strong); border-radius: 999px;
49 color: var(--text-secondary); cursor: pointer;
50 font-size: var(--fs-3xs-2xs); line-height: 1.7; padding: 0 10px;
51}
52.imp-chip:hover:not(:disabled), .post-btn:hover:not(:disabled), .ref-act:hover:not(:disabled),
53.imp-note-send:hover:not(:disabled), .imp-note-copy:hover:not(:disabled),
54.imp-keep:hover:not(:disabled), .imp-send:hover:not(:disabled), .imp-vote:hover:not(:disabled),
55.shr-btn:hover:not(:disabled), #panel-social .pair-btn:hover:not(:disabled) {
56 border-color: var(--accent); color: var(--accent-text);
57}
58.imp-chip:disabled, .post-btn:disabled, .ref-act:disabled, .imp-note-send:disabled,
59.imp-note-copy:disabled, .imp-keep:disabled, .imp-send:disabled, .imp-vote:disabled,
60.shr-btn:disabled, #panel-social .pair-btn:disabled {
61 opacity: 0.5; cursor: default;
62}
63/* Filled while active: the chip whose view is showing, the vote that is cast.
64 One chip per thing, filled while that thing is open. */
65.imp-chip.on, .imp-vote.on {
66 background: var(--accent-soft); border-color: var(--accent); color: var(--accent-text);
67}
68/* PRIMARY: the panel's main verbs carry one step more size, and no more -- a
69 Send is not louder than the answer beside it, it is just the main act. Keep
70 and Send share it, and the People view's primary (`.pair-btn` that is not a
71 `.ghost`) with them, so "Add somebody" reads as this panel's primary rather
72 than pairing.js's. */
73.imp-keep, .imp-send, .post-send, #panel-social .pair-btn:not(.ghost) {
74 font-size: var(--fs-2xs-xs);
75}
76/* The information mark, between the chips and the closer.
77 `.icon-btn` is right for the paint -- the circled "i" brings its own ring and
78 must not be given a second one by a plate -- but its padding is a top BAR's
79 room, and 5px 7px made a 30px box beside a 22px closer. Squared to the
80 closer's box, which is what the row is measured on; the ink stays the 16px
81 `.ic` either way. */
82.imp-info { width: 22px; height: 22px; padding: 0; flex: none; }
83
84/* THE HEAD WRAPS, AND NO CHIP SHRINKS. Four chips beside a heading, an
85 information mark and a closer do not fit a 390px dock, and `.railhead` is a
86 flex ROW whose heading takes `flex: 1` — so the chips were squeezed below
87 their own labels and "Messages" and "People" were drawn one on top of the
88 other, each clipped, on a phone. Screenshot: dev/shots/social-phone.png,
89 2026-08-17.
90
91 Scoped to this panel, because every other head in the app carries one control
92 or two and none of them needs a second line. The heading keeps `flex: 1` so
93 it still pushes the chips right when there IS room, but may not shrink; the
94 chips take a line of their own when there is not. */
95#panel-social .railhead { flex-wrap: wrap; row-gap: 2px; }
96#panel-social .railhead > span:first-child { flex: 1 0 auto; }
97/* AND THE CROSS KEEPS THE CORNER WHILE THE CHIPS WRAP. `order` decides which
98 items a flex line takes, so the heading and the head's controls make the
99 first line and the chips -- given a whole line by `flex-basis: 100%` -- make
100 the second. Without it the closer wrapped along with the chips and finished
101 248px in from the panel's right edge and 47px down, which is the one panel in
102 the app whose cross was not in its top-right corner. */
103#panel-social .railhead > .railhead-acts { order: 1; display: flex; align-items: center; gap: 4px; flex: none; }
104#panel-social .imp-chips { order: 2; flex-basis: 100%; }
105.imp-chips { flex-wrap: wrap; row-gap: 3px; min-width: 0; }
106/* A chip is its label. Below that width it is not a shorter chip, it is an
107 unreadable one — the same rule this file keeps for the line under the note
108 box, one level down. */
109.imp-chip { flex: none; white-space: nowrap; }
110
111.imp-view { display: flex; flex-direction: column; min-height: 0; flex: 1; }
112.imp-view[hidden] { display: none; }
113
114/* ── Messages and People ────────────────────────────────────── */
115
116/* What each of the two shells says while it is a shell. `.rail-note` is the
117 app's own "there is nothing here" paragraph, used unchanged so this reads as
118 the same kind of sentence as "No chats yet." It is HIDDEN by the lane that
119 fills the list, not removed, so an emptied list gets its sentence back. */
120.imp-off { padding: 12px 10px; }
121.imp-off[hidden] { display: none; }
122
123.imp-list { flex: 1; min-height: 0; overflow-y: auto; padding: 2px 0 8px; }
124
125/* ── A private message ──────────────────────────────────────── */
126
127/* js/post.js draws this and owns no stylesheet, so it is dressed here, beside
128 the panel it lives in. Everything is in the palette's own variables, as the
129 top of this file requires.
130
131 THE ONE RULE THAT IS NOT DECORATION: a message carries NO OFFICIAL SHAPE.
132 There is no seal, no badge, no accent-coloured frame and no icon on any row,
133 because the only thing that could earn one is a verified signature and this
134 panel draws every message the same. A stranger's first message and the
135 operator's fifth look alike, and that is deliberate -- an app that dresses
136 one of them up is an app somebody can dress themselves up in. */
137
138.post-tray, .post-list, .post-notices { padding: 2px 10px; }
139.post-tray h3 {
140 margin: 8px 0 4px; font-size: var(--fs-3xs); letter-spacing: 0.05em;
141 text-transform: uppercase; color: var(--text-muted); font-weight: 600;
142}
143
144.post-msg, .post-req, .post-notice {
145 padding: 8px 0; border-bottom: 1px solid var(--border);
146}
147.post-msg:last-child, .post-req:last-child { border-bottom: 0; }
148
149/* Your own words, set apart by INDENT rather than by colour: a sent message and
150 a received one are the same kind of thing and colour would rank them. */
151.post-out { padding-left: 14px; border-left: 2px solid var(--border-strong); }
152
153.post-who { display: flex; align-items: baseline; gap: 6px; flex-wrap: wrap; }
154.post-name { font-size: var(--fs-xs); color: var(--text-primary); overflow-wrap: anywhere; }
155/* The fingerprint, which is the part that cannot be chosen. Monospaced so two
156 of them can be compared by eye, and quiet so it does not read as a title. */
157.post-fp {
158 font-family: var(--font-mono); font-size: var(--fs-3xs);
159 color: var(--text-muted); overflow-wrap: anywhere;
160}
161
162/* What somebody wrote, whole, with their own line breaks. Not clipped: this is
163 the message, and a message shortened to fit a column is a message the reader
164 has to go somewhere else to finish. */
165.post-body {
166 margin: 4px 0 0; white-space: pre-wrap; overflow-wrap: anywhere;
167 font-size: var(--fs-xs); color: var(--text-secondary); line-height: 1.5;
168}
169.post-body:empty { display: none; }
170
171/* It arrived and it will not open. Said, rather than left as a gap -- and the
172 reason underneath it, because "something went wrong" that names nothing is
173 what sends somebody to the console. */
174.post-bad { margin: 4px 0 0; font-size: var(--fs-xs); color: var(--warn); }
175.post-bad-why {
176 margin: 2px 0 0; font-size: var(--fs-3xs-2xs); color: var(--text-muted);
177 overflow-wrap: anywhere;
178}
179
180.post-refs { margin-top: 4px; }
181
182.post-acts { display: flex; gap: 6px; margin-top: 7px; flex-wrap: wrap; }
183/* `.post-btn` is the one pill (top of file). Nothing of its own. */
184
185/* The relay's own row: no author, no reply control, its own section. It is not
186 in the message stream and must not read as though it were, so it is set in
187 the muted colour every other piece of app furniture uses. */
188.post-notice p {
189 margin: 0; font-size: var(--fs-3xs-2xs); color: var(--text-muted);
190 line-height: 1.45;
191}
192
193/* ── The box, and what it says about who can read it ────────── */
194
195.post-write {
196 display: flex; flex-direction: column; gap: 6px;
197 padding: 9px 10px; border-top: 1px solid var(--border);
198 background: var(--bg-secondary);
199}
200.post-to {
201 background: var(--bg-tertiary); color: var(--text-primary);
202 border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
203 padding: 4px 6px; font: inherit; font-size: var(--fs-xs);
204}
205/* WHO CAN READ THIS, above the button, where the hand passes on its way to it
206 -- the same placement and the same reasoning as `.imp-public` above. It wraps
207 and is read in full at any dock width, because by this file's own rule a fact
208 the user is meant to act on may not be ellipsised. */
209.post-audience {
210 margin: 0; font-size: var(--fs-3xs-2xs); color: var(--text-secondary);
211 line-height: 1.45; overflow-wrap: anywhere;
212}
213.post-text {
214 width: 100%; box-sizing: border-box; min-height: 72px; resize: vertical;
215 background: var(--bg-tertiary); color: var(--text-primary);
216 border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
217 padding: 7px 8px; font: inherit; font-size: var(--fs-sm); line-height: 1.45;
218}
219.post-text:focus { outline: none; border-color: var(--accent); }
220.post-text::placeholder { color: var(--text-muted); }
221/* Named again on the button. A control labelled plain "Send" in two places that
222 do opposite things is the defect the wording exists to prevent, so this one
223 is no louder than the public one beside it and says which channel it is. */
224/* The one pill, at the primary size, laid at the start of its row. */
225.post-send { align-self: flex-start; }
226.post-note { margin: 0; min-height: 1.2em; font-size: var(--fs-3xs-2xs); color: var(--accent-text); }
227.post-note:empty { min-height: 0; }
228
229/* Nothing here yet, and nobody to write to. Two different sentences: the first
230 is an empty list, the second is a stage a new account is in and says what to
231 do next rather than disabling a control with no explanation. */
232.post-empty, .post-nobody {
233 margin: 12px 0; font-size: var(--fs-xs); color: var(--text-muted); line-height: 1.5;
234}
235.post-nobody { margin: 0; }
236
237/* ── A reference, drawn as a chip ───────────────────────────── */
238
239/* NOT a link, and drawn so that it does not look like one: no underline, no
240 link colour, a bordered box with a button in it. A reference is navigation
241 inside this app, and a chip that looked like a URL would be a chip somebody
242 tries to copy out.
243
244 It arrives SHUT. Nothing is read until the button is pressed -- ten proposal
245 chips resolved on opening an inbox is ten metered requests against the
246 reader's own allowance -- so the first state is a kind, an id and one
247 control. */
248.ref-chip {
249 display: flex; align-items: baseline; flex-wrap: wrap; gap: 4px 7px;
250 margin: 5px 0; padding: 6px 8px;
251 background: var(--bg-secondary);
252 border: 1px solid var(--border);
253 border-radius: var(--radius-sm);
254 font-size: var(--fs-3xs-2xs);
255}
256.ref-kind {
257 flex: none; font-size: var(--fs-3xs); letter-spacing: 0.05em;
258 text-transform: uppercase; color: var(--text-muted);
259}
260.ref-name { flex: 1; min-width: 0; color: var(--text-primary); overflow-wrap: anywhere; }
261.ref-note { flex-basis: 100%; color: var(--text-muted); overflow-wrap: anywhere; }
262/* The one pill (top of file); it only holds its place in the chip's flex row. */
263.ref-act { flex: none; }
264
265/* There is no opening this. The same word `attachTile` uses for an attachment
266 that cannot be reached and for a trashed Diamond, so the three read alike.
267 The reason is TEXT and not only a tooltip: a tooltip is unavailable to a
268 touch, and a chip that is silently inert on a phone is the failure this whole
269 state exists to end. */
270.ref-chip.shut { opacity: 0.75; border-style: dashed; }
271.ref-chip.shut .ref-name { color: var(--text-muted); }
272/* The sender's own description, drawn only where the resolution failed and said
273 to be theirs. It is the one thing on this chip that did not come from this
274 device, so it is set apart and never styled as a title. */
275.ref-said {
276 flex-basis: 100%; color: var(--text-secondary); font-style: italic;
277 overflow-wrap: anywhere;
278}
279
280/* ── Writing a note ─────────────────────────────────────────── */
281
282.imp-write { padding: 8px 10px; border-bottom: 1px solid var(--border); }
283
284.imp-box {
285 width: 100%; box-sizing: border-box; min-height: 96px; resize: vertical;
286 background: var(--bg-tertiary); color: var(--text-primary);
287 border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
288 padding: 7px 8px; font: inherit; font-size: var(--fs-sm);
289 line-height: 1.45;
290}
291.imp-box:focus { outline: none; border-color: var(--accent); }
292.imp-box::placeholder { color: var(--text-muted); }
293
294/* What goes with the note: the exact characters that will be appended, in a
295 row with a closer on it. Closing it takes the line off the screen and off
296 the wire — so it has to be READABLE, in full, or the closer is a choice
297 nobody could make. */
298.imp-with {
299 display: flex; align-items: flex-start; gap: 6px; margin-top: 6px;
300 padding: 5px 4px 5px 7px;
301 background: var(--bg-secondary); border: 1px solid var(--border);
302 border-radius: var(--radius-sm);
303}
304.imp-with[hidden] { display: none; }
305.imp-with-label { display: block; font-size: var(--fs-3xs); letter-spacing: 0.05em;
306 text-transform: uppercase; color: var(--text-muted); margin-bottom: 2px; }
307.imp-with-body { flex: 1; min-width: 0; }
308.imp-with-text { font-size: var(--fs-3xs-2xs); color: var(--text-secondary);
309 line-height: 1.45; white-space: normal; overflow-wrap: anywhere; }
310
311/* What pressing Send does, said above Send. It is read on the way to the
312 button, so it sits between the note and the buttons rather than under them —
313 and it is a fact the user is meant to act on, so by this file's own rule it
314 wraps and is read in full at any dock width rather than being ellipsised. The
315 colour is `--text-secondary` and not `--text-muted`: this is one step louder
316 than the notes around it and no more, because a panel that shouts at somebody
317 for reporting a fault gets fewer reports. */
318.imp-public {
319 margin: 7px 0 0; font-size: var(--fs-3xs-2xs); color: var(--text-secondary);
320 line-height: 1.45; overflow-wrap: anywhere;
321}
322/* Not offered where Send is not offered: a person with no voice cannot send,
323 and telling them their notes will be published would be false. */
324.imp-public[hidden] { display: none; }
325
326.imp-acts { display: flex; align-items: center; gap: 6px; margin-top: 7px; flex-wrap: wrap; }
327/* `.imp-keep` and `.imp-send` are the one pill at the primary size (top of
328 file). Send is the one act here that reaches the network, and it is NOT
329 louder than Keep -- both carry the primary size, because they are two honest
330 answers to the same question and making one shout would be the panel guessing
331 which was meant. */
332.imp-send[hidden] { display: none; }
333
334/* Who it goes as. Beside the button and not in a tooltip: a person must never
335 find out afterwards that their name went too. */
336.imp-as { flex-basis: 100%; font-size: var(--fs-3xs-2xs); color: var(--text-muted); }
337/* Said only when there is no voice: with one, `.imp-public` above the buttons
338 says it and says more. Stated rather than left to the browser, because an
339 empty flex item with a 100% basis still takes a row of the wrap. */
340.imp-as[hidden] { display: none; }
341.imp-say { min-height: 1.2em; margin-top: 5px; font-size: var(--fs-3xs-2xs); color: var(--accent-text); }
342
343/* ── The compose box, and the queue under it ────────────────── */
344
345/* The compose box sits at the top of the Proposals view now, not in a view of
346 its own: write a note, post it (verbatim or polished) straight as a proposal.
347 `.imp-write` already dresses it; `.imp-compose` only keeps it from growing so
348 the proposals list below stays the thing the view is for. The box gives way,
349 with its own scroller, exactly as the old notes list made it. */
350#improve-props-view > .imp-compose { flex: 0 1 auto; min-height: 0; overflow-y: auto; }
351
352/* The queue: notes waiting to send. Collapses when empty so the compose box sits
353 straight above the proposals in the ordinary case, and takes a capped, scrolling
354 height when it fills rather than pushing the proposals off the screen. */
355.imp-queue:empty { display: none; }
356.imp-queue { flex: 0 1 auto; max-height: 40%; overflow-y: auto; border-bottom: 1px solid var(--border); }
357.imp-queue-head { font-weight: 600; }
358
359.imp-note { padding: 7px 10px; border-bottom: 1px solid var(--border); }
360.imp-note:last-child { border-bottom: 0; }
361/* The note as it was written, newlines and all -- the panel is the only place it
362 still exists until it sends, so it is shown whole rather than as a first line,
363 but it stops growing so one long note does not hide the rest of the queue. */
364.imp-note-text {
365 white-space: pre-wrap; overflow-wrap: anywhere;
366 font-size: var(--fs-xs); color: var(--text-secondary); line-height: 1.45;
367 max-height: 7.5em; overflow-y: auto;
368}
369.imp-note-foot { display: flex; align-items: center; gap: 6px; margin-top: 5px; flex-wrap: wrap; }
370.imp-note-state { font-size: var(--fs-3xs-2xs); color: var(--text-muted); }
371/* `.imp-note-send` and `.imp-note-copy` are the one pill (top of file). */
372.imp-note-drop { margin-left: auto; }
373
374/* ── Settings ────────────────────────────────────────────────── */
375
376/* The voice this device posts with, and the drafts the model prepared. Two
377 sections, each under a small eyebrow, drawn by improve.js's `drawSettings`. */
378.imp-settings { padding: 8px 10px; overflow-y: auto; }
379.imp-set-head { margin: 12px 0 6px; }
380.imp-set-head:first-child { margin-top: 2px; }
381.imp-set-note { margin: 0 0 6px; }
382
383/* ── Proposals ──────────────────────────────────────────────── */
384
385/* Which build the counts are from. It sits above the list, where somebody
386 reading the numbers meets it before them rather than after — the same place
387 and the same reasoning as `.trash-note`. */
388.imp-asat { padding: 6px 10px; font-size: var(--fs-3xs-2xs); color: var(--text-muted);
389 border-bottom: 1px solid var(--border); }
390.imp-asat:empty { display: none; }
391
392.imp-props { flex: 1; min-height: 0; overflow-y: auto; padding: 2px 0 8px; }
393.imp-props .rail-note { padding: 12px 10px; }
394
395.imp-prop { border-bottom: 1px solid var(--border); }
396
397/* The admin panel's shape: a coloured dot, a label and a value. A row that can
398 be acted on is a button, and this one opens the thing it names. */
399.imp-prop-row {
400 display: flex; align-items: center; gap: 7px; width: 100%;
401 background: none; border: none; cursor: pointer; text-align: left;
402 padding: 7px 10px; color: var(--text-primary); font: inherit;
403 font-size: var(--fs-xs);
404}
405.imp-prop-row:hover { background: var(--bg-tertiary); }
406.imp-dot { flex: none; width: 7px; height: 7px; border-radius: 50%; background: var(--text-muted); }
407.imp-prop[data-state="open"] .imp-dot { background: var(--accent); }
408/* `accepted` is the forge's word; `taken` was this panel's before it read the
409 forge, and no proposal now carries it. Without this line the dot fell through
410 to `--text-muted` and an accepted proposal was drawn IDENTICALLY to a declined
411 one — the two outcomes a reader most needs told apart. */
412.imp-prop[data-state="accepted"] .imp-dot { background: var(--warn); }
413.imp-prop[data-state="done"] .imp-dot { background: var(--ok); }
414.imp-prop[data-state="declined"] .imp-dot { background: var(--text-muted); }
415.imp-prop-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
416.imp-prop-tally { flex: none; font-size: var(--fs-3xs-2xs); color: var(--text-muted);
417 font-variant-numeric: tabular-nums; }
418
419.imp-prop-body { padding: 0 10px 9px 24px; }
420.imp-prop-body[hidden] { display: none; }
421.imp-prop-says { margin: 0 0 5px; font-size: var(--fs-xs); color: var(--text-secondary); line-height: 1.5; }
422.imp-prop-facts { font-size: var(--fs-3xs-2xs); color: var(--text-muted); }
423
424.imp-votes { display: flex; gap: 6px; margin-top: 7px; }
425/* `.imp-vote` is the one pill, and `.imp-vote.on` its filled cast state, both at
426 the top of this file. */
427/* There is no `.held` state and there is no rule for one. A dashed "cast but not
428 counted yet" variant was drawn here for a client-side vote queue, and that queue was
429 rejected -- two stores of truth about one proposal, and the client's is stale the
430 moment somebody else votes (`dev/IMPROVE_CONTRACT.md` §5.8). Nothing has set the class
431 since; the rule outlived its setter until 2026-08-28. A vote is cast against the forge
432 or it is not cast, so `.on` is the only state there is to draw. */
433
434/* ============================================================
435 The Share view — www/js/share.js
436 ------------------------------------------------------------
437 Two blocks in the order a person meets them: taking a share in
438 needs nothing but the file, and sending one needs a Diamond and
439 somebody to send it to. Its own `shr-` prefix rather than
440 borrowing the `post-` furniture above, because these belong to
441 a different module and a reader tracing a class should land in
442 the file that draws it.
443 ============================================================ */
444
445.shr-block {
446 padding: 9px 10px;
447 border-bottom: 1px solid var(--border);
448}
449.shr-block:last-child { border-bottom: 0; }
450
451.shr-head {
452 margin: 0 0 4px;
453 font-size: var(--fs-xs); font-weight: 600;
454 color: var(--text-primary);
455}
456
457/* What the control is for, and what it will not do without asking. Quiet, and
458 never trimmed: the sentence about a page being somebody else's program is the
459 whole of the consent design being legible before anybody presses anything. */
460.shr-note {
461 margin: 0 0 7px;
462 font-size: var(--fs-3xs-2xs); line-height: 1.45;
463 color: var(--text-muted);
464}
465
466.shr-row { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
467
468/* `.shr-btn` is the one pill (top of file); it keeps a focus ring of its own. */
469.shr-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
470
471/* Who it goes to. The skin gives a form input room to type and this is a
472 toolbar; the same collision that made the file viewer's edit boxes 43px tall
473 in a row of 22px buttons, so the height is stated here too. */
474.imp-list .shr-who {
475 background: var(--bg-tertiary); border: 1px solid var(--border-strong);
476 color: var(--text-primary); border-radius: var(--radius-sm);
477 padding: 1px 6px; height: auto; min-height: 0;
478 font-size: var(--fs-3xs-2xs); line-height: 1.7;
479 max-width: 60%;
480}
481.imp-list .shr-who:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
482
483/* What happened. Never clipped and never a spinner: a sentence saying a file
484 was saved and must now be given to somebody is an instruction, and half of
485 one is no use. */
486.shr-say {
487 margin: 7px 0 0;
488 font-size: var(--fs-3xs-2xs); line-height: 1.5;
489 color: var(--text-secondary);
490 white-space: pre-wrap; overflow-wrap: anywhere;
491}
492.shr-say[hidden] { display: none; }
493.shr-warn { color: var(--warn); }
494
495/* ── The triage plan ────────────────────────────────────────── */
496
497/* One draft in a plan, told from the next one.
498 *
499 * `triage.js` draws its rows out of `.imp-*` on purpose -- the box is
500 * `.imp-box`, the acts are `.imp-acts`, the refusal is `.imp-err` -- and it
501 * needed nothing of its own until there were several drafts on the screen at
502 * once. Photographed at a 300px dock and at 390px: a draft's last button and
503 * the next draft's "New proposal" sit against each other with nothing between
504 * them, so a list of separately sendable items reads as one wall of text and
505 * a reader cannot see which box a Send belongs to. Every OTHER trg- class is
506 * still bare, and should stay that way; this is the one thing the screen asked
507 * for.
508 *
509 * `--border`, and the same 1px it is drawn at on `.imp-note` and `.imp-prop`
510 * two hundred lines above -- the drafts are a list of items in the same panel
511 * and a louder line would say they were a different kind of thing. See the
512 * rule at the head of this file: a divider is not a control's boundary. */
513.trg-draft { padding: 8px 0; border-bottom: 1px solid var(--border); }
514.trg-draft:last-child { border-bottom: none; }
515
516/* ── The approve-list queue ─────────────────────────────────────
517 *
518 * LANE NOTE: this block is the approve-list lane's (js/approvelist.js). Another
519 * lane may also edit improve.css; keep this delimited so the two do not collide.
520 *
521 * It mirrors `.trg-draft` directly above -- one queued draft told from the next
522 * by the same 1px `--border`, because the queue is a list of items in the same
523 * panel and a louder line would say they were a different kind of thing. The row
524 * head is laid out so the tick, the kind and the target do not run together,
525 * which is the same defect the `.trg-draft` rule two lines up was drawn out of.
526 * Every other apl- class reuses `.imp-box`, `.imp-acts`, `.imp-err`, `.imp-as`
527 * and needs nothing of its own. */
528
529/* THE QUEUE SCROLLS INSIDE THE PANEL, and takes the floor while it is on
530 * screen. `host()` draws the drafts into `#improve-approve`, which had no rule
531 * of its own until a plan of twenty drafts overran it: a bare flex child grows
532 * to its whole content, and `.social`'s clip then hides every draft below the
533 * fold with nothing to scroll to them. Photographed 2026-08-30 at 1400px and at
534 * a phone's 390px, twenty drafts deep.
535 *
536 * The idiom is `.imp-notes`/`.imp-props` above -- `min-height: 0` on a flex
537 * child is what lets it shrink past its content so its own `overflow-y` engages
538 * -- with two turns of its own:
539 *
540 * IT COLLAPSES WHEN EMPTY. `host()` leaves the div in place between runs, so
541 * without this an empty queue would still claim a flex share and starve the
542 * notes list. The same `:empty` rule `.imp-asat` keeps.
543 *
544 * IT IS THE ACTIVE TASK, so it keeps a floor and the kept-notes list below
545 * gives way -- the reverse of that list's usual claim, because somebody
546 * reviewing fresh drafts is not reading old notes. Both floors at once would
547 * re-overflow the view, so the sibling rule drops the notes floor exactly
548 * while a queue is up. */
549.imp-approve:empty { display: none; }
550.imp-approve { flex: 1 1 0%; min-height: 55%; overflow-y: auto; padding: 2px 0 8px; }
551.imp-approve:not(:empty) ~ .imp-notes { min-height: 0; }
552
553.apl-row { padding: 8px 0; border-bottom: 1px solid var(--border); }
554.apl-row:last-child { border-bottom: none; }
555.apl-rowhead { display: flex; align-items: baseline; gap: 6px; flex-wrap: wrap; }
556.apl-tick { align-self: center; }
557
558/* The "Raised -- see it in Improve" confirmation, shown after a proposal is
559 posted or queued. The hub link opens the Improve panel (tracker.js). */
560.imp-raised { display: flex; align-items: baseline; gap: 4px; margin: 6px 0 2px;
561 font-size: var(--fs-2xs-xs); color: var(--text-muted); }
562.imp-raised[hidden] { display: none; }
563.imp-raised-hub { background: none; border: none; padding: 0; font: inherit; cursor: pointer;
564 color: var(--accent-text); text-decoration: underline; }
565.imp-raised-hub:hover { color: var(--accent-hover); }