Oregami
Repositories/oxedyne/daimond

oxedyne/daimond/www/css/responsive.css

20.8 KiB, 1 run

created by r2519314175:1125, 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/* ============================================================
2 Daimond — mobile responsive layer
3 ------------------------------------------------------------
4 Loaded AFTER app.css. On small screens the four desktop
5 panels become one panel at a time, switched with the bottom
6 nav (.mnav). The active panel is chosen by body[data-mpanel].
7 Everything here is confined to `@media (max-width: 760px)`
8 so desktop (>760px) is untouched.
9 ============================================================ */
10
11@media (max-width: 760px) {
12
13 /* The page itself never scrolls or drags: it is pinned to the DYNAMIC
14 viewport (dvh — the space actually visible, excluding the browser's
15 address bar, which `100vh` wrongly includes so the app spilled below the
16 fold and you had to drag to reach the bar). Panels scroll internally. */
17 html, body {
18 height: 100vh; /* fallback for engines without dvh */
19 height: 100dvh;
20 overflow: hidden;
21 overscroll-behavior: none;
22 max-width: 100%;
23 }
24
25 #app {
26 height: 100vh;
27 height: 100dvh;
28 /* The foot stays at nought: the bottom bar owns the home indicator's
29 space, in its own padding, and adding it here as well would lift the
30 bar clear of the screen edge by twice the inset. */
31 padding: calc(8px + var(--safe-t)) calc(8px + var(--safe-r)) 0 calc(8px + var(--safe-l));
32 gap: 6px;
33 overflow: hidden;
34 }
35 /* The top bar must never push the layout wider than the screen. */
36 .topbar { overflow: hidden; }
37 .top-meter { min-width: 0; overflow: hidden; }
38 /* ...and `overflow: hidden` is not a fit, it is a guillotine. Signed in, the
39 row once ran 22-40px past a phone's width, so the appearance-and-layout
40 button -- theme, skin, reading size -- was CLIPPED OFF the screen on every
41 phone tested, and on an iPhone SE the guide "?" went with it. The maker's
42 badge held that slot then, and stood down here because a signature is
43 worth less room than the two controls it would push off.
44
45 Nothing stands down now: the badge has moved into the About dialog and the
46 slot holds a 32px icon button instead of a ~70px lozenge, so the row is
47 NARROWER than it was even with the badge hidden. The phone therefore keeps
48 About, which is where the badge's two claims now are -- a signature the
49 phone could never show at all is worse than one behind a button. */
50
51 /* The lockup drops to the base logo height for the WHOLE mobile shell, not
52 only for phones. The wordmark was redrawn at 4.44:1 (it was 2.65:1), so at
53 the desktop's 36px it is 160px wide rather than 96, and the row -- burger,
54 lockup, five controls -- ran past its box anywhere below 476px. At 24px the
55 lockup is 106px and the row fits from 422px up, which covers the whole band
56 this shell is drawn on above the phone rule below.
57
58 Scoped to `.topbar` on purpose. These rules were written unscoped, and
59 `.brand-wordmark` is also worn by the About dialog's lockup (it carries the
60 class for the light/dark swap) -- so an unscoped height here silently beat
61 `.about-word`, and About drew its signature at 20-24px on every phone
62 instead of the 46px its own rule asks for. */
63 .topbar .brand-wordmark { height: var(--logo-h); }
64
65 /* Desktop-only machinery disappears. */
66 .phandle { display: none !important; }
67 .panel-close { display: none !important; }
68 /* Except the RAIL's. Every other panel is a destination reached from the
69 bottom bar and has nothing to be closed back to, but the rail here is the
70 DRAWER, and the drawer covers 335 of a 390px screen. Its only dismissals
71 were a tap on the 55px of scrim beside it and the hamburger — which the
72 drawer itself is drawn on top of. A surface that big needs a way out on
73 the surface. The click is caught in mobile.js and closes the drawer, not
74 the panel. */
75 .panel.rail .panel-close { display: flex !important; }
76 /* The chip row is not in the header here -- it IS the footer (see .mnav in
77 css/mobile.css, and `placeChips` in js/mobile.js, which moves the one row
78 between the two). It used to be hidden on a phone altogether. */
79
80 /* One DESTINATION at a time, full width. The dwellable panels — Chat, Email,
81 Files, Agents — take the whole screen and are chosen from the bottom bar.
82 The desktop zones collapse (`display: contents` lets their panels lay out
83 against .main directly) and the inline widths/heights the desktop resizers
84 set must lose, hence !important. The rail (a drawer) and the stage guests
85 (a sheet) are positioned by mobile.css and are excluded here. */
86 .main { position: relative; }
87 .pcol, .stage, .dock { display: contents !important; width: auto !important; }
88 .main .panel:not(.rail) { height: auto !important; }
89 .main .panel:not(.rail) { display: none !important; width: 100% !important; min-width: 0 !important; flex: 1 !important; }
90 body[data-mpanel="ai"] .main .ai { display: flex !important; }
91 body[data-mpanel="agents"] .main .agents { display: flex !important; }
92 body[data-mpanel="mail"] .main .mail { display: flex !important; }
93 body[data-mpanel="work"] .main .work { display: flex !important; }
94
95 /* The bottom bar. Its contents are the chip row, and its rules are in
96 css/mobile.css with the rest of the phone shell; what stays here is the
97 band itself, because the layout above depends on its height. */
98 .mnav {
99 display: flex;
100 align-items: stretch;
101 flex: none;
102 border-top: 1px solid var(--border);
103 background: var(--bg-primary);
104 margin: 0 -8px;
105 }
106
107 /* ── The header's controls SCROLL ─────────────────────────────
108 Reported from a phone: "this header does not scroll so I can't see a lot
109 of buttons." Every other stage panel's head wraps (`.web .chead, .doc
110 .chead, .msg .chead { flex-wrap: wrap }` in app.css) and the AI panel's
111 never did, so its row of chips — mode, Concise, Copy, Steps, Collapse,
112 Fold on a Diamond, and full screen — squeezed the title to an ellipsis and
113 then simply ran off the end of the panel, unreachable.
114
115 SCROLLED RATHER THAN WRAPPED, deliberately. Wrapping costs a second 36px
116 row of vertical space on every screen for ever, on the axis a phone has
117 least of; scrolling costs nothing and is the gesture the user asked for.
118 The title keeps a floor so it cannot be crushed to nothing in the trade,
119 and the scrollbar is hidden because a phone has no room for one and the
120 content is dragged, not clicked. */
121 .panel.ai .chead { gap: 6px; }
122 .panel.ai .ctitle { min-width: 4.5em; }
123 .panel.ai .chead-right {
124 flex: 1 1 auto;
125 min-width: 0;
126 overflow-x: auto;
127 overflow-y: hidden;
128 scrollbar-width: none;
129 -webkit-overflow-scrolling: touch;
130 /* Without this the chips shrink to fit instead of overflowing, which is
131 the state that hid them: a row that fits is a row nothing scrolls. */
132 justify-content: flex-start;
133 }
134 .panel.ai .chead-right::-webkit-scrollbar { display: none; }
135 .panel.ai .chead-right > * { flex: none; }
136
137 /* Comfortable chat input row. */
138 .chat-output { padding: 12px 14px; }
139 .chat-msg-user .chat-msg-content,
140 .chat-msg-assistant .chat-msg-content { max-width: 100%; }
141 .chat-msg-content pre,
142 .chat-msg-content table,
143 .tool-args,
144 .tool-result { max-width: 100%; overflow-x: auto; }
145 .chat-input-bar { gap: 8px; padding: 8px 10px; }
146 /* 16px avoids iOS Safari's auto-zoom on focus. */
147 #chat-input { font-size: var(--fs-xl); padding: 11px 13px; }
148 /* Send and the walk-back sit in one row and are one control on desktop, both
149 32px square. Only Send was grown for touch, so the pair read as a large
150 button beside a small one, bottom-aligned and mismatched. They grow together. */
151 #chat-send, #chat-jump, #chat-end { min-width: 46px; width: 46px; height: 46px; font-size: var(--fs-2xl); }
152
153 /* ── The box you type in gets the room ────────────────────────
154 Measured on a 390px phone: the bar is 372 wide and the text box got 170 of
155 it, because Send and the two walk-back chevrons take 46 each plus gaps —
156 three buttons eating more of the row than the thing the row is for.
157
158 The two CHEVRONS ARE NOT COMPOSITION. They walk the transcript; they have
159 nothing to do with the message being written, and on the one screen where
160 space is scarce they were charging full price for it. So on a phone they
161 leave the bar and float at the foot of the thread, where what they act on
162 actually is. Absolute, so the DOM and every handler are untouched — this is
163 a placement, not a rebuild.
164
165 The text box then takes the width they gave up: 170px becomes about 280.
166 Nothing is lost and nothing new is drawn, which is what "without eating
167 screen real estate" has to mean. */
168 .chat-input-bar { position: relative; }
169 .chat-input-bar #chat-jump,
170 .chat-input-bar #chat-end {
171 position: absolute;
172 bottom: calc(100% + 10px);
173 width: 40px; min-width: 40px; height: 40px;
174 border-radius: 50%;
175 /* Reading over the thread rather than sitting in a bar, so it needs its
176 own ground: over a message bubble a transparent button is a floating
177 glyph nobody can tell is pressable. */
178 background: var(--bg-secondary);
179 border: 1px solid var(--border);
180 box-shadow: 0 2px 8px rgb(0 0 0 / 0.28);
181 opacity: 0.94;
182 z-index: 3;
183 }
184 .chat-input-bar #chat-jump { right: 62px; }
185 .chat-input-bar #chat-end { right: 12px; }
186 /* WHAT THEY FLOAT OVER, which the first attempt got wrong and a screenshot
187 caught: they cleared the composer and landed squarely on the attachments
188 strip, hiding the end of "Mark one in with the paperclip". The strip is not
189 always there — a Diamond with folders marked in draws rows instead — so
190 lifting the buttons by a fixed amount would clear it in one state and float
191 them into the middle of the thread in the other.
192 Keeping the anchor and giving the strip room on its right is the version
193 that is correct in both: nothing moves, and no words go under a button.
194
195 The room goes on the BODY and not on the strip, which was the second thing
196 a screenshot caught: padding the whole strip dragged the group's own `+`
197 button 104px in from the edge it belongs on. The head row is a title and a
198 button, neither of which the chevrons ever reach; only the body sits under
199 them. */
200 .chat-attach-strip .ws-body,
201 .chat-attach-strip .attach-body { padding-right: 100px; }
202
203 /* Give lists a little more row height for touch. */
204 .session-box { padding: 10px 11px; }
205 .files-row { padding: 8px 12px; }
206
207 /* A thumb's floor. The shared touch block below lifts the small controls to
208 ~34px, which suits a dense tablet dock; on a phone, where these are the
209 only controls in the bar, they get the full 40px box. Confined to the
210 phone breakpoint on purpose -- 40px each would burst the Workspace
211 panel's six-button head in a 260px dock column on a tablet. */
212 .top-actions .icon-btn,
213 .top-actions .update-chip,
214 #drawer-btn { min-width: 40px; min-height: 40px; }
215 .railhead .addbtn { width: 40px; height: 40px; }
216 /* The search box was 29px tall: legible, but nothing like a target. */
217 .diamond-search { padding: 9px 10px; }
218
219 /* The rail is the DRAWER here, and every row in it is something a thumb has
220 to land on: the status rows were 23px tall, the account row 26, the drawer's
221 own close 21 square. Dense is right on a desktop, where these are read with
222 a pointer over them; in a drawer they need a finger's worth of height.
223 (The drawer scrolls, so the extra height costs nothing that matters.) */
224 /* `.astat-sync` is here because its chip is pressable in the one state the
225 user can lift -- "Sync off" opens Credits, and a safe start turns syncing
226 back on -- so it is a target and needs a finger's worth of height like the
227 rows around it. Without it the sync row sat 12px shorter than its
228 neighbours and read as a caption rather than a row. */
229 .astat-row.astat-btn,
230 .astat-row.astat-sync { padding-top: 9px; padding-bottom: 9px; }
231 .astat-id .user-row { padding: 9px 4px; }
232 .astat-id #settings-btn { min-width: 40px; min-height: 40px; }
233 .admin-item { padding-top: 10px; padding-bottom: 10px; }
234 .tile-label { padding-top: 6px; padding-bottom: 6px; }
235 /* Steps / collapse, in the chat header: 26px tall is a pointer's target, not
236 a thumb's. */
237 .chead .chip-btn { padding: 8px 10px; }
238
239 /* ── Full screen, on a phone ──────────────────────────────────────
240 The desktop rules (app.css, `[data-cfull]`) take the top bar, the rail and
241 the dock; here the bottom bar goes too, because a phone's app switcher is
242 the last piece of chrome around the page.
243
244 THE FOOT'S SAFE INSET COMES BACK WITH IT. `#app` above sets its bottom
245 padding to nought and says why: the bar owns the home indicator's space in
246 its own padding, and adding it twice would lift the bar off the screen
247 edge. With the bar gone there is nothing holding that space, so the panel
248 would run under the indicator -- and the crystal page's last row with it. */
249 :root[data-cfull] .mnav { display: none !important; }
250 :root[data-cfull] #app { padding-bottom: var(--safe-b); }
251 /* The way out is the only control left in the header, and on a phone it is
252 the one a thumb has to find. */
253 :root[data-cfull] .chead .cfull-btn { min-height: 40px; padding: 8px 12px; }
254}
255
256/* Touch tap targets: secondary controls default to ~22px which is under the
257 24px WCAG floor; enlarge on small screens, and reveal hover-only close
258 buttons (there is no hover on touch). */
259@media (max-width: 760px), (pointer: coarse) {
260 /* Seven controls the existing lifts in this block missed. The worst was the
261 tag disclosure at 315x16.8 -- the way into the whole filing system, in a
262 drawer whose entire purpose is to be worked with a thumb. 24x24 is the
263 WCAG 2.2 AA floor. */
264 .tagf-toggle { padding-top: 5px; padding-bottom: 5px; }
265 .tagf-pool .tag-chip,
266 .tagf-active .tag-chip,
267 .session-box-meta .tag-chip { padding-top: 3px; padding-bottom: 3px; }
268 .files-mode-btn,
269 .files-mode-chip { min-height: 24px; padding-top: 4px; padding-bottom: 4px; }
270 .tile-fold { padding-top: 5px; padding-bottom: 5px; }
271 /* The wordmark keeps its 20px ink and gains a 24px target around it. */
272 .brand { display: flex; align-items: center; min-height: 24px; }
273 /* The chips are 24px here rather than 18, so the same cap would show the
274 same three and a half rows in half again the height -- and the drawer has
275 less to spare than the desktop rail, not more. Two and a half rows on a
276 phone: still cut through a row, so it still says there is more of it,
277 without the filter taking the space the Diamonds came for. */
278 .tagf-pool, .tagf-active { max-height: calc(6em + 16px); }
279 .addbtn { width: 34px; height: 34px; font-size: var(--fs-xl); }
280 .icon-btn { padding: 8px 10px; font-size: var(--fs-2xl); }
281 /* ── The closer's thumb floor ─────────────────────────────────────────────
282 Every way out of every dialog, popover, drawer and sheet, at 44x44 — the
283 iOS Human Interface floor, above the 24px WCAG 2.2 AA one, because this is
284 the control a user reaches for when they are stuck and there is no second
285 attempt if they miss it.
286
287 The BOX grows; the ink does not. `.ic` stays 16px inside it, so a phone
288 gets a bigger target rather than a bigger cross.
289
290 This is the user's report answered: on a 390px screen the appearance menu
291 is 359px wide, which leaves 19px either side to tap and no closer at all.
292 A margin is not a way out. */
293 .ui-close { width: 44px; height: 44px; }
294 /* The two closers written in static markup, which predate `.ui-close` and
295 carry it now: the phone sheet's and the admin drawer's. Named as well,
296 because each also carries a size of its own from the block it was tuned
297 in and the last rule would otherwise win. */
298 .msheet-close, #admin-close, .admin-back { min-width: 44px; min-height: 44px; }
299
300 /* These named classes that do not exist (`.files-row-del`), so on touch the
301 file buttons stayed hidden and could not be reached at all. The real class
302 is `.files-del`, and the file buttons hide with `visibility`, not
303 `opacity`. The tile's closer cross was removed in phase C -- Delete lives
304 at the foot of the cog dialog now -- so its three rules went with it. */
305 .files-row .files-del { visibility: visible; opacity: 1; }
306
307 /* A tablet keeps the desktop bar and its chip row, where the controls were
308 23-36px tall. They grow in HEIGHT only: the chip row already has more
309 chips than it can show, so nothing here may take a pixel of width. */
310 .top-actions .icon-btn,
311 .top-actions .update-chip { min-height: 40px; }
312 .ptag { padding-top: 8px; padding-bottom: 8px; }
313}
314
315/* ── The smallest screens ────────────────────────────────────────────────────
316 320px (an iPhone SE / iPhone 5) has 304px of usable bar, and the phone layout
317 above wants 343. Rather than drop another control -- the appearance menu is
318 the one that was being lost, and it is the way to the theme, the skin and the
319 reading size -- the bar itself gets smaller: a shorter wordmark, tighter gaps
320 and the compact 34px touch box. Everything stays reachable. */
321/* A Diamond's labelled rows stack below the phone breakpoint.
322 The label holds a fixed 116px so the pulldowns line up their left edges —
323 which is right on a desktop and wrong at 380px, where 116 plus the gap leaves
324 the select too narrow for its own text and it overflowed the card by 19px.
325 `verify_raildialogs` measured it at warm-dark-380. Stacked, each control gets
326 the whole width and the column they were lining up for is moot.
327 Every name the rows and their labels go by is listed. Miss one and that row
328 alone keeps its 116px column and overflows the card exactly as before —
329 which is why the colour rows are named here as well as the model rows. */
330@media (max-width: 760px) {
331 .tile-dlg-field,
332 .tile-dlg-model { flex-direction: column; align-items: stretch; gap: 4px; }
333 .tile-dlg-label,
334 .tile-dlg-model .tile-model-chip { flex: 0 0 auto; width: auto; text-align: left; }
335 /* A swatch stretched to the card's width is a colour BAR, not a chip a
336 thumb presses; it keeps its size and sits at the start of its own row. */
337 .tile-dlg-swatch { align-self: flex-start; }
338}
339
340/* THE WORDMARK STANDS DOWN BEFORE A CONTROL DOES, and on a phone it now stands
341 down to the mark alone. The lockup was redrawn at 4.44:1, so even at the base
342 24px it is 106px wide, and the row -- burger, lockup, five controls -- needs
343 422px to hold that. Every phone but the two Pro Maxes is narrower, and the
344 only heights that would have fitted (17px at 390) put the lettering below
345 legible. Squashing the artwork is not on the table: the proportions are the
346 author's.
347
348 So the WORD goes and the MARK stays. The mark is the brand -- it is what the
349 chat header wears at 18px (`#chead-mark`) and what the extension ships alone
350 -- and at 24px tall it is 17px wide, which the row can hold down to 320px with
351 room to spare. That is why the height rule that used to drop to 20px below 380
352 has gone: with the word off the bar there is nothing left to save.
353
354 Swapped with `content`, not by hiding one image and showing another, so the
355 two ink variants keep their single light/dark rule and the markup is
356 untouched. The identity modal does the same thing two files over. */
357@media (max-width: 430px) {
358 .topbar .brand-wordmark { content: url("../assets/daimond_mark.svg"); }
359}
360
361/* 380, not the 360 this began at. The bar's right-hand row is five controls
362 once you are signed in -- update, About, link a device, guide, appearance --
363 and at the full 40px touch box those five want 216px, which the layout only
364 affords from 380 up. Between 361 and 379 the bar's `overflow: hidden` was
365 taking 3-17px off the appearance menu at the end of the row: an iPhone SE
366 (2nd/3rd gen) and an iPhone 8 are both 375.
367
368 The policy is the one this block was written for and is unchanged by About
369 arriving: the bar gets smaller rather than shedding a control, because every
370 one of the five is the only way to the thing it opens. `verify_about.mjs`
371 measures the row for a clip at 320, 375 and 390 so the next control added
372 here fails loudly rather than quietly amputating the last one. */
373@media (max-width: 380px) {
374 .topbar { gap: 8px; }
375 /* 2px between the buttons rather than 4. The TARGETS keep their 34px, which
376 is what a thumb lands on; it is only the air between them that goes. */
377 .top-actions { gap: 2px; }
378 .top-actions .icon-btn,
379 .top-actions .update-chip,
380 #drawer-btn { min-width: 34px; min-height: 34px; }
381 /* The unlock lockup, at 54px, is 240px wide -- which is the entire content
382 width of a 90vw card on a 320px phone, so it ran to both padding edges and
383 over them. 40px is 177px, and leaves the card its margins back. */
384 #identity-modal .login-logo { height: 40px; }
385}