reviewzy 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -60,6 +60,20 @@
60
60
  --surface-2: oklch(0.345 0.026 280); /* hover, nested */
61
61
  --surface-3: oklch(0.395 0.028 280); /* pressed, selected */
62
62
 
63
+ /* The sky: one light above the page, the page hue drifted toward the accent,
64
+ painted on body::after as a radial ground 780px tall. The stops are
65
+ declared grounds, never an alpha over the page, so each is a measured pair.
66
+ On the peak: --text 9.89, --text-2 7.13, --text-3 5.37, --text-4 4.62:1,
67
+ --accent-text 7.73. On the mid: 10.87 / 7.84 / 5.90 / 5.08, --accent-text
68
+ 8.50. NO HEADROOM: --text-4 falls under 4.5:1 once the ground passes
69
+ L 0.31, so the peak never rises. */
70
+ --sky-peak: oklch(0.305 0.040 252);
71
+ --sky-mid: oklch(0.278 0.032 262);
72
+ /* The accent band's ground, the page's closing ask: the page tone turned
73
+ toward the sapphire. --text 11.26, --text-2 8.12, --text-3 6.11, --text-4
74
+ 5.26:1, --accent-text 8.81, and --accent as a fill 7.21 against it. */
75
+ --band-accent: oklch(0.265 0.050 242);
76
+
63
77
  /* The system draws its own scrollbar, so the thumb is a graphic this file owns
64
78
  and it clears 3:1 on every ground a scroller has: 5.20:1 on --well, 4.68 on
65
79
  --bg, 4.05 on --surface-1, 3.43 on a --surface-2 hover tint. --surface-3, the
@@ -87,25 +101,45 @@
87
101
 
88
102
  Measured on a 300x140 card, blue channel against --bg, deepest pixel and
89
103
  the width of that last 1-level band:
90
- --elev-1 24 levels under, 12 beside; band 5px under, 5px beside
104
+ --elev-1 23 levels under, 11 beside; band 3px under, 2px beside
91
105
  --elev-2 23 / 12; band 7 / 6
92
106
  --elev-float 34 / 18; band 8 / 6
93
107
  The shipped v1 set measured 9 / 5 with a 15px band, which is the ring.
94
108
 
109
+ DEPTH IS NOT WHAT DRAWS THE CORNER, REACH IS. --elev-1's outer coat ran
110
+ `0 8px 16px -6px` and its footprint reached 21px under the card, 13 beside
111
+ and 18 out of the corner on the diagonal, so the far end of the footprint
112
+ closed as a curve tracing the box and the corner read as a smear. Depth was
113
+ never the problem: at `0 4px 8px -4px` the deepest pixel moves 24 to 23 and
114
+ the reach halves to 10 / 6 / 10.
115
+
95
116
  THE EDGE IS WHAT THE EYE READS AS THE CARD STARTING, not the tone step.
96
117
  A card sits .045 L over the page and .065 is not available: the fourth
97
118
  text step falls to 4.40:1 on it. So separation is bought on the edge and
98
- the contact instead, where no colour moves and no ratio re-derives. */
119
+ the contact instead, where no colour moves and no ratio re-derives.
120
+ Measured on a card at --surface-1: --lit-1 paints 18 sRGB levels of lift
121
+ along the top, --lit-2 21.
122
+
123
+ A CARD CARRIES A DRAWN LINE. `.card` and `.stat-card` take `--card-line`,
124
+ a crisp 1px line drawn outside the box (box-shadow 0 0 0 1px, zero blur,
125
+ content box unchanged). A card at rest
126
+ wears the one line and no coat. Every other raised surface keeps the straight --lit-1,
127
+ and glass keeps --glass-edge. The line is a hairline, not a graphic, so it
128
+ is not held to a 3:1 floor; measured with tools/contrast.mjs against the
129
+ card's own fill and every surface it sits on, tightest first: sky peak
130
+ 2.03:1, --surface-1 (the fill) 2.08:1, sky mid 2.24:1, --band-accent
131
+ 2.32:1, --bg 2.41:1, --well 2.67:1. */
99
132
  --lit-1: inset 0 1px 0 0 oklch(1 0 0 / 0.085);
100
133
  --lit-2: inset 0 1px 0 0 oklch(1 0 0 / 0.100);
101
- --elev-1: var(--lit-1),
102
- 0 1px 2px -1px oklch(0.11 0.025 280 / 0.72),
103
- 0 3px 6px -2px oklch(0.11 0.025 280 / 0.68),
104
- 0 8px 16px -6px oklch(0.11 0.025 280 / 0.64);
105
- --elev-2: var(--lit-2),
106
- 0 1px 2px -1px oklch(0.11 0.025 280 / 0.66),
107
- 0 4px 8px -2px oklch(0.11 0.025 280 / 0.62),
108
- 0 10px 18px -8px oklch(0.11 0.025 280 / 0.58);
134
+ --card-line: oklch(0.48 0.032 280);
135
+ --contact-1: 0 1px 2px -1px oklch(0.11 0.025 280 / 0.72),
136
+ 0 2px 4px -1px oklch(0.11 0.025 280 / 0.66),
137
+ 0 4px 8px -4px oklch(0.11 0.025 280 / 0.60);
138
+ --contact-2: 0 1px 2px -1px oklch(0.11 0.025 280 / 0.66),
139
+ 0 4px 8px -2px oklch(0.11 0.025 280 / 0.62),
140
+ 0 10px 18px -8px oklch(0.11 0.025 280 / 0.58);
141
+ --elev-1: var(--lit-1), var(--contact-1);
142
+ --elev-2: var(--lit-2), var(--contact-2);
109
143
  --elev-float: var(--lit-2),
110
144
  0 2px 4px -1px oklch(0.09 0.022 280 / 0.72),
111
145
  0 6px 12px -3px oklch(0.09 0.022 280 / 0.68),
@@ -125,7 +159,7 @@
125
159
  tint with --bg: a bar is the page held still, and content passing under it
126
160
  is the only thing that says a surface is there. Panels keep the raised
127
161
  tone at the clearest alpha the dim ramp allows: --text-2 holds 4.67:1 on
128
- the worst case at 0.86 and fails at 0.82 (4.07, round 1), so 0.86 is the
162
+ the worst case at 0.86 and fails at 0.82 (4.09:1), so 0.86 is the
129
163
  floor. On that worst case, a white plate composited through the panel,
130
164
  --text holds 6.48:1, --glass-dim 4.67, --glass-danger 4.62. The panel
131
165
  moves 35.7 sRGB levels black to white, the measured (1 - alpha) * 255
@@ -133,23 +167,49 @@
133
167
  crushing the backdrop enough to protect it crushes the blur with it,
134
168
  measured across nine chains. ---- */
135
169
  --glass-bar-tint: oklch(0.255 0.024 280 / 0.90);
136
- /* the bar carries only --text and --text-2: 6.21:1 for text-2 on the bar's
137
- worst case (a white plate through 0.90 of the page colour), no re-floor */
170
+ /* the bar re-floors the same tokens as a panel, since the glass block names
171
+ both: --text-3/--text-4/--text-placeholder become --glass-dim, --danger
172
+ becomes --glass-danger, --text-disabled becomes --glass-disabled. Measured
173
+ over the bar's worst plate (white through 0.90 of the page colour): --text-2
174
+ 6.21:1, --glass-danger 6.14:1, --glass-disabled 4.34:1. */
138
175
  --glass-tint: oklch(0.300 0.024 280 / 0.86);
139
176
  --glass-blur: 22px;
140
177
  --glass-sat: 1.25;
141
178
  --glass-dim: oklch(0.800 0.030 280); /* --text-3 and --text-4 on a panel:
142
179
  4.67:1 on the worst case; unlifted, .720 / .680 fall to 3.52 / 3.03 */
143
180
  --glass-danger: oklch(0.84 0.140 15); /* 4.62:1 on the panel worst case.
144
- --danger is 3.31:1 there, and the round-1 value L 0.78 fell to 4.04 at
181
+ --danger is 3.31:1 there, and L 0.78 fell to 4.04 at
145
182
  0.86, so the re-floor moves with the alpha */
146
183
  --glass-disabled: oklch(0.700 0.030 280); /* 3.27:1 on the panel worst case.
147
184
  --text-disabled falls to 2.49 there, and the opacity: 0.45 costume this
148
185
  replaces measured 2.17, which is what the panel had been shipping */
149
186
  /* A translucent tint over a translucent surface composites twice and drifts 21
150
187
  levels with the backdrop: --accent-text on it measures 4.40:1. The same mix
151
- laid down opaque holds one tone and 5.95:1. */
152
- --glass-quiet: color-mix(in oklab, var(--accent) 16%, var(--surface-1));
188
+ laid down opaque holds one tone and 5.95:1 on a panel. This is the accent's
189
+ share of that opaque fill; the glass block mixes --accent-quiet from it onto
190
+ each surface's own --glass-ground, so a bar's fill reads the bar's tone. */
191
+ --glass-quiet-share: 16%;
192
+ /* --danger-text on a glass surface, where --danger is already --glass-danger:
193
+ the hover and press tints mix opaque onto the surface (--tint-ground), so
194
+ the plate behind it no longer moves them. On the panel 5.64 / 4.74:1, on
195
+ the bar 6.55 / 5.38, the menu hover 6.50; 0.86 leaves the panel press at
196
+ 4.52, so 0.88 holds a margin over the floor. */
197
+ --glass-danger-text: oklch(0.88 0.140 15);
198
+ /* what every translucent tint in components.css mixes onto. Transparent, so
199
+ off glass a tint paints exactly as `X p%, transparent` did. A glass surface
200
+ sets its own tone here, turning each tint inside it into an opaque mix the
201
+ backdrop cannot move; every rule that paints an opaque ground resets it to
202
+ transparent, so a tint nested in that ground composites over it. One value
203
+ for both themes. */
204
+ --tint-ground: transparent;
205
+ /* the same glass tone for a tint painted OUTSIDE its own box (an invalid
206
+ field's ring): an opaque ground resets --tint-ground on itself, but its
207
+ ring lands on whatever surrounds it, so no rule resets this one */
208
+ --glass-ground: transparent;
209
+ /* --accent-line on a glass surface, laid opaque on the surface's own
210
+ --glass-ground at the same 38 percent, so a focus ring and a primary edge
211
+ stop drifting with the backdrop the way the quiet share stopped the fill */
212
+ --glass-line-share: 38%;
153
213
  /* The one place a drawn line beats the lit edge. A glass panel has no reliable
154
214
  tone difference from its backdrop, since it takes that backdrop on, so the
155
215
  edge is what says where the pane starts. Still a hairline. */
@@ -168,7 +228,7 @@
168
228
  /* the hint inside a control. One step under --text-4 so a placeholder does
169
229
  not read as a typed value or as metadata: 4.99:1 on --well. */
170
230
  --text-placeholder: oklch(0.630 0.030 280);
171
- /* a control that cannot act, her call 2026-09-02, replacing the opacity: 0.45
231
+ /* a control that cannot act, replacing the opacity: 0.45
172
232
  ten declarations across twelve component classes rode. It takes the 3:1 graphics floor rather than the 4.5:1
173
233
  text floor, so it can sit a real step under --text-4: 4.99:1 on --well, 4.50
174
234
  on --bg, 3.89 on --surface-1, 3.29 on the --surface-2 a disabled fill takes.
@@ -188,15 +248,25 @@
188
248
  --accent-glow: 0 0 10px 0 oklch(0.76 0.110 238 / 0.55);
189
249
  --on-accent: oklch(0.22 0.020 280); /* 8.22:1 on the fill */
190
250
 
251
+ /* no on-colour: identity rule 7 makes accent-2 annotation only, never a fill,
252
+ so the pair it would sit on cannot exist */
191
253
  --accent-2: oklch(0.72 0.130 42); /* editorial, 6.09:1, once per screen */
192
- --on-accent-2: oklch(0.22 0.020 42); /* 6.69:1 on the fill */
254
+
255
+ /* The brand mark's ring: rose, the identity accent. A 2px graphic on the
256
+ .sona, held to the 3:1 graphics floor on every bar surface it sits on;
257
+ measured with tools/contrast.mjs: surface-1 6.06:1, well 7.78:1, --bg
258
+ 7.01:1, sky peak 5.93:1 dark; 5.16 / 4.20 / 4.59 / 5.16 light. */
259
+ --brand-ring: oklch(0.76 0.110 340);
193
260
 
194
261
  /* ---- Status. Levelled under --text so no pill outshouts a title. ---- */
195
262
  --success: oklch(0.80 0.130 150); /* 8.89:1 */
196
263
  --warning: oklch(0.84 0.110 90); /* 9.68:1 */
197
264
  --danger: oklch(0.72 0.140 15); /* 5.98:1 */
198
- --on-success: oklch(0.22 0.02 150); /* 9.68:1 on the fill */
199
- --on-warning: oklch(0.22 0.02 90); /* 10.59:1 on the fill */
265
+ /* the danger label on its own hover and press tint: --danger there falls to
266
+ 4.04 / 3.49:1 on surface-1, because label and tint are one hue. Lightness
267
+ stepped, chroma held: 6.04 / 5.16:1 on --bg, 5.27 / 4.55 on surface-1, and
268
+ 5.76 on .menu-item.danger's 10 percent hover. */
269
+ --danger-text: oklch(0.81 0.140 15);
200
270
  --on-danger: oklch(0.22 0.02 15); /* 6.58:1 on the fill */
201
271
  --info: var(--accent); /* folded in: it was the same hue */
202
272
 
@@ -228,8 +298,9 @@
228
298
 
229
299
  /* ---- Radius. Softer than v1's 1px, which rendered as 0. ---- */
230
300
  --r-1: 10px; /* controls */
231
- --r-2: 14px; /* cards, menus */
301
+ --r-2: 14px; /* cards, menus, icon tiles */
232
302
  --r-3: 18px; /* modals, sheets */
303
+ --r-4: 24px; /* KPI tiles */
233
304
  --r-pill: 999px;
234
305
 
235
306
  /* ---- Motion. Duration follows distance. ---- */
@@ -245,9 +316,17 @@
245
316
  --dur-out-near: calc(var(--dur-near) * 0.6);
246
317
  --dur-out-far: calc(var(--dur-far) * 0.6);
247
318
 
248
- /* ---- Type. 8 distinct steps; 11px and 12px are real tokens. ---- */
319
+ /* ---- The jelly press. A spring settle, NOT a travel
320
+ distance, so it takes no --dur-* slot: --press-dur is how long the wobble
321
+ takes to settle, and --press-spring is the 320/18 spring sampled over
322
+ 49 points as a linear() curve, normalized to settle at 1. ---- */
323
+ --press-dur: 380ms;
324
+ --press-spring: linear(0, 2.0833% 0.0098, 4.1667% 0.0372, 6.2500% 0.0793, 8.3333% 0.1334, 10.4167% 0.1970, 12.5000% 0.2675, 14.5833% 0.3428, 16.6667% 0.4208, 18.7500% 0.4996, 20.8333% 0.5777, 22.9167% 0.6538, 25.0000% 0.7267, 27.0833% 0.7954, 29.1667% 0.8592, 31.2500% 0.9176, 33.3333% 0.9702, 35.4167% 1.0168, 37.5000% 1.0574, 39.5833% 1.0919, 41.6667% 1.1205, 43.7500% 1.1435, 45.8333% 1.1613, 47.9167% 1.1740, 50.0000% 1.1823, 52.0833% 1.1865, 54.1667% 1.1871, 56.2500% 1.1845, 58.3333% 1.1793, 60.4167% 1.1718, 62.5000% 1.1626, 64.5833% 1.1520, 66.6667% 1.1404, 68.7500% 1.1281, 70.8333% 1.1155, 72.9167% 1.1028, 75.0000% 1.0904, 77.0833% 1.0783, 79.1667% 1.0668, 81.2500% 1.0560, 83.3333% 1.0461, 85.4167% 1.0370, 87.5000% 1.0289, 89.5833% 1.0217, 91.6667% 1.0155, 93.7500% 1.0103, 95.8333% 1.0060, 97.9167% 1.0026, 1);
325
+
326
+ /* ---- Type. 9 distinct steps; 11px and 12px are real tokens. ---- */
249
327
  --t-1: 11px; --t-2: 12px; --t-3: 14px; --t-4: 16px;
250
328
  --t-5: 20px; --t-6: 26px; --t-7: 34px; --t-8: 46px;
329
+ --t-9: 64px;
251
330
  --caps: 0.09em; /* the one tracking value */
252
331
 
253
332
  /* ---- Controls share one height ladder with inputs ---- */
@@ -255,13 +334,18 @@
255
334
 
256
335
  /* ---- Layout ---- */
257
336
  --page-w: 1200px; /* .page column; one override retunes a build */
258
- /* prose measure, MEASURED against loaded Onest at --t-4 rather than estimated:
259
- 528px lands 64.9 and 68.1 characters per line on two samples, 664px lands
260
- 84.4 and 85.3. A line that runs the full page column is a line nobody
261
- finishes. Erring short is the safer side of 66. */
337
+ /* prose measure, MEASURED against loaded Onest at --t-4 rather than estimated.
338
+ A line that runs the full page column is a line nobody finishes, and erring
339
+ short is the safer side of 66. The width sweep behind the value, five
340
+ candidates against two samples, is in references/rhythm.md; it lived here
341
+ too and the two copies had already drifted apart on one sample. */
262
342
  --page-narrow: 528px;
263
- --navbar-h: 56px; /* the sticky header and the anchor offset both derive from it */
264
- --topbar-h: 52px; /* the mobile bar; the sticky header pins under this one below 768px */
343
+ --navbar-h: 56px; /* the navbar's own height, and nothing else reads it as an offset */
344
+ --topbar-h: 52px; /* the mobile bar's own height */
345
+ /* How far down anything pinned to the top has to start, which is NOT a bar's
346
+ height: it is the height of whatever bar this page actually holds, and a
347
+ sidebar shell holds none. Derived on the root below, never set by a page. */
348
+ --pin-top: 0px;
265
349
 
266
350
  /* ---- Fonts ---- */
267
351
  --font-sans: "Onest", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
@@ -283,7 +367,7 @@
283
367
  }
284
368
 
285
369
  /* Radius preset: sharp is the opt-in variant, real 0 */
286
- [data-radius="sharp"] { --r-1: 0px; --r-2: 0px; --r-3: 0px; }
370
+ [data-radius="sharp"] { --r-1: 0px; --r-2: 0px; --r-3: 0px; --r-4: 0px; }
287
371
 
288
372
  /* ---- Light theme (Latte). EVERY colour and elevation token is
289
373
  re-declared, because a partial override is the bug that broke
@@ -299,6 +383,16 @@
299
383
  --well: oklch(0.925 0.008 275);
300
384
  --bg: oklch(0.955 0.006 275);
301
385
  --surface-1: oklch(0.995 0.003 275); /* cards still rise */
386
+ /* Light falls onto a light page too: the top goes whiter, which only raises
387
+ every text ratio there. On the peak: --text 13.46, --text-4 5.55:1,
388
+ --accent-text 5.68; on the mid 12.72 / 5.25 / 5.37. The peak sits at the
389
+ card's own lightness, so a card under it separates on the drawn line
390
+ alone. */
391
+ --sky-peak: oklch(0.995 0.008 240);
392
+ --sky-mid: oklch(0.975 0.007 255);
393
+ /* --text 11.17, --text-2 6.94, --text-3 4.92, --text-4 4.61:1, --accent-text
394
+ 4.72; --accent as a fill 3.82 against it, over the 3:1 graphics floor */
395
+ --band-accent: oklch(0.930 0.030 238);
302
396
 
303
397
  --line: oklch(0.890 0.012 275); /* barely there, 1.22:1 */
304
398
  --line-soft: oklch(0.920 0.010 275);
@@ -325,13 +419,15 @@
325
419
  --on-accent: oklch(0.995 0.003 238);
326
420
 
327
421
  --accent-2: oklch(0.52 0.150 42); /* 5.16:1 */
328
- --on-accent-2: oklch(0.995 0.003 42);
422
+
423
+ --brand-ring: oklch(0.55 0.140 340);
329
424
 
330
425
  --success: oklch(0.50 0.150 150); /* 4.85:1 */
331
426
  --warning: oklch(0.50 0.130 90); /* 5.25:1 */
332
427
  --danger: oklch(0.52 0.190 15); /* 5.36:1 */
333
- --on-success: oklch(0.995 0.003 150);
334
- --on-warning: oklch(0.995 0.003 90);
428
+ /* hover / press: 5.25 / 4.61:1 on --bg, 5.87 / 5.10 on surface-1, 6.49 on
429
+ .menu-item.danger's hover; --danger there falls to 4.14 / 3.63 on --bg */
430
+ --danger-text: oklch(0.46 0.190 15);
335
431
  --on-danger: oklch(0.995 0.003 15);
336
432
  --info: var(--accent);
337
433
 
@@ -342,18 +438,32 @@
342
438
  banding that governs the dark set is not the binding constraint here; the
343
439
  geometry is shared because a component must not change depth on a toggle.
344
440
  Measured on a 300x140 card, blue channel against --bg, deepest pixel and
345
- the last 1-level band: --elev-1 33 under / 13 beside, band 4 / 4;
441
+ the last 1-level band: --elev-1 30 under / 13 beside, band 2 / 2;
346
442
  --elev-2 32 / 14, band 3 / 2; --elev-float 42 / 19, band 5 / 3. */
347
- --lit-1: inset 0 -1px 0 0 oklch(0.55 0.03 275 / 0.10);
348
- --lit-2: inset 0 -1px 0 0 oklch(0.55 0.03 275 / 0.12);
349
- --elev-1: var(--lit-1),
350
- 0 1px 2px -1px oklch(0.55 0.03 275 / 0.26),
351
- 0 3px 6px -2px oklch(0.55 0.03 275 / 0.23),
352
- 0 8px 16px -6px oklch(0.55 0.03 275 / 0.21);
353
- --elev-2: var(--lit-2),
354
- 0 1px 2px -1px oklch(0.55 0.03 275 / 0.24),
355
- 0 4px 8px -2px oklch(0.55 0.03 275 / 0.21),
356
- 0 10px 18px -8px oklch(0.55 0.03 275 / 0.19);
443
+
444
+ /* The underside line is a mid tone on a near-white card, so an alpha buys
445
+ fewer levels here than dark's white on --surface-1: at 0.10 it painted 15
446
+ against dark's 18. The two themes are matched on the PAINTED delta rather
447
+ than on each other's alphas, measured on a card: 0.125 lands 18, 0.145
448
+ lands 21. Matched in sRGB levels, not in oklab: a
449
+ perceptual match wants 0.165, which reaches 93 percent of --line-soft's
450
+ own step on a card and starts reading as a drawn line rather than a lit
451
+ edge. */
452
+ --lit-1: inset 0 -1px 0 0 oklch(0.55 0.03 275 / 0.125);
453
+ --lit-2: inset 0 -1px 0 0 oklch(0.55 0.03 275 / 0.145);
454
+ /* the card line is a hairline, not a graphic, so no 3:1 floor applies;
455
+ measured with tools/contrast.mjs against every surface a card sits on,
456
+ tightest first: --well 1.99:1, --band-accent 2.03:1, --bg 2.18:1,
457
+ --sky-mid 2.31:1, --surface-1 2.45:1, --sky-peak 2.45:1. */
458
+ --card-line: oklch(0.72 0.020 275);
459
+ --contact-1: 0 1px 2px -1px oklch(0.55 0.03 275 / 0.26),
460
+ 0 2px 4px -1px oklch(0.55 0.03 275 / 0.23),
461
+ 0 4px 8px -4px oklch(0.55 0.03 275 / 0.21);
462
+ --contact-2: 0 1px 2px -1px oklch(0.55 0.03 275 / 0.24),
463
+ 0 4px 8px -2px oklch(0.55 0.03 275 / 0.21),
464
+ 0 10px 18px -8px oklch(0.55 0.03 275 / 0.19);
465
+ --elev-1: var(--lit-1), var(--contact-1);
466
+ --elev-2: var(--lit-2), var(--contact-2);
357
467
  --elev-float: var(--lit-2),
358
468
  0 2px 4px -1px oklch(0.55 0.03 275 / 0.26),
359
469
  0 6px 12px -3px oklch(0.55 0.03 275 / 0.23),
@@ -363,18 +473,29 @@
363
473
  /* Glass. The same split the dark block ships: bars tint with --bg, panels
364
474
  hold surface-1 at the clearest alpha the ramp allows. Worst case on a light
365
475
  page is black behind the panel; composited through it, --text holds 10.76:1,
366
- --text-2 6.68, the dim step 4.72 at 0.500 (re-measured twice 2026-09-02, independently) and --danger 4.81. The shipped
367
- --text-4 value 0.515 measures 4.44 there, so it joins the dim step. 0.90 is
368
- 25.5 sRGB levels black to white. --accent-text stays bound to its opaque
369
- quiet fill (4.89:1, unchanged, because the fill is opaque). */
370
- --glass-bar-tint: oklch(0.955 0.006 275 / 0.92);
476
+ --text-2 6.68, the dim step 4.72 at 0.500 (re-measured twice 2026-09-02,
477
+ independently) and --danger 4.81. The shipped --text-4 value 0.515 measures
478
+ 4.44 there, so it joins the dim step. 0.90 is 25.5 sRGB levels black to
479
+ white. --accent-text stays bound to its opaque quiet fill: 4.89:1 on a
480
+ panel's, but 4.38 on a bar's own, so every accent label that can sit on a
481
+ bar carries a mix: the shared light remix at 4.98, `.btn-primary`'s own
482
+ at 6.27.
483
+
484
+ The bar alpha is the floor, not a preference. At 0.92 the bar re-floors
485
+ --text-3/--text-4/--text-placeholder to --glass-dim, which measured 4.41:1
486
+ over black, and --glass-danger 4.48:1, both under 4.5:1. At 0.93 both clear:
487
+ --glass-dim 4.52:1 and --glass-danger 4.59:1 over black, and --glass-disabled
488
+ 3.22:1 over the 3:1 floor. */
489
+ --glass-bar-tint: oklch(0.955 0.006 275 / 0.93);
371
490
  --glass-tint: oklch(0.995 0.003 275 / 0.90);
372
491
  --glass-dim: oklch(0.500 0.018 275);
373
492
  --glass-danger: var(--danger);
493
+ --glass-danger-text: var(--danger-text);
374
494
  /* light needs no lift here: --text-disabled already measures 3.37:1 on the
375
495
  panel worst case, where dark's falls to 2.49 */
376
496
  --glass-disabled: var(--text-disabled);
377
- --glass-quiet: color-mix(in oklab, var(--accent) 12%, var(--surface-1));
497
+ --glass-quiet-share: 12%;
498
+ --glass-line-share: 34%;
378
499
  --glass-edge: oklch(0.45 0.03 275 / 0.12);
379
500
 
380
501
  /* A light page has less room under a dim than a dark one: at the 0.7 the dark
@@ -388,13 +509,13 @@
388
509
  mauve here too: against sky the 1-3 pair measured 0.0790 in oklab and now
389
510
  measures 0.1556.
390
511
 
391
- SLOTS 1 AND 4 ARE HELD AT THE sRGB BOUNDARY, her call 2026-09-02. At their
512
+ SLOTS 1 AND 4 ARE HELD AT THE sRGB BOUNDARY. At their
392
513
  old chromas the browser clipped them (slot 1's linear red measured -0.0182,
393
514
  slot 4's -0.0020), so the declared values named colours nobody saw and every
394
515
  distance taken from a declaration overstated the gap. The gamut runs out at
395
516
  C 0.1110 for slot 1 and 0.1378 for slot 4 at these lightnesses and hues;
396
- both sit under that with a little margin. `tools/cvd.mjs` reds on any slot
397
- outside sRGB, on every run. */
517
+ both sit under that with a little margin; the sRGB check reds on any slot
518
+ outside the gamut, on every run. */
398
519
  --chart-series-1: oklch(0.50 0.110 238);
399
520
  --chart-series-2: oklch(0.52 0.150 42);
400
521
  --chart-series-3: oklch(0.55 0.150 305);
@@ -424,6 +545,41 @@
424
545
  --cat-blue: oklch(0.559 0.226 262); --cat-lavender: oklch(0.664 0.175 273);
425
546
  }
426
547
 
548
+ /* ---- Language hues (GitHub-recognizable categorical) ----
549
+ The categorical ramp's per-language extension, for dots, bar segments and
550
+ legend marks. Hue and chroma are each language's own GitHub colour;
551
+ lightness is re-derived per theme so every mark clears the 3:1 graphics
552
+ floor on its theme's binding surface: dark marks are light and bind on the
553
+ lightest surface a dark-theme mark can sit on (--surface-1); light marks
554
+ are dark and bind on the darkest light-theme surface (--well). Worst
555
+ measured: dark 3.1:1, light 3.1:1. */
556
+ :root {
557
+ --cat-rust: oklch(0.768 0.081 51); --cat-javascript: oklch(0.896 0.153 102);
558
+ --cat-typescript: oklch(0.569 0.078 220); --cat-python: oklch(0.573 0.102 247);
559
+ --cat-csharp: oklch(0.559 0.179 141); --cat-java: oklch(0.604 0.123 69);
560
+ --cat-go: oklch(0.695 0.130 224); --cat-html: oklch(0.622 0.194 35);
561
+ --cat-css: oklch(0.585 0.104 300); --cat-shell: oklch(0.823 0.196 135);
562
+ --cat-php: oklch(0.579 0.091 272); --cat-kotlin: oklch(0.687 0.189 297);
563
+ --cat-qml: oklch(0.639 0.189 139); --cat-c: oklch(0.575 0.020 280);
564
+ --cat-cpp: oklch(0.661 0.206 7); --cat-vue: oklch(0.702 0.132 160);
565
+ --cat-svelte: oklch(0.654 0.234 34); --cat-lua: oklch(0.584 0.188 264);
566
+ --cat-nix: oklch(0.656 0.187 280); --cat-dockerfile: oklch(0.573 0.029 220);
567
+ --cat-makefile: oklch(0.564 0.137 135);
568
+ }
569
+ [data-theme="light"] {
570
+ --cat-rust: oklch(0.609 0.081 51); --cat-javascript: oklch(0.598 0.153 102);
571
+ --cat-typescript: oklch(0.523 0.078 220); --cat-python: oklch(0.535 0.102 247);
572
+ --cat-csharp: oklch(0.540 0.179 141); --cat-java: oklch(0.604 0.123 69);
573
+ --cat-go: oklch(0.588 0.130 224); --cat-html: oklch(0.622 0.194 35);
574
+ --cat-css: oklch(0.418 0.104 300); --cat-shell: oklch(0.585 0.196 135);
575
+ --cat-php: oklch(0.492 0.091 272); --cat-kotlin: oklch(0.622 0.189 297);
576
+ --cat-qml: oklch(0.586 0.189 139); --cat-c: oklch(0.450 0.020 280);
577
+ --cat-cpp: oklch(0.629 0.206 7); --cat-vue: oklch(0.587 0.132 160);
578
+ --cat-svelte: oklch(0.629 0.234 34); --cat-lua: oklch(0.271 0.188 264);
579
+ --cat-nix: oklch(0.616 0.187 280); --cat-dockerfile: oklch(0.406 0.029 220);
580
+ --cat-makefile: oklch(0.516 0.137 135);
581
+ }
582
+
427
583
  /* ---- Base reset ---- */
428
584
  *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
429
585
  html { font-size: 16px; }
@@ -438,6 +594,35 @@ body {
438
594
  -webkit-font-smoothing: antialiased;
439
595
  text-wrap: pretty;
440
596
  }
597
+ /* The sky: a 62% x 100% ellipse hung from the top centre, reaching --bg at its
598
+ own edge, so the 780px box draws no seam where it ends. It is its own layer
599
+ at z -2, under the dot grid, so it can move slower than the page: it drifts
600
+ up at a quarter of the scroll speed, and is gone once the
601
+ page has scrolled 3120px. Absolute by default, which scrolls it with the page,
602
+ so an engine with no scroll timeline and a reduced-motion reader both get the
603
+ plain ground. */
604
+ body::after {
605
+ content: "";
606
+ position: absolute;
607
+ inset: 0 0 auto;
608
+ height: 780px;
609
+ z-index: -2;
610
+ pointer-events: none;
611
+ background:
612
+ radial-gradient(ellipse 62% 100% at 50% 0%,
613
+ var(--sky-peak) 0%, var(--sky-mid) 42%, var(--bg) 100%);
614
+ }
615
+ @supports (animation-timeline: scroll()) {
616
+ @media (prefers-reduced-motion: no-preference) {
617
+ body::after {
618
+ position: fixed;
619
+ animation: sky-drift linear both;
620
+ animation-timeline: scroll(root);
621
+ animation-range: 0 3120px;
622
+ }
623
+ }
624
+ }
625
+ @keyframes sky-drift { to { transform: translateY(-780px); } }
441
626
  ::selection { background: var(--accent-quiet); color: var(--text); }
442
627
  /* `font-family` does not inherit into a <button>: the UA sets it, together with
443
628
  a 2px outset border, ButtonFace, ButtonText and centred text, and an author
@@ -475,8 +660,9 @@ h1 { font: 600 var(--t-8)/1.1 var(--font-sans); letter-spacing: -0.03em; }
475
660
  h2 { font: 600 var(--t-6)/1.15 var(--font-sans); letter-spacing: -0.02em; }
476
661
  h3 { font: 500 var(--t-5)/1.2 var(--font-sans); letter-spacing: -0.01em; }
477
662
  h4 { font: 500 var(--t-4)/1.3 var(--font-sans); }
478
- /* An anchored jump clears the sticky navbar instead of tucking under it. */
479
- [id] { scroll-margin-top: calc(var(--navbar-h) + var(--space-2)); }
663
+ /* An anchored jump clears whatever bar this page actually holds, not a fixed
664
+ navbar height: --pin-top is 0 on a page with no bar at all. */
665
+ [id] { scroll-margin-top: calc(var(--pin-top) + var(--space-2)); }
480
666
 
481
667
  /* ---- Utilities ---- */
482
668
  .label {