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); } |