@mkbabb/glass-ui 3.9.0 → 3.10.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 (121) hide show
  1. package/dist/{MetricBadge-lTc002u2.js → MetricBadge-BZVWIxNy.js} +6 -4
  2. package/dist/{Notification-BxK8LZMw.js → Notification-DLMhblXR.js} +2 -2
  3. package/dist/{Switch-LDKLmmgx.js → Switch-Ch-k3Rv-.js} +1 -1
  4. package/dist/animated-digit.js +1 -1
  5. package/dist/api/index.d.ts +1 -0
  6. package/dist/api.js +1 -1
  7. package/dist/aurora.js +236 -545
  8. package/dist/{badge-BvwvT50i.js → badge-CaoFJzRb.js} +1 -1
  9. package/dist/badge.js +1 -1
  10. package/dist/color.js +1 -1
  11. package/dist/{compile-DAjIoEy2.js → compile-BOXjLUxu.js} +23 -23
  12. package/dist/components/custom/aurora/composables/atoms.d.ts +21 -4
  13. package/dist/components/custom/aurora/composables/runtime.d.ts +0 -9
  14. package/dist/components/custom/aurora/composables/uniformBridge.d.ts +0 -11
  15. package/dist/components/custom/aurora/constants/renderMode.d.ts +0 -32
  16. package/dist/components/custom/aurora/constants/shaders/brush.glsl.d.ts +1 -1
  17. package/dist/components/custom/aurora/constants/shaders/mediums.glsl.d.ts +2 -2
  18. package/dist/components/custom/aurora/constants/shaders/tonemap.glsl.d.ts +1 -1
  19. package/dist/components/custom/aurora/index.d.ts +1 -1
  20. package/dist/components/custom/constellation/Constellation.vue.d.ts +73 -1
  21. package/dist/components/custom/constellation/constellationField.d.ts +255 -3
  22. package/dist/components/custom/constellation/index.d.ts +2 -2
  23. package/dist/components/custom/dock/DockTabButton.vue.d.ts +4 -3
  24. package/dist/components/custom/expandable-container/ExpandableContainer.vue.d.ts +29 -0
  25. package/dist/components/custom/fourier-field/FourierField.vue.d.ts +6 -0
  26. package/dist/components/custom/fourier-field/index.d.ts +25 -1
  27. package/dist/components/custom/fourier-field/math.d.ts +3 -8
  28. package/dist/components/custom/goo-blob/GooBlob.vue.d.ts +8 -10
  29. package/dist/components/custom/goo-blob/composables/useMetaballRenderer.d.ts +6 -14
  30. package/dist/components/custom/goo-blob/index.d.ts +1 -1
  31. package/dist/components/custom/goo-blob/types.d.ts +59 -25
  32. package/dist/components/custom/handmark/HandMark.vue.d.ts +29 -0
  33. package/dist/components/custom/handmark/brush.d.ts +219 -0
  34. package/dist/components/custom/handmark/freehand.d.ts +69 -0
  35. package/dist/components/custom/handmark/geometry.d.ts +32 -0
  36. package/dist/components/custom/handmark/index.d.ts +21 -0
  37. package/dist/components/custom/handmark/ink.d.ts +11 -0
  38. package/dist/components/custom/handmark/texture.d.ts +39 -0
  39. package/dist/components/custom/handmark/types.d.ts +53 -0
  40. package/dist/components/custom/handmark/useHandMark.d.ts +66 -0
  41. package/dist/components/custom/metric-badge/MetricBadge.vue.d.ts +8 -0
  42. package/dist/components/ui/slider/index.d.ts +20 -16
  43. package/dist/composables/color/index.d.ts +12 -7
  44. package/dist/composables/glass/webgl/shaders/procedural-color.glsl.d.ts +1 -6
  45. package/dist/composables/motion/core/index.d.ts +1 -0
  46. package/dist/composables/motion/useRouteTransition.d.ts +42 -0
  47. package/dist/constellation.js +212 -72
  48. package/dist/controls.js +1 -1
  49. package/dist/dark.js +1 -1
  50. package/dist/dock.js +1 -0
  51. package/dist/expandable-container.js +61 -38
  52. package/dist/forms.js +2 -2
  53. package/dist/fourier-field.js +89 -97
  54. package/dist/fourier-math.d.ts +1 -0
  55. package/dist/fourier-math.js +2 -0
  56. package/dist/glass-panel.js +1 -1
  57. package/dist/glass-ui.css +1 -1
  58. package/dist/glass-ui.js +81 -80
  59. package/dist/goo-blob.js +299 -284
  60. package/dist/handmark.d.ts +1 -0
  61. package/dist/handmark.js +609 -0
  62. package/dist/icon-tooltip.js +1 -1
  63. package/dist/index.d.ts +1 -0
  64. package/dist/keyboard.js +1 -1
  65. package/dist/labeled-field.js +4 -4
  66. package/dist/math-DTpRVC0e.js +27 -0
  67. package/dist/metric-badge.js +1 -1
  68. package/dist/metric-stack.js +1 -1
  69. package/dist/motion-core.js +70 -69
  70. package/dist/motion.js +2 -2
  71. package/dist/notification.js +1 -1
  72. package/dist/search.js +2 -2
  73. package/dist/{slider-DC2mwuoe.js → slider-CyzcxBoh.js} +6 -6
  74. package/dist/slider.js +1 -1
  75. package/dist/styles/animations.css +74 -18
  76. package/dist/styles/cards.css +2 -2
  77. package/dist/styles/components.css +1 -1
  78. package/dist/styles/dock/layer-group.css +16 -4
  79. package/dist/styles/dock-controls.css +39 -23
  80. package/dist/styles/drawer.css +12 -4
  81. package/dist/styles/glass.css +145 -9
  82. package/dist/styles/paper.css +68 -2
  83. package/dist/styles/theme.css +7 -0
  84. package/dist/styles/tokens.css +101 -15
  85. package/dist/styles/transitions.css +20 -2
  86. package/dist/styles/utilities.css +130 -40
  87. package/dist/styles/view-transition.css +51 -0
  88. package/dist/switch.js +1 -1
  89. package/dist/{useAnimatedNumber-C5I3wWd8.js → useAnimatedNumber-Bcpy5Etf.js} +1 -1
  90. package/dist/{useGlassRenderer-5I0YkI4t.js → useGlassRenderer-D7YGOYiM.js} +1 -1
  91. package/dist/useViewTransition-DaN5Zrox.js +21 -0
  92. package/dist/vReveal-zUIQk-rJ.js +53 -0
  93. package/dist/watercolor-dot.js +1 -1
  94. package/package.json +51 -9
  95. package/src/styles/animations.css +74 -18
  96. package/src/styles/cards.css +2 -2
  97. package/src/styles/dock/layer-group.css +16 -4
  98. package/src/styles/dock-controls.css +39 -23
  99. package/src/styles/drawer.css +12 -4
  100. package/src/styles/glass.css +145 -9
  101. package/src/styles/paper.css +68 -2
  102. package/src/styles/theme.css +7 -0
  103. package/src/styles/tokens.css +101 -15
  104. package/src/styles/transitions.css +20 -2
  105. package/src/styles/utilities.css +130 -40
  106. package/src/styles/view-transition.css +51 -0
  107. package/dist/components/custom/aurora/composables/gpuRuntime.d.ts +0 -16
  108. package/dist/components/custom/aurora/constants/shaders/aurora.wgsl.d.ts +0 -7
  109. package/dist/composables/glass/createGPUCanvas.d.ts +0 -40
  110. package/dist/vReveal-CqaCz_mC.js +0 -44
  111. /package/dist/{IconTooltip-yefK7cJc.js → IconTooltip-Cus0fvK0.js} +0 -0
  112. /package/dist/{Input-DWQQIOC4.js → Input-xuDwALQR.js} +0 -0
  113. /package/dist/{color-8jBjWiJ6.js → color-C5ls_QF7.js} +0 -0
  114. /package/dist/{constants-CS75AWHG.js → constants-yKeKnE84.js} +0 -0
  115. /package/dist/{presets-BKVyQfbt.js → presets-Dkdv55Gt.js} +0 -0
  116. /package/dist/{prng-wHuHlNHE.js → prng-D27-B3tK.js} +0 -0
  117. /package/dist/{useGlobalDark-yhL4Wp-s.js → useGlobalDark-d6waty-X.js} +0 -0
  118. /package/dist/{useIntersectionPause-Cc-H9ees.js → useIntersectionPause-Cck5Uxk8.js} +0 -0
  119. /package/dist/{useKeyboardShortcuts-Be3w_Rwk.js → useKeyboardShortcuts-CyIYrndH.js} +0 -0
  120. /package/dist/{useScrollProgress-CAH8Ofwt.js → useScrollProgress-CffFGLa5.js} +0 -0
  121. /package/dist/{useTextHighlight-DON84L8w.js → useTextHighlight-CWEi25hf.js} +0 -0
@@ -141,33 +141,89 @@
141
141
  100% { background-position: -200% 0; }
142
142
  }
143
143
 
144
- /* ── Sparkle sweep: four-point star traverses a surface diagonally ──
144
+ /* ── Crayon-wipe: the scroll-scrubbed draw-on (D6.c M1) ──────────────────────
145
145
  *
146
- * Reserved for the chassis primary-action hover (dock-tab-button[data-tier="primary"])
147
- * and the `Complete!` shimmer cycle. Single keyframe, ~600ms per pass; the
148
- * consumer rule sets the duration and easing. Reduced-motion guard at the
149
- * consumer site (per surface — not all sparkle uses share the same gate).
146
+ * The bidirectional, scroll-scrubbed DRAW-ON of a hand-mark (the crayon/pen
147
+ * underline, a drawn rule). The wipe is a `clip-path: inset()` of the LEFT edge
148
+ * only — the mark draws from inset(0 100% 0 0) (fully clipped from the right, so
149
+ * NOTHING shows) to inset(0) (fully revealed) — so the underline DRAWS as the
150
+ * figure scrolls into view and UN-DRAWS as it scrolls back up. `clip-path` is the
151
+ * compositor-eligible, FILTER-STABLE wipe (the medium's grain `feTurbulence`
152
+ * rasters ONCE and is untouched by the clip — the cardinal "no per-frame raster"
153
+ * discipline ds2-motion-field M1 names): clip-path animates on the compositor and
154
+ * never re-runs the static filter, unlike a `stroke-dashoffset` draw under the
155
+ * grain filter (which re-rasters per frame). The grammar mirrors scroll-driven.css
156
+ * (the `view()`/`scroll()` timeline home): the keyframe lives here (the shared
157
+ * @keyframes register); a consumer binds it to a NAMED `view-timeline` so the
158
+ * draw-on SCRUBS bidirectionally with scroll (forward → draws, back → un-draws),
159
+ * never a one-shot. The `.crayon-wipe` utility below is the binding.
160
+ *
161
+ * The keyframe itself is timeline-agnostic — it is equally a load-clock draw-on
162
+ * (bound to a keyframes.js Sequence) or a scroll scrub (bound to a view-timeline).
163
+ * The DEFAULT consumer is the scroll scrub (the figure-item underline drawing into
164
+ * the lower third), hence the utility's `view()` binding.
165
+ *
166
+ * Reduced-motion: the utility (NOT the keyframe) gates — under PRM the mark is SET
167
+ * to inset(0) (fully drawn, never animated), the correct degrade for a permanent
168
+ * structural draw (information parity: the underline is present, just not drawn).
150
169
  */
151
- @keyframes sparkle-sweep {
152
- 0% {
153
- transform: translate(-30%, -30%) scale(0.5);
154
- opacity: 0;
155
- }
156
- 25% {
157
- opacity: 1;
170
+ @keyframes crayon-wipe {
171
+ from {
172
+ /* clipped from the RIGHT by 100% → the mark is fully hidden (nothing
173
+ drawn yet); the left edge is the pen-tip about to move right. */
174
+ clip-path: inset(0 100% 0 0);
158
175
  }
159
- 100% {
160
- transform: translate(180%, 180%) scale(1.2);
161
- opacity: 0;
176
+ to {
177
+ /* fully revealed — the mark is wholly drawn. */
178
+ clip-path: inset(0 0 0 0);
162
179
  }
163
180
  }
164
181
 
165
- @media (prefers-reduced-motion: reduce) {
166
- @keyframes sparkle-sweep {
167
- 0%, 100% { opacity: 0; transform: none; }
182
+ /* ── .crayon-wipe — bind the wipe to a scroll view-timeline (D6.c M1) ──────────
183
+ * The scroll-scrubbed draw-on utility. A consumer puts `.crayon-wipe` on a
184
+ * hand-mark surface (the underline span, a drawn rule) and the mark draws/un-draws
185
+ * as it scrolls through its OWN `view()` timeline — a bidirectional scrub, free,
186
+ * off the main thread. The animation is `paused` with `animation-timeline: view()`
187
+ * so the scroll position IS the playhead (the native scroll-driven model — the
188
+ * timeline drives the frame, time does not). The named timeline is overridable
189
+ * (`--crayon-wipe-timeline`, default the element's own `view()`) so a consumer can
190
+ * scrub off a SHARED beat timeline (the §--beat-tl the atlas scroll-driven.css
191
+ * names) instead of the per-element view — that is the "named view-timeline so a
192
+ * consumer scrubs a draw-on bidirectionally" contract.
193
+ *
194
+ * @supports-gated + PRM-OUTER-gated, mirroring scroll-driven.css exactly: the
195
+ * whole binding sits under `prefers-reduced-motion: no-preference`, so under PRM
196
+ * the animation never attaches and the mark renders in its terminal `inset(0)`
197
+ * (fully-drawn) state — the static-draw degrade. The draw range is `entry`-keyed
198
+ * (the mark draws as it ENTERS the lower viewport), tunable via
199
+ * `--crayon-wipe-range-{start,end}`. */
200
+ @media (prefers-reduced-motion: no-preference) {
201
+ @supports ((animation-timeline: view()) and (animation-range: entry)) {
202
+ .crayon-wipe {
203
+ animation: crayon-wipe auto linear both;
204
+ animation-timeline: var(--crayon-wipe-timeline, view(block));
205
+ animation-range: var(--crayon-wipe-range-start, entry 0%)
206
+ var(--crayon-wipe-range-end, cover 35%);
207
+ }
168
208
  }
169
209
  }
170
210
 
211
+ /* ── Sparkle sweep: TOMBSTONE (D6.b — the disco-star excision) ──
212
+ *
213
+ * @deprecated 3.10 — DELETE in 3.11. The four-point `✦` star sweep was the one
214
+ * garish gesture in the library; the user named it for removal ("remove the disco
215
+ * star from all glass-ui elements"). Every in-tree consumer is re-homed: the
216
+ * `btn-audacious` hover affirmation now rides the specular catch-light alone; the
217
+ * gold-CTA rides the `btn-gold-bg-sweep` metal-catch; the chassis `complete`
218
+ * celebration re-homes to the `.text-gilt` one-shot (utilities.css). The keyframe
219
+ * body is EMPTIED rather than deleted for ONE minor so any out-of-tree consumer
220
+ * (the slides decks) fails SOFT — a no-op animation, not an unknown-animation
221
+ * error. The `--duration-sparkle` token is `@deprecated` alongside (tokens.css).
222
+ * Full delete (this block + the token + the theme bridge) lands in 3.11. */
223
+ @keyframes sparkle-sweep {
224
+ /* intentionally empty — tombstoned, see deprecation note above */
225
+ }
226
+
171
227
  /* ── Pulse dot bounce: dots variant of <Pulse> ──
172
228
  *
173
229
  * Three-dot loading indicator. Opacity 0.35 ↔ 1, scale 0.8 ↔ 1.2. Consumer
@@ -38,8 +38,8 @@
38
38
  mints the stacking context once. Longhand maps `translate(x, y)` 1:1. */
39
39
  translate: 0;
40
40
  transition:
41
- translate var(--duration-normal) var(--spring-bouncy),
42
- box-shadow var(--duration-normal) var(--ease-apple);
41
+ translate var(--duration-normal) var(--spring-smooth),
42
+ box-shadow var(--duration-normal) var(--ease-standard);
43
43
 
44
44
  &:hover:not(:disabled) {
45
45
  translate: var(--lift-sm) var(--lift-sm);
@@ -183,7 +183,19 @@
183
183
  it at the top tab and it NEVER reached the active tab below (the mis-alignment
184
184
  the user screenshotted). The default rule below travels Y; the `.vertical`
185
185
  group's ROW rail overrides back to X. The active-tab plate reads the tunable
186
- `--dock-layer-rail-active` rung. Both morph on `--dock-motion-resize`. */
186
+ `--dock-layer-rail-active` rung.
187
+
188
+ AY.W-DOCK2 (D5 one-clock) — the rail indicator travel moves OFF the fixed-
189
+ linear `--dock-motion-resize` SECOND clock (DK7 killed exactly this pattern
190
+ for the leaving-pane opacity but left it alive on the rail; H-dock §D7 L2) onto
191
+ the `--spring-snappy` register. DECISION: a rail tab switch is a DISCRETE
192
+ selection (the user picks a pane), NOT the box collapse↔expand morph — so it
193
+ does NOT fold onto `--dock-morph-t` (the morph scalar). It uses the iOS
194
+ segmented-control register (`--spring-snappy`, the SAME curve `SegmentedTabs`'
195
+ indicator rides — the confirmed discrete-selection register), so the rail
196
+ indicator glides between tabs on the house spring, never a fixed-linear timer.
197
+ The binding outcome: NO `--dock-motion-resize` survives on the rail (recorded
198
+ in W-DOCK2-DELTA.md). */
187
199
  .dock-layer-rail .dock-layer-tab-indicator {
188
200
  position: absolute;
189
201
  left: 0;
@@ -197,9 +209,9 @@
197
209
  color-mix(in srgb, var(--primary) 15%, transparent)
198
210
  );
199
211
  transition:
200
- width var(--dock-motion-resize),
201
- height var(--dock-motion-resize),
202
- transform var(--dock-motion-resize);
212
+ width var(--duration-fast) var(--spring-snappy),
213
+ height var(--duration-fast) var(--spring-snappy),
214
+ transform var(--duration-fast) var(--spring-snappy);
203
215
  pointer-events: none;
204
216
  }
205
217
 
@@ -24,23 +24,21 @@
24
24
  toward `--scale-press-dock` settles on a NO-OVERSHOOT spring (Apple,
25
25
  "Designing Fluid Interfaces": default 100% damping — no bounce — for a tap;
26
26
  reserve bounce for surfaces driven by gesture MOMENTUM). The dock controls
27
- are tap surfaces (no momentum), so the press re-binds `--dock-press-spring`
28
- from the bouncy preset (`--spring-bouncy`, ~+20% overshoot, defined in
29
- tokens.css §-2) to `--spring-smooth` — the critically-damped (ζ≈1, ~0.4%
30
- peak) iOS tap curve. Token-routed (an existing spring preset, never a raw
31
- cubic-bezier); the `--duration-fast` prefix is kept so the squish-back stays
32
- quick. Every dock-control `scale var(--dock-press-spring)` press transition
33
- (and the select chevron's `rotate`) inherits this one binding. A future
34
- momentum-gated surface (none today) would override this back to a bouncy
35
- preset on its own scope. The `:where()` group keeps zero specificity so a
36
- consumer override of `--dock-press-spring` still wins. */
27
+ are tap surfaces (no momentum).
28
+
29
+ AY.W-DOCK2 (D6) — the per-surface re-point of `--dock-press-spring` to
30
+ `--spring-smooth` is DELETED here: the ROOT token (`tokens.css`) IS the §6
31
+ doctrine register now (`--spring-smooth`), so the re-point was a dead
32
+ byte-identical redeclaration — the exact per-surface fork the §6 doctrine
33
+ claims to have killed. The dock controls inherit the correct smooth
34
+ transform-press register straight from the ROOT token; this `:where()` group
35
+ keeps ONLY the small-tile specular sizing below. */
37
36
  :where(
38
37
  .dock-icon-button,
39
38
  .dock-tab-button,
40
39
  .dock-select-trigger,
41
40
  .dock-dropdown-trigger
42
41
  ) {
43
- --dock-press-spring: var(--duration-fast) var(--spring-smooth);
44
42
  /* AX.W45-TUNE C4/C5 — the small-tile specular sizing. The dock controls are
45
43
  ~40px tiles, a fraction of a card's plate, so the global 36% specular
46
44
  radius blooms a tile-filling disc (the I.W6 finding). The family re-points
@@ -329,21 +327,28 @@
329
327
  /* Tier — primary ────────────────────────────────────────────
330
328
  *
331
329
  * Per W2.7 §9, §12, §13, §17. The primary tier composes three at-rest
332
- * marks (top-highlight, bottom under-shadow, baseline glass) with three
333
- * disco accents on hover (specular swap, sparkle sweep, disco-grain).
330
+ * marks (top-highlight, bottom under-shadow, baseline glass) with two
331
+ * disco accents on hover (specular swap, disco-grain).
332
+ *
333
+ * D6.b — the disco-STAR is GONE (the sparkle-sweep sweep is excised from the
334
+ * underlying `btn-audacious`, utilities.css). The primary dock button's gold
335
+ * identity — the "golden dock button" the user likes — is RE-EXPRESSED sans
336
+ * star: a tooled `--gold-rim` gilt edge (the brushed-brass-under-glass read)
337
+ * plus the surviving specular catch-light + disco-grain on hover. Three quiet
338
+ * golds replacing one loud star.
334
339
  *
335
340
  * Reservation: disco fires on hover only. At rest the button reads as
336
- * a Vignelli-restrained composed glass surface. The `--phase-color`
337
- * cascade (provided by `<InstrumentChassis>` via `data-phase`) tints
338
- * the disco-grain radial only on hover; idle is neutral.
341
+ * a Vignelli-restrained composed glass surface with a hairline gilt edge.
342
+ * The `--phase-color` cascade (provided by `<InstrumentChassis>` via
343
+ * `data-phase`) tints the disco-grain radial only on hover; idle is neutral.
339
344
  *
340
- * K.W6 Lane B — the canonical disco-grain + sparkle-sweep + specular
341
- * recipe lifted to `@utility btn-audacious` (utilities.css). The Vue
342
- * <DockTabButton> attaches `btn-audacious` automatically when the
343
- * consumer passes `data-tier="primary"`. The dock-local rules below
344
- * are STRICTLY the dock extension: structural shell (padding, base
345
- * surface tint, min-height) + phase-tint extensions (hover radial
346
- * uses `--phase-color`, plus the `[data-phase]` `::before` halo).
345
+ * K.W6 Lane B — the canonical disco-grain + specular recipe lifted to
346
+ * `@utility btn-audacious` (utilities.css). The Vue <DockTabButton> attaches
347
+ * `btn-audacious` automatically when the consumer passes `data-tier="primary"`.
348
+ * The dock-local rules below are STRICTLY the dock extension: structural shell
349
+ * (padding, base surface tint, min-height) + the gilt-edge gold identity +
350
+ * phase-tint extensions (hover radial uses `--phase-color`, plus the
351
+ * `[data-phase]` `::before` halo).
347
352
  */
348
353
  .dock-tab-button[data-tier="primary"] {
349
354
  --dock-tab-min-height: 4rem;
@@ -351,6 +356,17 @@
351
356
  background: color-mix(in srgb, var(--card) 60%, transparent);
352
357
  color: var(--foreground);
353
358
 
359
+ /* D6.b — the gilt edge: a 1px inner `--gold-rim` ring + a faint gold
360
+ top-catch, the tooled-brass identity that replaces the excised star.
361
+ A box-shadow (no layout, no paint cost), composing UNDER the hover
362
+ specular/grain. The gold reads as a bevel light at the edge, never a
363
+ fill — the surface still reads as composed glass. Idiom mirrors the
364
+ `.glass-gilt` modifier (glass.css region) at the dock-button scale. */
365
+ box-shadow:
366
+ inset 0 0 0 1px var(--gold-rim),
367
+ inset 0 0.5px 0 0
368
+ color-mix(in oklab, var(--gold-light) 30%, transparent);
369
+
354
370
  /* Phase-tint hover override — same disco-grain composition as
355
371
  * `btn-audacious`, but the radial uses `--phase-color` (instrument-
356
372
  * cluster axis) instead of the canonical `--primary`. The `--paper-
@@ -42,12 +42,20 @@
42
42
  height: auto;
43
43
  max-height: 97vh;
44
44
  margin-top: 6rem;
45
- border: 1px solid var(--border);
45
+ /* The overlay-band glass surface — the heaviest rung (the Drawer is a
46
+ modal-band sheet, z-index: --z-modal). The oklab tint composite reads the
47
+ W55 bright bucket (a live-behind sheet over a bright page is the canonical
48
+ over-light case); the --glass-level knob flattens it to solid --card +
49
+ blur(0) with the rest of the band; the --glass-shadow-overlay ladder rung
50
+ carries the rim + the --shadow-2xl lift through the ladder. */
51
+ background: color-mix(in oklab, var(--glass-bg-overlay), var(--glass-tint-source) var(--glass-tint-strength));
52
+ backdrop-filter: var(--glass-blur-overlay);
53
+ -webkit-backdrop-filter: var(--glass-blur-overlay);
54
+ border: 1px solid var(--glass-border-overlay);
46
55
  border-bottom: 0;
47
56
  border-start-start-radius: var(--radius-panel);
48
57
  border-start-end-radius: var(--radius-panel);
49
- background-color: var(--background);
50
- box-shadow: var(--shadow-2xl);
58
+ box-shadow: var(--glass-material-rim), var(--glass-shadow-overlay);
51
59
  }
52
60
 
53
61
  /*
@@ -74,7 +82,7 @@
74
82
  margin-top: 0;
75
83
  margin-bottom: 6rem;
76
84
  border-top: 0;
77
- border-bottom: 1px solid var(--border);
85
+ border-bottom: 1px solid var(--glass-border-overlay);
78
86
  border-radius: 0 0 var(--radius-panel) var(--radius-panel);
79
87
  }
80
88
 
@@ -46,8 +46,15 @@
46
46
 
47
47
  The `.glass-dock` SHELL is OUT of this group BY DESIGN — it hand-rolls a
48
48
  parallel surface (its own `--glass-*-dock` family + a local rim + an
49
- instrument-strip `::before`) the dock arm owns; the dock's catch-light
50
- lives on its CONTROL (`.dock-icon-button`, listed below), not the shell.
49
+ instrument-strip `::before`) the dock arm owns. The COHESION CONSEQUENCE is
50
+ DELIBERATE: the dock shell carries NO shared edge-gleam / moving-specular
51
+ `::before` (its only `::before` is the `variant-instrument-strip` engraved
52
+ bezel, dock/shell.css — a different effect). The dock's catch-light lives on
53
+ its CONTROLS (`.dock-icon-button` et al., listed below + in the opt-in
54
+ transition block), which ARE in the material group — NOT on the shell plate.
55
+ Adding a shell gleam would re-introduce glass-on-glass over the controls, so
56
+ the shell-without-gleam is a NAMED EXEMPTION, gated as `dock-shell-exempt` in
57
+ proof:glass-cohesion (the only exemption beside the legibility allowlist).
51
58
 
52
59
  The selector GROUP is the single source. Adding a surface to the band
53
60
  material is one comma-entry here, not a new opt-in class string. */
@@ -146,6 +153,37 @@
146
153
  a plain low-alpha warm overlay (the gleam composes normally — no blend, no
147
154
  blowout), the no-workaround floor; NOT a `screen` fallback. */
148
155
  mix-blend-mode: plus-lighter;
156
+ /* The moving-specular TRANSITION is NOT declared here. An idle/unwired
157
+ `::before` is STATIC (no interpolation track), so a bare glass rung / an
158
+ un-armed Card / a glass Button at rest attaches ZERO animation tracks (the
159
+ keyframes.js runtime counts 0 idle tracks, not 19). The transition rides
160
+ ONLY the WIRED + interactive surfaces — the opt-in scoped block below. */
161
+ }
162
+
163
+ /* AY.W-GLASS — the moving-specular transition is OPT-IN (the Card wire-or-omit
164
+ pattern, generalized). It attaches ONLY to a WIRED surface
165
+ (`.glass-specular-track` — Card emits it conditionally, the dock controls + the
166
+ Slider thumb compose it) OR to any glass surface under `:hover`/`:active` (the
167
+ pointer is present, so the interpolation is legitimately live). An idle/unwired
168
+ `.glass-floating` / `.glass-wash` / bare-rung `::before` carries NO transition
169
+ and thus NO keyframes.js track. The §6 register is PRESERVED (still
170
+ `--ease-standard` on the position-tracked + surface-opacity legs — H-motion §6
171
+ "position-tracked → --ease-standard"; only the SELECTOR scope changes). The dock
172
+ controls keep the always-on transition (they are interactive controls; their rest
173
+ intensity is 0 so they paint nothing at rest, but a control legitimately carries
174
+ the transition). */
175
+ .glass-specular-track::before,
176
+ .glass-material:hover::before, .glass-material:active::before,
177
+ .glass-wash:hover::before, .glass-wash:active::before,
178
+ .glass-quiet:hover::before, .glass-quiet:active::before,
179
+ .glass-resting:hover::before, .glass-resting:active::before,
180
+ .glass-floating:hover::before, .glass-floating:active::before,
181
+ .glass-overlay:hover::before, .glass-overlay:active::before,
182
+ .glass-card:hover::before, .glass-card:active::before,
183
+ .dock-icon-button::before,
184
+ .dock-tab-button::before,
185
+ .dock-select-trigger::before,
186
+ .dock-dropdown-trigger::before {
149
187
  /* Smooth the position + intensity between pointer samples (the typed
150
188
  @property regs make the percentage stops interpolable). */
151
189
  transition:
@@ -222,6 +260,97 @@
222
260
  --specular-intensity: var(--glass-specular-intensity-active, 0.16);
223
261
  }
224
262
 
263
+ /* ── LIQUID by tier — the moving-specular hover catch-light is the
264
+ FLOATING / OVERLAY tier's PROPERTY, not a per-surface wire (D6.c M10) ──────
265
+ The shared `:hover::before` rule above already lifts `--specular-intensity`
266
+ to the hover rung on EVERY material surface, so the catch-light wakes on
267
+ hover library-wide. What this block promotes is the FLOATING / OVERLAY
268
+ tier's identity: a navigation/overlay-band surface (a Dialog, a Sheet, a
269
+ Popover, the dock crest's `.glass-floating`) reads as LIQUID OUT OF THE BOX
270
+ — the catch-light is a tier guarantee, documented here so a consumer reaches
271
+ for `variant="floating"` and inherits the lens without a `useSpecularTracking`
272
+ wire. The pointer-anchored TRACKING still rides `useSpecularTracking` (a
273
+ host can sharpen the lens to the cursor); un-wired, the catch-light sits at
274
+ the centred `50%` fallback and breathes on hover — felt, not garish.
275
+
276
+ The magnitude rides the SAME `--glass-specular-intensity-hover` cohort the
277
+ aurora-calibrated π-ledger judges (tokens.css §specular-intensity), so the
278
+ liquid tier never out-shouts a number — it is the SAME gleam, named as a
279
+ tier default. The OPT-OUT survives: `.glass-no-liquid` on a floating/overlay
280
+ surface pins the rest rung (a flat tier for dense data, the deft escape). */
281
+ .glass-floating:not(.glass-no-liquid),
282
+ .glass-overlay:not(.glass-no-liquid) {
283
+ --glass-liquid: 1; /* the tier carries the liquid identity (informational marker) */
284
+ }
285
+ .glass-floating.glass-no-liquid:hover::before,
286
+ .glass-overlay.glass-no-liquid:hover::before,
287
+ .glass-floating.glass-no-liquid:active::before,
288
+ .glass-overlay.glass-no-liquid:active::before {
289
+ /* The deft escape — a dense-data floating surface that wants a FLAT tier
290
+ pins the catch-light at the clean rest rung even on hover/active. */
291
+ --specular-intensity: var(--glass-specular-intensity-rest, 0);
292
+ }
293
+ @media (prefers-reduced-motion: reduce) {
294
+ /* PRM fence — the liquid tier degrades to the static centred gleam. The
295
+ `useSpecularTracking` write is already PRM-gated (it skips the pointer
296
+ write under reduce); this pins the CSS half (`--specular-x/y: 50%`
297
+ centred) AND kills the position-track `transition` so an un-wired liquid
298
+ tile does NOT animate a travelling catch-light on hover under PRM. The
299
+ gleam may still LIFT on hover (a discrete opacity step, no interpolation
300
+ track) — the correct PRM degrade: no MOTION, the surface still responds.
301
+ This mirrors the shared moving-specular PRM bracket (one static-pin
302
+ convention across the specular cohort). */
303
+ .glass-floating::before,
304
+ .glass-overlay::before {
305
+ --specular-x: 50%;
306
+ --specular-y: 50%;
307
+ transition: none;
308
+ }
309
+ }
310
+
311
+ /* ── The GILT EDGE — `.glass-gilt`, the gilt-tooled glass modifier (D6.c M3/M12) ──
312
+ A fine book's cover is gold-tooled at the EDGE — never a gold fill. The
313
+ modifier composes on ANY tier (the tier is the FROST; the gilt is the
314
+ hairline gold catch on its rim) so a featured card OR the dock crest wears
315
+ the same recipe over its own backdrop. Two box-shadow layers ride INSIDE
316
+ the tier (they do not perturb the tier's own elevation stack — they are
317
+ additive inset rings):
318
+ (1) a 1px inset ring on `--gold-rim` — the tooled gold edge (D6.b lands
319
+ `--gold-rim` = `--gold` at ~40%α over the surface; referenced by name,
320
+ so the gilt resolves the moment the token cohort is minted), and
321
+ (2) a 0.5px gold top-catch highlight — the bevel light reading off the
322
+ upper edge, `--gold-light` at low α so it is a catch, not a band.
323
+ The surface still reads as cream-glass; the gold is the bevel light, the
324
+ SCARCE flourish (the consumption budget — at most one gilt surface per
325
+ route — is the atlas's D7 rule, not enforced here). */
326
+ .glass-gilt {
327
+ box-shadow:
328
+ inset 0 0 0 1px var(--gold-rim, color-mix(in oklab, var(--gold) 40%, transparent)),
329
+ inset 0 0.5px 0 0 color-mix(in oklab, var(--gold-light) 35%, transparent),
330
+ var(--glass-material-rim),
331
+ var(--glass-shadow-floating);
332
+ }
333
+
334
+ /* The liquid-metal CATCH-LIGHT (D6.c M12) — `.glass-gilt` overrides the
335
+ moving-specular `::before` sweep COLOR from warm-cream to GOLD, so the light
336
+ travelling across the gilt edge is golden the way gold leaf catches a warm
337
+ highlight. This is the single richest moment in the platform — gold + liquid
338
+ + specular — and therefore the most scarce (ONE surface ever, the title-page
339
+ flourish; the budget lives in D7). The geometry (bounded circle, the
340
+ `--glass-specular-size` falloff, the `plus-lighter` blend, the mask) is
341
+ INHERITED verbatim from the shared `::before`; only the gradient color stops
342
+ shift to the warm-gold mix. The intensity still rides the `--specular-intensity`
343
+ opacity multiplier (0 at rest), so a resting gilt surface paints NO gold sweep
344
+ — it wakes on hover like every liquid tier. */
345
+ .glass-gilt::before {
346
+ background: radial-gradient(
347
+ circle var(--glass-specular-size, 36%) at var(--specular-x, 50%) var(--specular-y, 50%),
348
+ color-mix(in oklab, var(--gold-light) 60%, hsl(40 35% 92%)) 0%,
349
+ hsl(40 35% 92% / 0) 70%,
350
+ transparent 100%
351
+ );
352
+ }
353
+
225
354
  /* ── Five-tier surface ladder (v0.8.0; visual only, no geometry) ──
226
355
  Tier semantics, alpha-monotonic per R3-spec §A:
227
356
  wash ~0.30α + light blur (was `subtle`)
@@ -430,7 +559,10 @@
430
559
  background: var(--paper-clean-texture);
431
560
  background-repeat: repeat;
432
561
  background-size: var(--paper-texture-size);
433
- opacity: var(--glass-grain-opacity);
562
+ /* D6.c M5 — the grain rung lifted off the dead 0.025 floor to a felt-but-
563
+ deft 0.038 (the page rung; the kitsch ceiling is ~0.05). The canonical
564
+ token still wins; this is the felt floor a no-override mount inherits. */
565
+ opacity: var(--glass-grain-opacity, 0.038);
434
566
  pointer-events: none;
435
567
  mix-blend-mode: overlay;
436
568
  }
@@ -947,17 +1079,20 @@
947
1079
  .glass-resting,
948
1080
  .glass-floating,
949
1081
  .glass-overlay,
950
- .glass-card {
1082
+ .glass-card,
1083
+ .glass-drawer {
951
1084
  border: 1px solid CanvasText;
952
1085
  }
953
1086
 
954
1087
  /* The floating + overlay rungs (Dialog / Sheet / Popover / DropdownMenu /
955
- HoverCard compose them) get the canonical `Canvas` fill so the modal reads as
956
- a DISTINCT boxed region OVER the page, not a transparent overlap — the
957
- hierarchy survives. The `CanvasText` edge above already draws the box; this
958
- opaque fill separates the floating layer from the content beneath it. */
1088
+ HoverCard compose them) + the Drawer (the overlay-band sheet, drawer.css)
1089
+ get the canonical `Canvas` fill so the modal reads as a DISTINCT boxed
1090
+ region OVER the page, not a transparent overlap — the hierarchy survives.
1091
+ The `CanvasText` edge above already draws the box; this opaque fill
1092
+ separates the floating layer from the content beneath it. */
959
1093
  .glass-floating,
960
- .glass-overlay {
1094
+ .glass-overlay,
1095
+ .glass-drawer {
961
1096
  background: Canvas;
962
1097
  }
963
1098
 
@@ -972,6 +1107,7 @@
972
1107
  .glass-floating::before,
973
1108
  .glass-overlay::before,
974
1109
  .glass-card::before,
1110
+ .glass-drawer::before,
975
1111
  .glass-wash::after,
976
1112
  .glass-quiet::after,
977
1113
  .glass-resting::after,
@@ -7,8 +7,54 @@
7
7
  *
8
8
  * Texture: feTurbulence baseFrequency 0.65, 4 octaves, desaturated via
9
9
  * feColorMatrix, blended multiply (light) / soft-light (dark). Tiles ~60px.
10
+ *
11
+ * ── THE GRAIN RUNGS (D6.c M5 — the printed-paper tooth, FELT not seen) ─────────
12
+ * The warm-paper grain must be FELT at rest — a premium editorial stock has a
13
+ * felt depth, and the measured finding (ds2-medium M5 / fd-usf-gallery §F) is
14
+ * that the shipped `--glass-grain-opacity: 0.025` floor reads as NOTHING. This
15
+ * file lifts the grain off that floor and mints a SECOND rung for chart plates:
16
+ *
17
+ * --glass-grain-opacity — the PAGE grain. Lifted here to a perceptible-
18
+ * but-deft 0.038 default (the local floor below).
19
+ * Numeric justification: the kitsch line is
20
+ * ~0.05 (above it the grain reads as screen-noise
21
+ * / a dirty monitor, ds2-medium M5); 0.025 is
22
+ * sub-perceptual ("felt as nothing"). 0.038 is
23
+ * the calibrated MIDPOINT — ~1.5× the dead floor,
24
+ * ~0.76× the kitsch ceiling — felt at rest, never
25
+ * seen AS grain. (The canonical token still lives
26
+ * in tokens.css; this utility reads it with the
27
+ * lifted fallback so a no-override mount is felt.)
28
+ * --glass-grain-opacity-plate — the PLATE grain (NEW rung). A chart plate is
29
+ * the FIGURE's "paper", so it wants slightly more
30
+ * tooth than the page (M5): 0.045 — still below
31
+ * the 0.05 kitsch ceiling, distinctly felt so the
32
+ * plate reads as PRINTED on the same stock as the
33
+ * page, not pasted onto it.
34
+ *
35
+ * THE DATA-REGION MASK CONTRACT (M5, non-negotiable). Grain on the FRAME, never
36
+ * under the FIGURE — a fleck over a choropleth reads as a data artifact; on the
37
+ * plate margin it reads as paper. `paper-grain-overlay` exposes a custom-property
38
+ * MASK SLOT (`--paper-grain-mask`) so a consumer can carve the data viewport out
39
+ * of the grain ::after: set it to a gradient/`url()` that is transparent over the
40
+ * data region and opaque over the frame, and the grain is masked off the figure.
41
+ * Default `none` (the whole surface is grained — the page/card case, no data to
42
+ * fence). The plate rung is opt-in via `[data-grain="plate"]`.
10
43
  */
11
44
 
45
+ /* The grain rung DEFAULTS — lifted off the tokens.css floor (D6.c M5). These are
46
+ plain custom-property fallbacks the utilities read; a consumer/tokens.css value
47
+ still wins (`var(--glass-grain-opacity, <floor>)`), so this only sets the floor
48
+ a no-override mount inherits. The plate rung is minted here (no canonical
49
+ tokens.css home is touched in this lane — D6.b owns tokens.css). */
50
+ @layer components {
51
+ :root {
52
+ /* The PLATE grain rung — distinct from the page rung; the figure's paper
53
+ wants more tooth. Minted in this lane; a consumer overrides at :root. */
54
+ --glass-grain-opacity-plate: 0.045;
55
+ }
56
+ }
57
+
12
58
  @utility paper-underpaint {
13
59
  position: fixed;
14
60
  inset: 0;
@@ -17,7 +63,8 @@
17
63
  background-image: url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' 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%3CfeBlend mode='multiply' in2='SourceGraphic'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='1'/%3E%3C/svg%3E");
18
64
  background-repeat: repeat;
19
65
  background-size: 60px 60px;
20
- opacity: var(--glass-grain-opacity);
66
+ /* Lifted floor — the page grain is FELT at rest (0.038), not the dead 0.025. */
67
+ opacity: var(--glass-grain-opacity, 0.038);
21
68
  mix-blend-mode: multiply;
22
69
  }
23
70
 
@@ -39,8 +86,27 @@
39
86
  background-image: url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' 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%3CfeBlend mode='multiply' in2='SourceGraphic'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='1'/%3E%3C/svg%3E");
40
87
  background-repeat: repeat;
41
88
  background-size: 60px 60px;
42
- opacity: var(--glass-grain-opacity);
89
+ /* Lifted floor — felt on the frame at rest (0.038). */
90
+ opacity: var(--glass-grain-opacity, 0.038);
43
91
  mix-blend-mode: overlay;
92
+ /* THE DATA-REGION MASK SLOT (M5). Default `none` = the whole surface is
93
+ grained (page/card — no data to fence). A consumer that grains a chart
94
+ plate sets `--paper-grain-mask` to a gradient/url() transparent over the
95
+ data viewport + opaque over the frame, carving the figure out of the
96
+ grain so no fleck lands on a tier fill. `mask` is Baseline; a
97
+ non-supporting engine ignores it and keeps the full-surface grain (the
98
+ page case is unaffected — the mask only matters when a consumer fences
99
+ the data region). */
100
+ mask: var(--paper-grain-mask, none);
101
+ -webkit-mask: var(--paper-grain-mask, none);
102
+ }
103
+
104
+ /* The PLATE rung — a chart plate opts in via `[data-grain="plate"]` to the
105
+ higher-tooth `--glass-grain-opacity-plate` (the figure's paper reads as the
106
+ same stock as the page, with a touch more tooth). Pair with the mask slot to
107
+ fence the data viewport. */
108
+ &[data-grain="plate"]::after {
109
+ opacity: var(--glass-grain-opacity-plate, 0.045);
44
110
  }
45
111
  }
46
112
 
@@ -308,6 +308,13 @@
308
308
  --color-gold: var(--gold);
309
309
  --color-gold-light: var(--gold-light);
310
310
  --color-gold-dark: var(--gold-dark);
311
+ --color-gold-deep: var(--gold-deep);
312
+ /* D6.b — the gilt family: the rim (gilt edge) + the legible gold ink. The
313
+ `@theme` bridge surfaces `text-gold-ink` / `border-gold-rim` utilities
314
+ beside the existing gold quad. `--gold-ink` clears cream-law ② on `--card`
315
+ in both arms (proof:gold-ink-contrast). */
316
+ --color-gold-rim: var(--gold-rim);
317
+ --color-gold-ink: var(--gold-ink);
311
318
 
312
319
  --color-rainbow-red: var(--rainbow-red);
313
320
  --color-rainbow-orange: var(--rainbow-orange);