Oregami
Repositories/oxedyne/daimond

oxedyne/daimond/www/css/guide.css

16.0 KiB, 1 run

created by r2519314175:1103, 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/* guide.css — the Daimond user guide.
2 *
3 * One stylesheet for the whole guide site. It reuses Daimond's own palette
4 * (see css/variables.css) so the guide reads as part of the app, whether it is
5 * opened directly at /guide/ or shown inside Daimond's Web panel.
6 *
7 * Theme: guide/frame.js puts data-theme, data-tone and data-ink on this root --
8 * mirrored from the app when the guide is framed, from the operating system
9 * when it is opened on its own. The colours themselves come from the app's
10 * css/variables.css, which every guide page loads first.
11 */
12
13/* Reading size. The guide is framed inside the app as often as it is opened on
14 * its own, so it carries the reader's chosen text size the same way it carries
15 * the theme: mirrored onto this root by the inline script in each page, with a
16 * standalone default of 1 when there is no app to mirror from. */
17html { font-size: calc(16px * var(--fs-scale, 1)); }
18
19/* ── Palette ──────────────────────────────────────────────────────────────
20 The guide loads the app's own css/variables.css, so every palette the app
21 offers is already here and none is restated. It used to carry copies of
22 three of them, which is why a candy reader once got a cream guide: a copy is
23 a palette that has to be remembered twice, and ten of them could not be.
24
25 Only what the app's stylesheet does not define, or defines differently for a
26 document rather than an application frame, belongs below. */
27:root {
28 --maxw: 760px;
29}
30
31/* ── Base ────────────────────────────────────────────────────────────────── */
32* { box-sizing: border-box; }
33
34html { -webkit-text-size-adjust: 100%; }
35
36body {
37 margin: 0;
38 font-family: var(--font);
39 font-size: 1rem;
40 line-height: 1.6;
41 color: var(--text-primary);
42 background: var(--bg-primary);
43 overflow-x: hidden; /* no horizontal page scroll */
44}
45
46img { max-width: 100%; height: auto; }
47
48a {
49 color: var(--accent-text);
50 text-decoration: none;
51}
52a:hover { text-decoration: underline; }
53
54/* ── Header and navigation ───────────────────────────────────────────────── */
55.site-head {
56 position: sticky;
57 top: 0;
58 z-index: 10;
59 background: var(--bg-secondary);
60 border-bottom: 1px solid var(--border);
61}
62
63.site-head-inner {
64 max-width: var(--maxw);
65 margin: 0 auto;
66 padding: 0.75rem 1.25rem;
67 display: flex;
68 flex-wrap: wrap;
69 align-items: center;
70 gap: 0.5rem 1.25rem;
71}
72
73.brand {
74 display: inline-flex;
75 /* The wordmark is an image, whose baseline is its bottom edge, so a baseline
76 lockup drops the "Guide" label below it. Centre the two instead. */
77 align-items: center;
78 gap: 0.5rem;
79 flex: none;
80}
81.brand:hover { text-decoration: none; }
82
83.wordmark { height: 22px; width: auto; display: block; }
84.wm-light { display: none; }
85
86/* Show the dark-lettered wordmark wherever the palette takes dark lettering.
87 One rule, on the ink axis, so a palette added later is never missed. */
88:root[data-ink="dark"] .wm-dark { display: none; }
89:root[data-ink="dark"] .wm-light { display: block; }
90
91.brand-sub {
92 font-size: 0.8rem;
93 letter-spacing: 0.08em;
94 text-transform: uppercase;
95 color: var(--text-muted);
96}
97
98.site-nav {
99 display: flex;
100 flex-wrap: wrap;
101 gap: 0.35rem 0.9rem;
102 font-size: 0.9rem;
103}
104.site-nav a {
105 color: var(--text-secondary);
106 padding: 0.15rem 0;
107 border-bottom: 2px solid transparent;
108}
109.site-nav a:hover { color: var(--text-primary); text-decoration: none; }
110.site-nav a[aria-current="page"] {
111 color: var(--text-primary);
112 border-bottom-color: var(--accent);
113}
114
115/* ── Main column ─────────────────────────────────────────────────────────── */
116main {
117 max-width: var(--maxw);
118 margin: 0 auto;
119 padding: 1.5rem 1.25rem 4rem;
120}
121
122.lede {
123 font-size: 1.1rem;
124 color: var(--text-secondary);
125}
126
127h1 {
128 font-size: 1.9rem;
129 line-height: 1.2;
130 margin: 0.5rem 0 0.75rem;
131 letter-spacing: -0.01em;
132}
133h2 {
134 font-size: 1.35rem;
135 margin: 2.25rem 0 0.5rem;
136 padding-top: 0.5rem;
137 border-top: 1px solid var(--border);
138}
139h3 {
140 font-size: 1.05rem;
141 margin: 1.5rem 0 0.35rem;
142}
143
144p { margin: 0.6rem 0; }
145
146strong, b { color: var(--text-primary); font-weight: 650; }
147
148ul, ol { margin: 0.6rem 0; padding-left: 1.4rem; }
149li { margin: 0.35rem 0; }
150
151/* A control the reader must find on screen: a small pill that stands in for a
152 button or a row label in the app. */
153.ui {
154 font-family: var(--font);
155 font-weight: 600;
156 color: var(--accent-text);
157 background: var(--accent-soft);
158 border: 1px solid var(--border-2);
159 border-radius: var(--radius-sm);
160 padding: 0.05em 0.4em;
161 white-space: nowrap;
162 font-size: 0.92em;
163}
164
165code, kbd {
166 font-family: var(--font-mono);
167 font-size: 0.88em;
168 background: var(--bg-tertiary);
169 border: 1px solid var(--border);
170 border-radius: var(--radius-sm);
171 padding: 0.05em 0.35em;
172 /* A path or a flag has no spaces in it, so on a phone it is one unbreakable
173 word wider than the column, and the whole page scrolls sideways to fit it.
174 Breaking mid-path is the lesser evil; the block form below keeps its own
175 lines intact and scrolls instead. */
176 overflow-wrap: anywhere;
177}
178
179/* A command to type at a terminal. A line too long for the column scrolls
180 inside the block rather than widening the page, which is why the box carries
181 the border and the code inside it carries none. */
182pre {
183 margin: 0.7rem 0;
184 padding: 0.7rem 0.9rem;
185 background: var(--bg-tertiary);
186 border: 1px solid var(--border);
187 border-radius: var(--radius-sm);
188 overflow-x: auto;
189 font-size: 0.88rem;
190 line-height: 1.55;
191}
192pre code {
193 background: none;
194 border: 0;
195 padding: 0;
196 font-size: 1em;
197 white-space: pre;
198 overflow-wrap: normal;
199}
200
201/* Numbered getting-started steps. */
202ol.steps {
203 list-style: none;
204 counter-reset: step;
205 padding-left: 0;
206}
207ol.steps > li {
208 counter-increment: step;
209 position: relative;
210 padding: 0.9rem 0.9rem 0.9rem 3.2rem;
211 margin: 0.75rem 0;
212 background: var(--bg-secondary);
213 border: 1px solid var(--border);
214 border-radius: var(--radius);
215}
216ol.steps > li::before {
217 content: counter(step);
218 position: absolute;
219 left: 0.9rem;
220 top: 0.9rem;
221 width: 1.6rem;
222 height: 1.6rem;
223 display: flex;
224 align-items: center;
225 justify-content: center;
226 font-weight: 700;
227 font-size: 0.95rem;
228 color: var(--on-fill, #fff);
229 background: var(--accent);
230 border-radius: 50%;
231}
232ol.steps > li h3 { margin-top: 0; }
233
234/* Callout for honest limits and notes. */
235.note {
236 margin: 1rem 0;
237 padding: 0.75rem 1rem;
238 background: var(--bg-tertiary);
239 border: 1px solid var(--border);
240 border-left: 3px solid var(--accent);
241 border-radius: var(--radius-sm);
242 color: var(--text-secondary);
243}
244.note strong { color: var(--text-primary); }
245.note > ul:last-child, .note > p:last-child { margin-bottom: 0; }
246
247/* The stronger form: something that will stop the reader outright, or cost them
248 an account. It is a different colour from the ordinary note because a reader
249 who has learned that every callout is optional reads none of them, and these
250 two are not optional. The palette's own --warn, so every theme carries it. */
251.note.stop {
252 background: var(--warn-bg);
253 border-color: var(--warn);
254 border-left-color: var(--warn);
255 color: var(--text-primary);
256}
257.note.stop code { background: var(--bg-tertiary); }
258
259/* Card grid for panel tours. */
260.cards {
261 display: grid;
262 grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
263 gap: 0.75rem;
264 margin: 1rem 0;
265}
266.card {
267 background: var(--bg-secondary);
268 border: 1px solid var(--border);
269 border-radius: var(--radius);
270 padding: 0.85rem 1rem;
271}
272.card h3 { margin: 0 0 0.25rem; font-size: 1rem; }
273.card p { margin: 0; font-size: 0.92rem; color: var(--text-secondary); }
274
275/* ── A control that does something ────────────────────────────────────────
276 The guide describes the app; this is the one block in it that ASKS the app
277 for something, so it is drawn as a thing to press rather than as prose with
278 a link in it. The reply lands in `.do-say`, which is a live region: a reader
279 using a screen reader must hear the outcome, since the visible change it is
280 reporting happens in the app behind this frame. */
281.do {
282 margin: 1.25rem 0;
283 padding: 1rem;
284 background: var(--bg-secondary);
285 border: 1px solid var(--border);
286 border-radius: var(--radius);
287}
288.do-btn {
289 font: inherit;
290 font-weight: 600;
291 color: var(--on-fill, #fff);
292 background: var(--accent);
293 border: 1px solid var(--accent);
294 border-radius: var(--radius-sm);
295 padding: 0.55rem 1.1rem;
296 cursor: pointer;
297}
298.do-btn:hover { filter: brightness(1.08); }
299.do-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
300/* Disabled is the state between the ask and the answer, and it has to READ as
301 waiting rather than as broken. */
302.do-btn[disabled] { opacity: 0.55; cursor: default; filter: none; }
303/* Empty until there is something to say, and taking no room while it is. */
304.do-say { margin: 0.6rem 0 0; color: var(--text-primary); font-weight: 600; }
305.do-say:empty { display: none; }
306.do-note { margin: 0.6rem 0 0; font-size: 0.9rem; color: var(--text-muted); }
307
308/* ── Screenshot slots ─────────────────────────────────────────────────────
309 The main agent will set a real src on each img later. Until then the figure
310 shows a dashed placeholder box labelled with its data-shot name, and the
311 caption sits below. */
312.shot {
313 margin: 1.5rem 0;
314 position: relative;
315}
316.shot img {
317 display: block;
318 width: 100%;
319 border: 1px solid var(--border);
320 border-radius: var(--radius);
321 background: var(--bg-secondary);
322}
323/* A portrait dialog screenshot should not sprawl to full text width. */
324.shot.narrow img { max-width: 300px; margin: 0 auto; }
325.shot figcaption { margin-top: 0.5rem; font-size: 0.9rem; color: var(--text-muted); text-align: center; }
326.shot img[data-src]:not([src]) {
327 min-height: 210px;
328 border: 2px dashed var(--border-2);
329 background: var(--bg-tertiary);
330}
331.shot:has(img[data-src]:not([src]))::before {
332 content: "Screenshot: " attr(data-shot);
333 position: absolute;
334 left: 0;
335 right: 0;
336 top: 0;
337 height: 210px;
338 display: flex;
339 align-items: center;
340 justify-content: center;
341 color: var(--text-muted);
342 font-size: 0.85rem;
343 letter-spacing: 0.03em;
344 pointer-events: none;
345}
346.shot figcaption {
347 margin-top: 0.4rem;
348 font-size: 0.85rem;
349 color: var(--text-muted);
350 text-align: center;
351}
352
353/* A labelled schematic (inline SVG), theme-aware via the CSS variables. */
354.diagram {
355 margin: 1.5rem 0;
356}
357.diagram svg {
358 display: block;
359 width: 100%;
360 height: auto;
361 border: 1px solid var(--border);
362 border-radius: var(--radius);
363 background: var(--bg-primary);
364}
365.diagram figcaption {
366 margin-top: 0.5rem;
367 font-size: 0.85rem;
368 color: var(--text-muted);
369 text-align: center;
370 line-height: 1.5;
371}
372
373/* ── Footer ──────────────────────────────────────────────────────────────── */
374.site-foot {
375 max-width: var(--maxw);
376 margin: 0 auto;
377 padding: 1.5rem 1.25rem 3rem;
378 border-top: 1px solid var(--border);
379 color: var(--text-muted);
380 font-size: 0.85rem;
381 display: flex;
382 flex-wrap: wrap;
383 gap: 0.5rem 1.25rem;
384 justify-content: space-between;
385 align-items: center;
386}
387.site-foot nav { display: flex; flex-wrap: wrap; gap: 0.75rem; }
388.site-foot a { color: var(--text-muted); }
389
390/* Onward links between pages. */
391.next {
392 margin-top: 2.5rem;
393 padding-top: 1rem;
394 border-top: 1px solid var(--border);
395 display: flex;
396 flex-wrap: wrap;
397 gap: 0.75rem;
398 justify-content: space-between;
399 font-size: 0.95rem;
400}
401
402/* ── Narrow screens ──────────────────────────────────────────────────────── */
403@media (max-width: 560px) {
404 body { font-size: 0.9375rem; }
405 h1 { font-size: 1.6rem; }
406 .site-head-inner { padding: 0.6rem 1rem; }
407 main { padding: 1.25rem 1rem 3rem; }
408 .site-nav { font-size: 0.85rem; gap: 0.3rem 0.7rem; }
409}
410
411/* ── The guide's search ──────────────────────────────────────────────────────
412 notes2.txt line 96 asks for this as the fallback for a reader who would
413 rather not spend tokens asking Daimond Help. It sits at the end of the
414 header row and takes the leftover width, so it never pushes the nav to a
415 second line on a wide screen and drops below it on a narrow one.
416
417 The panel is absolutely positioned so opening it does not move the page
418 under the reader's eye -- a list that shoves the article down is a list you
419 have to fight. */
420.gsearch {
421 position: relative;
422 flex: 1 1 12rem;
423 min-width: 9rem;
424 max-width: 22rem;
425 margin-left: auto;
426}
427.gsearch-in {
428 width: 100%;
429 box-sizing: border-box;
430 font: inherit;
431 font-size: 0.9rem;
432 padding: 0.35rem 0.6rem;
433 color: var(--text-primary);
434 background: var(--bg-primary);
435 border: 1px solid var(--border-strong);
436 border-radius: var(--radius-sm, 4px);
437}
438.gsearch-in::placeholder { color: var(--text-muted); }
439.gsearch-in:focus {
440 outline: 2px solid var(--accent);
441 outline-offset: 1px;
442 border-color: var(--accent);
443}
444/* The browser's own clear button, which is a different shape in every engine
445 and a different colour from everything around it. Escape clears the box. */
446.gsearch-in::-webkit-search-cancel-button { display: none; }
447
448.gsearch-panel {
449 position: absolute;
450 top: calc(100% + 0.35rem);
451 right: 0;
452 width: min(30rem, 92vw);
453 max-height: min(28rem, 70vh);
454 overflow-y: auto;
455 z-index: 20;
456 background: var(--bg-secondary);
457 border: 1px solid var(--border-strong);
458 border-radius: var(--radius-sm, 4px);
459 box-shadow: 0 8px 28px rgb(0 0 0 / 28%);
460}
461.gsearch-panel[hidden] { display: none; }
462
463.gsearch-hit {
464 display: block;
465 padding: 0.5rem 0.7rem;
466 border-bottom: 1px solid var(--border);
467 color: var(--text-secondary);
468 font-size: 0.875rem;
469 line-height: 1.45;
470}
471.gsearch-hit:last-child { border-bottom: none; }
472.gsearch-hit:hover { text-decoration: none; }
473/* One highlight, driven by the keyboard AND by the pointer, so there are never
474 two rows that both look like the one Enter would take. */
475.gsearch-hit.on { background: var(--bg-hover); }
476.gsearch-hit:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
477
478.gsearch-t {
479 display: block;
480 color: var(--text-primary);
481 font-weight: 600;
482}
483.gsearch-u {
484 display: block;
485 font-size: 0.78rem;
486 color: var(--text-muted);
487 margin-top: 0.05rem;
488}
489.gsearch-b {
490 display: block;
491 margin-top: 0.2rem;
492 color: var(--text-secondary);
493}
494.gsearch-hit mark {
495 background: var(--accent-soft, transparent);
496 color: var(--text-primary);
497 font-weight: 600;
498 padding: 0 1px;
499 border-radius: 2px;
500}
501.gsearch-none {
502 margin: 0;
503 padding: 0.75rem 0.7rem;
504 color: var(--text-muted);
505 font-size: 0.875rem;
506}
507/* The announcement for a screen reader, and nothing for anyone else. Not
508 `display:none`, which is not announced at all. */
509.gsearch-live {
510 position: absolute;
511 width: 1px; height: 1px;
512 overflow: hidden;
513 clip-path: inset(50%);
514 white-space: nowrap;
515}
516
517/* Where a result landed. A fragment jump alone leaves the reader at a heading
518 that looks like every other heading, so the target says so for a moment. */
519.gsearch-landed {
520 animation: gsearch-flash 2.6s ease-out;
521 border-radius: var(--radius-sm, 4px);
522}
523@keyframes gsearch-flash {
524 0%, 55% { background: var(--accent-soft, transparent); box-shadow: 0 0 0 6px var(--accent-soft, transparent); }
525 100% { background: transparent; box-shadow: 0 0 0 6px transparent; }
526}
527@media (prefers-reduced-motion: reduce) {
528 .gsearch-landed { animation: none; outline: 2px solid var(--accent); outline-offset: 4px; }
529}
530
531@media (max-width: 560px) {
532 .gsearch { flex: 1 1 100%; max-width: none; margin-left: 0; order: 3; }
533 .gsearch-panel { width: 100%; right: auto; left: 0; }
534}