@mkbabb/glass-ui 3.10.1 → 3.11.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.
Files changed (279) hide show
  1. package/dist/{CardAction-DrAcAV4d.js → CardAction-DuArVky-.js} +2 -2
  2. package/dist/{CommandShortcut-mV2e1aG6.js → CommandShortcut-CefCj5mt.js} +2 -2
  3. package/dist/{DataTable-C3E6jZ5B.js → DataTable-CUHMukiQ.js} +2 -2
  4. package/dist/DeckProgress-Dmk4_AKN.js +23 -0
  5. package/dist/{DialogContent-BxaaafFi.js → DialogContent-BRkT1cjP.js} +1 -1
  6. package/dist/{HoverPopover-CDA_SaCe.js → HoverPopover-2LOIW34a.js} +1 -1
  7. package/dist/InstrumentRail-CUJ05YVG.js +44 -0
  8. package/dist/{MetricBadge-idgrkqoP.js → MetricBadge-BZVWIxNy.js} +7 -5
  9. package/dist/{Notification-Cu0nN17_.js → Notification-DLMhblXR.js} +1 -1
  10. package/dist/{NumberFieldContent-CHEWghnY.js → NumberFieldContent-BbLOh8RU.js} +2 -2
  11. package/dist/{PopoverContent-DpUvdYY-.js → PopoverContent-BId6ggf_.js} +1 -1
  12. package/dist/{Progress-BW-StFw2.js → Progress-iEuD9pxG.js} +1 -1
  13. package/dist/{ScrollingText-DruYCni_.js → ScrollingText-BPP84wKo.js} +1 -1
  14. package/dist/{SelectScrollDownButton-pirzjm--.js → SelectScrollDownButton-T32cVFeM.js} +3 -3
  15. package/dist/{Switch-DeAqg6pi.js → Switch-Ch-k3Rv-.js} +1 -1
  16. package/dist/{TabsList-BAVzOZpg.js → TabsList-B8CVOQPC.js} +2 -2
  17. package/dist/{Toaster-CvuuW_y9.js → Toaster-1rYWrmAm.js} +46 -49
  18. package/dist/{ToggleGroupItem-DtD37b4G.js → ToggleGroupItem-COu_4IW4.js} +21 -17
  19. package/dist/animated-digit.js +2 -2
  20. package/dist/api/index.d.ts +5 -4
  21. package/dist/api.js +2 -2
  22. package/dist/aurora.js +70 -70
  23. package/dist/{badge-NdCQMvvB.js → badge-CaoFJzRb.js} +1 -1
  24. package/dist/badge.js +1 -1
  25. package/dist/{button-DyiWFAOE.js → button-DN39cGqL.js} +12 -12
  26. package/dist/button.js +1 -1
  27. package/dist/canvas.js +2 -2
  28. package/dist/card.js +1 -1
  29. package/dist/carousel.js +5 -5
  30. package/dist/collapsible.js +1 -1
  31. package/dist/{color-B65wWyQz.js → color-C5ls_QF7.js} +7 -9
  32. package/dist/color.js +1 -1
  33. package/dist/command.js +1 -1
  34. package/dist/components/custom/aurora/constants/presets.d.ts +62 -5
  35. package/dist/components/custom/aurora/constants/shaders/mediums.glsl.d.ts +1 -1
  36. package/dist/components/custom/aurora/index.d.ts +1 -1
  37. package/dist/components/custom/constellation/Constellation.vue.d.ts +0 -12
  38. package/dist/components/custom/constellation/constellationField.d.ts +151 -10
  39. package/dist/components/custom/constellation/index.d.ts +1 -3
  40. package/dist/components/custom/controls/DarkModeToggle.vue.d.ts +0 -11
  41. package/dist/components/custom/deck-progress/DeckProgress.vue.d.ts +27 -0
  42. package/dist/components/custom/deck-progress/index.d.ts +2 -0
  43. package/dist/components/custom/dialog-native/GlassDialogNative.vue.d.ts +57 -0
  44. package/dist/components/custom/dialog-native/index.d.ts +1 -0
  45. package/dist/components/custom/dock/DockTabButton.vue.d.ts +4 -3
  46. package/dist/components/custom/dock/GlassDock.vue.d.ts +115 -3
  47. package/dist/components/custom/dock/composables/dockMorphContext.d.ts +0 -36
  48. package/dist/components/custom/dock/composables/useLayerTransition.d.ts +0 -44
  49. package/dist/components/custom/expandable-container/ExpandableContainer.vue.d.ts +37 -5
  50. package/dist/components/custom/fourier-field/index.d.ts +1 -1
  51. package/dist/components/custom/fourier-field/math.d.ts +0 -17
  52. package/dist/components/custom/glass-panel/GlassPanel.vue.d.ts +41 -0
  53. package/dist/components/custom/glass-panel/index.d.ts +2 -0
  54. package/dist/components/custom/goo-blob/composables/useBlobPointer.d.ts +3 -4
  55. package/dist/components/custom/goo-blob/composables/useMetaballRenderer.d.ts +8 -11
  56. package/dist/components/custom/handmark/HandMark.vue.d.ts +29 -0
  57. package/dist/components/custom/handmark/brush.d.ts +242 -0
  58. package/dist/components/custom/handmark/freehand.d.ts +69 -0
  59. package/dist/components/custom/handmark/geometry.d.ts +50 -0
  60. package/dist/components/custom/handmark/index.d.ts +21 -0
  61. package/dist/components/custom/handmark/ink.d.ts +11 -0
  62. package/dist/components/custom/handmark/texture.d.ts +39 -0
  63. package/dist/components/custom/handmark/types.d.ts +53 -0
  64. package/dist/components/custom/handmark/useHandMark.d.ts +87 -0
  65. package/dist/components/custom/header-ribbon/HeaderRibbon.vue.d.ts +31 -0
  66. package/dist/components/custom/header-ribbon/index.d.ts +2 -0
  67. package/dist/components/custom/header-ribbon/types.d.ts +7 -0
  68. package/dist/components/custom/instrument-chassis/InstrumentChassis.vue.d.ts +2 -2
  69. package/dist/components/custom/instrument-rail/InstrumentRail.vue.d.ts +91 -0
  70. package/dist/components/custom/instrument-rail/index.d.ts +2 -0
  71. package/dist/components/custom/metric-badge/MetricBadge.vue.d.ts +8 -0
  72. package/dist/components/ui/drawer/Drawer.vue.d.ts +0 -1
  73. package/dist/components/ui/dropdown-menu/DropdownMenuSubContent.vue.d.ts +2 -2
  74. package/dist/components/ui/slider/index.d.ts +19 -22
  75. package/dist/components/ui/toast/use-toast.d.ts +2 -2
  76. package/dist/components/ui/toggle/Toggle.vue.d.ts +1 -1
  77. package/dist/components/ui/toggle/index.d.ts +1 -1
  78. package/dist/components/ui/toggle-group/ToggleGroup.vue.d.ts +7 -1
  79. package/dist/components/ui/toggle-group/ToggleGroupItem.vue.d.ts +3 -1
  80. package/dist/components/ui/toggle-group/toggleGroupContext.d.ts +13 -0
  81. package/dist/composables/color/index.d.ts +0 -12
  82. package/dist/composables/dark/index.d.ts +1 -1
  83. package/dist/composables/dark/useGlobalDark.d.ts +21 -1
  84. package/dist/composables/glass/useSpecularTracking.d.ts +3 -12
  85. package/dist/composables/motion/core/index.d.ts +1 -0
  86. package/dist/composables/motion/index.d.ts +0 -2
  87. package/dist/composables/motion/useRouteTransition.d.ts +42 -0
  88. package/dist/composables/motion/useSpringMount.d.ts +1 -1
  89. package/dist/configurator.js +1 -1
  90. package/dist/confirm-dialog.js +2 -2
  91. package/dist/constellation.js +225 -260
  92. package/dist/controls.js +14 -48
  93. package/dist/dark.d.ts +1 -1
  94. package/dist/dark.js +1 -1
  95. package/dist/data-table.js +1 -1
  96. package/dist/deck-progress.d.ts +1 -0
  97. package/dist/deck-progress.js +2 -0
  98. package/dist/dialog.js +2 -2
  99. package/dist/dock.js +345 -340
  100. package/dist/dockContext-BKeIMmNk.js +9 -0
  101. package/dist/{dropdown-menu-DVnQcA0E.js → dropdown-menu-D77jX6Df.js} +4 -7
  102. package/dist/dropdown-menu.js +1 -1
  103. package/dist/expandable-container.js +79 -38
  104. package/dist/forms.js +2 -2
  105. package/dist/fourier-field.js +102 -137
  106. package/dist/fourier-math.js +2 -2
  107. package/dist/glass-panel.d.ts +1 -0
  108. package/dist/glass-panel.js +44 -0
  109. package/dist/glass-ui.css +1 -1
  110. package/dist/glass-ui.js +209 -287
  111. package/dist/goo-blob.js +212 -243
  112. package/dist/handmark.d.ts +1 -0
  113. package/dist/handmark.js +662 -0
  114. package/dist/header-ribbon.d.ts +1 -0
  115. package/dist/header-ribbon.js +63 -0
  116. package/dist/hover-popover.js +1 -1
  117. package/dist/icon-tooltip.js +1 -1
  118. package/dist/index.d.ts +3 -0
  119. package/dist/instrument-rail.d.ts +1 -0
  120. package/dist/instrument-rail.js +2 -0
  121. package/dist/keyboard.js +1 -1
  122. package/dist/labeled-field.js +5 -5
  123. package/dist/{math-BUU9hHID.js → math-DTpRVC0e.js} +2 -18
  124. package/dist/metric-badge.js +1 -1
  125. package/dist/metric-stack.js +1 -1
  126. package/dist/motion-core.js +70 -69
  127. package/dist/motion.js +37 -38
  128. package/dist/notification.js +1 -1
  129. package/dist/number-field.js +1 -1
  130. package/dist/popover.js +1 -1
  131. package/dist/{presets-1OhFpaIC.js → presets-BP_ALM0m.js} +14 -1
  132. package/dist/progress.js +1 -1
  133. package/dist/pulse.js +1 -1
  134. package/dist/reactive.js +1 -1
  135. package/dist/scrolling-text.js +1 -1
  136. package/dist/search.js +6 -6
  137. package/dist/select.js +1 -1
  138. package/dist/{sheet-ChSXcJpU.js → sheet-VZEtNFvO.js} +1 -1
  139. package/dist/sheet.js +1 -1
  140. package/dist/{slider-sfl8gFDD.js → slider-ClgpP7EB.js} +4 -4
  141. package/dist/slider.js +1 -1
  142. package/dist/sortable-list.js +2 -2
  143. package/dist/stacked-icons.js +6 -6
  144. package/dist/styles/animations.css +111 -15
  145. package/dist/styles/components.css +1 -1
  146. package/dist/styles/dock/layer-group.css +0 -22
  147. package/dist/styles/dock/layers.css +1 -11
  148. package/dist/styles/dock/morph.css +19 -95
  149. package/dist/styles/dock/shell.css +0 -12
  150. package/dist/styles/dock-controls.css +31 -28
  151. package/dist/styles/drawer.css +0 -2
  152. package/dist/styles/floating-panel.css +0 -2
  153. package/dist/styles/glass-refract.css +0 -2
  154. package/dist/styles/glass.css +1188 -31
  155. package/dist/styles/hover-popover.css +0 -2
  156. package/dist/styles/index.css +6 -24
  157. package/dist/styles/instrument-chassis.css +0 -4
  158. package/dist/styles/paper.css +68 -2
  159. package/dist/styles/theme.css +20 -13
  160. package/dist/styles/tokens.css +2360 -22
  161. package/dist/styles/typography.css +6 -18
  162. package/dist/styles/utilities.css +1291 -19
  163. package/dist/styles/view-transition.css +51 -0
  164. package/dist/switch.js +1 -1
  165. package/dist/tabs.js +98 -98
  166. package/dist/timeline.js +2 -2
  167. package/dist/toast.js +1 -1
  168. package/dist/toggle-group.js +1 -1
  169. package/dist/typewriter.js +1 -1
  170. package/dist/{useAnimatedNumber-DfVgP4te.js → useAnimatedNumber-Bcpy5Etf.js} +1 -1
  171. package/dist/{useConfiguratorState-BdQXh9ie.js → useConfiguratorState-DxAqksK6.js} +1 -1
  172. package/dist/useGlassRenderer-D7YGOYiM.js +69 -0
  173. package/dist/{useGlobalDark-D7R9OypB.js → useGlobalDark-D1wgEqfW.js} +21 -2
  174. package/dist/useSpecularTracking-BVRZsRHA.js +26 -0
  175. package/dist/useSpringMount-BJSHcLl3.js +113 -0
  176. package/dist/useViewTransition-DaN5Zrox.js +21 -0
  177. package/dist/vReveal-zUIQk-rJ.js +53 -0
  178. package/dist/watercolor-dot.js +2 -2
  179. package/package.json +57 -33
  180. package/src/styles/animations.css +111 -15
  181. package/src/styles/dock/layer-group.css +0 -22
  182. package/src/styles/dock/layers.css +1 -11
  183. package/src/styles/dock/morph.css +19 -95
  184. package/src/styles/dock/shell.css +0 -12
  185. package/src/styles/dock-controls.css +31 -28
  186. package/src/styles/glass.css +1188 -31
  187. package/src/styles/index.css +6 -24
  188. package/src/styles/paper.css +68 -2
  189. package/src/styles/theme.css +20 -13
  190. package/src/styles/tokens.css +2360 -22
  191. package/src/styles/typography.css +6 -18
  192. package/src/styles/utilities.css +1291 -19
  193. package/src/styles/view-transition.css +51 -0
  194. package/dist/components/custom/aurora/constants/shaders/vangogh-medium.glsl.d.ts +0 -1
  195. package/dist/components/custom/constellation/composables/createConstellationField.d.ts +0 -35
  196. package/dist/components/custom/constellation/composables/useConstellationPointer.d.ts +0 -35
  197. package/dist/components/custom/constellation/constants.d.ts +0 -36
  198. package/dist/components/custom/constellation/constellationDraw.d.ts +0 -18
  199. package/dist/components/custom/constellation/constellationInteraction.d.ts +0 -102
  200. package/dist/components/custom/dock/composables/useDockMorphWindow.d.ts +0 -15
  201. package/dist/components/custom/dock/composables/useDockShellProps.d.ts +0 -131
  202. package/dist/components/custom/dock/constants.d.ts +0 -23
  203. package/dist/components/custom/fourier-field/presets.d.ts +0 -54
  204. package/dist/components/custom/goo-blob/composables/buildMetaballProgram.d.ts +0 -18
  205. package/dist/components/custom/goo-blob/composables/uploadBlobUniforms.d.ts +0 -41
  206. package/dist/components/custom/goo-blob/constants.d.ts +0 -52
  207. package/dist/components/custom/goo-blob/shaders/metaball-uniforms.glsl.d.ts +0 -1
  208. package/dist/components/custom/tabs/constants.d.ts +0 -12
  209. package/dist/components/custom/underline/GlassUnderline.vue.d.ts +0 -84
  210. package/dist/components/custom/underline/index.d.ts +0 -2
  211. package/dist/components/custom/underline/types.d.ts +0 -56
  212. package/dist/composables/motion/curves.d.ts +0 -48
  213. package/dist/composables/motion/springPresets.d.ts +0 -33
  214. package/dist/composables/motion/suite.d.ts +0 -2
  215. package/dist/dockContext-bho8B1A4.js +0 -17
  216. package/dist/motion-curves.d.ts +0 -1
  217. package/dist/motion-curves.js +0 -130
  218. package/dist/springPresets-aRSm-4_k.js +0 -40
  219. package/dist/styles/glass/a11y-fallback.css +0 -245
  220. package/dist/styles/glass/ladder.css +0 -304
  221. package/dist/styles/glass/material.css +0 -238
  222. package/dist/styles/glass/progress-rail.css +0 -78
  223. package/dist/styles/glass/squircle.css +0 -66
  224. package/dist/styles/glass/surfaces.css +0 -306
  225. package/dist/styles/tokens/color-radius.css +0 -250
  226. package/dist/styles/tokens/dark-arm.css +0 -194
  227. package/dist/styles/tokens/glass.css +0 -413
  228. package/dist/styles/tokens/light-dark.css +0 -158
  229. package/dist/styles/tokens/offsets-sizing.css +0 -406
  230. package/dist/styles/tokens/property-regs.css +0 -169
  231. package/dist/styles/tokens/scale-paper.css +0 -361
  232. package/dist/styles/tokens/scheme-motion.css +0 -334
  233. package/dist/styles/tokens/shadow.css +0 -132
  234. package/dist/styles/utilities/a11y-overrides.css +0 -140
  235. package/dist/styles/utilities/animate.css +0 -26
  236. package/dist/styles/utilities/base.css +0 -365
  237. package/dist/styles/utilities/btn.css +0 -399
  238. package/dist/styles/utilities/components.css +0 -317
  239. package/dist/underline.d.ts +0 -1
  240. package/dist/underline.js +0 -85
  241. package/dist/useSpecularTracking-DFK_fhpG.js +0 -32
  242. package/dist/useSpringMount-DXUzxBjV.js +0 -108
  243. package/dist/vReveal-CqaCz_mC.js +0 -44
  244. package/src/styles/glass/a11y-fallback.css +0 -245
  245. package/src/styles/glass/ladder.css +0 -304
  246. package/src/styles/glass/material.css +0 -238
  247. package/src/styles/glass/progress-rail.css +0 -78
  248. package/src/styles/glass/squircle.css +0 -66
  249. package/src/styles/glass/surfaces.css +0 -306
  250. package/src/styles/tokens/color-radius.css +0 -250
  251. package/src/styles/tokens/dark-arm.css +0 -194
  252. package/src/styles/tokens/glass.css +0 -413
  253. package/src/styles/tokens/light-dark.css +0 -158
  254. package/src/styles/tokens/offsets-sizing.css +0 -406
  255. package/src/styles/tokens/property-regs.css +0 -169
  256. package/src/styles/tokens/scale-paper.css +0 -361
  257. package/src/styles/tokens/scheme-motion.css +0 -334
  258. package/src/styles/tokens/shadow.css +0 -132
  259. package/src/styles/utilities/a11y-overrides.css +0 -140
  260. package/src/styles/utilities/animate.css +0 -26
  261. package/src/styles/utilities/base.css +0 -365
  262. package/src/styles/utilities/btn.css +0 -399
  263. package/src/styles/utilities/components.css +0 -317
  264. /package/dist/{CollapsibleContent-Drow5WkV.js → CollapsibleContent-CP9qF1v4.js} +0 -0
  265. /package/dist/{DialogFooter-DufLHi0y.js → DialogFooter-Gprw8H_A.js} +0 -0
  266. /package/dist/{IconTooltip-BrQ5p181.js → IconTooltip-Cus0fvK0.js} +0 -0
  267. /package/dist/{Input-B-F818RU.js → Input-xuDwALQR.js} +0 -0
  268. /package/dist/{_plugin-vue_export-helper-DWghtRt7.js → _plugin-vue_export-helper-ok8pgSl5.js} +0 -0
  269. /package/dist/{compile-Ce_V55zi.js → compile-BOXjLUxu.js} +0 -0
  270. /package/dist/{constants-C8NMonuo.js → constants-yKeKnE84.js} +0 -0
  271. /package/dist/{prng-CjVgzjQT.js → prng-D27-B3tK.js} +0 -0
  272. /package/dist/{resolveCanvasColor-CRvVzOvP.js → resolveCanvasColor-BBiYfp8g.js} +0 -0
  273. /package/dist/{useCanvas2D-CSeH8h16.js → useCanvas2D-ClrQuZtp.js} +0 -0
  274. /package/dist/{useIntersectionPause-DAdVPVp4.js → useIntersectionPause-Cck5Uxk8.js} +0 -0
  275. /package/dist/{useInterval-D3USn0yc.js → useInterval-Dsmqwi03.js} +0 -0
  276. /package/dist/{useKeyboardShortcuts-i8tHd5WO.js → useKeyboardShortcuts-CyIYrndH.js} +0 -0
  277. /package/dist/{useScrollProgress-CcgmgMQt.js → useScrollProgress-CffFGLa5.js} +0 -0
  278. /package/dist/{useSortable-DnyGXKKY.js → useSortable-C8ReQ50P.js} +0 -0
  279. /package/dist/{useTextHighlight-B3b10W6r.js → useTextHighlight-CWEi25hf.js} +0 -0
@@ -8,27 +8,2365 @@
8
8
  * Structural tokens (durations, easings, springs, z-index, focus ring,
9
9
  * interactive scales) are stable across consumers. Visual tokens (colors,
10
10
  * shadows, radii, grain opacities) are consumer-overridable.
11
- *
12
- * This file is the thin @import ROOT of the token cascade. The token body is
13
- * carved into cohesive `tokens/*.css` partials (each under the no-god-module
14
- * 500-line bound), @import-ed below in CASCADE ORDER. The cascade is isomorphic:
15
- * the master :root sub-ranges each carry their own :root{} (adjacent
16
- * same-selector blocks resolve identically), and the post-root overrides
17
- * (light-dark, the .dark arm, the @property registrations) keep their source
18
- * order. See docs/precepts/design-idioms.md §cohesion-aware partials. The
19
- * partial cascade order is BINDING — light-dark after the master partials
20
- * (overrides them), dark-arm after light-dark, property-regs last
21
- * (registrations + the specular magnitude cohort that reads them).
22
11
  */
23
12
 
24
- /* CSS @import rules must precede every other rule. The nine partials @import in
25
- cascade order: */
26
- @import "./tokens/scheme-motion.css";
27
- @import "./tokens/color-radius.css";
28
- @import "./tokens/shadow.css";
29
- @import "./tokens/glass.css";
30
- @import "./tokens/offsets-sizing.css";
31
- @import "./tokens/scale-paper.css";
32
- @import "./tokens/light-dark.css";
33
- @import "./tokens/dark-arm.css";
34
- @import "./tokens/property-regs.css";
13
+ :root {
14
+ /* ═══════════════════════════════════════════════
15
+ §-1 COLOR-SCHEME (AQ.W2 §1)
16
+
17
+ Lets native UI (scrollbars, spinbuttons, form-control chrome, the
18
+ default focus ring) honor the OS preference when the consumer has
19
+ NOT pinned a `.dark` class. glass-ui's dark mode is class-driven
20
+ (`.dark`, theme.css `@variant dark`), so `.dark` overrides to a hard
21
+ `dark` regardless of OS — but absent the class, native chrome should
22
+ still track `prefers-color-scheme`. `light dark` (not `only light`)
23
+ on `:root` delivers that; `.dark` hard-pins `dark`.
24
+
25
+ Baseline Widely Available → adopt natively, no `@supports` guard
26
+ (older engines ignore the property and keep default-light chrome).
27
+ `accent-color` (AQ.W2 §4) is the companion native-control brand;
28
+ both are inherited built-in <color>/keyword properties re-specified
29
+ inside `.dark`. The `--accent-color` token is defined in the §5
30
+ color block below (custom properties resolve order-independently).
31
+ ═══════════════════════════════════════════════ */
32
+ color-scheme: light dark;
33
+ accent-color: var(--accent-color);
34
+
35
+ /* ═══════════════════════════════════════════════
36
+ §0 FONT TOKEN SOURCE-OF-TRUTH
37
+
38
+ The single authoritative declaration of the library's font register.
39
+ theme.css bridges each --font-stack-* to its Tailwind --font-* namespace
40
+ var; typography.css consumes the bridged --font-* names. The public
41
+ runtime knobs remain --font-*, --leading-*, --tracking-*.
42
+
43
+ ONE brand register: Plus Jakarta Sans (text + display) + Fira Code (mono).
44
+ --font-stack-text drives the body/heading/prose/display register — the
45
+ library DEFAULT is the register every live surface paints (the demo +
46
+ speedtest render Plus Jakarta everywhere). The calibrated "Plus Jakarta
47
+ Sans Fallback" / "Fira Code Fallback" faces (declared in typography.css)
48
+ wrap the system stacks with Capsize metric overrides so the swap window
49
+ is geometry-neutral (zero CLS). --font-stack-display aliases the text
50
+ register: there is no separate display-serif voice. */
51
+ --font-stack-text: "Plus Jakarta Sans", "Plus Jakarta Sans Fallback", system-ui, sans-serif;
52
+ --font-stack-display: var(--font-stack-text);
53
+ --font-stack-sans: var(--font-stack-text);
54
+ --font-stack-mono: "Fira Code", "Fira Code Fallback", "Fira Mono", monospace;
55
+
56
+ --type-leading-micro: 1.2;
57
+ --type-leading-caption: 1.3;
58
+ --type-leading-small: 1.4;
59
+ --type-leading-body: 1.5;
60
+ --type-leading-prose: 1.618;
61
+ --type-leading-heading: 1.2;
62
+ --type-leading-display: 1.1;
63
+
64
+ --type-tracking-tight: -0.025em;
65
+ --type-tracking-snug: -0.01em;
66
+ --type-tracking-normal: 0;
67
+ --type-tracking-wide: 0.025em;
68
+ --type-tracking-wider: 0.05em;
69
+ --type-tracking-caps: 0.1em;
70
+
71
+ /* ═══════════════════════════════════════════════
72
+ §1 DURATION
73
+ ═══════════════════════════════════════════════ */
74
+ --duration-instant: 0.1s;
75
+ --duration-fast: 0.2s;
76
+ --duration-normal: 0.3s;
77
+ --duration-slow: 0.45s;
78
+ --duration-panel: 0.55s;
79
+ --duration-xl: 1s;
80
+ --duration-xxl: 1.5s;
81
+ --duration-shimmer-fast: 3s;
82
+ --duration-shimmer: 5s;
83
+ /* The gold ONE-SHOT seal envelope (E7c) — a PRESSED seal is a quick gesture
84
+ (in + hold + out), ≈1.5s end-to-end, NOT the 5s infinite-loop `--duration-
85
+ shimmer`. `.text-gilt` reads THIS so "catches the light once and recedes"
86
+ reads as a gesture, never a marquee that then hard-cuts. */
87
+ --duration-seal: 1.5s;
88
+ /* Sparkle micro-event — primary-CTA disco-grain sparkle sweep.
89
+ @deprecated 3.10 — the disco-star is EXCISED (D6.b); the `sparkle-sweep`
90
+ keyframe is tombstoned (animations.css) and no live recipe consumes this
91
+ duration. Retained one minor so an out-of-tree consumer resolves a value
92
+ rather than `unset`; full delete (this token + the theme bridge + the
93
+ keyframe) lands in 3.11. */
94
+ --duration-sparkle: 600ms;
95
+
96
+ /* AH.W5-e (FA-8 §4d) — motion-duration + staged-reveal canon.
97
+
98
+ Six duration tokens + three delay tokens promoted from raw literals
99
+ at speedtest's celebratory-reveal SFCs (SpeedtestResults, ThankYou,
100
+ ResultStack, CompleteBadge). Unified under the `--motion-*`
101
+ namespace (parallel to `--motion-ease-*`) so all motion tokens
102
+ cluster at one canon — duration + easing arm together. */
103
+
104
+ /* Staged-entrance pairing — opacity + transform Vue <Transition> arms
105
+ in the celebratory headline + result-row reveals. Partners with
106
+ `--motion-ease-staged-entrance` / `--motion-ease-overshoot`. */
107
+ --motion-duration-staged: 320ms;
108
+
109
+ /* One-shot celebratory shimmer — gold-headline shimmer animation
110
+ duration + staging delay. Shared canon so the speedtest results
111
+ headline and the thank-you headline stay in lockstep. */
112
+ --motion-duration-complete-shimmer: 2.4s;
113
+ --motion-delay-complete-shimmer: 220ms;
114
+ /* The gold-headline shimmer reads these two tokens under a one-shot
115
+ animation: at 2.4s + 220ms delay the bright band traverses the headline
116
+ glyphs cleanly. Load-bearing — zeroing either collapses the shimmer to
117
+ an imperceptible blow-through. */
118
+
119
+ /* CompleteBadge staged-reveal cluster — the disc / ring / check curve
120
+ fires at three asymmetric durations + delays per the A4-AF staged-
121
+ variety design intent. Promoting the magic numbers makes the
122
+ staged-variety visible at the canon. */
123
+ --motion-duration-badge-disc: 420ms;
124
+ --motion-duration-badge-ring: 560ms;
125
+ --motion-duration-badge-check: 380ms;
126
+ --motion-delay-badge-disc: 80ms;
127
+ --motion-delay-badge-ring: 220ms;
128
+ --motion-delay-badge-check: 460ms;
129
+
130
+ /* Staggered-reveal increment canon — three perceptually-distinct tiers for
131
+ per-row/per-cell/per-bar cascades:
132
+ • tight (40ms) — dense row cascades, sub-perceptual gap
133
+ • default (80ms) — the canonical row-stagger rhythm (matches
134
+ `useStagger`'s built-in fallback)
135
+ • relaxed (120ms) — wider cascades where each reveal carries its own
136
+ attention budget
137
+ Adjacent tiers keep cross-beats quiet (80 is the LCM of 40 and 2/3 of
138
+ 120). Consumers read these via inline-style calc() or `getComputedStyle`
139
+ at runtime. */
140
+ --motion-stagger-tight: 40ms;
141
+ --motion-stagger-default: 80ms;
142
+ --motion-stagger-relaxed: 120ms;
143
+
144
+ /* ═══════════════════════════════════════════════
145
+ §2 EASING — Spring curves via linear()
146
+
147
+ Generated from `scripts/regen-spring-tokens.mjs` — DO NOT hand-edit.
148
+ To tune a spring: edit the PRESETS table in the script and re-run
149
+ `node scripts/regen-spring-tokens.mjs`. The script invokes
150
+ `springLinearStops()` from `@mkbabb/keyframes.js` which solves the
151
+ analytic second-order damped harmonic oscillator and emits CSS
152
+ `linear()` stops; this is the single source of truth tying each
153
+ (response, ζ) pair to its serialized curve.
154
+
155
+ iOS-canonical (response, dampingFraction) per AL-X4 §3:
156
+ smooth: (0.50s, ζ=0.86) — gentle settle, sub-perceptual overshoot ~0.5%
157
+ snappy: (0.35s, ζ=0.65) — quick crisp, overshoot ~+6.8%
158
+ bouncy: (0.50s, ζ=0.45) — emphatic overshoot ~+20.5%
159
+ gentle: (0.70s, ζ=1.00) — patient critically-damped
160
+ dock: (0.32s, ζ=0.70) — dock expand/collapse morph, overshoot ~+4.6%
161
+
162
+ Note on `smooth`: at ζ=0.86 the analytic solver produces a peak
163
+ of 1.00498 (+0.5%) at the 24% slot — mathematically present but
164
+ sub-perceptual. I keep ζ=0.86 rather than promoting to ζ=1.0
165
+ because true critical damping is `gentle`'s register, and
166
+ `smooth` wants a slightly faster visual settle than `gentle`'s
167
+ patient curve. The 0.5% peak does not read as overshoot to the
168
+ eye but does narrowly distinguish `smooth` from `gentle`.
169
+
170
+ ── EASING DOCTRINE (AX.W52 §6 — which easing fits which job) ──
171
+ ONE recorded rule so the animation easing stops forking live. This
172
+ FORMALIZES the already-correct `transitions.css` idiom (enter=spring,
173
+ exit=bezier) as the house rule every recipe reaches for:
174
+
175
+ | Transition kind | Easing |
176
+ |--------------------------------------------------|-------------------------------------|
177
+ | Surface props (bg, border, color, box-shadow, | `--ease-standard` (bezier — a |
178
+ | opacity) | colour cross-fade reads as a |
179
+ | | wobble on a spring) |
180
+ | Transform — hover/press/active (scale, translate,| `--spring-smooth` (the ONE button/ |
181
+ | rotate) | interactive scale register) or |
182
+ | | `--spring-snappy` (crisp press) |
183
+ | Enter (mount, popover open, dialog in) | `--spring-bouncy` / `--spring-snappy`|
184
+ | Exit (unmount, close) | `--ease-out` / `--ease-standard` |
185
+ | | (bezier, NO overshoot — an exit |
186
+ | | must never overshoot past gone) |
187
+ | Position-tracked (specular pointer follow) | `--ease-standard` |
188
+
189
+ The BUTTON family (`.btn-pill` / `.tap-squish` / `.glass-btn` /
190
+ `btn-interactive`) UNIFIES its `scale` transform leg onto ONE register —
191
+ `--spring-smooth` — so hover + press read as one motion language (the
192
+ hover lift is the restrained `--scale-hover-btn` = 1.05, not the 1.08
193
+ `--scale-hover`). Springs read alive on TRANSFORM and ENTER (overshoot =
194
+ physicality); a spring on a COLOR or an EXIT reads as a wobble/hesitation.
195
+ `--ease-apple` is the ambient-only register (consumed solely by the Pulse
196
+ aura `--animate-ambient-pulse-easing` — NOT a second smooth-ease authority
197
+ for interactive transforms; those use `--spring-smooth`).
198
+ ═══════════════════════════════════════════════ */
199
+ --spring-smooth: linear(0, 0.09804 2.041%, 0.29288 4.082%, 0.49447 6.122%, 0.66436 8.163%, 0.79208 10.204%, 0.88050 12.245%, 0.93754 14.286%, 0.97182 16.327%, 0.99083 18.367%, 1.00027 20.408%, 1.00413 22.449%, 1.00502 24.490%, 1.00453 26.531%, 1.00355 28.571%, 1.00254 30.612%, 1.00169 32.653%, 1.00105 34.694%, 1.00060 36.735%, 1.00031 38.776%, 1.00014 40.816%, 1.00005 42.857%, 1.00000 44.898%, 1.00000 46.939%, 1.00000 48.980%, 1.00000 51.020%, 1.00000 53.061%, 1.00000 55.102%, 1.00000 57.143%, 1.00000 59.184%, 1.00000 61.224%, 1.00000 63.265%, 1.00000 65.306%, 1.00000 67.347%, 1.00000 69.388%, 1.00000 71.429%, 1.00000 73.469%, 1.00000 75.510%, 1.00000 77.551%, 1.00000 79.592%, 1.00000 81.633%, 1.00000 83.673%, 1.00000 85.714%, 1.00000 87.755%, 1.00000 89.796%, 1.00000 91.837%, 1.00000 93.878%, 1.00000 95.918%, 1.00000 97.959%, 1);
200
+ --spring-snappy: linear(0, 0.10438 2.041%, 0.32622 4.082%, 0.56668 6.122%, 0.77153 8.163%, 0.91961 10.204%, 1.01073 12.245%, 1.05549 14.286%, 1.06804 16.327%, 1.06171 18.367%, 1.04686 20.408%, 1.03044 22.449%, 1.01629 24.490%, 1.00596 26.531%, 0.99954 28.571%, 0.99633 30.612%, 0.99537 32.653%, 0.99575 34.694%, 0.99674 36.735%, 0.99786 38.776%, 0.99884 40.816%, 0.99956 42.857%, 1.00001 44.898%, 1.00024 46.939%, 1.00000 48.980%, 1.00000 51.020%, 1.00000 53.061%, 1.00000 55.102%, 1.00000 57.143%, 1.00000 59.184%, 1.00000 61.224%, 1.00000 63.265%, 1.00000 65.306%, 1.00000 67.347%, 1.00000 69.388%, 1.00000 71.429%, 1.00000 73.469%, 1.00000 75.510%, 1.00000 77.551%, 1.00000 79.592%, 1.00000 81.633%, 1.00000 83.673%, 1.00000 85.714%, 1.00000 87.755%, 1.00000 89.796%, 1.00000 91.837%, 1.00000 93.878%, 1.00000 95.918%, 1.00000 97.959%, 1);
201
+ --spring-bouncy: linear(0, 0.11105 2.041%, 0.36430 4.082%, 0.65496 6.122%, 0.90929 8.163%, 1.08828 10.204%, 1.18289 12.245%, 1.20482 14.286%, 1.17641 16.327%, 1.12214 18.367%, 1.06276 20.408%, 1.01239 22.449%, 0.97809 24.490%, 0.96099 26.531%, 0.95826 28.571%, 0.96514 30.612%, 0.97666 32.653%, 0.98873 34.694%, 0.99867 36.735%, 1.00520 38.776%, 1.00825 40.816%, 1.00847 42.857%, 1.00686 44.898%, 1.00443 46.939%, 1.00199 48.980%, 1.00004 51.020%, 0.99880 53.061%, 0.99827 55.102%, 0.99829 57.143%, 0.99866 59.184%, 0.99916 61.224%, 0.99966 63.265%, 1.00004 65.306%, 1.00027 67.347%, 1.00000 69.388%, 1.00000 71.429%, 1.00000 73.469%, 1.00000 75.510%, 1.00000 77.551%, 1.00000 79.592%, 1.00000 81.633%, 1.00000 83.673%, 1.00000 85.714%, 1.00000 87.755%, 1.00000 89.796%, 1.00000 91.837%, 1.00000 93.878%, 1.00000 95.918%, 1.00000 97.959%, 1);
202
+ --spring-gentle: linear(0, 0.09415 2.041%, 0.27374 4.082%, 0.45506 6.122%, 0.60779 8.163%, 0.72570 10.204%, 0.81213 12.245%, 0.87336 14.286%, 0.91570 16.327%, 0.94445 18.367%, 0.96371 20.408%, 0.97645 22.449%, 0.98481 24.490%, 0.99025 26.531%, 0.99377 28.571%, 0.99604 30.612%, 0.99749 32.653%, 0.99841 34.694%, 0.99900 36.735%, 0.99937 38.776%, 0.99961 40.816%, 0.99975 42.857%, 0.99985 44.898%, 1.00000 46.939%, 1.00000 48.980%, 1.00000 51.020%, 1.00000 53.061%, 1.00000 55.102%, 1.00000 57.143%, 1.00000 59.184%, 1.00000 61.224%, 1.00000 63.265%, 1.00000 65.306%, 1.00000 67.347%, 1.00000 69.388%, 1.00000 71.429%, 1.00000 73.469%, 1.00000 75.510%, 1.00000 77.551%, 1.00000 79.592%, 1.00000 81.633%, 1.00000 83.673%, 1.00000 85.714%, 1.00000 87.755%, 1.00000 89.796%, 1.00000 91.837%, 1.00000 93.878%, 1.00000 95.918%, 1.00000 97.959%, 1);
203
+ --spring-dock: linear(0, 0.10282 2.041%, 0.31773 4.082%, 0.54783 6.122%, 0.74309 8.163%, 0.88550 10.204%, 0.97598 12.245%, 1.02452 14.286%, 1.04368 16.327%, 1.04501 18.367%, 1.03739 20.408%, 1.02681 22.449%, 1.01672 24.490%, 1.00873 26.531%, 1.00323 28.571%, 0.99996 30.612%, 0.99837 32.653%, 0.99789 34.694%, 0.99805 36.735%, 0.99848 38.776%, 0.99897 40.816%, 0.99940 42.857%, 0.99972 44.898%, 0.99993 46.939%, 1.00004 48.980%, 1.00000 51.020%, 1.00000 53.061%, 1.00000 55.102%, 1.00000 57.143%, 1.00000 59.184%, 1.00000 61.224%, 1.00000 63.265%, 1.00000 65.306%, 1.00000 67.347%, 1.00000 69.388%, 1.00000 71.429%, 1.00000 73.469%, 1.00000 75.510%, 1.00000 77.551%, 1.00000 79.592%, 1.00000 81.633%, 1.00000 83.673%, 1.00000 85.714%, 1.00000 87.755%, 1.00000 89.796%, 1.00000 91.837%, 1.00000 93.878%, 1.00000 95.918%, 1.00000 97.959%, 1);
204
+
205
+ /* Core cubic-bezier (exits, fallbacks, non-spring uses) */
206
+ --motion-ease-standard: cubic-bezier(0.4, 0, 0.2, 1);
207
+ --motion-ease-out: cubic-bezier(0, 0, 0.2, 1);
208
+ --motion-ease-in: cubic-bezier(0.4, 0, 1, 1);
209
+ --motion-ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
210
+
211
+ --ease-standard: var(--motion-ease-standard);
212
+ --ease-out: var(--motion-ease-out);
213
+ --ease-in: var(--motion-ease-in);
214
+ --ease-out-expo: var(--motion-ease-out-expo);
215
+
216
+ /* Apple-inspired — micro-interactions, switches, dock hover */
217
+ --motion-ease-apple: cubic-bezier(0.25, 0.1, 0.25, 1);
218
+
219
+ --ease-apple: var(--motion-ease-apple);
220
+ --ease-spring: var(--spring-snappy);
221
+ --ease-decelerate: var(--ease-out);
222
+ --ease-accelerate: var(--ease-in);
223
+
224
+ /* ═══════════════════════════════════════════════
225
+ §2.A AMBIENT MOTION — Pulse aura tokens
226
+
227
+ Breathing rhythm for `<Pulse variant="aura">`. The 6s duration is the
228
+ canon ambient-pulse rate; scale 1.0 ↔ 1.15 reads as a quiet halo at
229
+ intensity="normal" (Pulse remaps min/max from its `intensity` prop).
230
+ Reduced motion: Pulse.vue parks at the min stop, breath cycle disabled.
231
+
232
+ AX.W57 (P6) — the aura re-baselined from a glowing DISC to an AMBIENT
233
+ halo. The egregiousness was doubly-stacked: a 0.55-density currentColor
234
+ radial multiplied by an element opacity the `ambient-pulse` keyframe
235
+ drove up to 0.95 mid-breath (so a saturated viz hue read as a near-
236
+ opaque throbbing blob). The fix is two decoupled, single-source knobs:
237
+ · `--pulse-aura-strength` (0..1) — the ONE gradient-density scalar
238
+ both color-mix stops derive via calc() (replaces the
239
+ `--pulse-aura-opacity-pct` / `-pct-num` twin desync trap — the
240
+ percent and the raw number had to be hand-kept in sync).
241
+ · `--pulse-aura-breath-{min,max}` — the aura's OWN element-opacity
242
+ range, decoupled from the shared `--pulse-aura-opacity-*` shimmer
243
+ tokens (those stay 0.55/0.95 for the neutral skeleton-breath, where
244
+ a grey shimmer at that range is the correct soft swell).
245
+ The loud old read is the explicit `intensity="vivid"` opt-in. */
246
+ --animate-ambient-pulse-duration: 6s;
247
+ --animate-ambient-pulse-scale-min: 1.0;
248
+ --animate-ambient-pulse-scale-max: 1.15;
249
+ --animate-ambient-pulse-easing: var(--ease-apple);
250
+
251
+ /* Aura gradient density — ONE scalar (0..1). The center stop paints at
252
+ `strength`, the mid stop at `strength * 0.35` (both via calc() in
253
+ Pulse.vue). 0.22 reads as an ambient wash, not a glowing disc. */
254
+ --pulse-aura-strength: 0.22;
255
+
256
+ /* Aura element-opacity breath range (the `ambient-pulse` keyframe stops).
257
+ Quiet: the halo stays a wash at every phase of the 6s cycle. */
258
+ --pulse-aura-breath-min: 0.28;
259
+ --pulse-aura-breath-max: 0.42;
260
+
261
+ /* Skeleton-breath shimmer range — the SHARED neutral-shimmer stops
262
+ (`.skeleton-breath` reads these; a grey shimmer at 0.55↔0.95 is the
263
+ correct soft swell, distinct from the aura's calmer breath above). */
264
+ --pulse-aura-opacity-min: 0.55;
265
+ --pulse-aura-opacity-max: 0.95;
266
+
267
+ /* ═══════════════════════════════════════════════
268
+ §2.B SECTIONED PROGRESS — phase-bus rail (AB.W3.T2)
269
+
270
+ The sectioned `<Progress variant="sectioned">` exposes one
271
+ height knob + one track tint knob. Defaults size the rail at
272
+ 0.875rem (14px) — the W3 spec minimum (≥ 14px mobile, ≥ 18px
273
+ desktop). Consumers override the height per breakpoint via the
274
+ same token name on the consumer-side surface.
275
+
276
+ The bar reads as a recessed glass channel: inner top
277
+ catch-light + lower inner shadow + small outer drop. The
278
+ depth lives entirely in the Progress.vue scoped block; this
279
+ token only parameterises the rail BACKGROUND and HEIGHT —
280
+ depth shadows compose from the existing --shadow-color rung.
281
+ ═══════════════════════════════════════════════ */
282
+ --progress-sectioned-height: 0.875rem;
283
+ --progress-sectioned-track: var(--secondary);
284
+
285
+ /* ═══════════════════════════════════════════════
286
+ §2.C ANIMATION EXPRESSIVENESS
287
+
288
+ Three idiom families:
289
+ • Progress lifecycle — the progress bar rides an intake pulse on
290
+ rising-edge, a crescendo brightening at ≥85%, and a one-shot discharge
291
+ flash at 100% (the crescendo is the typed `@property --progress-crescendo`).
292
+ • Chassis phase-tint companion — `@property --phase-tint-amount` so the
293
+ chassis backdrop warms toward the active phase, interpolating with
294
+ `--phase-color` across the consumer's phase-handoff window (600ms default).
295
+ • Audacious button press-ripple — `btn-audacious` gains a typed
296
+ `@property --ripple-radius` ramping 0 → max over the ripple duration.
297
+ ═══════════════════════════════════════════════ */
298
+
299
+ /* Progress lifecycle — typed crescendo + segment-local durations */
300
+ --motion-duration-progress-intake: 220ms; /* rising-edge pulse */
301
+ --motion-duration-progress-crescendo: 240ms; /* leading-edge brighten */
302
+ --motion-duration-progress-indeterminate: 4s; /* indeterminate sweep period */
303
+
304
+ /* Chassis depth-of-tint peak — the amount `--phase-tint-amount`
305
+ interpolates toward when a phase is active. Subtle by intent;
306
+ a 6% color-mix reads as "warmth toward the phase" without
307
+ overwhelming the chassis chrome's resting register. */
308
+ --phase-tint-peak: 6%;
309
+
310
+ /* Audacious button press-ripple — geometry + duration. The ripple
311
+ radius caps at 12rem so an oversized button doesn't paint a
312
+ transparent halo beyond its bounds (the parent's `overflow:
313
+ hidden` clips the visible region; the cap keeps the gradient
314
+ falloff well-defined). */
315
+ --ripple-radius-max: 12rem;
316
+ --motion-duration-ripple: 340ms;
317
+
318
+ /* Skeleton breath variant — the M.3 "known-imminent" loading
319
+ register. 6s cycle (the canon ambient rate) so a known-imminent
320
+ skeleton breathes in lockstep with the dial and the Pulse aura.
321
+ Coexists with the 1.5s shimmer (sliding gradient) and the
322
+ Tailwind animate-pulse (2s, short-wait) variants — distinct
323
+ temporal registers per use-case. */
324
+ --skeleton-breath-duration: 6s;
325
+
326
+ /* ═══════════════════════════════════════════════
327
+ §3 Z-INDEX SCALE
328
+ ═══════════════════════════════════════════════ */
329
+ /* V.W2 T12 — Aurora background tier (sits behind --z-background).
330
+ Consumed by `<Aurora>` in speedtest's App.vue (`-z-10` literal today). */
331
+ --z-behind: -10;
332
+ --z-background: 0;
333
+ --z-content: 10;
334
+ --z-controls: 20;
335
+ --z-bar: 30;
336
+ --z-header: 35;
337
+ --z-dock: 40;
338
+ --z-panel: 45;
339
+ --z-overlay: 50;
340
+ --z-hovercard: 120;
341
+ --z-tooltip: 120;
342
+ --z-popover: 130;
343
+ --z-modal: 140;
344
+ --z-fullscreen: 150;
345
+ --z-toast: 160;
346
+ --z-toggle: 999;
347
+ --z-max: 9999;
348
+ --z-debug: 99999;
349
+
350
+ /* §4 BORDER RADIUS — authored ONCE in theme.css's leading plain @theme
351
+ block (AV.W16 TW1). A plain @theme mints each `--radius-*` as a global
352
+ :root var, so the `var(--radius-card)` recipe reads across this file
353
+ resolve from that source. The prior §4 twin here doubled the override
354
+ surface (the anti-pattern the @theme-inline migration kills) — excised;
355
+ retune the scale at theme.css. */
356
+
357
+ /* ═══════════════════════════════════════════════
358
+ §5 COLOR PALETTE — Warm cream / muted black
359
+
360
+ Neutral scale: perceptually-uniform L* steps at hue 48 (warm paper).
361
+ Each pair visibly distinct at 20px viewing distance.
362
+ 0 → 1 : 3 L (page → soft field)
363
+ 1 → 2 : 5 L (soft field → chip / tab-inactive)
364
+ 2 → 3 : 8 L (chip → hover / highlight)
365
+ 3 → 4 : 12 L (highlight → subtle border)
366
+ 4 → 5 : 25 L (border → muted text, contrast jump)
367
+ 5 → 6 : 10 L (muted text → strong-muted text, secondary register)
368
+
369
+ Semantic aliases re-point into the scale — no back-compat shims.
370
+ ═══════════════════════════════════════════════ */
371
+ --neutral-0: hsl(48 12% 98%); /* L 98 — page surface */
372
+ --neutral-1: hsl(48 10% 95%); /* L 95 — soft field bg */
373
+ --neutral-2: hsl(48 9% 90%); /* L 90 — chip / tab-inactive bg */
374
+ --neutral-3: hsl(48 8% 82%); /* L 82 — hover / highlight bg */
375
+ --neutral-4: hsl(48 7% 70%); /* L 70 — subtle border */
376
+ --neutral-5: hsl(48 6% 40%); /* L 40 — muted text (WCAG AA: 5.23:1 vs L98 page / 4.91:1 vs L95 muted) */
377
+ /* W7-α' (AK-FD1) — strong-muted text rung. One principled ladder step
378
+ (10 L darker, the 5→6 cadence above) past muted text toward
379
+ --foreground (L10). Sits at the 1/3 point of the L40→L10 muted→primary
380
+ gap: legible secondary text that reads less-faint than muted but not as
381
+ assertive as primary. By-COLOUR per the canon (a neutral step, not an
382
+ alpha mute); hue held at 48, sat nudged 6→7 toward --foreground's 10.
383
+ WCAG AA: 7.79:1 vs L98 page / 7.31:1 vs L95 muted (cf. neutral-5's
384
+ 5.23:1) — comfortably above the 4.5:1 body floor. */
385
+ --neutral-6: hsl(48 7% 30%); /* L 30 — strong-muted text (secondary register) */
386
+
387
+ --background: var(--neutral-0);
388
+ --foreground: hsl(24 10% 10%);
389
+ --card: var(--neutral-0);
390
+ --card-foreground: var(--foreground);
391
+ --popover: var(--neutral-0);
392
+ --popover-foreground: var(--foreground);
393
+
394
+ /* R0G-5 (AO.W4) — warm-neutral public-data panel surface. One warmth step
395
+ off --card (hue 44 vs 48, +4 sat, −2 L) so a consumer's aurora-OFF
396
+ /dashboard/* surfaces read with the admin canon's warm-neutral chrome
397
+ instead of the colder bare card. Opaque surface (the --card family), not
398
+ a --surface-tint-* overlay. Consumer applies on /dashboard/* only. */
399
+ --surface-public-data-panel: hsl(44 16% 96%);
400
+
401
+ --muted: var(--neutral-1);
402
+ --muted-foreground: var(--neutral-5);
403
+ /* W7-α' (AK-FD1) — one rung less-faint than --muted-foreground for
404
+ secondary text that reads too-faint at the muted rung over resting
405
+ glass (CardDescription). By-colour neutral step, not alpha. */
406
+ --muted-foreground-strong: var(--neutral-6);
407
+ --secondary: var(--neutral-2);
408
+ --secondary-foreground: var(--foreground);
409
+ --accent: var(--neutral-3);
410
+ --accent-foreground: var(--foreground);
411
+
412
+ --border: var(--neutral-4);
413
+ --input: var(--neutral-4);
414
+
415
+ --primary: hsl(24 10% 10%);
416
+ --primary-foreground: var(--neutral-0);
417
+ --destructive: hsl(0 72% 50%);
418
+ --destructive-foreground: var(--neutral-0);
419
+ --ring: hsl(24 10% 10%);
420
+ --shadow: hsl(24 10% 10%);
421
+ --shadow-color: var(--foreground);
422
+
423
+ /* AQ.W2 §4 — native-control accent. `accent-color` is an inherited
424
+ built-in <color>, so per the §2b resolve-late discipline it reads a
425
+ dedicated UNREGISTERED token set per-mode (NOT light-dark()) and is
426
+ re-specified inside `.dark` — otherwise the accent freezes to the
427
+ light shade at the cascade root. Light: muted-black checkmarks /
428
+ sliders on warm cream. Dark arm (`.dark`) reads `--ring` (mid-tone),
429
+ not the near-white dark `--primary`, so a checkbox's white checkmark
430
+ keeps contrast headroom (accent-color.md §5). */
431
+ --accent-color: var(--primary);
432
+
433
+ /* Border hairline at whisper alpha — the canonical rung for
434
+ section-spine / panel-edge hairlines a consumer wants to read as an
435
+ iOS-26 whisper rather than a hard rule. Consumers compose this named
436
+ rung instead of hand-mixing `color-mix(... var(--border) ...)`
437
+ per-site (token-first). 45% is the canonical whisper. Auto-dark via
438
+ `--border` (which is `var(--neutral-4)`, light-dark()-resolved). */
439
+ --border-soft: color-mix(in srgb, var(--border) 45%, transparent);
440
+
441
+ /* AQ.W3 §W3.4 — tokenized scrollbar thumb + track. Minted in the color
442
+ block so the `scrollbar-color` / `::-webkit-scrollbar-thumb` rungs read a
443
+ single source. Auto-dark via `--muted-foreground` (light-dark()-resolved,
444
+ AQ.W2) — the OS scrollbar chrome also honors `color-scheme: dark`, so the
445
+ custom thumb tracks the dark surface without an explicit dark mirror. The
446
+ thumb α (25%) matches the prior inline `.scrollbar-thin` value byte-for-
447
+ byte; `transparent` track keeps the scroll-pane glass reading through. */
448
+ --scrollbar-thumb: color-mix(in srgb, var(--muted-foreground) 25%, transparent);
449
+ --scrollbar-track: transparent;
450
+
451
+ /* Foreground-tint rungs — canonical handles for the 36+ raw
452
+ `color-mix(in srgb, var(--foreground) N%, transparent)` sites surveyed
453
+ in R5 axis-1. Each rung's α is named in its key (4 → 4%, 22 → 22%).
454
+ Auto-dark via --foreground; no explicit dark mirrors needed. R5
455
+ vocab.γ gap row 1. Consumers land in W2.B + W3.C. */
456
+ --surface-tint-4: color-mix(in srgb, var(--foreground) 4%, transparent);
457
+ --surface-tint-6: color-mix(in srgb, var(--foreground) 6%, transparent);
458
+ --surface-tint-8: color-mix(in srgb, var(--foreground) 8%, transparent);
459
+ --surface-tint-10: color-mix(in srgb, var(--foreground) 10%, transparent);
460
+ --surface-tint-12: color-mix(in srgb, var(--foreground) 12%, transparent);
461
+ --surface-tint-15: color-mix(in srgb, var(--foreground) 15%, transparent);
462
+ --surface-tint-18: color-mix(in srgb, var(--foreground) 18%, transparent);
463
+ --surface-tint-22: color-mix(in srgb, var(--foreground) 22%, transparent);
464
+ --surface-tint-25: color-mix(in srgb, var(--foreground) 25%, transparent);
465
+ /* Higher-α rungs added at L.W5 (absorbs K R4 — 4 P1 sites whose
466
+ literal `color-mix(in srgb, var(--foreground) {35,40,70}%, transparent)`
467
+ expressions bypassed the canonical token family). See `docs/tranches/L/
468
+ audit/W5-A-doc-cohort-proof.md`. */
469
+ --surface-tint-35: color-mix(in srgb, var(--foreground) 35%, transparent);
470
+ --surface-tint-40: color-mix(in srgb, var(--foreground) 40%, transparent);
471
+ --surface-tint-70: color-mix(in srgb, var(--foreground) 70%, transparent);
472
+
473
+ /* Muted-tinted surface rungs — canonical handles for the 9 raw
474
+ `color-mix(in srgb, var(--muted) N%, transparent)` sites in
475
+ FuzzySearch / SegmentedTabs / Slider scoped CSS. (AI.W5-δ — the
476
+ ProgressiveSidebar consumer retired at the wave; the canonical token
477
+ rung lives on for the remaining consumers.) Auto-dark via --muted.
478
+ R5 vocab.γ axis-1 row. */
479
+ --muted-soft: color-mix(in srgb, var(--muted) 30%, transparent);
480
+ --muted-medium: color-mix(in srgb, var(--muted) 50%, transparent);
481
+
482
+ /* ═══════════════════════════════════════════════
483
+ §5c CONSTELLATION — Canvas2D proximity-graph lattice (AX.W17)
484
+
485
+ The H.W4 legibility intelligence promoted from the slides deck to
486
+ LIBRARY identity: the node tones, the edge hairline color, the per-mode
487
+ edge-alpha multipliers, and the field-yields-to-type translucency knob.
488
+ Only `--constellation-accent` (the anomaly tint) stays a CONSUMER preset
489
+ (slides aliases it to `--ncsu-red`); the library ships a neutral default.
490
+
491
+ PLAIN-hsl LITERALS, per arm — NEVER `light-dark()` and NEVER a
492
+ `var(--neutral-*)`/`var(--foreground)`/`var(--border)`/`var(--card)`
493
+ alias. Canvas2D silently REJECTS a `light-dark()` value into
494
+ `strokeStyle`/`fillStyle` (the W30 86.3%-red-splatter cardinal defect),
495
+ and the rejection is identical whether the value is a literal
496
+ `light-dark(…)` OR a `var()` that RESOLVES to one — so the constellation
497
+ tokens are plain-hsl LEAVES, the `:root` vs `.dark` cascade carrying two
498
+ literals. `proof:constellation-tokens` clause (c) machine-locks this
499
+ (the no-`light-dark(`-substring NOR transitive-`var()` static assertion).
500
+ ═══════════════════════════════════════════════ */
501
+ /* Node tones — readable on the warm-cream ground. The two-tone split (the
502
+ `dim` half rides `-node-dim`). Warm-grey ink, NOT a neutral-ladder alias. */
503
+ --constellation-node: hsl(36 8% 46%);
504
+ --constellation-node-dim: hsl(36 8% 60%);
505
+ /* Edge hairline stroke — warm near-black on cream (the plain-hsl complement
506
+ the slides deck reads to fix the `--foreground` light-dark() leak). */
507
+ --constellation-line: hsl(24 14% 14%);
508
+ /* Anomaly tint — NEUTRAL library default (a desaturated warm accent). The
509
+ consumer overrides this (slides → `--ncsu-red`); the library does NOT
510
+ bake a brand hue. This is the single consumer-preset boundary. */
511
+ --constellation-accent: hsl(18 58% 46%);
512
+ /* Edge-alpha multipliers — replace the magic `0.17` hairline / pointer-web
513
+ literals. The cream ground lifts the node tones, so the field can recede;
514
+ the focus-proximity edge reads a touch stronger. */
515
+ --constellation-edge-alpha: 0.22;
516
+ --constellation-edge-focus-alpha: 0.34;
517
+ /* §7 field-yields-to-type translucency knob — the legible-but-RECESSIVE
518
+ MIDPOINT, NOT max-legibility. On cream the node tones already read, so the
519
+ field recedes further: BELOW the slides 0.92 max-legibility reference. */
520
+ --constellation-alpha: 0.80;
521
+
522
+ /* §interaction — the warp spring + the gravity-well tunings (NUMERIC, NOT
523
+ colors — read ONCE on mount via parseFloat, never reach a Canvas2D
524
+ fillStyle, so immune to the W30 light-dark()/Canvas2D leak; AY.W-CON2).
525
+ Mode-INVARIANT, declared ONCE in :root (the .dark arm needs no numeric
526
+ re-declare — a per-mode value SPLIT is what proof:constellation-tokens
527
+ clause (c) flags, and these tunings are identical in both modes).
528
+ `--constellation-warp-response` is the keyframes.js ANGULAR PERIOD (the
529
+ SwiftUI .spring(response:) axis), NOT a settle-duration — at ζ=1 the 2%
530
+ settle lands at t₂ ≈ 5.83/ω₀ ≈ 0.93·response (the engine keeps the
531
+ keyframes.js ω₀ = 2π/response convention, mints no second ω). */
532
+ --constellation-warp-response: 0.55; /* s — keyframes.js angular period (NOT a settle) */
533
+ --constellation-warp-zeta: 1.0; /* critically damped — a focal mark must NOT ring */
534
+ --constellation-well-gain: 14000; /* inverse-square force scale (the held-pull gain) */
535
+ --constellation-well-reach: 340; /* base-width px — the well's reach (k-scaled at step) */
536
+ --constellation-well-ramp: 4.0; /* 1/s — the ARM rate (≈0.25s bloom; release is the fixed brisk WELL_RELEASE_RAMP — the field-cools invariant, not consumer-tunable) */
537
+ --constellation-well-max-speed: 4.0; /* base-width px/frame cap — the no-slingshot clamp */
538
+ --constellation-well-hold-ms: 140; /* ms hold before the well arms */
539
+ --constellation-wander-idle: 8000; /* ms — min idle between auto re-targets (the W-CON1 drift cadence) */
540
+ --constellation-wander-jitter: 8000; /* ms — random extra idle per cadence (so the rhythm is not metronomic) */
541
+
542
+ /* ═══════════════════════════════════════════════
543
+ §6 SECTION PALETTE — 13-stop jewel tones (light)
544
+ ═══════════════════════════════════════════════ */
545
+ /* AV.W16 TW2 — oklch ramp (perceptually-even L; hue preserved from the
546
+ prior hsl() cadence). Categorical jewel tones — distinct hues, not a
547
+ monotone scale. Authored as oklch pairs with the .dark mirror. */
548
+ --section-color-0: oklch(0.552 0.192 359.8); /* rose */
549
+ --section-color-1: oklch(0.502 0.165 305.9); /* purple */
550
+ --section-color-2: oklch(0.484 0.163 265.5); /* indigo */
551
+ --section-color-3: oklch(0.542 0.089 222.8); /* teal-cyan */
552
+ --section-color-4: oklch(0.551 0.088 171.1); /* forest */
553
+ --section-color-5: oklch(0.623 0.124 69.6); /* amber */
554
+ --section-color-6: oklch(0.579 0.201 30.4); /* tomato-red */
555
+ --section-color-7: oklch(0.532 0.180 317.5); /* violet */
556
+ --section-color-8: oklch(0.520 0.176 8.4); /* ruby */
557
+ --section-color-9: oklch(0.492 0.038 239.6); /* slate-blue */
558
+ --section-color-10: oklch(0.556 0.103 128.8); /* olive */
559
+ --section-color-11: oklch(0.601 0.092 208.0); /* ocean */
560
+ --section-color-12: oklch(0.513 0.163 291.9); /* periwinkle */
561
+
562
+ /* Accent colors */
563
+ --accent-pink: oklch(0.613 0.197 353.8);
564
+ --section-heading: oklch(0.545 0.189 352.8);
565
+ --accent-red: oklch(0.574 0.216 27.5);
566
+
567
+ /* ═══════════════════════════════════════════════
568
+ §6b VIZ BASIS — core brand hues
569
+ ═══════════════════════════════════════════════ */
570
+ --viz-fourier: oklch(0.579 0.201 30.4);
571
+ --viz-chebyshev: oklch(0.484 0.163 265.5);
572
+ --viz-legendre: oklch(0.532 0.180 317.5);
573
+ --viz-amber: var(--section-color-5);
574
+ --viz-green: var(--section-color-4);
575
+
576
+ /* Semantic UI accents */
577
+ --tier-featured: oklch(0.841 0.173 84.2);
578
+ --tier-saved: oklch(0.676 0.176 252.3);
579
+ --like: oklch(0.633 0.200 24.9);
580
+ --success: oklch(0.720 0.192 149.5);
581
+ --warning: oklch(0.770 0.165 70.6);
582
+ --info: oklch(0.626 0.186 259.6);
583
+ --delete: oklch(0.597 0.198 25.5);
584
+
585
+ /* Semantic foregrounds — text/glyph atop --success/--warning/--info plates.
586
+ Notification.vue currently hardcodes `text-white`/`text-foreground`
587
+ inline; W2 migrates consumers to read these tokens. Warning carries a
588
+ dark glyph because the amber plate is too luminous for white. R5
589
+ vocab.γ gap row 4. */
590
+ --success-foreground: var(--neutral-0);
591
+ --warning-foreground: hsl(24 10% 10%);
592
+ --info-foreground: var(--neutral-0);
593
+
594
+ /* ═══════════════════════════════════════════════
595
+ §7 SHADOWS — cartoon offset is the signature
596
+ ═══════════════════════════════════════════════ */
597
+ --shadow-cartoon: 3px 3px 0px 0px color-mix(in srgb, var(--foreground) 8%, transparent);
598
+ --shadow-cartoon-hover: 4px 4px 0px 0px color-mix(in srgb, var(--foreground) 10%, transparent);
599
+ --shadow-soft: 0 4px 12px rgba(0, 0, 0, 0.1);
600
+ --shadow-elevated: 0 8px 24px rgba(0, 0, 0, 0.12);
601
+ --shadow-modal:
602
+ 4px 4px 0px 0px color-mix(in srgb, var(--foreground) 8%, transparent),
603
+ 0 24px 64px color-mix(in srgb, var(--foreground) 12%, transparent);
604
+
605
+ --cartoon-shadow: var(--shadow-cartoon);
606
+ --cartoon-shadow-hover: var(--shadow-cartoon-hover);
607
+ --soft-shadow: var(--shadow-soft);
608
+ --elevated-shadow: var(--shadow-elevated);
609
+ --modal-shadow: var(--shadow-modal);
610
+
611
+ /* Elevation scale */
612
+ --shadow-xs: 0 1px 3px color-mix(in srgb, var(--shadow-color) 4%, transparent),
613
+ 0 1px 2px color-mix(in srgb, var(--shadow-color) 6%, transparent);
614
+ --shadow-sm: 0 2px 8px color-mix(in srgb, var(--shadow-color) 6%, transparent);
615
+ --shadow-md: 0 4px 16px color-mix(in srgb, var(--shadow-color) 8%, transparent);
616
+ --shadow-lg: 0 4px 20px color-mix(in srgb, var(--shadow-color) 12%, transparent);
617
+ --shadow-xl: 0 8px 24px color-mix(in srgb, var(--shadow-color) 14%, transparent);
618
+ --shadow-2xl: 0 25px 50px -12px color-mix(in srgb, var(--shadow-color) 25%, transparent);
619
+
620
+ /* Z.W2.T3a — uniform-cast shadow rung. Offset 0 / 0, no directional
621
+ Y bias. Use case: dock-hosted icons (e.g. the speedtest settings
622
+ gear) where the surrounding dock shadow's downward cast reads as
623
+ a per-icon right-edge halo on the rightmost child (A2 §B7). The
624
+ uniform variant carries the same color-mix recipe family as the
625
+ sized rungs (`--shadow-md`, `--shadow-lg`) so the perceived
626
+ elevation is a peer, not a sibling. Consumers compose via
627
+ `--shadow-dock-override: var(--shadow-uniform)` per-instance, or
628
+ attach to per-icon-button stacks where directional cast is wrong-
629
+ shape. Documented in DESIGN.md token table. */
630
+ --shadow-uniform: 0 0 12px color-mix(in srgb, var(--shadow-color) 8%, transparent);
631
+
632
+ /* Card = soft drop plate by default. The cartoon offset is a brand-flavoured
633
+ decoration (a sticker look), not the canonical card shape. Consumers
634
+ wanting the cartoon look pass <Card surface="cartoon">, which composes the
635
+ `.cartoon-surface` utility (it reads --shadow-cartoon directly). R3-spec
636
+ §F.1, S.W4; Q.W3 Lane H. */
637
+ --shadow-card: var(--shadow-md);
638
+ --card-shadow: var(--shadow-card);
639
+
640
+ /* AI.W1-α — sticky CardHeader background. Parameterises the
641
+ `bg-card/60` literal value.js's PaneHeader.vue carried so the
642
+ glass-ui canon owns the recipe. The scroll-driven `<CardHeader
643
+ shrink>` choreography reads this token for its sticky-header
644
+ backdrop tint; consumers retune per-app by overriding the
645
+ variable at any ancestor. Auto-dark via `--card`. */
646
+ --card-header-bg: color-mix(in srgb, var(--card) 60%, transparent);
647
+
648
+ /* Dock shadows — a single omnidirectional glow in the
649
+ `--shadow-md`/`--shadow-lg` family (a directional drop concentrated on
650
+ the pill's rounded right cap as a right-edge halo; the dock's own
651
+ border already defines the silhouette). Per-instance directional drop
652
+ reinstates via `--shadow-dock-override` (consumed by every `.glass-dock`
653
+ state selector — see dock.css). */
654
+ --shadow-dock: 0 0 20px color-mix(in srgb, var(--shadow-color) 14%, transparent);
655
+ --shadow-dock-collapsed: 0 0 12px color-mix(in srgb, var(--shadow-color) 12%, transparent);
656
+ --dock-shadow: var(--shadow-dock);
657
+ --dock-shadow-collapsed: var(--shadow-dock-collapsed);
658
+
659
+ /* AX.W04 F1 — the wrap/card-tier dock shadow. A wrapped multi-row dock (and
660
+ any finite-radius card silhouette) is a THICKER material than a flat thin
661
+ pill — WWDC25 §219 "material thickens as glass flexes larger". So when the
662
+ wrap dock expands it lifts its elevation onto the FLOATING-tier shadow
663
+ stack (the `--glass-shadow-floating` family — `--shadow-xl` + the
664
+ `--glass-material-rim` hairline + the `--glass-under-shadow-vivid` under-cast)
665
+ rather than carrying the flat `--shadow-dock` glow of a 40px pill. Composed
666
+ on `:root` as ONE consumer-overridable token (per the cartoon-shadow
667
+ override contract — override on the cascade, never a dead local). The dock
668
+ reads it via `calc()`-on-`--dock-expand-t` so the lift glides in lockstep
669
+ with the pill→card radius morph (ONE clock, ONE register). */
670
+ --glass-material-rim: 0 0 0 0.5px color-mix(in srgb, var(--shadow-color) 5%, transparent);
671
+ --shadow-dock-wrap: var(--shadow-xl),
672
+ var(--glass-material-rim),
673
+ var(--glass-under-shadow-vivid);
674
+
675
+ /* Cartoon shadow color primitives (kept for tiered cartoon utilities) */
676
+ --shadow-cartoon-color: rgb(0 0 0 / 0.12);
677
+ --shadow-cartoon-color-soft: rgb(0 0 0 / 0.06);
678
+
679
+ /* Tiered cartoon shadows */
680
+ --shadow-cartoon-sm: -3px 2px 1px color-mix(in srgb, var(--shadow-color) 10%, transparent),
681
+ 0 3px 1px color-mix(in srgb, var(--shadow-color) 10%, transparent),
682
+ -3px 3px 1px color-mix(in srgb, var(--shadow-color) 6%, transparent);
683
+ --shadow-cartoon-md: -4px 3px 1px color-mix(in srgb, var(--shadow-color) 12%, transparent),
684
+ 0 4px 1px color-mix(in srgb, var(--shadow-color) 12%, transparent),
685
+ -4px 4px 2px color-mix(in srgb, var(--shadow-color) 8%, transparent);
686
+ --shadow-cartoon-lg: -6px 4px 1px color-mix(in srgb, var(--shadow-color) 12%, transparent),
687
+ 0 6px 1px color-mix(in srgb, var(--shadow-color) 12%, transparent),
688
+ -6px 6px 2px color-mix(in srgb, var(--shadow-color) 8%, transparent);
689
+ --cartoon-shadow-sm: var(--shadow-cartoon-sm);
690
+ --cartoon-shadow-md: var(--shadow-cartoon-md);
691
+ --cartoon-shadow-lg: var(--shadow-cartoon-lg);
692
+
693
+ /* ═══════════════════════════════════════════════
694
+ §8 GLASSMORPHISM — Five-tier surface ladder (v0.8.0)
695
+
696
+ Tier semantics, alpha-monotonic:
697
+ wash ~0.30α + light blur (was `subtle`)
698
+ quiet ~0.50α + medium blur (the formerly missing rung)
699
+ resting ~0.65α + canonical blur (was `default`; the speedtest plate)
700
+ floating ~0.80α + heavier blur (was `elevated`)
701
+ overlay ~0.95α + heaviest blur (NEW; modal-over-modal)
702
+
703
+ v0.8.0 retires the four-tier `subtle | default | medium | elevated`
704
+ ladder outright. No legacy aliases. Consumers migrate at the same
705
+ breaking change. R3-spec §A.
706
+ ═══════════════════════════════════════════════ */
707
+
708
+ /* Opacity primitives */
709
+ --glass-opacity-wash: 0.30;
710
+ --glass-opacity-quiet: 0.50;
711
+ --glass-opacity-resting: 0.65;
712
+ --glass-opacity-floating: 0.80;
713
+ --glass-opacity-overlay: 0.95;
714
+
715
+ /* Blur primitives. The radii here feather the dock + meter card to a
716
+ barely-perceptible diffusion that lets the substrate behind it bleed
717
+ through cleanly while keeping hero typography crisp at meter scale.
718
+ The resting blur sits at 12px so a glass plate reads as a diffusion
719
+ without consumer-side override. */
720
+ --glass-blur-wash-radius: 1px;
721
+ --glass-blur-quiet-radius: 10px;
722
+ --glass-blur-resting-radius: 12px;
723
+ --glass-blur-floating-radius: 16px;
724
+ /* AV.W7 F2 — clamped from 24px into the 8–15px budget band. A blur radius
725
+ past ~20px is exponentially costlier (`backdrop-filter` is glass-ui's most
726
+ expensive idiom — the radius is the dominant cost). 15px is the band
727
+ ceiling; the lower rungs (wash/quiet/resting 1/10/12) sit in-band and the
728
+ floating 16px is the design ceiling at the band edge (left as-is). A
729
+ high-resolution display restores the richer 24px wash via the @media arm
730
+ below — the cost is amortised by the extra device pixels. */
731
+ --glass-blur-overlay-radius: 15px;
732
+ /* Dock blur radius — 11px, paired with the 0.42 opacity below: a crisp
733
+ glass pill (the iOS Control Center register), not a feathered wash.
734
+ The dock composes `blur()` only (no saturate); the
735
+ `prefers-reduced-transparency` bracket at glass.css maps it to the
736
+ opacity-only register. */
737
+ --glass-blur-dock-radius: 11px;
738
+
739
+ /* AX.W54 — each blur radius is scaled by the ONE `--glass-level` scalar (in
740
+ lockstep with the opacity seam above): level=1 byte-identical; level=0 →
741
+ blur(0) (the clean solid floor with the opacity); level>1 → more diffusion.
742
+ The per-rung `saturate()`/`brightness()` companions are UNTOUCHED (the level
743
+ scales the RADIUS, not the saturation — W52's calm light-concentration). */
744
+ --glass-blur-wash: blur(calc(var(--glass-blur-wash-radius) * var(--glass-level))) saturate(1.05);
745
+ /* AV.W15 M1 — the `quiet` rung gained `saturate(1.05) brightness(1.02)`
746
+ so the lower ladder reads with the same saturation life as `resting`+
747
+ (Apple: Liquid Glass concentrates light, it does not desaturate). The
748
+ `brightness(1.02)` is the small lift the spec names — it lands `quiet`
749
+ between bare `wash` and `resting` without over-brightening the rung.
750
+ The `prefers-reduced-transparency: reduce` bracket at glass.css maps
751
+ `--glass-blur-quiet: none`, so this whole chain drops with the blur. */
752
+ --glass-blur-quiet: blur(calc(var(--glass-blur-quiet-radius) * var(--glass-level))) saturate(1.05) brightness(1.02);
753
+ --glass-blur-resting: blur(calc(var(--glass-blur-resting-radius) * var(--glass-level))) saturate(1.05);
754
+ /* AX.W52 D19 — the floating/overlay backdrop saturate tamed from 1.4/1.5 to a
755
+ gentle ~1.18/1.2 (real liquid glass concentrates light ~1.1–1.2×; the prior
756
+ 1.4–1.5× over-juiced the bleed-through into a garish smear over a colored
757
+ substrate). wash/quiet/resting already sit at 1.05 — untouched. */
758
+ --glass-blur-floating: blur(calc(var(--glass-blur-floating-radius) * var(--glass-level))) saturate(1.18);
759
+ --glass-blur-overlay: blur(calc(var(--glass-blur-overlay-radius) * var(--glass-level))) saturate(1.2);
760
+ /* Dock blur composes blur() only — no saturate. */
761
+ --glass-blur-dock: blur(calc(var(--glass-blur-dock-radius) * var(--glass-level)));
762
+
763
+ /* AX.W52 — the GLASS BUTTON blur. The `glass`/`glass-wash` button variants
764
+ compose the `.glass-wash` tier (`--glass-blur-wash` = blur(1px)) — a
765
+ sub-perceptual diffusion authored for a small detail TILE, not a button
766
+ the user reads as "glass." A button is a hero control: it wants a real
767
+ glass blur. `--glass-blur-btn` lifts the variant onto the `quiet`-tier
768
+ radius (10px — a genuine liquid-glass diffusion) with the same gentle
769
+ `saturate(1.05) brightness(1.02)` light-concentration the quiet rung
770
+ carries (NOT a colour-juice). The wash TILE tier (MetricCell et al.) keeps
771
+ its 1px — only the glass BUTTON variants reach for this. */
772
+ --glass-blur-btn: blur(calc(var(--glass-blur-quiet-radius) * var(--glass-level))) saturate(1.05) brightness(1.02);
773
+
774
+ /* Dock-specific opacity (0.42) — lighter than `resting` (0.65) so the dock
775
+ reads as a translucent overlay rather than a panel, backdrop bleed
776
+ visible through it. Consumed by `.glass-dock` via `--glass-bg-dock`. The
777
+ value holds the surface register against the tightened 11px blur so the
778
+ dock reads as painted chrome, not a frosted ghost. */
779
+ --glass-opacity-dock: 0.42;
780
+
781
+ /* Dock foreground over an animated backdrop. A dock floating over an
782
+ aurora/animated surface (`value.js` dock-over-aurora, a slides routed
783
+ control, bbnf) sits on a mid-luminance, shifting field where the muted
784
+ default fg can drop below contrast. This token is the dock control's
785
+ foreground base — a complete `hsl()` color consumed via `var()`; for the
786
+ muted icon weight the control mixes it down with `--opacity-icon-muted`.
787
+ Defaults to `var(--foreground)` so the dock paints byte-identically to
788
+ today; a consumer overrides it per-backdrop (e.g. a near-white over a
789
+ dark aurora) without touching library source.
790
+
791
+ AX.W55 — this is the FOREGROUND half of the adaptive story, now reconciled
792
+ INTO the `--glass-backdrop` probe (not a third fork): under the bright bucket
793
+ (`@container style(--glass-backdrop: light)` in dock.css) it re-points toward
794
+ the contrast-clearing ink, and an `@supports (color: contrast-color(…))`
795
+ block flips it natively on supporting engines. The `var(--foreground)`
796
+ default keeps the zero-delta rest state. */
797
+ --dock-fg-on-aurora: var(--foreground);
798
+
799
+ /* Chassis-specific opacity. Looser than `default` (0.50) so the
800
+ <InstrumentChassis> surface reads as glass-over-aurora at its
801
+ 64rem-wide footprint; the default tier stays calibrated for cards.
802
+ Mirrors the dock-tier pattern (own opacity primitive + composed bg).
803
+ Light 0.28 sits in the gap between dock (0.32) and a hypothetical
804
+ wash tier; dark 0.36 mirrors the +0.08 step the dock takes from
805
+ `glass-bg-medium` (0.65) down to its own 0.32. */
806
+ --glass-opacity-chassis: 0.28;
807
+
808
+ /* Composed backgrounds — AX.W54: the opacity is threaded through the ONE
809
+ `--glass-level` clarity scalar. Each rung's effective alpha is
810
+ `1 - (1 - <rung-opacity>) * --glass-level`: at level=1 it collapses to the
811
+ hand-tuned `<rung-opacity>` (byte-identical); at level=0 it resolves `1`
812
+ (solid `--card` — the opaque escape); at level>1 it goes clearer. ONE
813
+ expression, the same seven rungs. */
814
+ --glass-bg-wash: color-mix(in srgb, var(--card) calc((1 - (1 - var(--glass-opacity-wash)) * var(--glass-level)) * 100%), transparent);
815
+ --glass-bg-quiet: color-mix(in srgb, var(--card) calc((1 - (1 - var(--glass-opacity-quiet)) * var(--glass-level)) * 100%), transparent);
816
+ --glass-bg-resting: color-mix(in srgb, var(--card) calc((1 - (1 - var(--glass-opacity-resting)) * var(--glass-level)) * 100%), transparent);
817
+ --glass-bg-floating: color-mix(in srgb, var(--card) calc((1 - (1 - var(--glass-opacity-floating)) * var(--glass-level)) * 100%), transparent);
818
+ --glass-bg-overlay: color-mix(in srgb, var(--card) calc((1 - (1 - var(--glass-opacity-overlay)) * var(--glass-level)) * 100%), transparent);
819
+ /* The dock bg ALSO rides the oklab tint seam (AX.W54 amendment): the same
820
+ `color-mix(in oklab, …, --glass-tint-source --glass-tint-strength)` wrapper
821
+ the rungs reach for, ZERO-delta at `--glass-tint-strength: 0%` — so W55's
822
+ adaptive darken can reach the dock (the G2 over-light surface) without a
823
+ second hand-rolled recipe. */
824
+ --glass-bg-dock: color-mix(in oklab, color-mix(in srgb, var(--card) calc((1 - (1 - var(--glass-opacity-dock)) * var(--glass-level)) * 100%), transparent), var(--glass-tint-source) var(--glass-tint-strength));
825
+ --glass-bg-chassis: color-mix(in srgb, var(--card) calc((1 - (1 - var(--glass-opacity-chassis)) * var(--glass-level)) * 100%), transparent);
826
+
827
+ /* Composed borders — foreground-tinted */
828
+ --glass-border-wash: color-mix(in srgb, var(--foreground) 8%, transparent);
829
+ --glass-border-quiet: color-mix(in srgb, var(--foreground) 10%, transparent);
830
+ --glass-border-resting: color-mix(in srgb, var(--foreground) 12%, transparent);
831
+ --glass-border-floating: color-mix(in srgb, var(--foreground) 15%, transparent);
832
+ --glass-border-overlay: color-mix(in srgb, var(--foreground) 18%, transparent);
833
+ --glass-border-dock: color-mix(in srgb, var(--foreground) 11%, transparent);
834
+ --glass-border: var(--glass-border-quiet);
835
+ --glass-border-strong: var(--glass-border-floating);
836
+
837
+ /* Shared decorative. Light grain dropped 0.035 → 0.025 in P.W1.B —
838
+ at the chassis scale the original alpha read as crawl over the new
839
+ lower-alpha chassis bg; the grain's role is texture, not reading,
840
+ so it sits at the perceptual floor. Dark companion drops 0.06 →
841
+ 0.045 below to keep the soft-light blend alive without stippling. */
842
+ --glass-grain-opacity: 0.025;
843
+ --glass-grain-opacity-disco: 0.08;
844
+ --glass-highlight: inset 0 0.5px 0 0 hsl(0 0% 100% / 0.25);
845
+
846
+ /* Hairline-accent — 0.5px catch-light + under-shadow pair.
847
+ Single token composes the canonical 2-stop hairline used by dock,
848
+ instrument-chassis. The `.hairline-accent` utility
849
+ (utilities.css §hairline) adopts these via `box-shadow:`. */
850
+ --hairline-catch-light: inset 0 0.5px 0 0 rgb(255 255 255 / 0.06);
851
+ --hairline-under-shadow: 0 0.5px 0 0 rgb(0 0 0 / 0.04);
852
+ --border-hairline: var(--hairline-catch-light), var(--hairline-under-shadow);
853
+
854
+ /* Specular catch-light — 1.5px-tall top-edge highlight that swaps in for
855
+ --glass-highlight on interactive moments (e.g. primary-action hover).
856
+ Reads as a real catch-light on a curved surface. The `-dark` companion
857
+ is overridden inside `.dark`; the named token here is the explicit
858
+ handle consumers can reference to compose dark-aware layers. */
859
+ --glass-specular: inset 0 1.5px 0 0 hsl(0 0% 100% / 0.45);
860
+ --glass-specular-dark: inset 0 1.5px 0 0 hsl(0 0% 100% / 0.30);
861
+
862
+ /* AV.W15 M3 — Full-perimeter rim light (the "rim that defines the
863
+ silhouette", WWDC25 sess. 219). Distinct from the top-only
864
+ `--glass-highlight` (a 0.5px top-edge catch-light): this is a 0.75px
865
+ inset ring around the WHOLE silhouette, four edges, reading as the
866
+ catch-light that wraps the glass plate the way iOS-26 Liquid Glass
867
+ carries a rim, not just a top highlight. Composed as a SECOND inset
868
+ box-shadow layer alongside `--glass-highlight` on the floating/dock
869
+ tiers (glass.css wires it). The alpha is deliberately low (0.18 light
870
+ / 0.10 dark) — the rim is decoration and must not eat AA legibility
871
+ over a busy backdrop; it sits below the perceptual reading threshold
872
+ on text. The `-dark` companion is overridden inside `.dark` mirroring
873
+ the `--glass-specular-dark` precedent. */
874
+ --glass-edge-light: inset 0 0 0 0.75px hsl(0 0% 100% / 0.18);
875
+ --glass-edge-light-dark: inset 0 0 0 0.75px hsl(0 0% 100% / 0.10);
876
+
877
+ /* AW.W23 — content-aware adaptive tint (the Baseline-2025 "tint informed by
878
+ surrounding content" axis, WWDC25 sess. 219). A surface can tint toward a
879
+ consumer-provided dominant backdrop color via a `color-mix(in oklab, …)`
880
+ recipe (oklab is the mwg-preferred space for tint generation; NEVER a
881
+ lightness-shift in `oklch(from …)` — browsers don't gamut-map yet).
882
+
883
+ DEFAULT is zero delta: `--glass-tint-source` is the warm-white card itself
884
+ and `--glass-tint-strength` is 0%, so the rung background mixes 0% toward
885
+ the source = byte-identical to today. A consumer sets `--glass-tint-source`
886
+ to a sampled aurora hue + a strength ≤30% to bias the surface. Alpha-safe:
887
+ the mix is bounded ≤30% and never re-points the card's own alpha.
888
+
889
+ AX.W55 — the DEFAULTS stay `var(--card)`/`0%` (the zero-delta rest state).
890
+ The backdrop-luminance probe (`--glass-backdrop`, below) is what OVERRIDES
891
+ them under a bright backdrop; the `:root` defaults are NEVER touched — the
892
+ bucket blocks (glass.css / dock.css `@container style(--glass-backdrop:
893
+ light)`) do the lift, so a surface over today's rich/dark background reads
894
+ byte-identical. */
895
+ --glass-tint-source: var(--card);
896
+ --glass-tint-strength: 0%;
897
+
898
+ /* AX.W55 — adaptive glass legibility (the iOS-26/27 "locally darken the glass
899
+ over light content" move; G2). There is NO web API that reads the pixels
900
+ painted BEHIND a `backdrop-filter` element, so the backdrop-luminance signal
901
+ is a DECLARATIVE BUCKET a consumer sets where it KNOWS the backdrop is bright
902
+ (a near-white card surface, a pale aurora bleed, a bright data grid). It is
903
+ read by the `@container style(--glass-backdrop: light)` blocks on every glass
904
+ family (the shipped density-cascade mechanism — utilities.css §density) which
905
+ lift `--glass-tint-strength` toward the AA floor + re-point `--glass-tint-
906
+ source` to the warm-ink below, mixing the rung TOWARD ink via the EXISTING
907
+ `color-mix(in oklab)` seam — ZERO new compositing seam.
908
+
909
+ --glass-backdrop: the bucket. `dark` (or unset) = today's zero-delta;
910
+ `light` engages the bright-bucket darken. An
911
+ INHERITING ident set on any ancestor reaches every
912
+ descendant glass surface (the @container read).
913
+ --glass-backdrop-luma: the numeric escape hatch — a sampled backdrop
914
+ relative-luminance (0..1) a heavier JS observer sets
915
+ when the declarative bucket is too coarse. The TOKEN
916
+ is minted (the cascade reads it if set); the OBSERVER
917
+ that sets it stays demo-private until a 2nd consumer
918
+ arrives (the no-overfitting bar — the DEFAULT path is
919
+ the declarative bucket, not a speculative sampler).
920
+ --glass-tint-ink: the LOW-LUMINANCE warm-ink the bright bucket re-points
921
+ `--glass-tint-source` to — the warm dark `--foreground`
922
+ family (NOT white; a lightener over light content is
923
+ exactly the W52-fixed wrong direction). It auto-flips
924
+ with `--foreground` (dark-mode it resolves the light
925
+ warm ink — but the bright-backdrop case is a light-mode
926
+ concern). Overridable like every magnitude here.
927
+
928
+ The bright-bucket `--glass-tint-strength` is clamped to a BOUNDED AA-clearing
929
+ floor — `--glass-tint-strength-aa` (≤24%, the iOS clamp; the lowest value that
930
+ clears 4.5:1 over synthetic white, capped LOW so the surface STAYS translucent,
931
+ "let as much content through as possible"). Recorded here as the ceiling the
932
+ bucket blocks drive to; the live 4.5:1 readback (proof:adaptive-glass π arm)
933
+ ratifies the exact value. */
934
+ --glass-backdrop: dark;
935
+ --glass-backdrop-luma: ;
936
+ --glass-tint-ink: var(--foreground);
937
+ --glass-tint-strength-aa: 18%;
938
+
939
+ /* AW.W23 — chromatic edge dispersion (the "polished glass" rim fringe Apple
940
+ shows). A warm fringe on one edge + a cool fringe on the other, low-alpha,
941
+ biased warm-cream/NCSU-red (NOT iOS-blue). Composed OVER the W22 uniform
942
+ rim under `prefers-reduced-transparency: no-preference`; dropped under
943
+ `reduce` (it is a transparency garnish). The two directional inset rings
944
+ read as light refracting through the glass edge. */
945
+ --glass-fringe-warm: oklch(0.92 0.06 40 / 0.22);
946
+ --glass-fringe-cool: oklch(0.90 0.05 230 / 0.16);
947
+ --glass-edge-dispersion:
948
+ inset 0.75px 0 0 0 var(--glass-fringe-warm),
949
+ inset -0.75px 0 0 0 var(--glass-fringe-cool);
950
+
951
+ /* AX.W52 D19 — the moving-specular gleam radius (glass.css `.glass-material::
952
+ before`). The ONE knob that BOUNDS the gleam geometry as a percentage of the
953
+ plate so it can NEVER again become a whole-surface disc (the prior implicit
954
+ `circle at …` ran its stops to 55–75% of the half-diagonal — a wash on a
955
+ small tile). 36% reads as a tracked edge glint. Override on `:root` to retune
956
+ library-wide; a per-surface override clips the gleam tighter/wider. */
957
+ --glass-specular-size: 36%;
958
+
959
+ /* Chassis curvature illusion — a subtle radial gradient at the chassis
960
+ top centre that lifts the surface without committing to literal 3D.
961
+ Composed into <InstrumentChassis>'s background stack. AX.W52 D19 — the
962
+ prior pure-white `hsl(0 0% 100% / 0.06)` reaching `transparent 60%` was a
963
+ broad top-centre WASH (the #1 fixed-anchor rest-bloom culprit). Re-derived
964
+ onto the warm-cream house identity (`hsl(40 30% 96%)`, L < 100% so the warm
965
+ hue survives — mirroring the moving gleam), the alpha routed through the
966
+ SINGLE overridable `--glass-curvature-intensity` knob (0.02 default, a third
967
+ of the old 0.06), and the ellipse tightened to `transparent 38%` so it reads
968
+ as a thin top-edge sheen, not a central wash. */
969
+ --glass-curvature-intensity: 0.02;
970
+ --glass-curvature-overlay: radial-gradient(
971
+ ellipse at 50% -20%,
972
+ hsl(40 30% 96% / var(--glass-curvature-intensity)),
973
+ transparent 38%
974
+ );
975
+ /* The light-arm dark alias kept on the same warm-cream + token recipe so a
976
+ non-`.dark` consumer reading `…-overlay-dark` still resolves a softened
977
+ sheen; the `.dark` block below lowers `--glass-curvature-intensity` so the
978
+ deep-canvas arm is genuinely softer (the prior byte-identical dark arm read
979
+ HARDER over the dark canvas). */
980
+ --glass-curvature-overlay-dark: var(--glass-curvature-overlay);
981
+
982
+ /* Chart palette aliases — speedtest et al. read these for phase tinting.
983
+ Defaults map to viz-basis hues; consumers can override per app. */
984
+ --chart-ping: var(--viz-chebyshev);
985
+ --chart-download: var(--viz-fourier);
986
+ --chart-upload: var(--viz-amber);
987
+ --chart-jitter: var(--viz-legendre);
988
+
989
+ /* O.W6 Lane D — WCAG companion `--chart-{phase}-label` tokens
990
+ (speedtest AC.W6c.T2 — F1.V-04 closure).
991
+
992
+ The canvas-side hues above preserve their saturated brand register
993
+ (correct against the meter dial); the COMPANION tokens darken to
994
+ L≈0.40 so phase-label text on `--background` clears the WCAG 2.1
995
+ AA-body 4.5:1 contrast floor. Mirrors the two-token discipline
996
+ already established by `--meter-{background-color,track-stroke}`:
997
+ one token feeds the canvas/visual, the second feeds the label
998
+ text.
999
+
1000
+ Light-mode L≈0.40 in OKLCH preserves the hue rotation while
1001
+ dropping luminance enough for body contrast; dark-mode flips to
1002
+ L≈0.85 against the dark canvas. Consumers reach these via
1003
+ `color: var(--chart-ping-label)` (etc.) on label elements ONLY —
1004
+ chart strokes/fills continue to read `--chart-ping`. */
1005
+ --chart-ping-label: oklch(0.40 0.18 230);
1006
+ --chart-download-label: oklch(0.40 0.20 22);
1007
+ --chart-upload-label: oklch(0.40 0.16 55);
1008
+ --chart-jitter-label: oklch(0.40 0.20 305);
1009
+
1010
+ /* O.W6 Lane D — Meter track stroke (speedtest AC.W6c.T3 — F1.V-02
1011
+ closure). Promoted from speedtest's consumer-side declaration so
1012
+ glass-ui owns the canonical light + dark values. The light-mode
1013
+ value reads opaque foreground (composes through canvas
1014
+ `globalAlpha` in consumer renderers; e.g., speedtest's `rings.ts`
1015
+ `globalAlpha = 0.22` lands the phantom-track at the perceptual
1016
+ sweet-spot). The dark-mode companion lives under `.dark` below
1017
+ and ALSO reads `var(--foreground)` — the prior speedtest value
1018
+ `var(--background)` rendered bg-on-bg invisible at dark mode. */
1019
+ --meter-track-stroke: var(--foreground);
1020
+
1021
+ /* Shadow per tier — complete box-shadow lists for glass surface classes */
1022
+ --glass-shadow-wash: var(--shadow-sm), var(--glass-highlight);
1023
+ --glass-shadow-quiet: var(--shadow-md), var(--glass-highlight);
1024
+ --glass-shadow-resting: var(--shadow-lg), var(--glass-highlight);
1025
+ --glass-shadow-floating: var(--shadow-xl),
1026
+ 0 0 0 0.5px color-mix(in srgb, var(--shadow-color) 5%, transparent),
1027
+ var(--glass-highlight);
1028
+ --glass-shadow-overlay: var(--shadow-2xl),
1029
+ 0 0 0 0.5px color-mix(in srgb, var(--shadow-color) 8%, transparent),
1030
+ var(--glass-highlight);
1031
+ --glass-shadow: var(--glass-shadow-quiet);
1032
+ --glass-shadow-lg: var(--glass-shadow-floating);
1033
+
1034
+ /* ─────────────────────────────────────────────────────────────
1035
+ AL-W1-α augmented — SPINE 8th tier (HOUSING register)
1036
+
1037
+ Closes the AK W2 chassis-spine architectural underclaim per
1038
+ AL-X5 §4.1. The spine register is a polished SLEEVE, not glass:
1039
+ NO backdrop-filter, NO plate opacity, hairline outer stroke,
1040
+ interior radial vignette that subtly carries the phase-color
1041
+ cascade. Consumed exclusively by `<InstrumentChassis variant="spine">`
1042
+ at App-level mount paths (quiet-content routes).
1043
+
1044
+ The 4 tokens here cover the minimum viable substrate the SLIM
1045
+ Y4/Y6/Y7 retention set ratified. The 35 rejected axes (saturate
1046
+ band, edge-light per tier, depth-shadow per tier, tier-up macro)
1047
+ stay rejected per the conservative-creation mandate.
1048
+ ───────────────────────────────────────────────────────────── */
1049
+ --glass-spine-opacity: 0;
1050
+ --glass-spine-blur: 0px;
1051
+ --glass-spine-border: var(--surface-tint-8);
1052
+ --glass-spine-vignette: radial-gradient(
1053
+ ellipse at 50% 0%,
1054
+ color-mix(in oklab, var(--phase-color, var(--surface-tint-8)) 8%, transparent),
1055
+ transparent 60%
1056
+ );
1057
+
1058
+ /* ─────────────────────────────────────────────────────────────
1059
+ AL-W10 SLIM — UNDER-SHADOW recipe rungs
1060
+
1061
+ Per AL-X5 §4 + Y7 §3. iOS Liquid Glass surfaces carry a 0.5px
1062
+ dark hairline along the bottom edge (reads as under-shadow /
1063
+ glass thickness). The substrate `--hairline-under-shadow`
1064
+ already publishes one quiet rung; the four-rung ladder here
1065
+ names the SLIM retention set (Y4 §3.2 / Y6 §4.3) — `quiet`,
1066
+ `default`, `vivid`, `spine` cover the consumer landings without
1067
+ multiplying tokens per tier. The 3 hand-rolled literals at
1068
+ `instrument-chassis.css:52`, `dock.css:279`, `dock.css:952`
1069
+ retire to these.
1070
+
1071
+ Composes as a NON-INSET box-shadow layer alongside the existing
1072
+ `--glass-shadow-*` recipe (the inset under-shadow stays at the
1073
+ hand-rolled literal where it's load-bearing on chassis-internal
1074
+ surfaces).
1075
+ ───────────────────────────────────────────────────────────── */
1076
+ --glass-under-shadow-quiet: 0 2px 8px -1px oklch(0 0 0 / 0.04);
1077
+ --glass-under-shadow-default: 0 4px 16px -2px oklch(0 0 0 / 0.08);
1078
+ --glass-under-shadow-vivid: 0 8px 32px -4px oklch(0 0 0 / 0.12);
1079
+ --glass-under-shadow-spine: 0 1px 0 0 oklch(0 0 0 / 0.06);
1080
+
1081
+ /* Overlay scrim rungs — substrate-aware modal/dialog backdrop tints.
1082
+ Three weights cover the deployed range: dialog-scroll (subtle),
1083
+ canonical modal (default), drawer/full-cover (strong).
1084
+
1085
+ AY.W-PRIM-POLISH D3 — the scrim is its OWN identity token, NOT derived
1086
+ from `--shadow-color`. A scrim's role is to RECEDE: it must pin to INK in
1087
+ BOTH modes. The cartoon-shadow family rides `--shadow-color: var(--foreground)`
1088
+ (correct — a shadow re-tints under `.dark` by construction), but a scrim
1089
+ that does the same FLOODS the dark room with a cream fog (the old dark
1090
+ overlay painted `srgb(0.91… / 0.5)`, a near-white wash that BRIGHTENED the
1091
+ page at the modal moment). `--overlay-scrim-ink` is a fixed warm near-black
1092
+ (the light-mode `--foreground` value), re-resolved verbatim in `.dark` so
1093
+ the scrim recedes in both modes. Clean break, no alias (no-backwards-compat).
1094
+ The `--shadow-color` / `--shadow-cartoon-*` family is UNTOUCHED. */
1095
+ --overlay-scrim-ink: hsl(24 10% 10%);
1096
+ --overlay-scrim: color-mix(in srgb, var(--overlay-scrim-ink) 50%, transparent);
1097
+ --overlay-scrim-strong: color-mix(in srgb, var(--overlay-scrim-ink) 80%, transparent);
1098
+ --overlay-scrim-subtle: color-mix(in srgb, var(--overlay-scrim-ink) 40%, transparent);
1099
+
1100
+ /* ═══════════════════════════════════════════════
1101
+ §9 ANIMATION OFFSETS
1102
+ ═══════════════════════════════════════════════ */
1103
+ /* Hover lift offsets */
1104
+ --lift-sm: -1px;
1105
+ --lift-md: -2px;
1106
+ --lift-lg: -4px;
1107
+
1108
+ /* Layout tokens */
1109
+ --mask-fade-width: 1rem;
1110
+ /* J.W3.C — dock content overflow caps. When dock content exceeds
1111
+ these dimensions, the inner `.dock-layers` (horizontal) /
1112
+ `.glass-dock.vertical` (rail) container scrolls with a
1113
+ `.scroll-fade-{x,y}` mask-fade so the boundary feathers rather
1114
+ than cliffs. Defaults clamp to 80vw / 80vh capped at sensible
1115
+ desktop widths; consumers override per-dock by setting either
1116
+ token directly on a `.glass-dock` instance. User findings 1 + 5a. */
1117
+ /* AX.W04 — `--dock-max-inline-size` is the canonical inline cap EVERY overflow
1118
+ strategy reads: `overflow="scroll"` scrolls past it, and `overflow="wrap"`
1119
+ wraps to N rows on the over-cap crossing (the dock shrink-wraps to content,
1120
+ `max-inline-size` caps that intrinsic width at this token). The prior
1121
+ `--dock-overflow-bp: 640px` viewport-breakpoint token (the magic-640
1122
+ snap-back) is DELETED — the wrap is content-driven, not viewport-coupled, so
1123
+ there is no breakpoint threshold to name. */
1124
+ --dock-max-inline-size: min(80vw, 64rem);
1125
+ --dock-max-block-size: min(80vh, 48rem);
1126
+
1127
+ /* AB.W1.T1 — chassis max-block-size guardrail.
1128
+ Maximum block size a centred consumer-app card may consume while
1129
+ preserving the inline dock footprint. The recipe subtracts the
1130
+ consumer-owned `--dock-footer-space` (dock height + dock inset +
1131
+ card-edge inset) and `--page-padding-top` from the dynamic viewport,
1132
+ then takes one further rem of breathing room so the card never
1133
+ butts the dock — the user mandate is `≥ 1rem visible gap above
1134
+ dock`. Both consumer tokens carry safe fallbacks for callers that
1135
+ have not declared them.
1136
+
1137
+ Consumers should shrink internal regions (meter, readout, timeline)
1138
+ before falling back to scroll. This token is a guardrail against
1139
+ chassis overflow, NOT a license to make the card scroll as the
1140
+ default solution; clamp-first / no-scroll-first is the canon.
1141
+ See DESIGN.md `## Chassis sizing — dock-adjusted viewport`. */
1142
+ --chassis-max-block-size: calc(
1143
+ 100dvh
1144
+ - var(--dock-footer-space, 5.75rem)
1145
+ - var(--page-padding-top, 0rem)
1146
+ - 1rem
1147
+ );
1148
+ /* R0G-2 (AO.W3/W4) — chassis mobile dial-reserve canon. instrument-
1149
+ chassis.css's mobile breakpoint reserves the dial's final box from
1150
+ frame 0 via these vars (with the same values as inline fallbacks, the
1151
+ safety net). Token-first home so the vars resolve canonically. */
1152
+ --instrument-dial-min-height-mobile: 24rem;
1153
+ --instrument-dial-meter-reserve-mobile: minmax(0, 1fr);
1154
+
1155
+ --max-width-input: 24rem;
1156
+ --input-min-width-sm: 5rem;
1157
+ --min-width-input-sm: var(--input-min-width-sm);
1158
+ /* AU.W8b.4 — canonical popover width (Combobox list + anchor). Lifts the
1159
+ `w-[200px]` arbitrary-value wrap to the `w-popover` @theme utility. */
1160
+ --popover-width: 200px;
1161
+ --chart-height-compact: 15rem;
1162
+ --chart-height-default: 22.5rem;
1163
+ --chart-height-large: 25rem;
1164
+ --chart-margin: 1.25rem;
1165
+ --paper-texture-size: 200px 200px;
1166
+
1167
+ /* Border opacity scale */
1168
+ --border-opacity-light: 0.15;
1169
+ --border-opacity-medium: 0.25;
1170
+ --border-opacity-strong: 0.6;
1171
+
1172
+ /* ═══════════════════════════════════════════════
1173
+ §10 SIZING
1174
+ ═══════════════════════════════════════════════ */
1175
+ /* ── The ONE comfortable-sizing scale doctrine (AX.W51 D18) ──────────────
1176
+ `--ui-scale` is the MASTER comfortable-sizing scalar — ONE unitless knob
1177
+ (registered @property <number>, inherits:true, default the identity 1)
1178
+ every interactive-control magnitude derives through `calc(<base> *
1179
+ var(--ui-scale))`, so a single override grows control HEIGHT + PADDING +
1180
+ GAP + FONT-SIZE + GLYPH in LOCKSTEP, proportion preserved, no per-component
1181
+ hack. The cohorts below (`--control-h-*` heights, `--control-text*` control
1182
+ fonts, `--ui-glyph*` glyphs) are its reading surfaces; the CVA bases + the
1183
+ `.btn-pill`/`.input-pill` pills consume them.
1184
+
1185
+ ONE scale system, not three. The dock (`--dock-scale`) and the dropdown
1186
+ family (`--dropdown-text`, W50) are SPECIALIZATIONS that read this axis:
1187
+ `--dock-scale: calc(var(--ui-scale) * var(--dock-local-scale, 1))` — the
1188
+ dock multiplies ON TOP of the global comfort axis (its existing geometry
1189
+ `calc` cascade inherits the global axis for free, zero edit). A consumer
1190
+ re-tints the whole library's comfort from ONE `:root { --ui-scale: … }`
1191
+ override (or a scoped region — a dense data-table sets it lower) — the
1192
+ token-first axis.
1193
+
1194
+ EXCLUDED — the φ-DISPLAY ladder (`--type-display-*` / `--type-title` /
1195
+ `--type-heading` / `--type-subheading`): a hero/title is a TYPOGRAPHIC
1196
+ identity, NOT a touch target, so it does NOT grow on the comfort/coarse
1197
+ axis. Only interactive-CONTROL text rides `--ui-scale`. Containers (card /
1198
+ panel radii + paddings) are separate `--radius-*` / `--space-*` axes.
1199
+
1200
+ The desktop default is the IDENTITY (1) — byte-exact preservation, no
1201
+ reflow of shipped consumers. The comfort GROWTH lives at the coarse-pointer
1202
+ default (`--ui-coarse-scale`, §below) + the consumer override. The
1203
+ WCAG-2.5.5 44px touch floor survives as a `max(…, --control-floor)` clamp
1204
+ inside the scaled control-size (the coarse block lifts `--control-floor` to
1205
+ `--touch-target`, so a sub-1 scale cannot drop a control under the target).
1206
+ Coordinates with W54's `--glass-level` + W55's `--glass-tint-*` — SEPARATE
1207
+ axes (size vs material vs legibility); a control is comfortably-sized AND
1208
+ glass AND legible, three orthogonal knobs. */
1209
+ --ui-scale: 1;
1210
+ /* The global coarse-pointer comfort default — the WHOLE library grows ~1.5×
1211
+ on touch from ONE place (the @media (pointer: coarse) block at §below lifts
1212
+ :root --ui-scale to this). The dock's prior parallel 1.5× becomes the global
1213
+ default it now shares. Public library-default identity — a consumer sets
1214
+ 1.3 / 1.75 on :root or a scope. */
1215
+ --ui-coarse-scale: 1.5;
1216
+ /* The control-height cohort — the h-7/h-9/h-10/h-11 register (28/36/40/44px)
1217
+ the CVA size rungs encode, each scaled by the master axis. The CVA rungs
1218
+ read these via `h-[var(--control-h-*)]` instead of the raw `h-N` literal.
1219
+ `--control-floor` is the WCAG-2.5.5 clamp seam (0 at desktop, lifted to
1220
+ --touch-target under coarse-pointer); each height is `max(scaled, floor)`. */
1221
+ --control-floor: 0px;
1222
+ --control-h-xs: max(calc(1.75rem * var(--ui-scale)), var(--control-floor));
1223
+ --control-h-sm: max(calc(2.25rem * var(--ui-scale)), var(--control-floor));
1224
+ --control-h-md: max(calc(2.5rem * var(--ui-scale)), var(--control-floor));
1225
+ --control-h-lg: max(calc(2.75rem * var(--ui-scale)), var(--control-floor));
1226
+ /* The control-FONT register (RED 3 — the user's "font too small"). A comfort
1227
+ multiplier BETWEEN the static φ-ladder rung and the painted control font,
1228
+ so interactive-control text grows on the comfort axis while the φ-DISPLAY
1229
+ ladder stays a pure typographic scale (display headings do NOT grow on
1230
+ touch). `--control-text` is the workhorse (the `text-sm` register the
1231
+ buttons/inputs/triggers carried); `--control-text-sm` is the quieter rung
1232
+ (the `text-xs` badge/caption register). W50's `--dropdown-text` IS the
1233
+ picker-family specialization of this register. */
1234
+ --control-text: calc(var(--type-small) * var(--ui-scale));
1235
+ --control-text-sm: calc(var(--type-caption) * var(--ui-scale));
1236
+ /* The control-GLYPH cohort (RED 4 — the glyph grows WITH the box). The
1237
+ 1rem/0.875rem control-glyph register the `size-4`/`size-3.5` literals
1238
+ encode; the un-sized-<svg> CVA rule reads these via
1239
+ `size-[var(--ui-glyph)]`, KEEPING the `:not([class*=size-])` host-sized-icon
1240
+ escape intact (an explicit `size-9` still wins). */
1241
+ --ui-glyph: calc(1rem * var(--ui-scale));
1242
+ --ui-glyph-sm: calc(0.875rem * var(--ui-scale));
1243
+
1244
+ /* ── The picker-family type-scale (AX.W50 D17) ───────────────────────────
1245
+ ONE governed two-rung scale the WHOLE shadcn picker family (Select /
1246
+ DropdownMenu / Combobox / ContextMenu / Command / MultiSelect) reads, so the
1247
+ family paints at ONE uniform scale instead of the scattered raw
1248
+ `text-sm`/`text-xs` Tailwind-literal lottery (item rows at `text-sm`,
1249
+ headings/shortcuts at `text-xs`, the two filter inputs at mismatched heights).
1250
+ A consumer re-tints the whole family from ONE override.
1251
+
1252
+ It is a SPECIALIZATION of W51's control-font register (one scale system, not
1253
+ three): `--dropdown-text` reads `--control-text` (the comfort-scaled `text-sm`)
1254
+ and `--dropdown-text-secondary` reads `--control-text-sm` (the comfort-scaled
1255
+ `text-xs`), so the picker family rides the SAME `--ui-scale` comfort axis as
1256
+ every other control — a `--ui-scale` bump grows the dropdowns too. A consumer
1257
+ wanting the family LARGER than the rest overrides `--dropdown-text` directly
1258
+ (e.g. `var(--type-body)`); the override re-resolves every family surface.
1259
+
1260
+ Scale-contract (which surface reads which rung):
1261
+ • PRIMARY (`--dropdown-text`) → item rows, triggers, filter inputs, empties.
1262
+ • SECONDARY (`--dropdown-text-secondary`) → section labels, group-headings,
1263
+ keyboard shortcuts (the quieter-than-item caption register; resolves the
1264
+ witness-2 contradiction — the Select/DropdownMenu/ContextMenu labels
1265
+ collapse DOWN to the secondary rung to match the Combobox/Command headings,
1266
+ ONE rung for the section-label role family-wide).
1267
+ `--dropdown-input-height` unifies the two filter-input heights (Combobox was
1268
+ h-10, Command was h-11) onto ONE register — a specialization of W51's
1269
+ `--control-h-md` so the picker input scales on the comfort axis with the font. */
1270
+ --dropdown-text: var(--control-text);
1271
+ --dropdown-text-secondary: var(--control-text-sm);
1272
+ --dropdown-input-height: var(--control-h-md);
1273
+
1274
+ --size-icon-btn: 2.5rem;
1275
+ --icon-xs: 0.75rem;
1276
+ --icon-sm: 0.875rem;
1277
+ --icon-md: 1rem;
1278
+ --icon-lg: 1.25rem;
1279
+ --icon-xl: 1.5rem;
1280
+ /* V.W2 T9 — 32px rung; spinner glyphs (Loader2) + dense table-row buttons.
1281
+ Naming follows the radius ladder + Tailwind convention (2xl > xl > xxl) —
1282
+ see B6 §2.4 for the naming rationale. */
1283
+ --icon-2xl: 2rem;
1284
+ /* V.W2 T10 — 40px rung; empty-state hero glyph (DashboardMap MapIcon).
1285
+ AI.W5-ζ — rebound from `2.5rem` literal to `var(--size-icon-btn)` so
1286
+ the alignment is contractual single-source-of-truth rather than the
1287
+ coincidental numerical match noted prior. */
1288
+ --icon-3xl: var(--size-icon-btn);
1289
+ /* V.W2 T11 — 56px rung; FlowSelector school/home survey-flow picker.
1290
+ Largest rung in the icon ladder; reserved for hero-class glyphs. */
1291
+ --icon-hero: 3.5rem;
1292
+
1293
+ /* Golden-ratio spacing rungs — φ² and φ³. Preemptive substrate; W4 hero
1294
+ chassis + W5 StoryChassis migration land the consumers. R3 §A row 1.
1295
+ The 0..4 rungs are not currently named (consumers reach via
1296
+ Tailwind's spacing scale); 5/6 land here because their values fall
1297
+ outside the default ladder and the audacious chassis pattern wants
1298
+ a named handle. */
1299
+ --space-phi-5: 2.618rem; /* φ² */
1300
+ --space-phi-6: 4.236rem; /* φ³ */
1301
+
1302
+ /* Dock geometry — consumers may override locally */
1303
+ --dock-h: calc(var(--size-icon-btn) + 0.75rem + 3px);
1304
+ --dock-margin: 0.5rem;
1305
+ --dock-menubar-reserve: 4rem;
1306
+
1307
+ /* AX.W45 — the dock geometry multiplier. `--dock-scale` threads the WHOLE
1308
+ `.glass-dock[data-density]` geometry cascade (box, padding, gap, tab,
1309
+ tile-min, glyph) via `calc(<base> * --dock-scale)`, so a single knob grows
1310
+ every dock axis in lockstep. The WCAG 2.5.5 44px floor survives as a `max(…,
1311
+ 44px)` clamp INSIDE the scaled control-size.
1312
+
1313
+ AX.W51 RECONCILE (D18) — `--dock-scale` is RE-HOMED as a SPECIALIZATION of
1314
+ the global `--ui-scale` comfort axis: `calc(var(--ui-scale) *
1315
+ var(--dock-local-scale, 1))`. The dock now MULTIPLIES on top of the global
1316
+ comfort scalar — its whole existing geometry `calc` chain inherits the global
1317
+ axis for FREE (zero edit to the density cascade; the reconcile is at this ONE
1318
+ definition line). At the desktop identity (`--ui-scale: 1`,
1319
+ `--dock-local-scale: 1`) the dock is byte-identical to HEAD. On coarse-pointer
1320
+ the GLOBAL `:root { --ui-scale: var(--ui-coarse-scale, 1.5) }` block grows the
1321
+ dock WITH the rest of the library — ONE consistent touch scale, not a parallel
1322
+ dock-only 1.5×. `--dock-local-scale` is the dock-local EXTRA multiplier
1323
+ (default 1): a consumer who wants the dock LARGER than the global comfort sets
1324
+ it, and the coarse block lifts it to `--dock-mobile-scale` (default 1, the
1325
+ stack-extra) so the dock can grow an extra notch ON TOP of the global coarse
1326
+ default. `--dock-mobile-scale` is the public library-default identity
1327
+ (presets-in-consumers: a consumer sets 1.3/1.75 on `:root` or a dock scope). */
1328
+ --dock-scale: calc(var(--ui-scale) * var(--dock-local-scale, 1));
1329
+ --dock-local-scale: 1;
1330
+ --dock-mobile-scale: 1;
1331
+ /* Library glyph ownership — `.dock-icon-button > svg` reads this so the dock
1332
+ icon glyph scales WITH the box (mirroring `.dark-mode-toggle-button > svg`).
1333
+ A consumer-passed explicit lucide size class still WINS (utility layer >
1334
+ component layer); this is a DEFAULT, not a ceiling. */
1335
+ --dock-icon-glyph: calc(1.25rem * var(--dock-scale));
1336
+
1337
+ /* AX.W45-TUNE Q1 — the COLLAPSED-pill floor tokens. The collapsed summary
1338
+ used to fall to `--dock-layer-height` (the FULL control height, ~2.5rem) for
1339
+ its `min-width` with NO block floor, so the shrunken pill read as an
1340
+ un-collapsed control row, not a tight proportioned squircle. These two floor
1341
+ tokens (referenced by dock.css's `.collapsed .dock-layer--summary` min-size +
1342
+ the `--dock-pad-collapsed` morph floor) mint a TIGHT proportioned pill: the
1343
+ summary min-size is ≈70% of the full control height (a proportioned dot, not a
1344
+ full-row stub — the ratified default), and the collapsed padding is a step
1345
+ BELOW the expanded `--dock-padding-block` so the collapsed chrome hugs the
1346
+ glyph. The summary carries a SYMMETRIC `min-block-size` (dock.css) keyed to the
1347
+ same token so the pill floors on BOTH axes — a squircle, not a stub. Consumers
1348
+ retune the pill geometry per-instance via these two handles. */
1349
+ --dock-collapsed-summary-min-size: calc(var(--dock-layer-height, 2.5rem) * 0.7);
1350
+ /* AX.W61 — the collapsed padding now threads the ONE `--dock-scale` coarse-pointer
1351
+ multiplier DIRECTLY (at HEAD it was a flat 0.25rem off the scale, so the
1352
+ collapsed pad alone stayed desktop-sized at the 1.5x mobile scale while the
1353
+ box/glyph/min-size grew — a desync the morph then interpolated FROM). The
1354
+ summary min-size already threads `--dock-scale` transitively through
1355
+ `--dock-layer-height` (itself `calc(* --dock-scale)` at every density rung); this
1356
+ lands the collapsed pad on the same thread so the whole collapsed pill scales
1357
+ coherently on touch. The 0.25rem base stays the ratified tight floor. */
1358
+ --dock-collapsed-padding: calc(0.25rem * var(--dock-scale));
1359
+
1360
+ /* AX.W45-TUNE Q3/C3 + C5 — the dock-control family reads a real GLASS register
1361
+ on hover/active, NOT a same-hue `--card` mix at an alpha adjacent to the dock's
1362
+ own `--glass-bg-dock` substrate (a sub-1%-ΔL no-op — the loud Q3 tell: "nothing
1363
+ on hover, something on click"). Both legs now point at NAMED glass tiers a
1364
+ measurable step ABOVE the dock's 42%-opacity substrate:
1365
+ • hover → `--glass-bg-resting` (the 65% glass plate) — the control surface
1366
+ WAKES as a brighter glass tier on hover, reading the blurred dock
1367
+ substrate through it. The +ΔL above `--glass-bg-dock` is perceptible
1368
+ BEFORE click; the specular gleam + scale ride alongside (one register).
1369
+ • active → `--glass-bg-floating` (the 80% glass plate) — a tier ABOVE hover,
1370
+ the keyframes-dock "selected element reads as glass" model the user
1371
+ named (`pass-3:13`): the selected tile is a glass tier that shows the
1372
+ substrate through it, NOT the flat `--surface-tint-12` ink wash. The
1373
+ active ≠ hover register is the glass-tier step, not an alpha nudge.
1374
+ This is a REGISTER change (substrate→glass-tier), not a magnitude knob. The
1375
+ per-control `--dock-icon-hover-bg` override still wins if a consumer set it. */
1376
+ --dock-control-hover-bg: var(--glass-bg-resting);
1377
+ --dock-control-active-bg: var(--glass-bg-floating);
1378
+
1379
+ /* AX.W45-TUNE C4/C5 — the small-tile specular override. The 40px dock-control
1380
+ tile is a fraction of a card's plate, so the global `--glass-specular-size`
1381
+ (36% of the plate) blooms a disproportionate disc on a hover/active dock tile
1382
+ (the I.W6 finding). The dock controls scope a TIGHTER radius (22%) so the gleam
1383
+ wakes as a restrained EDGE-catch on the small tile, not a tile-filling bloom —
1384
+ the right resolution of I.W6 (sized-down on the small surface), not turning the
1385
+ specular off everywhere. Read by the dock-control `::before` recipe (glass.css)
1386
+ via the family-scoped override below; the global card/panel size is untouched. */
1387
+ --dock-control-specular-size: 22%;
1388
+
1389
+ /* AX.W45 DK8 — the layer-switcher rail surface ladder. The rail
1390
+ (`.dock-layer-rail` inside a `<DockLayerGroup>`) gets its own quiet plate +
1391
+ a tunable hover/active affordance, replacing the raw `--primary 15%` /
1392
+ `--accent 40%` mixes the indicator/tab used against an absent plate. */
1393
+ --dock-layer-rail-bg: var(--surface-tint-8);
1394
+ --dock-layer-rail-hover: color-mix(in srgb, var(--accent) 40%, transparent);
1395
+ --dock-layer-rail-active: color-mix(in srgb, var(--primary) 15%, transparent);
1396
+
1397
+ /* Dock active-state ladder (O.W6 Lane B)
1398
+ Token-overridable active paint for `.dock-icon-button`. Promoted
1399
+ from hardcoded `background: var(--muted); color: var(--foreground)`
1400
+ per bbnf-buddy O11/c R1: 7 `:deep()` escapes at `ToolsLayer.vue`
1401
+ redefine bg/color/scale/border/shadow on the active variant; the
1402
+ ladder absorbs ~3 of 7 at a stroke. Defaults preserve the prior
1403
+ visual contract (no transform, no border, no shadow) — overrides
1404
+ take effect per-instance via `:root` or a parent scope, e.g.
1405
+ `.tools-layer { --dock-active-scale: 1.2; --dock-active-bg: …; }`.
1406
+ ≥ 2-consumer coverage: bbnf-buddy ToolsLayer (override consumer) +
1407
+ speedtest dock (default consumer).
1408
+
1409
+ AX.W45 DK2 — re-pointed off the opaque `--muted` plate onto the glass-
1410
+ correct `--dock-control-active-bg` (a translucent tint a ladder step above
1411
+ hover), so the icon-button active no longer stamps a solid cream rectangle
1412
+ over the dock glass. The O.W6 active-ladder override consumers still retint
1413
+ per-instance; only the DEFAULT moved to glass-correct. */
1414
+ --dock-active-bg: var(--dock-control-active-bg);
1415
+ --dock-active-color: var(--foreground);
1416
+ --dock-active-scale: 1;
1417
+ --dock-active-border: none;
1418
+ --dock-active-shadow: none;
1419
+
1420
+ /* Q.W3 Lane G (Q-cos-13) — the O.W6 Lane D `--icon-tooltip-hit-area`
1421
+ token retired here. IconTooltip no longer owns a wrap-span; the
1422
+ slotted child carries its own WCAG 2.5.5 hit-area (interactive
1423
+ children via their four-state contract, bare glyphs via a
1424
+ `min-h-11 min-w-11` host). No orphaned token left behind. */
1425
+
1426
+ /* O.W6 Lane D — Dock touch-target floor. Mirrors the IconTooltip
1427
+ hit-area but for the dock-control surface. The base value tracks
1428
+ the desktop default (`--size-icon-btn`); the
1429
+ `@media (pointer: coarse)` block at `dock.css` lifts it to the
1430
+ WCAG 2.5.5 floor on touch devices. */
1431
+ --dock-touch-target: 2.75rem; /* 44px */
1432
+
1433
+ /* AQ.W3 §7 — canonical WCAG 2.5.5 (44px) touch-target floor for the
1434
+ NON-dock coarse-pointer surfaces (`Button size="icon"`,
1435
+ ExpandableContainer triggers, SegmentedTabs responsive mobile trigger). One
1436
+ named rung the `@media (pointer: coarse)` block in utilities.css reads,
1437
+ retiring the per-consumer one-liner `min-h-[44px]` patches. Equals
1438
+ `--dock-touch-target` numerically; kept as a distinct token so the
1439
+ dock floor and the general floor can be retuned independently. */
1440
+ --touch-target: 2.75rem; /* 44px */
1441
+
1442
+ /* MetricBadge — stacked variant geometry. The 2-row stacked layout
1443
+ (label / value+unit) wants a taller floor than the inline pill so the
1444
+ label has breathing room above the baseline pair. T.W2.T2 +
1445
+ audit B-spec §3.2 vertical-rhythm table land 42px (2.625rem) as the
1446
+ min-height with 6px (0.375rem) block padding. The inline ladder
1447
+ keeps its tight defaults via `--metric-badge-min-height` /
1448
+ `--metric-badge-padding-block` (declared inline at the
1449
+ `.metric-badge` selector). */
1450
+ --metric-badge-min-height-stacked: 2.625rem;
1451
+ --metric-badge-padding-block-stacked: 0.375rem;
1452
+
1453
+ /* Configurator — N.W2 Lane A density axis (4 rungs).
1454
+ The `comfortable` rung restates the pre-N.W2 baked recipe exactly
1455
+ (`gap-1.5 py-2` = 0.375rem / 0.5rem) so unparameterised consumers
1456
+ see no visual change. `mobile` halves the vertical breathing room
1457
+ for narrow-viewport studios; `spacious` doubles it for settings-
1458
+ page hosts. See `<ConfiguratorRow>` scoped CSS for the attribute-
1459
+ selector binding. */
1460
+ --configurator-row-gap-mobile: 0.25rem; /* 4px — tight */
1461
+ --configurator-row-gap-compact: 0.3125rem; /* 5px — slightly tighter */
1462
+ --configurator-row-gap-comfortable: 0.375rem; /* 6px — pre-N.W2 default */
1463
+ --configurator-row-gap-spacious: 0.75rem; /* 12px — generous */
1464
+
1465
+ --configurator-row-py-mobile: 0.25rem; /* 4px — tight */
1466
+ --configurator-row-py-compact: 0.375rem; /* 6px — slightly tighter */
1467
+ --configurator-row-py-comfortable: 0.5rem; /* 8px — pre-N.W2 default */
1468
+ --configurator-row-py-spacious: 0.875rem; /* 14px — generous */
1469
+
1470
+ /* Component font override (consumers override locally) */
1471
+ --select-font: inherit;
1472
+
1473
+ /* ═══════════════════════════════════════════════
1474
+ §11 INTERACTIVE SCALES & FOCUS
1475
+ ═══════════════════════════════════════════════ */
1476
+ --scale-hover: 1.08;
1477
+ /* AX.W52 — the restrained BUTTON hover lift. `--scale-hover` (1.08) is an
1478
+ 8% jump — emphatic for a marquee CTA, jarring as the general hover. The
1479
+ button family (the audacious CTAs + `btn-interactive`) lifts on the
1480
+ smaller `--scale-hover-btn` routed through `--spring-smooth` (the
1481
+ sub-perceptual settle, not the snappy bounce), so the hover reads as ONE
1482
+ coherent glide with the surface-leg cross-fade rather than a mechanical
1483
+ pop. The dock keeps its proportionally-larger `--scale-hover-dock` (small
1484
+ icon, bigger lift reads smaller).
1485
+ AX.W54 Q3 — lifted 1.035 → 1.05: the 1.035 overshot DOWN (W52 tamed the
1486
+ 1.08 snap so hard the lift read as nothing), so the glass hover now reads
1487
+ as THREE legs together on hover — the specular gleam waking, a perceptible
1488
+ scale, and the bg/border cross-fade — distinguishable from rest BEFORE the
1489
+ click. Still sub-perceptual-bounce on `--spring-smooth`; the magnitude
1490
+ moved, the easing register did not. */
1491
+ --scale-hover-btn: 1.05;
1492
+ --scale-hover-dock: 1.1;
1493
+ --scale-press: 0.96;
1494
+
1495
+ /* Press-scale rungs. `--scale-press` (0.96) is the canonical iOS Liquid
1496
+ Glass press value every primitive reaches for unless it has a documented
1497
+ reason (DESIGN.md §L3); `--scale-press-btn` aliases the slightly-softer
1498
+ `--scale-press-sm` (0.97) the button + slider recipes consume.
1499
+
1500
+ AT.W7-dock-b — press canon. `--scale-press-dock` previously hard-coded a
1501
+ deeper 0.92 (a second magic press value with no documented rationale),
1502
+ so a dock control pressed visibly harder than every other primitive. It
1503
+ now references the canonical `--scale-press` (0.96) — ONE source for the
1504
+ press scale across the system. A consumer wanting the dock's old deeper
1505
+ press overrides `--scale-press-dock` on the dock instance. */
1506
+ --scale-press-dock: var(--scale-press);
1507
+ --scale-press-sm: 0.97;
1508
+ --scale-press-btn: var(--scale-press-sm);
1509
+
1510
+ /* AX.W53 — the SegmentedTabs travel-squish cap. The active indicator
1511
+ (segmented/pill slider OR the underline rule) STRETCHES along its travel
1512
+ axis as it glides between tabs, then settles to fit — the Material 3
1513
+ ELASTIC / Apple Liquid-Glass "controls insist on animating themselves"
1514
+ register, kept RESTRAINED. The deform is volume-preserving (stretch X,
1515
+ compress Y in lockstep: `scale: var(--stretch) calc(1 / var(--stretch))`)
1516
+ so it reads as an elastic warp, never a size pump. The cap holds the peak
1517
+ LOW (1.08 ≈ +8%) — iOS 26.2 dialed motion DOWN; a high-frequency control
1518
+ wants a whisper of squish, not a bounce. A consumer dials the elasticity
1519
+ (or to 1 for none) without touching source. */
1520
+ --tab-indicator-max-stretch: 1.08;
1521
+ --opacity-disabled: 0.5;
1522
+ --opacity-icon-muted: 0.8;
1523
+
1524
+ --focus-ring-width: 2px;
1525
+ /* D6.b — the focus-ring color routes through a `--focus-ring-color`
1526
+ indirection (default `--ring`, so the resolved shadow is byte-identical to
1527
+ the prior hard `--ring` recipe). An opt-in channel (the `focus-ring-gold`
1528
+ utility, utilities.css) overrides ONLY this color to mint a gilded
1529
+ "you are here / this is live" ring — gold is the transient STATE voice;
1530
+ the resting active mark stays its structural color (the four-voice law). */
1531
+ --focus-ring-color: var(--ring);
1532
+ --focus-ring-shadow: 0 0 0 var(--focus-ring-width) color-mix(in srgb, var(--focus-ring-color) 30%, transparent),
1533
+ 0 0 8px color-mix(in srgb, var(--focus-ring-color) 15%, transparent);
1534
+ --focus-ring: var(--focus-ring-shadow);
1535
+
1536
+ /* ═══════════════════════════════════════════════
1537
+ §12 PAPER TEXTURES
1538
+ ═══════════════════════════════════════════════ */
1539
+ /* Canonical subtle paper-texture opacity — restored from fourier-original
1540
+ (`4df1a06:web/src/style.css:53-54`) after A.W3.5.ab confirmed the
1541
+ heretofore `opacity='1'` URI rendered as an overt grain field in every
1542
+ downstream consumer of `.paper-texture`. The SVG `opacity` attribute is
1543
+ baked at parse-time and cannot read a CSS var, so the subtle value must
1544
+ be inlined; if a consumer needs a heavier overlay, they should layer
1545
+ a wrapper element with its own `opacity:`/`mix-blend-mode:` atop. */
1546
+ --paper-clean-texture: url("data:image/svg+xml,%3Csvg width='200' height='200' viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.65' numOctaves='4' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.04'/%3E%3C/svg%3E");
1547
+ --paper-aged-texture: url("data:image/svg+xml,%3Csvg width='200' height='200' viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.5' numOctaves='5' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.06'/%3E%3C/svg%3E");
1548
+
1549
+ /* ═══════════════════════════════════════════════
1550
+ §13 GOLD (accent family kept for consumers using it)
1551
+ ═══════════════════════════════════════════════ */
1552
+ --gold: oklch(0.751 0.147 84.2);
1553
+ --gold-light: oklch(0.890 0.183 96.1);
1554
+ --gold-dark: oklch(0.599 0.132 64.2);
1555
+ /* AY.W-PRIM-POLISH D1 — the saturated CTA plate the AW.W13 gold-CTA contract
1556
+ names. A FIXED deep gold (mode-INVARIANT — the gold-CTA hover backplate must
1557
+ be dark in BOTH modes so the white label clears ≥4.5:1; deepening toward the
1558
+ mode-flipping `--foreground` brightened the dark plate to a cream-gold and
1559
+ broke dark). ~`--gold-dark` deepened toward warm-ink → ~rgb(120,87,45),
1560
+ 6.5:1 white in light / 5.5:1 in dark. The opaque base of `btn-audacious-gold`
1561
+ :hover. */
1562
+ --gold-deep: oklch(0.487 0.103 63.0);
1563
+
1564
+ /* D6.b — the chrome-gold additions (the rim + the legible ink), completing
1565
+ the gold quad into the gilt FAMILY. Gold is a metal, not a hue — it must
1566
+ read as reflectance, so it lives at the EDGE and the HIGHLIGHT, rarely as
1567
+ an area.
1568
+
1569
+ --gold-rim — the gilt EDGE catch: `--gold` at 40% over the surface, the
1570
+ 1px tooled bevel light the `.glass-gilt` modifier + the gilt dock button
1571
+ paint. Non-text → no contrast floor.
1572
+
1573
+ --gold-ink — legible gold-toned TEXT. Derived (NOT guessed) to clear the
1574
+ cream-law ② floor (APCA |Lc| ≥ 45) against `--card` in BOTH arms; the raw
1575
+ `--gold-dark` does not (it was minted as a plate hue). LIGHT arm:
1576
+ oklch(0.560 0.118 70) → |Lc| 69.9 on the cream `--card` (hsl 48 12% 98%).
1577
+ The dark-arm pair lifts L (the warm-dark stock) in the `.dark` block,
1578
+ reaching |Lc| 77.1 on the dark `--card`. Both verified by
1579
+ `proof:gold-ink-contrast`. */
1580
+ --gold-rim: color-mix(in oklab, var(--gold) 40%, transparent);
1581
+ --gold-ink: oklch(0.560 0.118 70);
1582
+
1583
+ --color-gold: var(--gold);
1584
+ --color-gold-light: var(--gold-light);
1585
+ --color-gold-dark: var(--gold-dark);
1586
+ --color-gold-deep: var(--gold-deep);
1587
+ --color-gold-rim: var(--gold-rim);
1588
+ --color-gold-ink: var(--gold-ink);
1589
+
1590
+ /* ═══════════════════════════════════════════════
1591
+ §14 RAINBOW PALETTE — oklch (AV.W16 TW2; mode-independent, no .dark
1592
+ mirror; hue preserved from the prior hsl() cadence)
1593
+ ═══════════════════════════════════════════════ */
1594
+ --rainbow-red: oklch(0.636 0.210 25.5);
1595
+ --rainbow-orange: oklch(0.737 0.163 58.9);
1596
+ --rainbow-yellow: oklch(0.900 0.183 103.7);
1597
+ --rainbow-green: oklch(0.772 0.233 144.8);
1598
+ --rainbow-blue: oklch(0.633 0.162 252.1);
1599
+ --rainbow-indigo: oklch(0.566 0.206 294.1);
1600
+ --rainbow-violet: oklch(0.684 0.250 327.9);
1601
+
1602
+ --rainbow-pastel-red: oklch(0.793 0.066 18.7);
1603
+ --rainbow-pastel-orange: oklch(0.823 0.059 57.3);
1604
+ --rainbow-pastel-yellow: oklch(0.889 0.068 98.1);
1605
+ --rainbow-pastel-green: oklch(0.826 0.072 149.5);
1606
+ --rainbow-pastel-blue: oklch(0.783 0.056 264.2);
1607
+ --rainbow-pastel-indigo: oklch(0.772 0.062 300.2);
1608
+ --rainbow-pastel-violet: oklch(0.799 0.070 314.4);
1609
+
1610
+ /* ═══════════════════════════════════════════════
1611
+ §16 TIMELINE
1612
+ Timeline primitive tokens. All three variants (scrubber +
1613
+ segmented + continuous) compose these. Both segmented + continuous
1614
+ arrays carry the same `TimelineSegment[]` data shape; only the
1615
+ rendering geometry differs. See DESIGN.md `## Timeline Primitive`.
1616
+ ═══════════════════════════════════════════════ */
1617
+
1618
+ /* Heights — per-variant rail thickness */
1619
+ --timeline-scrubber-height: 0.5rem; /* 8px — single-track scrubber */
1620
+ --timeline-segmented-height: 0.625rem; /* 10px — N adjacent bands */
1621
+ --timeline-continuous-height: 0.75rem; /* 12px — ONE pill, N regions */
1622
+
1623
+ /* Geometry — shared across all variants */
1624
+ --timeline-dot-size: 0.875rem; /* 14px — boundary dot diameter (pointer: fine) */
1625
+ --timeline-dot-size-touch: 1.25rem; /* 20px — boundary dot diameter (pointer: coarse) */
1626
+ --timeline-touch-target: 44px; /* AC.W6d F2.I-04 — WCAG 2.5.5 target-size floor.
1627
+ The `::before` halo around each dot extends to
1628
+ this dimension so finger-precision users meet
1629
+ the (target-size: 44×44 minimum) clause without
1630
+ the visible dot growing to that footprint at
1631
+ pointer: fine. */
1632
+ --timeline-segment-flex: 1 1 0; /* per-cell flex distribution (segmented) */
1633
+
1634
+ /* Boundary-dot glass knobs (Q.W4 Lane B — promoted from
1635
+ ContinuousTimeline.vue SFC fallbacks; `3cb70db` minted these as a
1636
+ private dialect). The continuous-variant dot is a GLASS primitive:
1637
+ a translucent tinted fill over a backdrop blur with a hairline
1638
+ ring. These knobs let consumers retune the resting glass + the
1639
+ per-state tints without a `:deep()` reach.
1640
+ fill — resting translucent fill
1641
+ blur — backdrop blur tier behind the dot
1642
+ ring — hairline glass ring (border-color)
1643
+ tint-current — wash colour for the active-phase dot
1644
+ tint-completed — wash colour for the completed-phase dot
1645
+ check-color — completion-tick stroke (defaults to the
1646
+ completed tint) */
1647
+ --timeline-dot-fill: var(--surface-tint-12);
1648
+ --timeline-dot-blur: var(--glass-blur-quiet);
1649
+ --timeline-dot-ring: var(--glass-border-floating);
1650
+ --timeline-dot-tint-current: var(--accent, var(--foreground));
1651
+ --timeline-dot-tint-completed: var(--success, var(--foreground));
1652
+ --timeline-dot-check-color: var(--timeline-dot-tint-completed);
1653
+
1654
+ /* Continuous-variant specific — seam dividers between regions.
1655
+ Set `--timeline-continuous-seam-opacity: 0` to suppress the
1656
+ inter-region 1px dividers entirely. Default `0.25` reads as a
1657
+ subtle hairline; the `--timeline-continuous-seam-color` knob lets
1658
+ consumers override the seam tint independently of opacity. */
1659
+ --timeline-continuous-seam-opacity: 0.25;
1660
+ --timeline-continuous-seam-color: color-mix(
1661
+ in srgb,
1662
+ var(--foreground) calc(var(--timeline-continuous-seam-opacity) * 100%),
1663
+ transparent
1664
+ );
1665
+
1666
+ /* Per-segment default gradient fallback — applied when a segment
1667
+ omits its `gradient` field. Consumers can override per-phase via
1668
+ the `--timeline-segment-gradient-{key}` knobs below. */
1669
+ --timeline-segment-default-gradient: linear-gradient(
1670
+ 90deg,
1671
+ var(--surface-tint-15),
1672
+ var(--surface-tint-25)
1673
+ );
1674
+
1675
+ /* Per-phase gradient defaults — paint the canonical chart palette
1676
+ at the gradient leading edge, fading to a 60%-opacity tail. The
1677
+ speedtest consumer wires these via `gradient: "var(--timeline-segment-gradient-ping)"`
1678
+ per segment; the same names work for any consumer who follows the
1679
+ ping/download/upload/jitter taxonomy. */
1680
+ --timeline-segment-gradient-ping: linear-gradient(
1681
+ 90deg,
1682
+ var(--chart-ping),
1683
+ color-mix(in srgb, var(--chart-ping) 60%, transparent)
1684
+ );
1685
+ --timeline-segment-gradient-download: linear-gradient(
1686
+ 90deg,
1687
+ var(--chart-download),
1688
+ color-mix(in srgb, var(--chart-download) 60%, transparent)
1689
+ );
1690
+ --timeline-segment-gradient-upload: linear-gradient(
1691
+ 90deg,
1692
+ var(--chart-upload),
1693
+ color-mix(in srgb, var(--chart-upload) 60%, transparent)
1694
+ );
1695
+ --timeline-segment-gradient-jitter: linear-gradient(
1696
+ 90deg,
1697
+ var(--chart-jitter),
1698
+ color-mix(in srgb, var(--chart-jitter) 60%, transparent)
1699
+ );
1700
+
1701
+ /* ═══════════════════════════════════════════════
1702
+ §17 METRIC
1703
+
1704
+ MetricRow / MetricStack value-clamp dialect. The declared values are the
1705
+ `audacious` poster register — the hero-scale clamp for a single giant
1706
+ number; MetricStack's `[data-register="result"]` selector overrides it
1707
+ locally for the compact multi-row ledger. Three clamp arms each: `min`
1708
+ (floor), `cqi` (the binding container-query middle term), `max`
1709
+ (ceiling); unit carries min/max only (its cqi is a fixed 6cqi).
1710
+ ═══════════════════════════════════════════════ */
1711
+
1712
+ /* Value register — audacious-poster hero clamp */
1713
+ --metric-row-value-clamp-min: 4.5rem;
1714
+ --metric-row-value-clamp-cqi: 34cqi;
1715
+ --metric-row-value-clamp-max: var(--type-display-hero);
1716
+
1717
+ /* Unit register — one tier below the value it qualifies */
1718
+ --metric-row-unit-clamp-min: 1.5rem;
1719
+ --metric-row-unit-clamp-max: 3.25rem;
1720
+
1721
+ /* Label register — uppercase, the binding row-height term in the
1722
+ compact `result` register */
1723
+ --metric-row-label-clamp-min: 1.125rem;
1724
+ --metric-row-label-clamp-cqi: 5cqi;
1725
+ --metric-row-label-clamp-max: 2.75rem;
1726
+
1727
+ /* ═══════════════════════════════════════════════
1728
+ §2.D INSTRUMENT-CLUSTER COMPOSITION RHYTHM
1729
+
1730
+ The instrument-chassis family owns the under-meter / progress-cluster
1731
+ grammar.
1732
+
1733
+ • `--meter-progress-inset` — the negative-margin inset that pulls the
1734
+ under-meter bar INTO the unpainted ring lane around the dial canvas
1735
+ (~4% of meter-size). Consumed at `.phase-progress` via
1736
+ `margin-block-start: calc(-1 * var(--meter-progress-inset))`. The
1737
+ `--meter-size` reference resolves against the consumer's own
1738
+ declaration; the 200px fallback keeps the calc well-defined.
1739
+ • `--progress-stack-gap` — vertical rhythm between any two stacked
1740
+ progress rails inside one cluster.
1741
+ ═══════════════════════════════════════════════ */
1742
+ --meter-progress-inset: calc(var(--meter-size, 200px) * 0.04);
1743
+ --progress-stack-gap: clamp(0.1875rem, 0.4cqi, 0.375rem);
1744
+
1745
+ /* ═══════════════════════════════════════════════
1746
+ §2.E CELEBRATION-TIER REGISTER
1747
+
1748
+ Shared peak for any "completion" / "thank-you" surface.
1749
+
1750
+ • `--celebration-row-rhythm` — gap between the cluster's rows (cqi-axis).
1751
+ • `--text-celebration-headline-size` — the single designed peak both
1752
+ celebration surfaces clamp toward (cqi-axis).
1753
+ • `--complete-headline-size` — the per-consumer font-size hook a consumer
1754
+ assigns the canon clamp to so its headline pegs at the shared peak;
1755
+ the default falls back to a smaller hero-rung clamp.
1756
+ ═══════════════════════════════════════════════ */
1757
+ --celebration-row-rhythm: clamp(0.5rem, 1.6cqi, 1rem);
1758
+ --text-celebration-headline-size: clamp(2.5rem, 12cqi, 9rem);
1759
+ --complete-headline-size: clamp(2.25rem, 11cqi, 4rem);
1760
+
1761
+ /* ═══════════════════════════════════════════════
1762
+ §19 TABLE DENSITY
1763
+
1764
+ A single unitless knob `--table-density` scales the cell padding +
1765
+ header row height of the Table / DataTable primitive. Default `1`
1766
+ resolves to 1rem cell padding + 3rem header height; 0.75 is a compact
1767
+ admin table, 1.25 is comfortable/touch-friendly. The derived tokens are
1768
+ read by the `table-cell` / `table-head` utilities; changing the knob
1769
+ cascades instantly via CSS inheritance — no JS prop needed.
1770
+ ═══════════════════════════════════════════════ */
1771
+ --table-density: 1;
1772
+ --table-cell-px: calc(1rem * var(--table-density));
1773
+ --table-cell-py: calc(1rem * var(--table-density));
1774
+ --table-head-h: calc(3rem * var(--table-density));
1775
+ --table-head-px: calc(1rem * var(--table-density));
1776
+
1777
+ /* ═══════════════════════════════════════════════
1778
+ §20 PLATFORM MOTION (AQ.W5)
1779
+
1780
+ The native-platform motion substrate axes. Three families, each read
1781
+ by a `@supports`-gated CSS recipe in `scroll-driven.css`,
1782
+ `animations.css` (§TOP-LAYER), and `view-transition.css` — every visual
1783
+ knob a token so a consumer retunes without editing library source.
1784
+
1785
+ SCROLL-DRIVEN (scroll-driven.css). Values mirror the JS-fallback
1786
+ composable defaults (`useScrollProgress` / `useStaggerReveal`) so the
1787
+ native CSS path and the fallback path read identically — no visual seam
1788
+ at the `@supports` boundary.
1789
+
1790
+ TOP-LAYER (animations.css §TOP-LAYER). The `@starting-style` entry/exit
1791
+ grammar for native `<dialog>` / `[popover]` glass surfaces. Backdrop dim
1792
+ uses the `hsl(var(--background) / α)` channel form — the legitimate
1793
+ single-token alpha case (AQ.W2's color-mix migration is orthogonal).
1794
+
1795
+ VIEW-TRANSITION (view-transition.css). The shared `.gl-list-item` group
1796
+ recipe + the `--vt-*` axes the `useViewTransition` substrate consumers
1797
+ (dock layer swap, muster J's verdict re-rank) tune their swap against.
1798
+ ═══════════════════════════════════════════════ */
1799
+ --scroll-progress-scroller: root;
1800
+ --scroll-reveal-rise: 6px;
1801
+ --scroll-reveal-range-end: 60%;
1802
+
1803
+ --top-layer-enter-scale: 0.96;
1804
+ --top-layer-backdrop-blur: 8px;
1805
+ --top-layer-backdrop-dim: 0.5;
1806
+
1807
+ --vt-duration: var(--duration-normal);
1808
+ /* AX.W05 — ONE iOS-spring vocabulary: every spring-flavored transition reads
1809
+ a governed `--spring-*` register. The legacy apple-spring cubic-bezier
1810
+ (+27.5% overshoot) was a SECOND authority beside the regen `--spring-*`
1811
+ cohort; it is EXCISED. Its emphatic-overshoot register maps to the playful
1812
+ `--spring-bouncy` (the closest `linear()` overshoot stop-set), so the VT
1813
+ default + the cards/top-layer transitions share one spring primitive
1814
+ (CPU + GPU sample the same curve). No cubic-bezier fallback seed survives —
1815
+ the `linear()` spring degrades to `ease` where `linear()` is unsupported. */
1816
+ --vt-ease: var(--spring-bouncy);
1817
+ --vt-rise: 8px;
1818
+
1819
+ /* AT.W6-dock-c / AU.W8 — the dock layer-swap morph timing, minted ONCE so
1820
+ both engine paths are timing-IDENTICAL: the FLIP fallback (via
1821
+ `--dock-motion-resize`), the native View-Transition group (its
1822
+ `animation-timing-function`), AND the runtime SpringProgress driver
1823
+ (AU.W8.3) consume this same `(0.32, 0.7)` source curve. Retires the AQ.W6
1824
+ fork where the VT path ran `--vt-ease` (apple-spring, +27.5% overshoot)
1825
+ while the FLIP ran the snappy spring. `--vt-ease` still defaults the
1826
+ other VT recipes (`.gl-list-item`).
1827
+
1828
+ AU.W8 — RETARGET snappy → dock. `--spring-snappy` (ζ=0.65, +6.8%) read as
1829
+ mechanical: a stiff plateau at ~48% with too little overshoot to feel
1830
+ alive ("not iOS-smooth"). AW.W2 then retuned the dock spring to the
1831
+ iOS-control register: `--spring-dock` ((0.32, 0.7), ~+4.6%) settles snappy
1832
+ with a restrained overshoot — the felt iOS dock morph, off the prior
1833
+ playful +18.5% register. The runtime driver's DOCK_SPRING const MIRRORS the
1834
+ (0.32, 0.7) PRESETS row, so the CSS token and the JS-driven morph are
1835
+ bit-identical. */
1836
+ --dock-resize-spring: var(--spring-dock);
1837
+
1838
+ /* AT.W7-dock-b — dock-control press micro-feedback spring. A TRANSFORM-ONLY
1839
+ spring (`scale`/`rotate`) the dock controls apply to their hover/press
1840
+ activation so the squish reads with a touch of spring-back rather than a
1841
+ flat ease. STRICTLY transform-scoped: it must never touch the surface-tier
1842
+ `[data-held]` fades (background/border/box-shadow), which stay on
1843
+ `--duration-fast var(--ease-standard)`. The duration pairs with
1844
+ `--duration-fast`; consumers retune the whole feel via this one token.
1845
+
1846
+ AY.W-DOCK2 (D6) — the register is `--spring-smooth`, the §6 easing-doctrine
1847
+ transform-press register (tokens.css §2: transform hover/press/active →
1848
+ `--spring-smooth`/`--spring-snappy`, NEVER `--spring-bouncy`; a press must
1849
+ settle, never overshoot into a visible bounce). The prior `--spring-bouncy`
1850
+ ROOT default was off-doctrine, shadowed for the four named controls by a
1851
+ per-surface re-point in dock-controls.css — the exact per-surface fork the
1852
+ doctrine kills. The re-point is now a DEAD byte-identical redeclaration and is
1853
+ DELETED; the ROOT token IS the doctrine register, so any other consumer of
1854
+ `--dock-press-spring` inherits the correct smooth register. */
1855
+ --dock-press-spring: var(--duration-fast) var(--spring-smooth);
1856
+ }
1857
+
1858
+ /* AX.W51 D18 — the GLOBAL coarse-pointer comfort amplification. The WHOLE library
1859
+ — buttons, inputs, badges, toggles, the dock (a specialization that reads
1860
+ `--ui-scale`) — grows ~1.5× on touch from this ONE place, the W45 dock-only 1.5×
1861
+ lifted to the master scalar so the touch scale is CONSISTENT across the library
1862
+ (at HEAD the dock grew but the buttons did not — the three-scale split). The
1863
+ WCAG-2.5.5 44px touch floor is enforced HERE too: `--control-floor` lifts to
1864
+ `--touch-target`, so every scaled control-height `max(scaled, floor)` clamps at
1865
+ ≥ 44px regardless of the scalar (a consumer dialing `--ui-scale` below 1 still
1866
+ cannot drop a control under the target). Fine-pointer is byte-identical
1867
+ (`--ui-scale` stays 1). `--ui-coarse-scale` is the public default a consumer
1868
+ retunes. */
1869
+ @media (pointer: coarse) {
1870
+ :root {
1871
+ --ui-scale: var(--ui-coarse-scale, 1.5);
1872
+ --control-floor: var(--touch-target, 2.75rem);
1873
+ }
1874
+ }
1875
+
1876
+ /* AV.W7 F2 — high-resolution overlay-blur restore. On a 2dppx+ display the
1877
+ richer 24px overlay wash is amortised by the extra device pixels (the blur
1878
+ samples a denser backing store), so the design ceiling is restored where it
1879
+ is affordable. Standard-density displays keep the 15px budget radius set in
1880
+ §-1a `:root`. Token-only override — every overlay surface that reads
1881
+ `--glass-blur-overlay-radius` inherits the restored value automatically. */
1882
+ @media (min-resolution: 2dppx) {
1883
+ :root {
1884
+ --glass-blur-overlay-radius: 24px;
1885
+ }
1886
+ }
1887
+
1888
+ /* ═══════════════════════════════════════════════
1889
+ §-1b LIGHT-DARK() COLOR ARCHITECTURE (AQ.W2 §2)
1890
+
1891
+ Every PAIRED color token (a token whose `:root` and `.dark` values are
1892
+ BOTH a <color> and differ) folds onto one `light-dark(light, dark)`
1893
+ declaration, keyed off the element's `color-scheme` (§-1). This is the
1894
+ single-source-of-truth collapse of the `.dark` mirror AND the dynamism
1895
+ win: an unregistered custom property holding a `light-dark()` expression
1896
+ re-resolves under a descendant `color-scheme` override (a `.dark`
1897
+ code-block embedded in a light page, or vice-versa).
1898
+
1899
+ DISCIPLINE (§2b — BINDING): these tokens MUST stay UNREGISTERED (no
1900
+ `@property … syntax:"<color>"`). A registered <color> would resolve ONCE
1901
+ at the declaring element and inherit the RESOLVED color, freezing the
1902
+ descendant re-resolution. glass-ui's only `@property` registrations (§18)
1903
+ are non-color (percentage/length), so the discipline holds — W2 adds NO
1904
+ color `@property`.
1905
+
1906
+ The semantic `var()`-aliases (`--background`, `--muted`, `--border`,
1907
+ `--secondary`, `--accent`, `--card-foreground`, …) do NOT appear here:
1908
+ they point at the SAME source name in both modes and auto-track once the
1909
+ `--neutral-*` ladder + `--foreground` resolve via `light-dark()`. Their
1910
+ redundant `.dark` re-declarations DELETE below.
1911
+
1912
+ FALLBACK (§2c): this whole block is `@supports`-gated, so an engine
1913
+ WITHOUT `light-dark()` skips it and the retained `.dark` class block (the
1914
+ SOLE fallback floor) drives dark exactly as at HEAD. `light-dark()` is the
1915
+ enhancement OVER the class — not a parallel alias. Baseline Newly
1916
+ Available; the fallback floor is the pre-existing class block (net-zero
1917
+ new fallback LOC).
1918
+ ═══════════════════════════════════════════════ */
1919
+ @supports (color: light-dark(white, black)) {
1920
+ :root {
1921
+ /* Neutral ladder — the source the var()-aliases track */
1922
+ --neutral-0: light-dark(hsl(48 12% 98%), hsl(24 8% 6%));
1923
+ --neutral-1: light-dark(hsl(48 10% 95%), hsl(24 6% 11%));
1924
+ --neutral-2: light-dark(hsl(48 9% 90%), hsl(24 5% 16%));
1925
+ --neutral-3: light-dark(hsl(48 8% 82%), hsl(24 5% 22%));
1926
+ --neutral-4: light-dark(hsl(48 7% 70%), hsl(24 5% 34%));
1927
+ --neutral-5: light-dark(hsl(48 6% 40%), hsl(48 5% 62%));
1928
+ --neutral-6: light-dark(hsl(48 7% 30%), hsl(48 6% 72%));
1929
+
1930
+ /* Core surfaces + ink */
1931
+ --foreground: light-dark(hsl(24 10% 10%), hsl(48 10% 90%));
1932
+ --card: light-dark(var(--neutral-0), hsl(24 8% 10%));
1933
+ --popover: light-dark(var(--neutral-0), hsl(24 8% 10%));
1934
+ --surface-public-data-panel: light-dark(hsl(44 16% 96%), hsl(36 9% 12%));
1935
+
1936
+ --primary: light-dark(hsl(24 10% 10%), hsl(48 10% 90%));
1937
+ --primary-foreground: light-dark(var(--neutral-0), hsl(24 10% 10%));
1938
+ /* AX.W44 — dark-arm lift. The stock shadcn dark `hsl(0 62.8% 30.6%)`
1939
+ (L≈30.6) was a saturated-PLATE value used AS INK over `--card` by the
1940
+ Alert destructive variant — 1.75:1 over card (illegible) / 1.91:1 vs the
1941
+ page. Lifted to `hsl(0 80% 60%)`: 4.60:1 as text over `--card` (clears the
1942
+ 4.5 body floor) AND 5.03:1 as a plate vs the dark page (clears the 3.0
1943
+ graphic floor). The light arm is UNCHANGED (already 4.70:1). Lockstep with
1944
+ the `.dark` fallback floor below (§2c). */
1945
+ --destructive: light-dark(hsl(0 72% 50%), hsl(0 80% 60%));
1946
+ --destructive-foreground: light-dark(var(--neutral-0), hsl(48 10% 90%));
1947
+ --ring: light-dark(hsl(24 10% 10%), hsl(48 10% 70%));
1948
+ --shadow: light-dark(hsl(24 10% 10%), hsl(0 0% 5%));
1949
+
1950
+ /* §4 native-control accent — the per-mode arms (dark reads the
1951
+ mid-tone ring for checkmark-glyph contrast headroom, NOT the
1952
+ near-white dark primary). Joins the light-dark() collapse; still
1953
+ re-specified as `accent-color` inside `.dark` per §2b. */
1954
+ --accent-color: light-dark(var(--primary), var(--ring));
1955
+
1956
+ /* Section palette — jewel tones brightened L for dark (oklch pairs;
1957
+ AV.W16 TW2 — hue preserved, L evened) */
1958
+ --section-color-0: light-dark(oklch(0.552 0.192 359.8), oklch(0.721 0.145 354.0));
1959
+ --section-color-1: light-dark(oklch(0.502 0.165 305.9), oklch(0.733 0.113 308.2));
1960
+ --section-color-2: light-dark(oklch(0.484 0.163 265.5), oklch(0.718 0.107 268.4));
1961
+ --section-color-3: light-dark(oklch(0.542 0.089 222.8), oklch(0.767 0.091 219.9));
1962
+ --section-color-4: light-dark(oklch(0.551 0.088 171.1), oklch(0.776 0.105 172.6));
1963
+ --section-color-5: light-dark(oklch(0.623 0.124 69.6), oklch(0.813 0.109 78.2));
1964
+ --section-color-6: light-dark(oklch(0.579 0.201 30.4), oklch(0.693 0.151 28.1));
1965
+ --section-color-7: light-dark(oklch(0.532 0.180 317.5), oklch(0.739 0.134 318.1));
1966
+ --section-color-8: light-dark(oklch(0.520 0.176 8.4), oklch(0.709 0.142 2.3));
1967
+ --section-color-9: light-dark(oklch(0.492 0.038 239.6), oklch(0.763 0.032 239.0));
1968
+ --section-color-10: light-dark(oklch(0.556 0.103 128.8), oklch(0.794 0.116 127.9));
1969
+ --section-color-11: light-dark(oklch(0.601 0.092 208.0), oklch(0.805 0.095 206.7));
1970
+ --section-color-12: light-dark(oklch(0.513 0.163 291.9), oklch(0.719 0.115 295.6));
1971
+
1972
+ --accent-pink: light-dark(oklch(0.613 0.197 353.8), oklch(0.683 0.131 354.7));
1973
+ --section-heading: light-dark(oklch(0.545 0.189 352.8), oklch(0.711 0.123 349.3));
1974
+ --accent-red: light-dark(oklch(0.574 0.216 27.5), oklch(0.644 0.165 22.9));
1975
+
1976
+ /* Viz basis (amber/green stay var()-aliases → auto-track) */
1977
+ --viz-fourier: light-dark(oklch(0.579 0.201 30.4), oklch(0.693 0.151 28.1));
1978
+ --viz-chebyshev: light-dark(oklch(0.484 0.163 265.5), oklch(0.718 0.107 268.4));
1979
+ --viz-legendre: light-dark(oklch(0.532 0.180 317.5), oklch(0.739 0.134 318.1));
1980
+
1981
+ /* WCAG companion chart labels */
1982
+ --chart-ping-label: light-dark(oklch(0.40 0.18 230), oklch(0.85 0.12 230));
1983
+ --chart-download-label: light-dark(oklch(0.40 0.20 22), oklch(0.85 0.14 22));
1984
+ --chart-upload-label: light-dark(oklch(0.40 0.16 55), oklch(0.85 0.12 55));
1985
+ --chart-jitter-label: light-dark(oklch(0.40 0.20 305), oklch(0.85 0.14 305));
1986
+
1987
+ /* Semantic UI accents */
1988
+ --tier-featured: light-dark(oklch(0.841 0.173 84.2), oklch(0.867 0.165 88.7));
1989
+ --tier-saved: light-dark(oklch(0.676 0.176 252.3), oklch(0.748 0.135 250.1));
1990
+ --like: light-dark(oklch(0.633 0.200 24.9), oklch(0.690 0.162 22.2));
1991
+ --success: light-dark(oklch(0.720 0.192 149.5), oklch(0.805 0.186 151.6));
1992
+ --warning: light-dark(oklch(0.770 0.165 70.6), oklch(0.809 0.150 76.6));
1993
+ --info: light-dark(oklch(0.626 0.186 259.6), oklch(0.713 0.138 259.5));
1994
+ --delete: light-dark(oklch(0.597 0.198 25.5), oklch(0.644 0.170 23.1));
1995
+
1996
+ /* Semantic foregrounds (warning-foreground is identical both modes
1997
+ → stays a single light-mode declaration, no light-dark() needed) */
1998
+ --success-foreground: light-dark(var(--neutral-0), hsl(48 10% 96%));
1999
+ --info-foreground: light-dark(var(--neutral-0), hsl(48 10% 96%));
2000
+
2001
+ /* Cartoon-shadow color primitives (§2d — plain colors, convert) */
2002
+ --shadow-cartoon-color: light-dark(rgb(0 0 0 / 0.12), rgb(255 255 255 / 0.07));
2003
+ --shadow-cartoon-color-soft: light-dark(rgb(0 0 0 / 0.06), rgb(255 255 255 / 0.03));
2004
+
2005
+ /* Gold family */
2006
+ --gold: light-dark(oklch(0.751 0.147 84.2), oklch(0.784 0.143 86.0));
2007
+ --gold-light: light-dark(oklch(0.890 0.183 96.1), oklch(0.898 0.182 97.4));
2008
+ --gold-dark: light-dark(oklch(0.599 0.132 64.2), oklch(0.678 0.150 63.8));
2009
+ }
2010
+ }
2011
+
2012
+ /* ═══════════════════════════════════════════════
2013
+ DARK MODE — warm dark, section palette brightened ~20% L
2014
+
2015
+ FALLBACK FLOOR (AQ.W2 §2c): this class block is the SOLE non-light-dark()
2016
+ dark path. On a `light-dark()`-supporting engine with `.dark` present,
2017
+ `.dark { color-scheme: dark }` (§1) makes every §-1b token resolve to its
2018
+ dark arm and these explicit dark values agree with it (kept as the floor
2019
+ for non-supporting engines). The pure `var()`-alias re-declarations
2020
+ (`--muted`/`--secondary`/`--accent`/`--border`/`--input`/… and the
2021
+ `--background`/`*-foreground` aliases) are DELETED — they auto-track the
2022
+ `--neutral-*` ladder + `--foreground` resolved above; only EXPLICIT-color
2023
+ dark values (and the non-color overrides §2d) stay.
2024
+ ═══════════════════════════════════════════════ */
2025
+ .dark {
2026
+ /* §1 — hard-pin native chrome to dark regardless of OS preference. §4 —
2027
+ re-specify the inherited `accent-color` so it re-resolves to the dark
2028
+ arm (§2b: an inherited built-in <color> set at `:root` would freeze to
2029
+ the light shade without this re-specification). */
2030
+ color-scheme: dark;
2031
+ accent-color: var(--accent-color);
2032
+
2033
+ /* Neutral scale mirrors light-mode pattern: step UP from the background's
2034
+ low L toward foreground. Same perceptual deltas as light. FALLBACK
2035
+ FLOOR — kept verbatim for non-light-dark() engines (§2c); on a
2036
+ supporting engine `color-scheme: dark` (above) drives the §-1b
2037
+ light-dark() arms and these agree. */
2038
+ --neutral-0: hsl(24 8% 6%); /* L 6 — page surface (dark) */
2039
+ --neutral-1: hsl(24 6% 11%); /* L 11 — soft field */
2040
+ --neutral-2: hsl(24 5% 16%); /* L 16 — chip / tab-inactive */
2041
+ --neutral-3: hsl(24 5% 22%); /* L 22 — hover / highlight */
2042
+ --neutral-4: hsl(24 5% 34%); /* L 34 — subtle border */
2043
+ --neutral-5: hsl(48 5% 62%); /* L 62 — muted text (WCAG AA: 7.39:1 vs L6 page / 6.58:1 vs L11 muted) */
2044
+ /* W7-α' (AK-FD1) — strong-muted text. Same principled ladder step as
2045
+ light, mirrored: 10 L LIGHTER than muted (62→72) toward --foreground
2046
+ (L90), the 1/3 point of the L62→L90 muted→primary gap. By-colour; hue
2047
+ 48, sat nudged 5→6. WCAG AA: 9.86:1 vs L6 page / 8.78:1 vs L11 muted
2048
+ (cf. neutral-5's 7.39:1). */
2049
+ --neutral-6: hsl(48 6% 72%); /* L 72 — strong-muted text (secondary register) */
2050
+
2051
+ /* --background / --card-foreground / --popover-foreground / --muted /
2052
+ --muted-foreground{,-strong} / --secondary{,-foreground} /
2053
+ --accent{,-foreground} / --border / --input are pure var()-aliases —
2054
+ DELETED here (AQ.W2 §2a): they auto-track the --neutral-* ladder +
2055
+ --foreground above. Re-declaring them would be redundant. */
2056
+ --foreground: hsl(48 10% 90%);
2057
+ --card: hsl(24 8% 10%);
2058
+ --popover: hsl(24 8% 10%);
2059
+
2060
+ /* R0G-5 (AO.W4) — dark mirror of the warm-neutral public-data panel.
2061
+ --card's dark hsl(24 8% 10%) nudged one warmth step toward amber
2062
+ (24→36) + +1 sat + +2 L so the dark dashboard panel lifts off the page. */
2063
+ --surface-public-data-panel: hsl(36 9% 12%);
2064
+
2065
+ --primary: hsl(48 10% 90%);
2066
+ --primary-foreground: hsl(24 10% 10%);
2067
+ /* AX.W44 — the `.dark` class fallback floor, in LOCKSTEP with the light-dark()
2068
+ enhancement arm above (§2c). Same lifted `hsl(0 80% 60%)` so a
2069
+ @supports-split engine and a class-toggle engine paint the SAME dark red
2070
+ (4.60:1 as ink over --card / 5.03:1 as a plate vs page). */
2071
+ --destructive: hsl(0 80% 60%);
2072
+ --destructive-foreground: hsl(48 10% 90%);
2073
+ --ring: hsl(48 10% 70%);
2074
+ --shadow: hsl(0 0% 5%);
2075
+
2076
+ /* Section palette — brightened for dark backgrounds (oklch; AV.W16 TW2 —
2077
+ mirrors the light-dark() dark halves above; hue preserved) */
2078
+ --section-color-0: oklch(0.721 0.145 354.0);
2079
+ --section-color-1: oklch(0.733 0.113 308.2);
2080
+ --section-color-2: oklch(0.718 0.107 268.4);
2081
+ --section-color-3: oklch(0.767 0.091 219.9);
2082
+ --section-color-4: oklch(0.776 0.105 172.6);
2083
+ --section-color-5: oklch(0.813 0.109 78.2);
2084
+ --section-color-6: oklch(0.693 0.151 28.1);
2085
+ --section-color-7: oklch(0.739 0.134 318.1);
2086
+ --section-color-8: oklch(0.709 0.142 2.3);
2087
+ --section-color-9: oklch(0.763 0.032 239.0);
2088
+ --section-color-10: oklch(0.794 0.116 127.9);
2089
+ --section-color-11: oklch(0.805 0.095 206.7);
2090
+ --section-color-12: oklch(0.719 0.115 295.6);
2091
+
2092
+ --accent-pink: oklch(0.683 0.131 354.7);
2093
+ --section-heading: oklch(0.711 0.123 349.3);
2094
+ --accent-red: oklch(0.644 0.165 22.9);
2095
+
2096
+ --viz-fourier: oklch(0.693 0.151 28.1);
2097
+ --viz-chebyshev: oklch(0.718 0.107 268.4);
2098
+ --viz-legendre: oklch(0.739 0.134 318.1);
2099
+ /* --viz-amber / --viz-green / --meter-track-stroke are pure var()-aliases
2100
+ (→ --section-color-5/-4 / --foreground) — DELETED (§2a); they auto-track
2101
+ their sources, which convert via §-1b. */
2102
+
2103
+ /* O.W6 Lane D — WCAG companion labels lift to L≈0.85 against the
2104
+ deep dark canvas; same two-token discipline as light mode (canvas
2105
+ hue preserved at `--chart-*`; only the label register shifts). */
2106
+ --chart-ping-label: oklch(0.85 0.12 230);
2107
+ --chart-download-label: oklch(0.85 0.14 22);
2108
+ --chart-upload-label: oklch(0.85 0.12 55);
2109
+ --chart-jitter-label: oklch(0.85 0.14 305);
2110
+
2111
+ --tier-featured: oklch(0.867 0.165 88.7);
2112
+ --tier-saved: oklch(0.748 0.135 250.1);
2113
+ --like: oklch(0.690 0.162 22.2);
2114
+ --success: oklch(0.805 0.186 151.6);
2115
+ --warning: oklch(0.809 0.150 76.6);
2116
+ --info: oklch(0.713 0.138 259.5);
2117
+ --delete: oklch(0.644 0.170 23.1);
2118
+
2119
+ /* Semantic foregrounds — off-white over dark plates. Mirrors the
2120
+ `--destructive-foreground` dark pattern (hsl 48 10% 90%). The warning
2121
+ glyph stays dark-on-amber; the dark amber rung is luminous enough that
2122
+ a darker glyph still reads (matches light-mode behaviour). */
2123
+ --success-foreground: hsl(48 10% 96%);
2124
+ --warning-foreground: hsl(24 10% 10%);
2125
+ --info-foreground: hsl(48 10% 96%);
2126
+
2127
+ --shadow-cartoon: 3px 3px 0px 0px color-mix(in srgb, var(--foreground) 6%, transparent);
2128
+ --shadow-cartoon-hover: 4px 4px 0px 0px color-mix(in srgb, var(--foreground) 8%, transparent);
2129
+ --shadow-soft: 0 4px 12px rgba(0, 0, 0, 0.3);
2130
+ --shadow-elevated: 0 8px 24px rgba(0, 0, 0, 0.35);
2131
+ --shadow-modal:
2132
+ 4px 4px 0px 0px color-mix(in srgb, var(--foreground) 6%, transparent),
2133
+ 0 24px 64px color-mix(in srgb, var(--foreground) 18%, transparent);
2134
+
2135
+ --shadow-cartoon-color: rgb(255 255 255 / 0.07);
2136
+ --shadow-cartoon-color-soft: rgb(255 255 255 / 0.03);
2137
+
2138
+ /* §5c CONSTELLATION — dark arm (AX.W17). The H.W4 dark-mode lift: the node
2139
+ tones step UP off the ink ground (the light arm's mid-grey would vanish on
2140
+ L6 black), the hairline goes warm off-white, the field carries a touch more
2141
+ presence to read at all. PLAIN-hsl literals, NEVER light-dark()/var(). */
2142
+ --constellation-node: hsl(42 9% 72%);
2143
+ --constellation-node-dim: hsl(42 8% 56%);
2144
+ --constellation-line: hsl(45 12% 86%);
2145
+ /* Anomaly — brightened for the deep canvas (same neutral-default register;
2146
+ the consumer still overrides to its brand). */
2147
+ --constellation-accent: hsl(20 70% 60%);
2148
+ /* The dark hairlines lift harder over the ink ground, so the edge multipliers
2149
+ run a touch lower than light to keep the field recessive. */
2150
+ --constellation-edge-alpha: 0.20;
2151
+ --constellation-edge-focus-alpha: 0.32;
2152
+ /* §7 — the ink ground needs MORE field presence than cream to read at all, so
2153
+ the dark alpha is modestly HIGHER than the light arm — but still BELOW the
2154
+ slides 1.0 max-legibility reference (the recessive midpoint, not 1.0). */
2155
+ --constellation-alpha: 0.88;
2156
+
2157
+ /* Glass opacity + grain for dark — five-tier ladder, alpha-monotonic.
2158
+ Each rung lifts ~0.08 from its light counterpart so the dark surfaces
2159
+ carry comparable read-weight against the deeper canvas. */
2160
+ --glass-opacity-wash: 0.38;
2161
+ --glass-opacity-quiet: 0.58;
2162
+ --glass-opacity-resting: 0.72;
2163
+ --glass-opacity-floating: 0.88;
2164
+ --glass-opacity-overlay: 0.96;
2165
+ --glass-opacity-chassis: 0.36;
2166
+ --glass-grain-opacity: 0.045;
2167
+ --glass-highlight: inset 0 0.5px 0 0 hsl(0 0% 100% / 0.08);
2168
+
2169
+ /* Hairline-accent dark mirror — see light counterpart at line ~404. */
2170
+ --hairline-catch-light: inset 0 0.5px 0 0 rgb(255 255 255 / 0.05);
2171
+ --hairline-under-shadow: 0 0.5px 0 0 rgb(0 0 0 / 0.06);
2172
+
2173
+ /* Specular + curvature — softened for dark backdrops. Disco-grain stays
2174
+ at the same opacity ceiling; the dark grain blend already lifts it. */
2175
+ --glass-specular: inset 0 1.5px 0 0 hsl(0 0% 100% / 0.30);
2176
+ /* AV.W15 M3 — the full-perimeter rim remaps to its dark weight inside
2177
+ `.dark` (mirroring the `--glass-specular` remap above). The lit token
2178
+ resolves to the `-dark` alpha on the deeper canvas. */
2179
+ --glass-edge-light: var(--glass-edge-light-dark);
2180
+
2181
+ /* AW.W23 — the chromatic-fringe + adaptive-tint dark arm. The fringe drops
2182
+ to a lower-luminance warm/cool pair on the deep canvas; `--glass-tint-
2183
+ source` auto-flips with `--card` (no override needed — the default already
2184
+ resolves the dark card). */
2185
+ --glass-fringe-warm: oklch(0.78 0.07 40 / 0.18);
2186
+ --glass-fringe-cool: oklch(0.74 0.06 230 / 0.14);
2187
+ /* AX.W52 D19 — the curvature sheen is genuinely SOFTER on the deep canvas (the
2188
+ prior dark arm re-declared the overlay BYTE-IDENTICAL to light, so it read
2189
+ HARDER over the dark substrate — an unsoftened-dark defect). The `:root`
2190
+ `--glass-curvature-overlay` already reads `var(--glass-curvature-intensity)`,
2191
+ so lowering just the intensity here re-resolves the SAME warm-cream recipe at
2192
+ a softer alpha — no byte-identical re-declaration. */
2193
+ --glass-curvature-intensity: 0.012;
2194
+
2195
+ /* Gold — boosted for dark (oklch; AV.W16 TW2) */
2196
+ --gold: oklch(0.784 0.143 86.0);
2197
+ --gold-light: oklch(0.898 0.182 97.4);
2198
+ --gold-dark: oklch(0.678 0.150 63.8);
2199
+ /* D6.b — the dark-arm gilt ink. `--gold-rim` auto-re-resolves (its color-mix
2200
+ reads the dark `--gold` above — no override). `--gold-ink` MUST lift L for
2201
+ the dark `--card` (hsl 24 8% 10%): a deep gold-dark ink would vanish into
2202
+ the near-black plate, so the dark ink is a BRIGHT gold —
2203
+ oklch(0.860 0.158 90) → |Lc| 77.1 on the dark `--card` (cream-law ②;
2204
+ verified by proof:gold-ink-contrast). */
2205
+ --gold-ink: oklch(0.860 0.158 90);
2206
+ }
2207
+
2208
+ /* ═══════════════════════════════════════════════
2209
+ §18 TYPED CSS PROPERTIES — Houdini @property registrations
2210
+ (AI.W4 grammar — adjacent to §2.C tokens above)
2211
+
2212
+ Three typed custom properties register with the CSS Houdini @property
2213
+ API so transitions interpolate VALUES (percentage / length) rather
2214
+ than the textual representation. Unregistered fallback is safe — the
2215
+ `initial-value` provides a sensible default for any consumer that
2216
+ imports this stylesheet on a browser that doesn't yet support the
2217
+ typed @property registration (the cross-fade collapses to the
2218
+ initial value, then re-paints at the new state).
2219
+
2220
+ Browser support: Chromium 85+, Safari 16.4+, Firefox 128+. The
2221
+ speedtest deployment target sits well within the floor.
2222
+
2223
+ • --progress-crescendo (percentage) — the leading-edge brightness
2224
+ ramp on `<Progress variant="gradient">` past 85% modelValue.
2225
+ `inherits: false` because the value is consumed at one
2226
+ declaration only (the gradient stop calculation); inheriting
2227
+ would let an unrelated ancestor accidentally drive a sibling
2228
+ progress bar.
2229
+
2230
+ • --phase-tint-amount (percentage) — the chassis depth-of-tint
2231
+ companion to `--phase-color`. `inherits: true` because the
2232
+ value cascades from the chassis root through every region
2233
+ wrapper that reads it for a sympathetic tint (matches the
2234
+ `--phase-color` inheritance pattern).
2235
+
2236
+ • --ripple-radius (length) — the `btn-audacious` press ripple
2237
+ radial. `inherits: false` because each button owns its own
2238
+ ripple state (a hovered parent must not push a ripple-radius
2239
+ onto a non-pressed child).
2240
+ ═══════════════════════════════════════════════ */
2241
+
2242
+ @property --progress-crescendo {
2243
+ syntax: "<percentage>";
2244
+ inherits: false;
2245
+ initial-value: 0%;
2246
+ }
2247
+
2248
+ @property --phase-tint-amount {
2249
+ syntax: "<percentage>";
2250
+ inherits: true;
2251
+ initial-value: 0%;
2252
+ }
2253
+
2254
+ @property --ripple-radius {
2255
+ syntax: "<length>";
2256
+ inherits: false;
2257
+ initial-value: 0px;
2258
+ }
2259
+
2260
+ /* ═══════════════════════════════════════════════
2261
+ §11b POINTER-ANCHORED MOVING SPECULAR (AV.W15)
2262
+
2263
+ The iOS-26 Liquid Glass "light travels around the material /
2264
+ illuminate under your fingertip" behaviour. A masked radial-gradient
2265
+ catch-light rides the surface at (--specular-x, --specular-y), driven
2266
+ by the consumer's pointer-tracked --mouse-x/--mouse-y write. Registered
2267
+ as typed custom properties so the position INTERPOLATES smoothly between
2268
+ pointer samples (a bare unregistered var() snaps; the @property reg lets
2269
+ the engine animate the percentage stop).
2270
+
2271
+ • --specular-x / --specular-y (percentage) — the catch-light centre.
2272
+ `inherits: false` because each glass surface owns its own pointer
2273
+ position (a hovered parent must not push its specular onto a child —
2274
+ the no-glass-on-glass discipline made mechanical).
2275
+
2276
+ • --specular-intensity (number) — the catch-light alpha multiplier.
2277
+ `inherits: false` for the same per-surface-ownership reason; rises on
2278
+ hover/active, settles to its rest floor on leave.
2279
+
2280
+ @property crosses Baseline 2024-07-09. The consumption (the masked
2281
+ radial + the reduced-motion static bracket + the var() fallback) lives
2282
+ in glass-specular-track.css; the registrations live here alongside the
2283
+ other three regs so the typed-property tier has one home.
2284
+ ═══════════════════════════════════════════════ */
2285
+
2286
+ @property --specular-x {
2287
+ syntax: "<percentage>";
2288
+ inherits: false;
2289
+ initial-value: 50%;
2290
+ }
2291
+
2292
+ @property --specular-y {
2293
+ syntax: "<percentage>";
2294
+ inherits: false;
2295
+ initial-value: 50%;
2296
+ }
2297
+
2298
+ @property --specular-intensity {
2299
+ syntax: "<number>";
2300
+ inherits: false;
2301
+ initial-value: 0;
2302
+ }
2303
+
2304
+ /* AX.W54 — the GLASS-FIRST single knob. `--glass-level` is the ONE clarity
2305
+ scalar threaded through BOTH glass ladders at their single sites (the
2306
+ `--glass-bg-*` opacity recipe + the `--glass-blur-*` radii). It is a typed,
2307
+ INHERITING <number> so a host setting it on any ancestor retunes every
2308
+ descendant glass surface (the @container-free cascade path) — and so it
2309
+ animates smoothly rather than snapping. The level acts as a clarity multiplier
2310
+ that PRESERVES level=1 (byte-identical to the hand-tuned ladder):
2311
+ • level 1 — the calibrated glass (today's surfaces, unchanged).
2312
+ • level 0 — the OPAQUE escape: solid `--card` + `blur(0)` through the SAME
2313
+ machinery (the `.glass-opaque` rung + the a11y brackets ride it).
2314
+ • level >1 — clearer (more transparent + more diffuse) in lockstep.
2315
+ Glass is the DEFAULT surface register for EVERY band (USER-DECIDED MAXIMAL);
2316
+ the opaque level-0 rung is the explicit opt-out. W55 carries the over-light
2317
+ legibility on the SEPARATE `--glass-tint-*` axis (the `--glass-backdrop` probe
2318
+ lifts the tint toward ink — level = opacity+blur, tint = darken-over-light, the
2319
+ two axes coordinate but never collapse: the a11y Clear↔Tinted escape rides
2320
+ level-up here AND tint-toward-ink under `prefers-contrast: more`). */
2321
+ @property --glass-level {
2322
+ syntax: "<number>";
2323
+ inherits: true;
2324
+ initial-value: 1;
2325
+ }
2326
+
2327
+ /* AX.W51 D18 — the master comfortable-sizing scalar registered typed. The
2328
+ `<number>` syntax guarantees `calc(<base> * var(--ui-scale))` is always valid (a
2329
+ malformed override CLAMPS to the initial value 1, not an invalidated calc — the
2330
+ fail-safe); `inherits: true` (unlike the per-element specular @property cohort)
2331
+ so a SCOPED override (a dense data-table region) cascades to every descendant
2332
+ control without a per-element set; the type makes it animatable (a density
2333
+ toggle COULD spring it). Mirrors the `--glass-level` registration above (the
2334
+ cascading-scalar idiom), distinct from the per-element specular @property cohort
2335
+ (those are `inherits: false`). */
2336
+ @property --ui-scale {
2337
+ syntax: "<number>";
2338
+ inherits: true;
2339
+ initial-value: 1;
2340
+ }
2341
+
2342
+ /* ── The specular-intensity magnitude cohort (AX.W09) ──────────────────────
2343
+ The catch-light alpha multiplier at each interaction state, as a SINGLE
2344
+ overridable token cohort (was three buried literals across glass.css —
2345
+ `0.35` rest floor / `0.6` hover / `0.85` active — and the `.dark` arm's
2346
+ `0.22`). `glass.css`'s `.glass-material::before` rest/hover/active rules READ
2347
+ these, so the subtle-vs-extreme magnitude is a knob a consumer (or the dark
2348
+ arm) retunes ON THE CASCADE — never a re-buried literal. Override on `:root`
2349
+ to retune library-wide; the cartoon-shadow override contract is the precedent.
2350
+
2351
+ Re-baselined AGAIN at AX.W52 D19 — with the central screen-disc replaced by a
2352
+ BOUNDED plus-lighter edge gleam (glass.css), the gleam is a whisper at every
2353
+ rung, so the cohort drops further (the W09 0.22/0.32 ceiling still lifted the
2354
+ whole-tile disc on hover):
2355
+ • rest 0 — static, unwired surfaces are CLEAN; the gleam wakes only on
2356
+ hover/active or on a pointer-wired surface. Aligns the painted
2357
+ rest with the @property `--specular-intensity { initial-value:
2358
+ 0 }` dormancy.
2359
+ • hover 0.10 — a whisper edge gleam, not a bloom.
2360
+ • active 0.16 — the press whisper. */
2361
+ :root {
2362
+ --glass-specular-intensity-rest: 0;
2363
+ --glass-specular-intensity-hover: 0.1;
2364
+ --glass-specular-intensity-active: 0.16;
2365
+ }
2366
+
2367
+ /* Over the deep canvas `plus-lighter` still lifts, so the dark arm runs a touch
2368
+ softer on the interaction rungs (rest already 0). */
2369
+ .dark {
2370
+ --glass-specular-intensity-hover: 0.08;
2371
+ --glass-specular-intensity-active: 0.12;
2372
+ }