Oregami
Repositories/oxedyne/daimond

oxedyne/daimond/www/css/viewer.css

24.8 KiB, 1 run

created by r2519314175:1143, 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 viewer.css — how a file looks when it is shown as what it is.
3 ------------------------------------------------------------
4 The markup is built by www/js/viewer.js. One root, `.fileview`,
5 carrying a `data-viewer` attribute that names the tier drawing
6 it, then a quiet meta line, any warnings, and a body.
7
8 Everything here is spent from the tokens in variables.css, so
9 the eleven palettes and the two skins are covered by not having
10 an opinion about colour. Two things do have an opinion:
11
12 * the hex dump keeps a monospace face and a fixed measure
13 whatever the skin says about de-teching telemetry, because
14 its columns ARE the information; and
15 * everything wide -- the dump, a table, a framed page --
16 scrolls inside its own box. The panel is 380px on a phone
17 and the page itself must never scroll sideways.
18 ============================================================ */
19
20/* The viewer owns whatever element it is handed, and fills it. */
21.fileview {
22 flex: 1; min-height: 0;
23 display: flex; flex-direction: column;
24 overflow: hidden;
25 background: var(--bg-primary);
26}
27
28/* ── The line that says what this is ─────────────────────────── */
29.fv-meta {
30 flex: none;
31 display: flex; align-items: baseline; justify-content: space-between; gap: 8px;
32 padding: 6px 12px;
33 border-bottom: 1px solid var(--border);
34 font-size: var(--fs-2xs);
35 color: var(--text-secondary);
36}
37.fv-fmt { font-weight: 600; letter-spacing: 0.02em; }
38.fv-size { font-family: var(--font-mono); white-space: nowrap; }
39
40/* A note explains; a warning says something is not as it seems. Both are
41 quiet -- neither is an error, and a file whose name and bytes disagree is
42 interesting rather than broken. */
43.fv-note,
44.fv-warn {
45 flex: none; margin: 0;
46 padding: 6px 12px;
47 font-size: var(--fs-xs);
48 line-height: 1.45;
49 border-bottom: 1px solid var(--border);
50}
51.fv-note { color: var(--text-muted); }
52.fv-warn { color: var(--warn); background: var(--warn-bg); }
53
54/* ── The body ────────────────────────────────────────────────── */
55.fv-body {
56 flex: 1; min-height: 0;
57 display: flex; flex-direction: column;
58 overflow: auto;
59}
60
61/* ── Tier 1: what the browser decodes ────────────────────────── */
62
63/* A soft chequer behind a picture, so a transparent PNG is a picture with
64 holes in it rather than a picture that half vanished into the panel. Built
65 from two surface tokens, so it follows every palette. */
66.fv-img {
67 flex: 1; min-height: 0;
68 max-width: 100%;
69 object-fit: contain;
70 align-self: center;
71 background-color: var(--bg-tertiary);
72 background-image:
73 linear-gradient(45deg, var(--bg-hover) 25%, transparent 25%, transparent 75%, var(--bg-hover) 75%),
74 linear-gradient(45deg, var(--bg-hover) 25%, transparent 25%, transparent 75%, var(--bg-hover) 75%);
75 background-size: 16px 16px;
76 background-position: 0 0, 8px 8px;
77}
78
79.fv-audio {
80 flex: none;
81 width: calc(100% - 24px);
82 margin: 12px;
83}
84
85.fv-video {
86 flex: 1; min-height: 0;
87 width: 100%;
88 background: #000; /* a letterbox is black in every palette */
89}
90
91/* A PDF, in the browser's own viewer. It is an <embed> and not a frame because
92 a sandbox attribute of any value stops Chrome instantiating that viewer; what
93 keeps the file from running as us is the blob's type, set in the JS. */
94.fv-doc {
95 flex: 1; min-height: 0;
96 width: 100%;
97 border: 0;
98 background: var(--bg-tertiary);
99}
100
101/* A framed file runs in an opaque origin (sandbox="allow-scripts", set in the
102 JS and nowhere else). Nothing here may add a permission to it. */
103.fv-frame {
104 flex: 1; min-height: 0;
105 width: 100%;
106 border: 0;
107 background: var(--bg-tertiary);
108}
109
110/* ── Tier 2: text-shaped structure ───────────────────────────── */
111
112.fv-plain {
113 flex: 1; min-height: 0; margin: 0;
114 padding: 12px 14px; overflow: auto;
115 white-space: pre-wrap; word-break: break-word;
116 font-family: var(--font-mono); font-size: var(--fs-sm);
117 color: var(--text-primary);
118}
119
120.fv-md {
121 flex: 1; min-height: 0;
122 padding: 12px 14px; overflow: auto;
123 font-size: var(--fs-base); line-height: 1.55;
124 color: var(--text-primary);
125}
126/* The renderer's own furniture -- code blocks, links, token colours -- comes
127 from render.css. What is left is the plain document flow, which the chat gets
128 from the bubble around it and a document has to be given. */
129.fv-md > :first-child { margin-top: 0; }
130.fv-md h1, .fv-md h2, .fv-md h3, .fv-md h4 { margin: 1.1em 0 0.45em; line-height: 1.25; }
131.fv-md h1 { font-size: var(--fs-3xl); }
132.fv-md h2 { font-size: var(--fs-2xl); }
133.fv-md h3 { font-size: var(--fs-xl); }
134.fv-md h4 { font-size: var(--fs-lg); }
135.fv-md p, .fv-md ul, .fv-md ol, .fv-md blockquote { margin: 0.55em 0; }
136.fv-md ul, .fv-md ol { padding-left: 1.4em; }
137.fv-md blockquote {
138 padding-left: 0.8em; border-left: 2px solid var(--border-2);
139 color: var(--text-secondary);
140}
141.fv-md hr { border: 0; border-top: 1px solid var(--border); margin: 1.2em 0; }
142.fv-md img { max-width: 100%; height: auto; }
143/* A wide table in a document scrolls in its own box, like every other wide
144 thing here. `display: block` on the table is what gives it one. */
145.fv-md table {
146 display: block; overflow-x: auto; max-width: 100%;
147 border-collapse: collapse; font-size: var(--fs-sm);
148}
149.fv-md th, .fv-md td { border: 1px solid var(--border); padding: 4px 8px; text-align: left; }
150.fv-md th { background: var(--bg-tertiary); color: var(--text-secondary); }
151
152/* A JSON tree. The disclosure marker is the browser's; the indent is one
153 ruled line per level, which is what makes a deep document readable at a
154 glance on a narrow panel. */
155.fv-jnode { padding: 0; }
156.fv-body > .fv-jnode,
157.fv-body > .fv-jrow { padding: 10px 12px; }
158.fv-jsum {
159 cursor: pointer; list-style: none;
160 display: flex; gap: 8px; align-items: baseline;
161 padding: 1px 0;
162 font-family: var(--font-mono); font-size: var(--fs-xs);
163}
164.fv-jsum::-webkit-details-marker { display: none; }
165.fv-jsum::before {
166 content: '▸'; color: var(--text-muted);
167 font-size: var(--fs-3xs); line-height: 1.6; width: 0.8em;
168}
169.fv-jnode[open] > .fv-jsum::before { content: '▾'; }
170.fv-jsum:hover { background: var(--bg-hover); }
171.fv-jsum:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
172.fv-jkids {
173 margin-left: 0.55em; padding-left: 0.75em;
174 border-left: 1px solid var(--border);
175}
176.fv-jrow {
177 display: flex; gap: 8px; align-items: baseline;
178 padding: 1px 0;
179 font-family: var(--font-mono); font-size: var(--fs-xs);
180 word-break: break-word;
181}
182.fv-jkey { color: var(--text-secondary); flex: none; }
183.fv-jkey::after { content: ':'; opacity: 0.6; }
184.fv-jshape { color: var(--text-muted); }
185.fv-jval { color: var(--text-primary); min-width: 0; }
186.fv-j-string { color: var(--accent-text); }
187.fv-j-number { color: var(--ok); }
188.fv-j-boolean { color: var(--warn); }
189.fv-j-null { color: var(--text-muted); font-style: italic; }
190
191/* A table scrolls inside its own box, in both directions. Fifty columns of
192 CSV must not push the panel -- or the page behind it -- sideways. */
193.fv-tablewrap {
194 flex: 1; min-height: 0;
195 overflow: auto;
196 padding: 0 0 8px 0;
197}
198.fv-table {
199 border-collapse: collapse;
200 font-size: var(--fs-xs);
201 font-variant-numeric: tabular-nums;
202}
203.fv-table th,
204.fv-table td {
205 border: 1px solid var(--border);
206 padding: 3px 8px;
207 text-align: left; vertical-align: top;
208 max-width: 28ch; overflow-wrap: anywhere;
209 color: var(--text-primary);
210}
211.fv-table th {
212 position: sticky; top: 0; z-index: 1;
213 background: var(--bg-tertiary);
214 color: var(--text-secondary);
215 font-weight: 600; white-space: nowrap;
216}
217.fv-table tr:nth-child(even) td { background: var(--bg-secondary); }
218
219/* ── What a reader may DO to the document ─────────────────────
220 `fv-bar` holds the controls, `fv-editrow` the fields one of them
221 opens. Both are rows of the panel and neither floats over the
222 document: the whole point of a reading view is that it stays
223 readable while somebody works on it, and a sheet over the top is
224 the same as taking it away. The typst live view above says this
225 about a rebuild and it is just as true of an edit.
226
227 The buttons wear `.fv-btn`, which the hex bar already defined, so
228 a control added here looks like a control already in the panel
229 rather than like a control from somewhere else. */
230.fv-bar {
231 flex: none;
232 display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
233 padding: 6px 12px;
234 border-bottom: 1px solid var(--border);
235}
236
237.fv-editrow {
238 flex: none;
239 display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
240 padding: 6px 12px;
241 border-bottom: 1px solid var(--border);
242 background: var(--bg-secondary);
243 font-size: var(--fs-xs);
244}
245.fv-editrow[hidden] { display: none; }
246.fv-editrow label {
247 display: flex; align-items: center; gap: 5px;
248 color: var(--text-secondary);
249}
250/* The fields are TOOLBAR-SIZED, and the SELECTOR is the whole of how, which is a
251 bug fixed rather than a style.
252
253 `skin-warm.css` gives every `input[type="text"]` room to type --
254 `:root[data-skin="warm"] input[type="text"]`, padding of 12.6px and 18.2px at
255 that skin's scale. It is loaded AFTER this file and its specificity is (0,3,1),
256 so `.fileview .fv-editrow input` at (0,2,1) lost: measured, the boxes came out
257 43px tall in a row of 22px buttons. That is the same rule that left `.tl-page`
258 above a tall empty pill with its digit clipped out, one step short of doing it
259 again -- a `width: 5ch` box under 18.2px of side padding has NO content width
260 at all.
261
262 Naming the type here is not enough on its own: `[type="text"]` counts on BOTH
263 sides, so `.fileview .fv-editrow input[type="text"]` is also (0,3,1) and a tie
264 goes to whichever file loads last, which is the skin. `.fv-body` is the third
265 class -- the row is always inside the panel's body -- and (0,4,1) wins
266 outright rather than on load order. Measured under both skins at 640px and
267 380px: 22px boxes in a row of 22px buttons.
268
269 The skin's rule is right for a form and wrong for a toolbar; this says which
270 this is. */
271.fileview .fv-body .fv-editrow input[type="text"],
272.fileview .fv-body .fv-editrow input[type="number"],
273.fileview .fv-body .fv-editrow select,
274.fileview .fv-body .fv-bar select {
275 background: var(--bg-tertiary); border: 1px solid var(--border-strong);
276 color: var(--text-primary); border-radius: var(--radius-sm);
277 padding: 2px 6px; height: auto; min-height: 0;
278 font-size: var(--fs-xs); line-height: 1.3;
279}
280.fileview .fv-body .fv-editrow input[type="text"] { width: 14ch; }
281.fileview .fv-body .fv-editrow input[type="number"] { width: 5ch; text-align: right; }
282.fv-editrow input:focus-visible,
283.fv-editrow select:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
284/* The row's own sentence takes the whole width under the fields, since it is a
285 sentence and not a field. It keeps `fv-note`'s colour and loses its border, so
286 the row reads as one thing. */
287.fv-editrow .fv-note {
288 flex: 1 0 100%;
289 padding: 2px 0 0 0;
290 border-bottom: 0;
291}
292
293/* On a phone the fields stack rather than shrink: a 14ch box squeezed into a
294 380px row is a box nobody can read what they typed in. */
295@media (max-width: 380px) {
296 .fv-bar { padding: 6px 10px; }
297 .fv-editrow { padding: 6px 10px; }
298 .fv-editrow label { flex: 1 0 100%; }
299 .fileview .fv-body .fv-editrow input[type="text"] { flex: 1 1 auto; width: auto; }
300}
301
302/* ── Tier 3: the honest floor ────────────────────────────────── */
303
304.fv-hexbar {
305 flex: none;
306 display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
307 padding: 6px 12px;
308 border-bottom: 1px solid var(--border);
309}
310.fv-btn {
311 background: transparent; border: 1px solid var(--border-strong);
312 color: var(--text-secondary); border-radius: var(--radius-sm);
313 padding: 3px 8px; cursor: pointer;
314 font-size: var(--fs-xs); line-height: 1.2;
315}
316.fv-btn:hover:not(:disabled) { color: var(--text-primary); border-color: var(--accent); }
317.fv-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
318.fv-btn:disabled { opacity: 0.5; cursor: default; }
319.fv-hexat {
320 font-family: var(--font-mono); font-size: var(--fs-2xs);
321 color: var(--text-muted); white-space: nowrap;
322}
323
324/* The dump keeps its measure. `white-space: pre` and its own horizontal
325 scrollbar: sixteen bytes to a line is the format, and a line that wraps at
326 380px is no longer a hex dump. The skin does not get to soften this face
327 either -- the columns only line up in a monospace. */
328.fv-hex {
329 flex: 1; min-height: 0; margin: 0;
330 padding: 10px 12px;
331 overflow: auto;
332 white-space: pre;
333 font-family: var(--font-mono);
334 font-size: var(--fs-2xs);
335 line-height: 1.45;
336 color: var(--text-primary);
337 tab-size: 4;
338}
339
340/* On a phone the dump is the widest thing in the app. It still scrolls in its
341 own box rather than wrapping, and the type drops one rung so that more of a
342 line is on screen before anybody has to swipe. */
343@media (max-width: 380px) {
344 .fv-hex { font-size: var(--fs-3xs); padding: 8px 10px; }
345 .fv-meta { padding: 6px 10px; }
346 .fv-note,
347 .fv-warn { padding: 6px 10px; }
348 .fv-hexbar { padding: 6px 10px; }
349 .fv-hexat { white-space: normal; }
350}
351
352/* ============================================================
353 The watched live document — www/js/typstwatch.js
354 ------------------------------------------------------------
355 The pages the author is looking at while he writes. It sits
356 INSIDE the Doc panel, over the <embed> the Compile button
357 filled, and takes nothing away: closing it puts the PDF back.
358
359 Two rules here are load-bearing and neither is decoration.
360
361 NOTHING MAY OVERLAY THE PAGES. The mark that a rebuild is
362 running is a dot and a few words in a bar ABOVE the document,
363 never a veil over it and never a spinner in the middle of it.
364 The whole point of the loop is that the last good pages stay
365 readable while the next ones are built, and dimming them is
366 the same as taking them away for anybody actually reading.
367
368 AND THE PAGES ARE NOT SKINNED. They are the book: the paper is
369 white because the paper prints white, and the ink is the ink
370 the PDF carries. The app's palette stops at the bar. A dark
371 theme that tinted the page would be showing the author a book
372 that does not exist.
373 ============================================================ */
374
375/* A ROW OF THE PANEL, NOT A SHEET OVER IT, and that is a bug fixed rather than a
376 style. Laid out first as `position: absolute; inset: 0`, which covered the
377 panel's own header — so the ✕ that closes the document was underneath it and the
378 reader could not put the document down. The live view is a third rendering of
379 the Doc panel beside the text view and the <embed>, so it takes its place in the
380 same column as the other two and leaves the header alone. */
381.typst-live {
382 flex: 1 1 auto;
383 display: flex; flex-direction: column;
384 min-height: 0;
385 background: var(--bg-tertiary);
386}
387
388.tl-bar {
389 display: flex; align-items: center; gap: 8px;
390 padding: 5px 10px;
391 border-bottom: 1px solid var(--border-subtle);
392 background: var(--bg-secondary);
393 font-size: var(--fs-2xs);
394 color: var(--text-muted);
395 flex: 0 0 auto;
396}
397.tl-says { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
398
399/* THE RAIL'S OWN SWITCH, at the far left of the bar, which is where every
400 document viewer puts it and therefore where a reader looks for it. */
401.tl-toc {
402 flex: 0 0 auto;
403 background: transparent; border: 1px solid var(--border-subtle);
404 color: var(--text-secondary); border-radius: var(--radius-sm);
405 padding: 0 6px; cursor: pointer;
406 font-size: var(--fs-xs); line-height: 1.5;
407}
408.tl-toc:hover { color: var(--text-primary); border-color: var(--accent); }
409.tl-toc:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
410.tl-toc[aria-pressed="true"] { color: var(--text-primary); border-color: var(--accent); }
411
412/* A dot, and what it means. Quiet on purpose: it reports, it does not ask. */
413.tl-mark {
414 width: 7px; height: 7px; border-radius: 50%;
415 flex: 0 0 auto;
416 background: var(--text-muted);
417}
418.tl-mark.tl-live { background: var(--ok, #3fa66a); }
419.tl-mark.tl-building { background: var(--accent); animation: tl-pulse 1.1s ease-in-out infinite; }
420.tl-mark.tl-stale,
421.tl-mark.tl-held { background: var(--warn, #c9932b); }
422.tl-mark.tl-dead { background: var(--danger, #c0453c); }
423@keyframes tl-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.25; } }
424/* A reader who has asked for less movement gets a steady dot rather than none:
425 the state is still legible, it simply stops moving. */
426@media (prefers-reduced-motion: reduce) {
427 .tl-mark.tl-building { animation: none; }
428}
429
430.tl-rebuild {
431 flex: 0 0 auto;
432 background: transparent; border: 1px solid var(--border-strong);
433 color: var(--text-secondary); border-radius: var(--radius-sm);
434 padding: 2px 8px; cursor: pointer;
435 font-size: var(--fs-2xs); line-height: 1.2;
436}
437.tl-rebuild:hover { color: var(--text-primary); border-color: var(--accent); }
438.tl-rebuild:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
439
440/* THE SETTINGS THE VIEW SHIPPED WITHOUT. A page box, a zoom and dark paper, in
441 the bar it already had rather than behind a menu: there are three of them, and
442 a menu holding three things is a place to hide them. They ride at the right so
443 the status keeps the left, which is where a reader looks first. */
444.tl-set {
445 flex: 0 0 auto;
446 display: flex; align-items: center; gap: 4px;
447}
448.tl-set button {
449 background: transparent; border: 1px solid var(--border-subtle);
450 color: var(--text-secondary); border-radius: var(--radius-sm);
451 padding: 1px 6px; cursor: pointer;
452 font-size: var(--fs-2xs); line-height: 1.3;
453 min-width: 22px;
454}
455.tl-set button:hover { color: var(--text-primary); border-color: var(--accent); }
456.tl-set button:focus-visible,
457.tl-page:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
458.tl-set .tl-fit { min-width: 42px; font-variant-numeric: tabular-nums; }
459.tl-set .tl-night { margin-left: 4px; }
460.tl-page {
461 width: 3.2em;
462 background: var(--bg-tertiary); border: 1px solid var(--border-subtle);
463 color: var(--text-primary); border-radius: var(--radius-sm);
464 padding: 1px 4px; text-align: right;
465 font-size: var(--fs-2xs); line-height: 1.3;
466 font-variant-numeric: tabular-nums;
467}
468/* AND THE PAGE NUMBER HAS TO FIT IN THE BOX, which it did not.
469
470 `skin-warm.css` gives every `input[type="text"]` room to type --
471 `padding: calc(9px * var(--space-scale)) calc(13px * var(--space-scale))`,
472 which at the warm skin's scale is 12.6px and 18.2px. That selector is
473 `:root[data-skin="warm"] input[type="text"]` and beats a bare `.tl-page`,
474 so on a box 36px wide with `box-sizing: border-box` the content width came
475 out at ZERO and `overflow: clip` took the digit away: the page box was a
476 tall empty pill beside "/ 4", in a bar of 20px controls, and had been since
477 it shipped. Measured, not guessed -- the computed padding is above and the
478 crop that found it is in the report.
479
480 The id wins the specificity, and the height is stated rather than left to
481 the padding so that the box matches the buttons it sits between. The skin's
482 rule is right for a form and wrong for a toolbar; this says which this is. */
483#typst-live .tl-page {
484 padding: 1px 4px;
485 height: auto;
486 min-height: 0;
487 border-radius: var(--radius-sm);
488}
489.tl-of { color: var(--text-muted); font-variant-numeric: tabular-nums; margin-right: 4px; }
490
491/* THE RAIL AND THE PAGES SHARE A ROW. The rail takes width from the pages
492 rather than sitting over them, so nothing is ever hidden behind it and the
493 zoom keeps meaning "as wide as what is left". */
494.tl-body {
495 flex: 1 1 auto; min-height: 0;
496 display: flex; align-items: stretch;
497}
498
499/* THE SECTIONS, HYPERLINKED. Its own scroller, because a long book's contents
500 is longer than the panel and the pages must not move when it is scrolled.
501 The page number rides right, as it does in a printed contents. */
502.tl-rail {
503 flex: 0 0 auto;
504 width: 210px; max-width: 45%;
505 overflow: auto;
506 border-right: 1px solid var(--border-subtle);
507 background: var(--bg-secondary);
508 padding: 6px 0;
509}
510.tl-rail[hidden] { display: none; }
511.tl-toclist { list-style: none; margin: 0; padding: 0; }
512.tl-toclist li { margin: 0; }
513.tl-tocgo {
514 display: flex; align-items: baseline; gap: 6px;
515 width: 100%;
516 background: transparent; border: 0;
517 color: var(--text-secondary);
518 text-align: left; cursor: pointer;
519 padding: 3px 8px 3px 10px;
520 font-size: var(--fs-2xs); line-height: 1.35;
521}
522.tl-tocgo:hover:not(:disabled) { color: var(--text-primary); background: var(--bg-tertiary); }
523.tl-tocgo:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
524/* An entry whose page is not known yet still READS -- it is the document's own
525 contents -- it simply cannot be jumped to. Dimming it says which. */
526.tl-tocgo:disabled { cursor: default; opacity: 0.55; }
527.tl-tocpage {
528 margin-left: auto; flex: 0 0 auto;
529 color: var(--text-muted);
530 font-variant-numeric: tabular-nums;
531}
532/* The level is the indent, and nothing else: a rail that also changed the size
533 of every level reads as six typefaces rather than one contents. */
534.tl-toclist li[data-level="2"] .tl-tocgo { padding-left: 22px; }
535.tl-toclist li[data-level="3"] .tl-tocgo { padding-left: 34px; }
536.tl-toclist li[data-level="4"] .tl-tocgo { padding-left: 46px; }
537.tl-toclist li[data-level="5"] .tl-tocgo { padding-left: 58px; }
538.tl-toclist li[data-level="6"] .tl-tocgo { padding-left: 70px; }
539.tl-toclist li[data-level="1"] .tl-tocgo { color: var(--text-primary); }
540/* Where the reader is, marked on the rail rather than announced anywhere. */
541.tl-toclist li[data-here="1"] .tl-tocgo {
542 color: var(--text-primary);
543 box-shadow: inset 2px 0 0 var(--accent);
544}
545.tl-tocnone {
546 margin: 0; padding: 8px 10px;
547 color: var(--text-muted);
548 font-size: var(--fs-2xs); line-height: 1.4;
549}
550
551/* The scroller, and the reader's place in it. Nothing else scrolls: the pages
552 are one tall column, so the scroll position IS the place in the book. */
553.tl-scroll {
554 flex: 1 1 auto; min-height: 0;
555 overflow: auto;
556 background: var(--bg-tertiary);
557 padding: 8px;
558}
559/* THE STACK, WHICH IS NOT ITSELF PAPER. Each page is its own sheet inside the
560 shadow root and carries its own white and its own shadow (`SHEET_CSS` in
561 `www/js/typstwatch.js`, because the app's stylesheet does not reach into a
562 shadow root); what shows between two sheets is the panel's own ground, and
563 that gap is the whole of "distinct pages, not a continuous one".
564
565 THE CONTAINER CARRIES THE HEIGHT AND EACH SHEET CARRIES ONE PAGE, which is
566 why this is `position: relative` with its height set from JavaScript. An SVG
567 as tall as a 251-page book is 192,000 pixels, and Chrome rasterises that far
568 from its origin at a reduced resolution: page 1 came out at 5.6% dark pixels
569 and page 40 at 0.00% -- the words still there in outline, the page a pale
570 wash. Measured, and it looks exactly like a colour bug. */
571.tl-pages {
572 display: block;
573 position: relative;
574 background: transparent;
575 margin: 0 auto;
576 /* The WIDTH IS SET FROM JAVASCRIPT, like the height, because the reader can
577 now ask for pages wider than the panel. A `max-width: 100%` here would have
578 clamped every zoom past "fit" back to the panel and made the control look
579 broken while doing exactly what it said. */
580 max-width: none;
581}
582
583/* DARK PAPER. The pages are inverted where they are drawn, so the panel around
584 them keeps the app's own ground and only the paper turns over; `hue-rotate`
585 after the inversion puts colours back roughly where they were, so a red figure
586 stays red instead of turning cyan. Nothing is recompiled and nothing about the
587 document changes: what is exported and printed is what it always was. */
588.typst-live[data-night="1"] .tl-pages {
589 filter: invert(1) hue-rotate(180deg);
590}
591
592/* The compiler's own words, under the document rather than over it, so a broken
593 build costs the reader a strip at the bottom and not the page. */
594.tl-err {
595 flex: 0 0 auto;
596 max-height: 32%;
597 overflow: auto;
598 margin: 0;
599 padding: 8px 10px;
600 border-top: 1px solid var(--border-subtle);
601 background: var(--bg-secondary);
602 color: var(--danger, #c0453c);
603 font-family: var(--font-mono);
604 font-size: var(--fs-2xs);
605 line-height: 1.45;
606 white-space: pre-wrap;
607 overflow-wrap: anywhere;
608}
609
610/* On a phone the bar loses the word and keeps the dot: the state is still
611 there, and the document gets the width. */
612@media (max-width: 380px) {
613 .tl-bar { padding: 4px 8px; }
614 .tl-scroll { padding: 4px; }
615 .tl-err { max-height: 40%; }
616 /* The page box and the percentage go; the zoom and the paper stay, because
617 those are the two a phone actually needs. The status keeps its dot. */
618 .tl-page, .tl-of, .tl-set .tl-fit { display: none; }
619 /* And the rail takes the whole panel rather than a slice of it: 210px of a
620 360px screen leaves a column of paper nobody can read. */
621 .tl-rail { width: 100%; max-width: none; border-right: 0; }
622 .typst-live[data-rail="1"] .tl-scroll { display: none; }
623}