lmjtfy.git / apps / lmjtfy / src / page.css
page.cssannotatedpage.csssource488 lines · 29.8 KB · raw
1/* After typesafe.ai: near-black on white, one pink band, ordered-dither dot
2   fields, and old operating system windows (black title bar, grey body, inset
3   double border). One look, light only, as theirs is.
4   Layout: a white sheet holding the headline and the Ask and Jev windows,
5   then a full-width pink band holding one window per call. */
6:root {
7  --ink: #1e1e1e;
8  --paper: #fefefe;
9  --grey: #dedede;
10  --mid: #c4c4c4;
11  --pink: #f386a1;
12  --plum: #8a2456;
13  --display: "Inter Tight", "Helvetica Neue", Arial, sans-serif;
14  --pixel: "VT323", "Courier New", monospace;
15  --mono: "JetBrains Mono", ui-monospace, Menlo, monospace;
16  color-scheme: light;
17}
18* { box-sizing: border-box; }
19/* The paper's dither is the body's, so the two white sections share one field
20   with no seam where the transcript begins. */
21body {
22  margin: 0; color: var(--ink);
23  background: var(--paper) var(--dither-paper, none) top center / 1200px 900px repeat;
24  image-rendering: pixelated;
25  font: 14px/1.5 var(--mono);
26}
27.wrap { max-width: 46rem; margin: 0 auto; padding-inline: 16px; }
28
29/* The dither fields are drawn once by page.js into two CSS variables, at one
30   image pixel per dot, and scaled up unsmoothed. */
31.sheet { padding-block: 0 3.5rem; }
32.band {
33  background: var(--pink) var(--dither-pink, none) top center / 1200px 900px repeat;
34  image-rendering: pixelated; padding-block: 2.5rem 3rem;
35  border-block: 1px solid var(--ink);
36}
37
38nav { display: flex; gap: 0.5rem; flex-wrap: wrap; }
39nav a, nav span {
40  font: 500 1.05rem/1 var(--display); letter-spacing: -0.02em;
41  padding: 0.55rem 0.5rem; background: var(--paper); color: var(--ink); text-decoration: none;
42}
43nav a.dark { background: var(--ink); color: var(--paper); }
44/* The dark links sit together on the right. */
45nav a:first-child { margin-right: auto; }
46
47.headline { position: relative; margin-block: 3rem 2.5rem; padding: 1.25rem 0.75rem; }
48/* Crop marks, as around their headlines. */
49.headline::before, .headline::after {
50  content: ""; position: absolute; inset: 0; pointer-events: none;
51  background:
52    linear-gradient(var(--ink), var(--ink)) top left / 10px 1px,
53    linear-gradient(var(--ink), var(--ink)) top left / 1px 10px,
54    linear-gradient(var(--ink), var(--ink)) top right / 10px 1px,
55    linear-gradient(var(--ink), var(--ink)) top right / 1px 10px,
56    linear-gradient(var(--ink), var(--ink)) bottom left / 10px 1px,
57    linear-gradient(var(--ink), var(--ink)) bottom left / 1px 10px,
58    linear-gradient(var(--ink), var(--ink)) bottom right / 10px 1px,
59    linear-gradient(var(--ink), var(--ink)) bottom right / 1px 10px;
60  background-repeat: no-repeat;
61}
62.headline::after { display: none; }
63.eyebrow { font: 500 0.95rem var(--display); letter-spacing: -0.01em; margin: 0 0 0.75rem; text-align: center; }
64h1 {
65  font: 500 clamp(2.6rem, 11vw, 6rem)/0.92 var(--display);
66  letter-spacing: -0.045em; margin: 0; text-align: center; text-wrap: balance;
67}
68
69/* A window. */
70.win { border: 1px solid var(--ink); background: var(--grey); min-width: 0; }
71.bar-title {
72  display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap;
73  background: var(--ink); color: var(--paper);
74  font: 1.25rem/1 var(--pixel); padding: 0.2rem 0.4rem; min-height: 1.6rem;
75}
76.bar-title .meta { margin-left: auto; color: var(--mid); }
77.win > .body {
78  margin: 3px; border: 1px solid var(--ink); padding: 0.9rem; min-width: 0;
79  display: grid; gap: 0.9rem;
80}
81.win.white > .body { background: var(--paper); }
82.icon { width: 1.1rem; height: 1.1rem; flex: none; shape-rendering: crispEdges; }
83.meatproxy { text-align: left; }
84.meatproxy .llms { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0.75rem; margin: 0.75rem 0 0; justify-items: center; }
85.meatproxy a.llm { display: grid; justify-items: center; gap: 0.35rem; min-width: 0; color: var(--ink); text-decoration: none; }
86.meatproxy a.llm span:last-child { text-decoration: underline; text-decoration-color: var(--ink); text-underline-offset: 0.2em; }
87.meatproxy a.llm:hover span:last-child, .meatproxy a.llm:focus-visible span:last-child { text-decoration-thickness: 2px; }
88.meatproxy .llm-mark { opacity: 0.8; transition: opacity 0.12s; }
89.meatproxy a.llm:hover .llm-mark, .meatproxy a.llm:focus-visible .llm-mark { opacity: 1; }
90@media (prefers-reduced-motion: reduce) { .meatproxy .llm-mark { transition: none; } }
91/* About four times a title-bar icon; the logos are 9 by 9 pixels in their own
92   colours, so only the size and the crispness are set here. */
93.meatproxy .llm-mark { display: block; width: min(4.5rem, 100%); aspect-ratio: 1; }
94.meatproxy .llm-mark svg { display: block; width: 100%; height: 100%; shape-rendering: crispEdges; image-rendering: pixelated; }
95.meatproxy .linkcard {
96  display: grid; max-width: 28rem; margin: 0.5rem 0 0; color: var(--ink); text-decoration: none;
97  border: 1px solid var(--ink); background: var(--paper); overflow: hidden; text-align: left;
98}
99.meatproxy .linkcard-image { display: block; width: 100%; height: auto; aspect-ratio: 1200 / 630; object-fit: cover; border-bottom: 1px solid var(--ink); }
100.meatproxy .linkcard-text { display: grid; gap: 0.2rem; padding: 0.6rem 0.75rem; min-width: 0; overflow-wrap: anywhere; }
101.meatproxy .linkcard-site, .meatproxy .linkcard-host { font-size: 0.8rem; opacity: 0.7; }
102.meatproxy .linkcard-title { font-weight: 500; }
103.meatproxy .linkcard-description { font-size: 0.9rem; }
104.icon.big { width: 3rem; height: 3rem; background: var(--paper); border: 1px solid var(--ink); padding: 3px; }
105
106.stack { display: grid; gap: 1.25rem; min-width: 0; }
107
108form { display: flex; flex-wrap: wrap; gap: 0.5rem; }
109input, #q {
110  flex: 1 1 14rem; min-width: 0;
111  font: 15px var(--mono); color: var(--ink); background: var(--paper);
112  border: 1px solid var(--ink); border-radius: 0; padding: 0.6rem 0.7rem;
113}
114button {
115  font: 1.35rem/1 var(--pixel); padding: 0.5rem 1.1rem; border-radius: 0; cursor: pointer;
116  background: var(--ink); color: var(--paper); border: 1px solid var(--ink);
117}
118button:disabled { opacity: 0.55; cursor: progress; }
119button.ghost { background: var(--paper); color: var(--ink); }
120:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }
121input:focus-visible, #q:focus-visible { outline-offset: 0; }
122/* The question box: one line to start, growing as the question wraps. Browsers
123   without `field-sizing` are grown by page.js. */
124#q { resize: none; field-sizing: content; line-height: 1.4; min-height: calc(1.4em + 1.2rem + 2px); max-height: 12rem; overflow-y: auto; display: block; }
125
126.live { margin: 0; min-height: 1.4rem; font: 1.2rem/1.15 var(--pixel); }
127.live .dot { display: inline-block; width: 0.5em; height: 0.5em; background: var(--paper); border: 1px solid var(--ink); margin-right: 0.5em; }
128.live .dot.yes { background: var(--ink); }
129.live .meta { color: #555; }
130
131#transcript { display: grid; gap: 0; min-width: 0; }
132.step { margin: 0 0 0.5rem; min-height: 1.4rem; font: 1.25rem/1.15 var(--pixel); text-transform: uppercase; letter-spacing: 0.03em; }
133.row { display: grid; grid-template-columns: 3.2rem 1fr; gap: 0.5rem; align-items: baseline; }
134.row > * { min-width: 0; }
135.who { font: 1.3rem/1 var(--pixel); }
136.q { overflow-wrap: anywhere; white-space: pre-wrap; }
137.said { display: grid; gap: 1.6rem; min-width: 0; }
138.result { min-width: 0; }
139.asked { margin: 0 0 0.5rem; font: 1.2rem/1.15 var(--pixel); color: #444; }
140.answer {
141  font: 500 clamp(3.4rem, 15vw, 6.5rem)/0.88 var(--display);
142  letter-spacing: -0.05em; margin: 0; color: var(--ink);
143  animation: stamp 260ms cubic-bezier(.2,1.6,.4,1);
144}
145.answer.pick { font-size: clamp(1.9rem, 8vw, 3.4rem); line-height: 0.95; overflow-wrap: anywhere; letter-spacing: -0.04em; }
146.answer.pick .of { font: 1.3rem/1 var(--pixel); letter-spacing: 0; white-space: nowrap; color: #444; }
147.answer.quiet { font-size: clamp(1.5rem, 6vw, 2.2rem); line-height: 1; letter-spacing: -0.03em; animation: none; }
148@keyframes stamp { from { transform: scale(1.8) rotate(-6deg); opacity: 0; } }
149.why { margin: 0.75rem 0 0; }
150.verdict { font: 1.2rem/1.15 var(--pixel); margin: 0.6rem 0 0; color: #444; }
151
152/* A probability, as their benchmark sliders: an inverted label, the figure
153   at the right, and a rule with a square at the value. */
154.bars { display: grid; gap: 0.55rem; margin-top: 1rem; }
155.bar { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 0.3rem 0.75rem; align-items: baseline; }
156.bar .label { font: 1.2rem/1.1 var(--pixel); overflow-wrap: anywhere; justify-self: start; padding: 0 0.3rem; background: var(--grey); }
157.bar.won .label { background: var(--ink); color: var(--paper); }
158.bar .tag { font: 1rem/1.1 var(--pixel); border: 1px solid currentColor; padding: 0 0.25rem; margin-left: 0.3rem; }
159.bar .pct { font: 1.2rem/1.1 var(--pixel); font-variant-numeric: tabular-nums; }
160.bar .track { grid-column: 1 / -1; height: 1px; background: var(--ink); position: relative; margin-block: 4px 5px; }
161.bar .thumb { position: absolute; top: -4px; width: 9px; height: 9px; margin-left: -4px; background: var(--ink); }
162.bar:not(.won) .thumb { background: var(--paper); border: 1px solid var(--ink); }
163
164/* A call: a window that folds. */
165details.win > summary { cursor: pointer; list-style: none; }
166details.win > summary::-webkit-details-marker { display: none; }
167details.win > summary .fold::before { content: "+"; }
168details.win[open] > summary .fold::before { content: "\2013"; }
169.fold { width: 1ch; text-align: center; }
170.wire { display: grid; gap: 0.3rem; min-width: 0; }
171.wire + .wire { border-top: 1px dotted var(--ink); padding-top: 0.8rem; }
172.label-s { font: 1.15rem/1 var(--pixel); text-transform: uppercase; letter-spacing: 0.04em; justify-self: start; background: var(--ink); color: var(--paper); padding: 0.05rem 0.35rem; }
173pre { margin: 0; font: 12.5px/1.55 var(--mono); white-space: pre-wrap; overflow-wrap: anywhere; min-width: 0; }
174.json .k { font-weight: 500; }
175.json .s { color: var(--plum); }
176.json .n { background: var(--paper); padding: 0 0.15em; }
177.note { margin: 0.25rem 0 0; font: 1.2rem/1.15 var(--pixel); }
178.note::before { content: "! "; }
179
180.fine { margin: 0; font-size: 0.8rem; }
181#budget .bars { margin-top: 0; }
182#budget .bar .pct { white-space: nowrap; }
183/* One row: the link takes what the label and the button leave, and is cut
184   with an ellipsis instead of wrapping. Where that would leave it under
185   12rem it drops to a line of its own. The button copies the whole link. */
186.share { display: flex; flex-wrap: wrap; gap: 0.5rem 0.9rem; align-items: center; min-width: 0; }
187.share span, .share button { flex: none; white-space: nowrap; }
188.share a, .share code { color: inherit; flex: 1 1 12rem; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
189/* The rules as a Rete network (diagram.rs, whose header says what each mark
190   means). A node or a line is one of four things:
191     holds  a rule that fired stands on it        filled ink, heavy line
192     spare  known to hold, and nothing needed it  ink outline, plain line
193     waits  on a fact not known yet               thin outline
194     fails  a test on it failed                   dashed, faint box, text kept readable
195   A rule that holds is pink, and one that decided a step carries its number.
196   On a narrow screen the drawing keeps its size and scrolls sideways. */
197.rete { overflow-x: auto; min-width: 0; }
198.rete svg { display: block; width: 100%; min-width: 660px; height: auto; font: 11px var(--mono); }
199.rete rect, .rete circle { fill: var(--paper); stroke: var(--ink); stroke-width: 1; }
200.rete text { fill: var(--ink); }
201.rete line, .rete path { stroke: var(--ink); stroke-width: 1; fill: none; opacity: 0.3; }
202.rete polygon { fill: var(--ink); opacity: 0.3; }
203.rete .frame rect { fill: none; stroke-dasharray: 1 3; }
204.rete .frame text { opacity: 0.7; }
205
206.rete line.holds, .rete path.holds, .rete .back.holds path { stroke-width: 2; opacity: 1; }
207.rete .back.holds polygon { opacity: 1; }
208.rete g.holds rect, .rete g.holds circle { fill: var(--ink); }
209.rete g.holds text { fill: var(--paper); }
210.rete g.rule.holds rect { fill: var(--pink); }
211.rete g.rule.holds text { fill: var(--ink); font-weight: 500; }
212.rete g.rule rect.nth { fill: var(--ink); }
213.rete g.rule text.nth { fill: var(--paper); }
214
215.rete line.spare, .rete path.spare { opacity: 0.7; }
216.rete g.spare rect, .rete g.spare circle { stroke-width: 1.5; }
217
218.rete line.fails, .rete path.fails, .rete .back.fails path { stroke-dasharray: 2 3; opacity: 0.2; }
219.rete .back.fails polygon { opacity: 0.2; }
220.rete g.fails rect, .rete g.fails circle { stroke-dasharray: 2 3; stroke-opacity: 0.35; }
221.rete g.fails text { opacity: 0.55; }
222/* The shared budget and the feed belong to the home page. They go once there
223   is an answer on the page, or a `?q=` question about to be typed. The
224   LMJTFY link in the corner is the way back. */
225body:has(#transcript > *) :is(#budget, #feed, #tally),
226body:has(#ask[data-type]:not([data-type=""])) :is(#budget, #feed, #tally) { display: none; }
227/* The feed: one line a question, cut with an ellipsis, its answer kept whole
228   up to half the row. It scrolls inside its window. */
229#feed:not(:has(.entry)) { display: none; }
230#more { justify-content: center; }
231.feed { list-style: none; margin: 0; padding: 0 0.6rem 0 0; display: grid; gap: 0.35rem; max-height: 13rem; overflow-y: auto; }
232.feed li { display: flex; gap: 0.9rem; justify-content: space-between; align-items: baseline; min-width: 0; }
233/* The question gives way, not the answer: an answer is cut only when it is
234   longer than half the row. */
235.feed a { color: inherit; flex: 1 1 0; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
236.read-as { opacity: 0.6; }
237.feed .times { opacity: 0.6; font-weight: 400; }
238.tally { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.35rem; }
239/* The playground: every test is a link, and what the rules say to do next. */
240.rete a { cursor: pointer; }
241.rete a:hover rect { stroke-width: 2; }
242.says { margin: 0; font-weight: 500; }
243.feed .said-s { flex: 0 0 auto; max-width: 50%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-weight: 500; }
244footer { padding-block: 1.5rem 3rem; font-size: 0.8rem; }
245footer a { color: inherit; }
246footer .about { display: inline-grid; grid-template-columns: auto 1fr; gap: 0.6rem; align-items: start; max-width: 30rem; }
247@media (prefers-reduced-motion: reduce) { .answer { animation: none; } }
248footer .clone { margin-top: 0.9rem; max-width: 40rem; }
249.inside { display: grid; grid-template-columns: max-content 1fr; gap: 0.45rem 1rem; margin: 0; }
250.inside dt { margin: 0; }
251.inside dd { margin: 0; min-width: 0; }
252.latest p { margin: 0 0 0.3rem; overflow-wrap: anywhere; }
253@media (max-width: 34rem) { .inside { grid-template-columns: 1fr; } .inside dd { margin-bottom: 0.4rem; } }
254/* On the code page the command is the point: it wraps rather than being cut. */
255.win .clone code { white-space: normal; overflow-wrap: anywhere; flex-basis: 100%; }
256
257/* The code pages. */
258.crumbs a, .bar-title a { color: inherit; }
259.code-page { margin-top: 2rem; }
260.bar-title .meta a { color: inherit; }
261.listing { width: 100%; border-collapse: collapse; }
262.listing td { padding: 0.3rem 0.4rem; border-top: 1px solid var(--grey); vertical-align: middle; }
263.listing tr:first-child td { border-top: 0; }
264.listing .name { display: flex; gap: 0.5rem; align-items: center; min-width: 0; overflow-wrap: anywhere; }
265.listing .name a { color: inherit; }
266.listing .size { text-align: right; white-space: nowrap; color: inherit; opacity: 0.7; }
267.source { padding: 0; overflow-x: auto; }
268.source pre { margin: 0; padding: 0.6rem 0; font: 0.85rem/1.45 var(--mono); counter-reset: none; }
269.source .line { display: block; padding-inline: 0.6rem 1rem; white-space: pre; }
270.source .line:target { background: var(--pink); }
271.source .number { display: inline-block; width: 3.5em; margin-right: 1rem; text-align: right; color: inherit; opacity: 0.45; text-decoration: none; user-select: none; }
272.md { line-height: 1.55; overflow-wrap: anywhere; }
273.md h1, .md h2, .md h3 { font-family: var(--display); line-height: 1.15; margin: 1.4rem 0 0.6rem; text-align: left; letter-spacing: -0.02em; }
274.md h1 { font-size: 2.2rem; }
275.md h2 { font-size: 1.5rem; }
276.md h3 { font-size: 1.15rem; }
277.md p, .md ul, .md ol { margin: 0 0 0.8rem; }
278.md h1:first-child, .md h2:first-child { margin-top: 0.2rem; }
279.md pre { background: var(--paper); border: 1px solid var(--ink); padding: 0.6rem 0.8rem; overflow-x: auto; }
280.md table { border-collapse: collapse; display: block; overflow-x: auto; }
281.md th, .md td { border: 1px solid var(--ink); padding: 0.3rem 0.55rem; text-align: left; vertical-align: top; overflow-wrap: normal; }
282.md td:first-child, .md th:first-child { white-space: nowrap; }
283.md th { background: var(--ink); color: var(--paper); }
284.md a { color: inherit; }
285
286/* Pages open now, and the toasts pushed over /live. */
287nav .online { font: 1.05rem/1 var(--pixel, var(--mono)); align-self: center; padding: 0.55rem 0.5rem; }
288/* `nav span` is a button's look; the wrapper and the list's parts are not
289   buttons. */
290nav .online-wrap { position: relative; display: flex; align-self: center; outline: none; padding: 0; background: none; }
291nav .places { display: none; position: absolute; top: 100%; right: 0; z-index: 20; min-width: 14rem; max-height: 60vh; overflow-y: auto;
292  margin: 0; padding: 0.3rem 0; list-style: none; background: var(--paper); border: 1px solid var(--ink); }
293nav .online-wrap:is(:hover, :focus-within) .places:has(li) { display: block; }
294nav .places li { display: flex; gap: 0.5rem; align-items: baseline; padding: 0.2rem 0.7rem; font: 1.1rem/1.2 var(--pixel, var(--mono)); white-space: nowrap; }
295nav .places span { font: inherit; letter-spacing: normal; padding: 0; background: none; }
296/* Windows has no flag emoji of its own, and the rest of the site is pixels:
297   SerenityOS's pixel emoji, cut down to the 26 letters a flag is made of and
298   the two vote thumbs (third-party/serenity-emoji). Fetched only when a page
299   shows one. */
300@font-face {
301  font-family: "Serenity Emoji";
302  src: url("/emoji.woff2") format("woff2");
303  unicode-range: U+1F1E6-1F1FF, U+1F44D-1F44E;
304  font-display: swap;
305}
306nav .places .flag { font-family: "Serenity Emoji", sans-serif; font-size: 1.1rem; }
307nav .places .where { flex: 1; }
308nav .places .times { opacity: 0.6; }
309nav .online::before { content: ""; display: inline-block; width: 0.55rem; height: 0.55rem; margin-right: 0.4rem; background: var(--pink); border: 1px solid var(--ink); }
310#toasts { position: fixed; right: 1rem; bottom: 1rem; z-index: 10; display: flex; flex-direction: column; gap: 0.5rem; width: min(24rem, calc(100vw - 2rem)); pointer-events: none; }
311.toast { pointer-events: auto; background: var(--paper); color: var(--ink); border: 2px solid var(--ink); padding: 0.6rem 0.8rem; font: 0.85rem/1.4 var(--mono); overflow-wrap: anywhere; animation: toast-in 0.25s steps(4); transition: opacity 0.4s; }
312.toast a { color: inherit; }
313/* The toasts sit inside nav, so nav's own link and button looks are undone. */
314nav #toasts a { font: inherit; letter-spacing: normal; padding: 0; background: none; text-decoration: underline; }
315.toast.gone { opacity: 0; }
316@keyframes toast-in { from { transform: translateY(1rem); opacity: 0; } }
317@media (prefers-reduced-motion: reduce) { .toast { animation: none; } }
318
319/* The two views of a directory, and a document's contents. */
320.bar-title.tabs { gap: 0; padding: 0; }
321.bar-title .tab { padding: 0.35rem 0.8rem; color: inherit; text-decoration: none; }
322.bar-title .tab.on { background: var(--paper); color: var(--ink); }
323.md .imports { border: 1px dashed var(--ink); padding: 0.45rem 0.7rem; }
324.md .toc { border: 1px solid var(--ink); background: var(--paper); padding: 0.6rem 0.9rem; margin: 0 0 1.2rem; }
325.md .toc-title { font: 1.15rem/1 var(--pixel); margin: 0 0 0.4rem; }
326.md .toc ol { margin: 0; padding-left: 1.4rem; }
327.md .toc li.sub { margin-left: 1.2rem; list-style: circle; }
328.md pre.mermaid { background: var(--paper); text-align: center; border: 1px solid var(--ink); }
329
330/* A document's diagrams and pictures open full size (page.js). */
331.md pre.mermaid svg, .md img { cursor: zoom-in; }
332.md img { max-width: 100%; height: auto; }
333dialog.zoom { width: 96vw; max-width: none; height: 94vh; max-height: none; padding: 0; border: 2px solid var(--ink); background: var(--paper); }
334dialog.zoom::backdrop { background: rgb(30 30 30 / 0.6); }
335dialog.zoom .close { position: sticky; top: 0.5rem; float: right; margin: 0.5rem; z-index: 1; background: var(--paper); }
336dialog.zoom .zoomed { padding: 1rem; overflow: auto; height: calc(100% - 2rem); }
337dialog.zoom .zoomed svg, dialog.zoom .zoomed img { display: block; width: 100%; min-width: 900px; height: auto; max-width: none; margin: 0 auto; }
338.toast.stays { background: var(--pink); display: flex; gap: 0.6rem; align-items: center; }
339.toast.stays .reload { font: 1.1rem/1 var(--pixel); padding: 0.3rem 0.8rem; }
340.toast.stays button { flex: none; white-space: nowrap; }
341.toast.stays .dismiss { margin-left: auto; padding: 0.1rem 0.5rem; }
342
343/* The code pages are an editor (view/code.rs): the top bar, then a sidebar
344   of folding panels beside a main pane, each scrolling on its own, the page
345   itself not scrolling. Wide content (diagrams, tables, source) scrolls
346   inside the pane instead of running out of it. */
347body.editor { height: 100vh; display: flex; flex-direction: column; overflow: hidden; background: var(--paper); }
348.editor-top { padding: 0.4rem 1rem; border-bottom: 1px solid var(--ink); background: var(--paper); }
349.ide { flex: 1; min-height: 0; display: grid; grid-template-columns: minmax(15rem, 19rem) minmax(0, 1fr); }
350.side { overflow: auto; border-right: 1px solid var(--ink); background: var(--grey); }
351.pane { overflow: auto; min-width: 0; background: var(--paper); }
352
353.panel { border-bottom: 1px solid var(--ink); }
354.panel > summary { list-style: none; cursor: pointer; background: var(--ink); color: var(--paper);
355  font: 1.1rem/1 var(--pixel); letter-spacing: 0.04em; text-transform: uppercase; padding: 0.4rem 0.7rem; }
356.panel > summary::-webkit-details-marker { display: none; }
357.panel > summary::before { content: "▾ "; }
358.panel:not([open]) > summary::before { content: "▸ "; }
359.panel-body { padding: 0.6rem 0.7rem; font-size: 0.85rem; }
360.panel-body .share { flex-direction: column; align-items: stretch; }
361.panel-body .share code { white-space: normal; overflow-wrap: anywhere; background: var(--paper); border: 1px solid var(--ink); padding: 0.4rem 0.5rem; }
362.panel-body .verdict { margin: 0.5rem 0 0; }
363
364.explorer { padding: 0.3rem 0; }
365.explorer .row { display: flex; gap: 0.4rem; align-items: center; color: inherit; text-decoration: none;
366  font: 0.82rem/1.55 var(--mono); white-space: nowrap; padding: 0 0.6rem 0 calc(1.3rem + var(--depth, 0) * 0.85rem); }
367.explorer .row:hover { background: var(--paper); }
368.explorer .row.on { background: var(--pink); }
369.explorer .row .icon { flex: none; }
370.explorer details.folder > summary { list-style: none; position: relative; }
371.explorer details.folder > summary::-webkit-details-marker { display: none; }
372.explorer details.folder > summary::before { content: "▸"; position: absolute; top: 0; left: calc(0.45rem + var(--depth, 0) * 0.85rem);
373  font: 0.75rem/1.85 var(--mono); cursor: pointer; }
374.explorer details.folder[open] > summary::before { content: "▾"; }
375.explorer details.folder > summary > .row { padding-left: calc(1.3rem + var(--depth, 0) * 0.85rem); }
376.explorer > .row { padding-left: 0.6rem; }
377.explorer > .verdict { margin: 0.4rem 0; padding: 0 0.7rem; }
378
379.projects { list-style: none; margin: 0; padding: 0.3rem 0; }
380.projects li { padding: 0.3rem 0.7rem; display: grid; gap: 0.1rem; }
381.projects li.on { background: var(--pink); }
382.projects a { display: flex; gap: 0.4rem; align-items: center; color: inherit; font: 0.85rem/1.3 var(--mono); }
383.topics { margin: 0.5rem 0 0; display: flex; flex-wrap: wrap; gap: 0.3rem; }
384.topic { font: 1rem/1 var(--pixel); border: 1px solid currentColor; padding: 0.15rem 0.4rem; }
385.projects .verdict { margin: 0; font-size: 0.78rem; }
386.panel-body pre.cargo { margin: 0.4rem 0; padding: 0.5rem; background: var(--paper); border: 1px solid var(--ink);
387  font: 0.72rem/1.5 var(--mono); white-space: pre; overflow-x: auto; }
388.panel-body .crates { margin: 0.5rem 0; padding-left: 1rem; font-size: 0.8rem; }
389.outline { margin: 0; padding: 0.5rem 0.7rem 0.6rem 1.8rem; font: 0.82rem/1.5 var(--mono); }
390.outline li.sub { margin-left: 1rem; list-style: circle; }
391.outline a { color: inherit; }
392
393.crumbs-bar { padding: 0.45rem 1rem; font: 0.85rem/1.3 var(--mono); border-bottom: 1px solid var(--grey); }
394.crumbs-bar a { color: inherit; }
395.tabs-bar { display: flex; align-items: stretch; background: var(--grey); border-bottom: 1px solid var(--ink);
396  position: sticky; top: 0; z-index: 2; overflow-x: auto; }
397.tab { display: flex; gap: 0.4rem; align-items: center; padding: 0.45rem 0.9rem; border-right: 1px solid var(--ink);
398  font: 0.85rem/1 var(--mono); color: inherit; text-decoration: none; white-space: nowrap; }
399.tab.on { background: var(--paper); border-bottom: 3px solid var(--pink); padding-bottom: calc(0.45rem - 3px); }
400.tab-note { opacity: 0.6; }
401.tab.step { background: var(--ink); color: var(--paper); max-width: 16rem; }
402.tab.step:hover { background: var(--pink); color: var(--ink); }
403.tab.step .step-label { overflow: hidden; text-overflow: ellipsis; }
404.tab.step .arrow { font-size: 0.75rem; }
405.tab.step.next { margin-left: auto; border-right: 0; border-left: 1px solid var(--ink); }
406.tab-meta { margin-left: auto; padding: 0.45rem 0.9rem; font: 0.8rem/1 var(--mono); white-space: nowrap; align-self: center; }
407.tab-meta a { color: inherit; }
408
409.doc { padding: 1.5rem 2rem 4rem; max-width: 62rem; min-width: 0; }
410.doc.source { max-width: none; padding: 0; }
411.doc.md :is(pre, table, pre.mermaid) { max-width: 100%; overflow-x: auto; }
412.doc.md img { max-width: 100%; }
413.contents-list { font: 0.9rem/1.7 var(--mono); }
414
415@media (max-width: 52rem) {
416  body.editor { height: auto; overflow: visible; }
417  .ide { display: flex; flex-direction: column; }
418  .pane { order: 1; overflow: visible; }
419  .side { order: 2; border-right: 0; border-top: 1px solid var(--ink); }
420  .doc { padding: 1rem; }
421  .tabs-bar { flex-wrap: wrap; }
422  .tab { flex: 1 1 auto; }
423  .tab.step { flex: 0 0 auto; }
424  .tab.step .step-label { display: none; }
425}
426
427/* Text set straight on the dot field gets paper of its own, so it reads. */
428footer .about > span, footer .clone { background: var(--paper); padding: 0.4rem 0.6rem; }
429
430/* Was Jev right? A vote each, one per browser. */
431.rating { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; margin-top: 0.8rem; }
432.rating:empty { display: none; }
433.rating .vote, .comment .vote { font: 1.1rem/1 var(--pixel, var(--mono)); padding: 0.3rem 0.6rem; background: var(--paper); color: var(--ink); border: 1px solid var(--ink); cursor: pointer; }
434.rating .vote.on { background: var(--pink); }
435.rating .vote:disabled { cursor: default; opacity: 0.6; }
436.rating .glyph { font-family: "Serenity Emoji", sans-serif; }
437
438/* The box under the vote opens by the grid trick: a row that goes from 0fr to
439   1fr takes the height of what is in it, which a height transition cannot. */
440.comment { display: grid; grid-template-rows: 0fr; transition: grid-template-rows 0.25s ease; }
441.comment.open { grid-template-rows: 1fr; }
442.comment-inner { min-height: 0; overflow: hidden; }
443.comment.open .comment-inner { padding-top: 0.6rem; }
444.comment-text { display: block; width: 100%; box-sizing: border-box; margin: 0.3rem 0 0.5rem; padding: 0.4rem 0.5rem; font: 1rem/1.3 var(--mono); background: var(--paper); color: var(--ink); border: 1px solid var(--ink); resize: vertical; }
445.comment-text::placeholder { color: #8a8a8a; opacity: 1; }
446.comment-actions { display: flex; flex-wrap: wrap; gap: 0.6rem; align-items: center; padding-bottom: 2px; }
447.comment-actions .vote:disabled { cursor: default; opacity: 0.6; }
448.comment .thanks:not(.shown), #comment-error:empty { display: none; }
449@media (prefers-reduced-motion: reduce) { .comment { transition: none; } }
450
451/* A call's answers: ◀ answer n of m ▶, and ↻ to ask again. */
452.versions { display: flex; flex-wrap: wrap; gap: 0.4rem; align-items: center; margin-bottom: 0.6rem; }
453.versions .step, .again { font: 1.05rem/1 var(--pixel, var(--mono)); padding: 0.25rem 0.55rem; background: var(--paper); color: var(--ink); border: 1px solid var(--ink); cursor: pointer; }
454.versions .step:hover, .again:hover { background: var(--pink); }
455
456/* A source file read as Docco reads one (view/code.rs, `source`): each run
457   of comments, rendered, beside the code under it. One row per run, so a
458   comment and its code start level. Narrow, the code goes under. */
459.doc.docco { max-width: none; padding: 0; }
460.docco .sec { display: grid; grid-template-columns: minmax(18rem, 34rem) minmax(0, 1fr); border-bottom: 1px solid var(--grey); }
461.docco .prose { padding: 0.7rem 1.2rem; font-size: 0.92rem; min-width: 0; }
462.docco .prose > :last-child { margin-bottom: 0; }
463.docco .prose h1 { font-size: 1.5rem; }
464.docco .prose h2 { font-size: 1.2rem; }
465.docco .source { background: color-mix(in srgb, var(--paper), var(--grey) 35%); border-left: 1px solid var(--grey); min-width: 0; }
466@media (max-width: 60rem) {
467  .docco .sec { grid-template-columns: minmax(0, 1fr); }
468  .docco .source { border-left: 0; border-top: 1px solid var(--grey); }
469}
470/* The colours of code (tree::annotate::Kind::class): the page's own ink,
471   plum and pink, and nothing brought in. */
472.source .c { opacity: 0.5; }
473.source .k { color: var(--plum); font-weight: 500; }
474.source .s, .source .n { color: color-mix(in srgb, var(--plum), var(--pink) 45%); }
475.source .t { font-weight: 500; }
476.source .f { color: color-mix(in srgb, var(--ink), var(--plum) 55%); }
477.source .m { opacity: 0.65; }
478
479/* The explorer's and the tabs' icons: Jerry's pixel icons
480   (third-party/jerrys-pixel-icons), sixteen pixels drawn large, shown small
481   and kept sharp. A folder has two, and its `details` says which shows. */
482img.icon.px { width: 18px; height: 18px; image-rendering: pixelated; }
483.explorer .shut, .explorer .opened { display: contents; }
484.explorer details.folder:not([open]) > summary .opened, .explorer details.folder[open] > summary .shut { display: none; }
485
486/* Questions already asked that what is being typed could be the start of. */
487.suggested { list-style: none; margin: 0.4rem 0 0; padding: 0; font: 0.85rem/1.5 var(--mono); }
488.suggested:empty { display: none; }