@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
@@ -80,9 +80,12 @@
80
80
  --duration-xxl: 1.5s;
81
81
  --duration-shimmer-fast: 3s;
82
82
  --duration-shimmer: 5s;
83
- /* Sparkle micro-event — primary-CTA disco-grain sparkle sweep, success
84
- affirmations. R5 vocab.γ gap row 3 (single-site at dock.css:735 today,
85
- reserved for J.W2.7 §17 audacious-CTA buildout). */
83
+ /* Sparkle micro-event — primary-CTA disco-grain sparkle sweep.
84
+ @deprecated 3.10 — the disco-star is EXCISED (D6.b); the `sparkle-sweep`
85
+ keyframe is tombstoned (animations.css) and no live recipe consumes this
86
+ duration. Retained one minor so an out-of-tree consumer resolves a value
87
+ rather than `unset`; full delete (this token + the theme bridge + the
88
+ keyframe) lands in 3.11. */
86
89
  --duration-sparkle: 600ms;
87
90
 
88
91
  /* AH.W5-e (FA-8 §4d) — motion-duration + staged-reveal canon.
@@ -181,7 +184,7 @@
181
184
  The BUTTON family (`.btn-pill` / `.tap-squish` / `.glass-btn` /
182
185
  `btn-interactive`) UNIFIES its `scale` transform leg onto ONE register —
183
186
  `--spring-smooth` — so hover + press read as one motion language (the
184
- hover lift is the restrained `--scale-hover-btn` = 1.035, not the 1.08
187
+ hover lift is the restrained `--scale-hover-btn` = 1.05, not the 1.08
185
188
  `--scale-hover`). Springs read alive on TRANSFORM and ENTER (overshoot =
186
189
  physicality); a spring on a COLOR or an EXIT reads as a wobble/hesitation.
187
190
  `--ease-apple` is the ambient-only register (consumed solely by the Pulse
@@ -511,6 +514,26 @@
511
514
  field recedes further: BELOW the slides 0.92 max-legibility reference. */
512
515
  --constellation-alpha: 0.80;
513
516
 
517
+ /* §interaction — the warp spring + the gravity-well tunings (NUMERIC, NOT
518
+ colors — read ONCE on mount via parseFloat, never reach a Canvas2D
519
+ fillStyle, so immune to the W30 light-dark()/Canvas2D leak; AY.W-CON2).
520
+ Mode-INVARIANT, declared ONCE in :root (the .dark arm needs no numeric
521
+ re-declare — a per-mode value SPLIT is what proof:constellation-tokens
522
+ clause (c) flags, and these tunings are identical in both modes).
523
+ `--constellation-warp-response` is the keyframes.js ANGULAR PERIOD (the
524
+ SwiftUI .spring(response:) axis), NOT a settle-duration — at ζ=1 the 2%
525
+ settle lands at t₂ ≈ 5.83/ω₀ ≈ 0.93·response (the engine keeps the
526
+ keyframes.js ω₀ = 2π/response convention, mints no second ω). */
527
+ --constellation-warp-response: 0.55; /* s — keyframes.js angular period (NOT a settle) */
528
+ --constellation-warp-zeta: 1.0; /* critically damped — a focal mark must NOT ring */
529
+ --constellation-well-gain: 14000; /* inverse-square force scale (the held-pull gain) */
530
+ --constellation-well-reach: 340; /* base-width px — the well's reach (k-scaled at step) */
531
+ --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) */
532
+ --constellation-well-max-speed: 4.0; /* base-width px/frame cap — the no-slingshot clamp */
533
+ --constellation-well-hold-ms: 140; /* ms hold before the well arms */
534
+ --constellation-wander-idle: 8000; /* ms — min idle between auto re-targets (the W-CON1 drift cadence) */
535
+ --constellation-wander-jitter: 8000; /* ms — random extra idle per cadence (so the rhythm is not metronomic) */
536
+
514
537
  /* ═══════════════════════════════════════════════
515
538
  §6 SECTION PALETTE — 13-stop jewel tones (light)
516
539
  ═══════════════════════════════════════════════ */
@@ -1052,13 +1075,22 @@
1052
1075
 
1053
1076
  /* Overlay scrim rungs — substrate-aware modal/dialog backdrop tints.
1054
1077
  Three weights cover the deployed range: dialog-scroll (subtle),
1055
- canonical modal (default), drawer/full-cover (strong). Currently
1056
- 5 raw `bg-black/{40,50,80}` sites across Dialog/Sheet/Drawer/
1057
- ConfirmDialog hardcode the value; W2.A migrates consumers.
1058
- R5 vocab.γ gap row 2. */
1059
- --overlay-scrim: color-mix(in srgb, var(--shadow-color) 50%, transparent);
1060
- --overlay-scrim-strong: color-mix(in srgb, var(--shadow-color) 80%, transparent);
1061
- --overlay-scrim-subtle: color-mix(in srgb, var(--shadow-color) 40%, transparent);
1078
+ canonical modal (default), drawer/full-cover (strong).
1079
+
1080
+ AY.W-PRIM-POLISH D3 — the scrim is its OWN identity token, NOT derived
1081
+ from `--shadow-color`. A scrim's role is to RECEDE: it must pin to INK in
1082
+ BOTH modes. The cartoon-shadow family rides `--shadow-color: var(--foreground)`
1083
+ (correct — a shadow re-tints under `.dark` by construction), but a scrim
1084
+ that does the same FLOODS the dark room with a cream fog (the old dark
1085
+ overlay painted `srgb(0.91… / 0.5)`, a near-white wash that BRIGHTENED the
1086
+ page at the modal moment). `--overlay-scrim-ink` is a fixed warm near-black
1087
+ (the light-mode `--foreground` value), re-resolved verbatim in `.dark` so
1088
+ the scrim recedes in both modes. Clean break, no alias (no-backwards-compat).
1089
+ The `--shadow-color` / `--shadow-cartoon-*` family is UNTOUCHED. */
1090
+ --overlay-scrim-ink: hsl(24 10% 10%);
1091
+ --overlay-scrim: color-mix(in srgb, var(--overlay-scrim-ink) 50%, transparent);
1092
+ --overlay-scrim-strong: color-mix(in srgb, var(--overlay-scrim-ink) 80%, transparent);
1093
+ --overlay-scrim-subtle: color-mix(in srgb, var(--overlay-scrim-ink) 40%, transparent);
1062
1094
 
1063
1095
  /* ═══════════════════════════════════════════════
1064
1096
  §9 ANIMATION OFFSETS
@@ -1485,8 +1517,15 @@
1485
1517
  --opacity-icon-muted: 0.8;
1486
1518
 
1487
1519
  --focus-ring-width: 2px;
1488
- --focus-ring-shadow: 0 0 0 var(--focus-ring-width) color-mix(in srgb, var(--ring) 30%, transparent),
1489
- 0 0 8px color-mix(in srgb, var(--ring) 15%, transparent);
1520
+ /* D6.b — the focus-ring color routes through a `--focus-ring-color`
1521
+ indirection (default `--ring`, so the resolved shadow is byte-identical to
1522
+ the prior hard `--ring` recipe). An opt-in channel (the `focus-ring-gold`
1523
+ utility, utilities.css) overrides ONLY this color to mint a gilded
1524
+ "you are here / this is live" ring — gold is the transient STATE voice;
1525
+ the resting active mark stays its structural color (the four-voice law). */
1526
+ --focus-ring-color: var(--ring);
1527
+ --focus-ring-shadow: 0 0 0 var(--focus-ring-width) color-mix(in srgb, var(--focus-ring-color) 30%, transparent),
1528
+ 0 0 8px color-mix(in srgb, var(--focus-ring-color) 15%, transparent);
1490
1529
  --focus-ring: var(--focus-ring-shadow);
1491
1530
 
1492
1531
  /* ═══════════════════════════════════════════════
@@ -1508,10 +1547,40 @@
1508
1547
  --gold: oklch(0.751 0.147 84.2);
1509
1548
  --gold-light: oklch(0.890 0.183 96.1);
1510
1549
  --gold-dark: oklch(0.599 0.132 64.2);
1550
+ /* AY.W-PRIM-POLISH D1 — the saturated CTA plate the AW.W13 gold-CTA contract
1551
+ names. A FIXED deep gold (mode-INVARIANT — the gold-CTA hover backplate must
1552
+ be dark in BOTH modes so the white label clears ≥4.5:1; deepening toward the
1553
+ mode-flipping `--foreground` brightened the dark plate to a cream-gold and
1554
+ broke dark). ~`--gold-dark` deepened toward warm-ink → ~rgb(120,87,45),
1555
+ 6.5:1 white in light / 5.5:1 in dark. The opaque base of `btn-audacious-gold`
1556
+ :hover. */
1557
+ --gold-deep: oklch(0.487 0.103 63.0);
1558
+
1559
+ /* D6.b — the chrome-gold additions (the rim + the legible ink), completing
1560
+ the gold quad into the gilt FAMILY. Gold is a metal, not a hue — it must
1561
+ read as reflectance, so it lives at the EDGE and the HIGHLIGHT, rarely as
1562
+ an area.
1563
+
1564
+ --gold-rim — the gilt EDGE catch: `--gold` at 40% over the surface, the
1565
+ 1px tooled bevel light the `.glass-gilt` modifier + the gilt dock button
1566
+ paint. Non-text → no contrast floor.
1567
+
1568
+ --gold-ink — legible gold-toned TEXT. Derived (NOT guessed) to clear the
1569
+ cream-law ② floor (APCA |Lc| ≥ 45) against `--card` in BOTH arms; the raw
1570
+ `--gold-dark` does not (it was minted as a plate hue). LIGHT arm:
1571
+ oklch(0.560 0.118 70) → |Lc| 69.9 on the cream `--card` (hsl 48 12% 98%).
1572
+ The dark-arm pair lifts L (the warm-dark stock) in the `.dark` block,
1573
+ reaching |Lc| 77.1 on the dark `--card`. Both verified by
1574
+ `proof:gold-ink-contrast`. */
1575
+ --gold-rim: color-mix(in oklab, var(--gold) 40%, transparent);
1576
+ --gold-ink: oklch(0.560 0.118 70);
1511
1577
 
1512
1578
  --color-gold: var(--gold);
1513
1579
  --color-gold-light: var(--gold-light);
1514
1580
  --color-gold-dark: var(--gold-dark);
1581
+ --color-gold-deep: var(--gold-deep);
1582
+ --color-gold-rim: var(--gold-rim);
1583
+ --color-gold-ink: var(--gold-ink);
1515
1584
 
1516
1585
  /* ═══════════════════════════════════════════════
1517
1586
  §14 RAINBOW PALETTE — oklch (AV.W16 TW2; mode-independent, no .dark
@@ -1767,8 +1836,18 @@
1767
1836
  flat ease. STRICTLY transform-scoped: it must never touch the surface-tier
1768
1837
  `[data-held]` fades (background/border/box-shadow), which stay on
1769
1838
  `--duration-fast var(--ease-standard)`. The duration pairs with
1770
- `--duration-fast`; consumers retune the whole feel via this one token. */
1771
- --dock-press-spring: var(--duration-fast) var(--spring-bouncy);
1839
+ `--duration-fast`; consumers retune the whole feel via this one token.
1840
+
1841
+ AY.W-DOCK2 (D6) — the register is `--spring-smooth`, the §6 easing-doctrine
1842
+ transform-press register (tokens.css §2: transform hover/press/active →
1843
+ `--spring-smooth`/`--spring-snappy`, NEVER `--spring-bouncy`; a press must
1844
+ settle, never overshoot into a visible bounce). The prior `--spring-bouncy`
1845
+ ROOT default was off-doctrine, shadowed for the four named controls by a
1846
+ per-surface re-point in dock-controls.css — the exact per-surface fork the
1847
+ doctrine kills. The re-point is now a DEAD byte-identical redeclaration and is
1848
+ DELETED; the ROOT token IS the doctrine register, so any other consumer of
1849
+ `--dock-press-spring` inherits the correct smooth register. */
1850
+ --dock-press-spring: var(--duration-fast) var(--spring-smooth);
1772
1851
  }
1773
1852
 
1774
1853
  /* AX.W51 D18 — the GLOBAL coarse-pointer comfort amplification. The WHOLE library
@@ -2112,6 +2191,13 @@
2112
2191
  --gold: oklch(0.784 0.143 86.0);
2113
2192
  --gold-light: oklch(0.898 0.182 97.4);
2114
2193
  --gold-dark: oklch(0.678 0.150 63.8);
2194
+ /* D6.b — the dark-arm gilt ink. `--gold-rim` auto-re-resolves (its color-mix
2195
+ reads the dark `--gold` above — no override). `--gold-ink` MUST lift L for
2196
+ the dark `--card` (hsl 24 8% 10%): a deep gold-dark ink would vanish into
2197
+ the near-black plate, so the dark ink is a BRIGHT gold —
2198
+ oklch(0.860 0.158 90) → |Lc| 77.1 on the dark `--card` (cream-law ②;
2199
+ verified by proof:gold-ink-contrast). */
2200
+ --gold-ink: oklch(0.860 0.158 90);
2115
2201
  }
2116
2202
 
2117
2203
  /* ═══════════════════════════════════════════════
@@ -184,10 +184,28 @@
184
184
  * surface). The grammar is single-event entrance + dwell + exit;
185
185
  * the next toast (if any) enters on its own contract.
186
186
  *
187
+ * §6 EASING-DOCTRINE EXEMPTION (AY.W-MOTION — DELIBERATE KEEP).
188
+ * Every glass-ui-OWNED entrance surface (`dialog-scale`, `pop`,
189
+ * `dropdown`, `fade-slide`, `dock-in`) rides the house `--spring-*`
190
+ * enter / `--ease-out`/`--ease-standard` exit vocabulary (the §6
191
+ * doctrine, tokens.css:162-190). The Toast does NOT — and that is
192
+ * BY DESIGN, not an unexamined default. The reka-ui `ToastRoot`
193
+ * primitive OWNS its data-state choreography: the entrance slide
194
+ * direction tracks the viewport origin AND the swipe-end gesture
195
+ * (`data-[swipe=end]:slide-out-to-right-full`), so the entrance/exit
196
+ * are coupled to reka's `tw-animate-css` data-state machinery. The §6
197
+ * spring vocabulary governs glass-ui-OWNED surfaces; the reka-Toast
198
+ * entrance is not one — re-authoring it onto a `<Transition>` would
199
+ * fork the primitive's swipe-coupled contract (and `tw-animate-css`
200
+ * is the shipped optionalPeer this contract depends on). The gate
201
+ * (proof:animation-coherence) carries the Toast surface in its
202
+ * REGISTER_ASSIGNMENT_ALLOW set so this keep reads as a gated,
203
+ * deliberate exemption.
204
+ *
187
205
  * PRM is honoured at the `@media (prefers-reduced-motion: reduce)`
188
206
  * block below — the slide motion retires; the fade-in/out
189
- * remains. No source change in W4; this comment block IS the
190
- * documentation closure for the W4-M.3 toast contract row.
207
+ * remains. No source motion change; this comment block IS the
208
+ * documentation closure for the toast contract row.
191
209
  */
192
210
 
193
211
  /* ── Reduced motion: preserve fades, remove transform/scale motion ── */
@@ -143,6 +143,20 @@
143
143
  box-shadow: var(--focus-ring-shadow);
144
144
  }
145
145
 
146
+ /* D6.b — `focus-ring-gold`: the opt-in gilded focus ring. Overrides ONLY
147
+ `--focus-ring-color` (tokens.css) → the shared `--focus-ring-shadow`
148
+ re-resolves to gold for THIS element; the default ring elsewhere is
149
+ unchanged. Gold-as-state is the "you are here / this is live" mark — a
150
+ transient highlight (appears on focus-visible, gone on blur), so gold is
151
+ lawful here where a permanent gold furniture would not be. Mirror of the
152
+ `focus-ring` custom-property pattern: a consumer composes
153
+ `class="focus-ring focus-ring-gold"` to gild the ring on the active rung;
154
+ the resting mark keeps its structural color (the four-voice law: red is
155
+ identity, gold is distinction). */
156
+ .focus-ring-gold {
157
+ --focus-ring-color: var(--gold);
158
+ }
159
+
146
160
  /* ── Hairline-accent — 0.5px catch-light + under-shadow pair ──
147
161
  Composes the canonical `--border-hairline` token (tokens.css §
148
162
  Glass-decorative). Adopt at any surface that wants the canonical
@@ -366,6 +380,30 @@
366
380
  }
367
381
  }
368
382
 
383
+ /* D6.b — `.text-gilt`: the brand-polish ONE-SHOT shimmer. Same gold-clip
384
+ gradient pass as `.gold-shimmer`, but the gold passes ONCE then SETS — a
385
+ seal being pressed, not a marquee looping forever. This is the lawful home
386
+ for the chassis "Complete!" celebration the disco-star used to carry: the
387
+ gold sweeps once over the completion label on resolve, then holds the
388
+ static gold gradient.
389
+
390
+ `animation-iteration-count: 1` + `animation-fill-mode: forwards` so the
391
+ slide runs exactly one pass and the final keyframe (background-position
392
+ -200%) sticks — no per-frame repaint after settle. PRM users get the
393
+ static gradient, no pass (the gold is still present, just unanimated). */
394
+ .text-gilt {
395
+ background: linear-gradient(90deg, var(--color-gold-dark), var(--color-gold-light), var(--color-gold), var(--color-gold-light), var(--color-gold-dark));
396
+ background-size: 250% 100%;
397
+ background-clip: text;
398
+ -webkit-background-clip: text;
399
+ color: transparent;
400
+ }
401
+ @media (prefers-reduced-motion: no-preference) {
402
+ .text-gilt {
403
+ animation: gold-shimmer-slide var(--duration-shimmer) var(--ease-out-expo) 1 forwards;
404
+ }
405
+ }
406
+
369
407
  /* ── Keyboard key ── */
370
408
  .kbd {
371
409
  display: inline-flex;
@@ -570,6 +608,56 @@
570
608
  letter-spacing: 0.1em;
571
609
  }
572
610
 
611
+ /* D6.b — `MetricBadge variant="record"`: the gilded record/peak value.
612
+ The highest-leverage gold moment on a DATA atlas — a tiny gilt pill on the
613
+ single record / peak / featured figure (the all-time-high, the top
614
+ district). Gold marks the SUPERLATIVE, exactly one per view: a gilt pill on
615
+ a single figure reads as a medal — civic and earned, not decorative.
616
+
617
+ The recipe composes ON the base `.metric-badge` glass plate (it is a
618
+ MODIFIER, never a replacement): a 1px `--gold-rim` tooled edge + `--gold-ink`
619
+ text + a FAINT gold wash (≤8% alpha — never an opaque gold fill, which would
620
+ read as a fourth DATA category and break one-color-one-meaning). The wash
621
+ sits over the badge's own `--glass-bg-quiet` plate via `color-mix`, so the
622
+ cream glass still reads through; the gold is the leaf, not the area.
623
+
624
+ `--gold-ink` clears cream-law ② (APCA |Lc| ≥ 45) on `--card` in both arms
625
+ (proof:gold-ink-contrast), and the faint wash only lightens the plate it
626
+ sits on, so the ink stays legible. */
627
+ .metric-badge--record {
628
+ border-color: var(--gold-rim);
629
+ background:
630
+ linear-gradient(
631
+ 0deg,
632
+ color-mix(in oklab, var(--gold) 8%, transparent),
633
+ color-mix(in oklab, var(--gold) 8%, transparent)
634
+ ),
635
+ var(--metric-badge-bg, var(--glass-bg-quiet));
636
+ box-shadow:
637
+ inset 0 0 0 1px var(--gold-rim),
638
+ inset 0 0.5px 0 0
639
+ color-mix(in oklab, var(--gold-light) 30%, transparent),
640
+ 0 1px 6px color-mix(in srgb, var(--shadow-color) 8%, transparent);
641
+ }
642
+
643
+ /* The record value reads in legible gold ink (the medal numeral). Scoped to
644
+ the amount slot so the unit/label keep their muted register — the gold is
645
+ on the figure, not the furniture. */
646
+ .metric-badge--record .metric-badge__amount {
647
+ color: var(--gold-ink);
648
+ }
649
+
650
+ .metric-badge--record:hover {
651
+ /* Hover deepens the rim to full `--gold` and lifts the wash one rung —
652
+ the gilt edge catches a touch more light, never a fill. */
653
+ border-color: var(--gold);
654
+ box-shadow:
655
+ inset 0 0 0 1px var(--gold),
656
+ inset 0 0.5px 0 0
657
+ color-mix(in oklab, var(--gold-light) 40%, transparent),
658
+ 0 2px 10px color-mix(in srgb, var(--shadow-color) 12%, transparent);
659
+ }
660
+
573
661
  /* Q.W3 Lane A — the `.glass-dock[data-density="…"]` density rungs that
574
662
  set `--dock-tab-h` + `--dock-label-size` were consolidated into
575
663
  `dock.css` (their canonical dock-styles home). They formerly lived
@@ -851,28 +939,17 @@
851
939
  --ripple-radius: var(--ripple-radius-max, 12rem);
852
940
  }
853
941
 
854
- /* Sparkle glyph — hidden at rest, animated on hover. The star
855
- sweeps diagonally from top-left to bottom-right per the canonical
856
- sparkle-sweep keyframe (animations.css). */
857
- &::after {
858
- content: "✦";
859
- position: absolute;
860
- top: 0.25rem;
861
- left: 0.5rem;
862
- color: hsl(0 0% 100% / 0.7);
863
- font-size: 0.75rem;
864
- line-height: 1;
865
- pointer-events: none;
866
- opacity: 0;
867
- will-change: transform, opacity;
868
- z-index: 1;
869
- }
870
-
871
- @media (prefers-reduced-motion: no-preference) {
872
- &:hover::after {
873
- animation: sparkle-sweep var(--duration-sparkle) var(--ease-out-expo);
874
- }
875
- }
942
+ /* D6.b — the disco-star EXCISED. The `::after { content:"✦" }` glyph and
943
+ its `&:hover::after { animation: sparkle-sweep … }` trigger are gone (the
944
+ user's named removal: "remove the disco star from all glass-ui elements").
945
+ A primary action now announces itself by SETTLING into a richer surface,
946
+ not by emitting a sparkle: the whole hover affirmation rides the existing
947
+ `&:hover` specular catch-light box-shadow (`--glass-specular`, above) —
948
+ honest physics, not casino-skeuomorphism. Gold CTAs add the
949
+ `btn-gold-bg-sweep` metal-catch (a metal catching light is honest; a star
950
+ is not). One garish gesture out, zero garish gestures in. The
951
+ `sparkle-sweep` keyframe is TOMBSTONED in animations.css (empty body,
952
+ full delete in 3.11); `--duration-sparkle` is `@deprecated` in tokens.css. */
876
953
 
877
954
  /* PRM bracket — the press-ripple retires; the press still reads
878
955
  via the audacious recipe's transform/scale beat (the ripple is
@@ -900,14 +977,14 @@
900
977
  token triad (tokens.css §13).
901
978
 
902
979
  Design register:
903
- • PRM no-preference: shimmer sweeps continuously at `--duration-shimmer`
904
- rate; the sparkle-sweep (from btn-audacious) coexists on hover.
980
+ • PRM no-preference: the gold sweep IS the gold-CTA affirmation — a metal
981
+ catching light is honest physics (the disco-star is gone, D6.b).
905
982
  • PRM reduce: static gold tint shows on hover; no animation.
906
983
  • The gold shimmer is a background-image layer ABOVE the existing
907
984
  btn-audacious paper-grain + primary-radial layers, blended with
908
985
  `overlay` so the glass substrate reads through at partial opacity.
909
- • The `::before` ripple and `::after` sparkle from btn-audacious are
910
- unchanged; this modifier only retunes the hover background layers.
986
+ • The `::before` ripple from btn-audacious is unchanged; the `::after`
987
+ sparkle is EXCISED (D6.b) — this modifier only retunes the hover layers.
911
988
  • Consumer applies: `<Button variant="gold-audacious">Next →</Button>` */
912
989
  @utility btn-audacious-gold {
913
990
  /* At rest: a faint warm gold tint over the glass surface reads as "warm
@@ -920,27 +997,40 @@
920
997
  color-mix(in srgb, var(--color-gold) 8%, transparent)
921
998
  );
922
999
 
923
- /* Hover: replace the audacious primary-radial with a gold sweep shimmer.
924
- The shimmer gradient rides above the paper-grain texture; the `overlay`
925
- blend-mode concentrates the gold on bright grain facets and recedes on
926
- dark ones — the same lens metaphor as the btn-audacious primary radial. */
1000
+ /* Hover: deepen the plate to a SATURATED gold the white label clears (AW.W13
1001
+ names a "saturated gold backplate"; the D1 fix). The bottom layer is the
1002
+ OPAQUE fixed deep gold (`--color-gold-deep`, mode-INVARIANT) — a solid plate
1003
+ at ~6.5:1 (light) / ~5.5:1 (dark) against white, well below the ~0.18
1004
+ luminance floor white text needs for ≥4.5:1 (was a translucent 22-30% wash
1005
+ over near-cream glass → 1.29:1 white-on-pale-gold). The plate is FIXED-dark
1006
+ in both modes — deepening toward the mode-flipping `--foreground` brightened
1007
+ the dark plate to a cream-gold; `--color-gold-deep` does not flip. The
1008
+ shimmer sweep rides ABOVE as a LOW-alpha gold gradient (normal blend, ≤18% —
1009
+ it textures the plate without brightening it past the floor), and the
1010
+ paper-grain overlays for the disco facet. */
927
1011
  &:hover:not(:disabled) {
928
1012
  background-image:
929
1013
  var(--paper-clean-texture),
930
1014
  linear-gradient(
931
1015
  90deg,
932
1016
  transparent 0%,
933
- color-mix(in srgb, var(--color-gold-dark) 22%, transparent) 25%,
934
- color-mix(in srgb, var(--color-gold-light) 30%, transparent) 50%,
935
- color-mix(in srgb, var(--color-gold-dark) 22%, transparent) 75%,
1017
+ color-mix(in srgb, var(--color-gold) 14%, transparent) 25%,
1018
+ color-mix(in srgb, var(--color-gold) 18%, transparent) 50%,
1019
+ color-mix(in srgb, var(--color-gold) 14%, transparent) 75%,
936
1020
  transparent 100%
1021
+ ),
1022
+ linear-gradient(
1023
+ 135deg,
1024
+ var(--color-gold-deep),
1025
+ color-mix(in srgb, var(--color-gold-deep), var(--color-gold-dark) 18%) 50%,
1026
+ var(--color-gold-deep)
937
1027
  );
938
- background-blend-mode: overlay, normal;
939
- background-size: var(--paper-texture-size), 250% 100%;
940
- background-position: 0 0, 100% 0;
1028
+ background-blend-mode: overlay, normal, normal;
1029
+ background-size: var(--paper-texture-size), 250% 100%, 100% 100%;
1030
+ background-position: 0 0, 100% 0, 0 0;
941
1031
  box-shadow:
942
1032
  var(--glass-specular),
943
- 0 0 0 1px color-mix(in srgb, var(--color-gold) 30%, transparent),
1033
+ 0 0 0 1px color-mix(in srgb, var(--color-gold-dark) 40%, transparent),
944
1034
  0 0.5px 0 0 rgb(0 0 0 / 0.06);
945
1035
  }
946
1036
 
@@ -956,8 +1046,8 @@
956
1046
  a text-clip gradient). This variant sweeps the `background-position` of the
957
1047
  250% wide gradient across the button's bounding box. */
958
1048
  @keyframes btn-gold-bg-sweep {
959
- 0% { background-position: 0 0, 100% 0; }
960
- 100% { background-position: 0 0, -100% 0; }
1049
+ 0% { background-position: 0 0, 100% 0, 0 0; }
1050
+ 100% { background-position: 0 0, -100% 0, 0 0; }
961
1051
  }
962
1052
 
963
1053
  /* ── Table density utilities — AN.R0 ─────────────────────────────────────
@@ -1037,7 +1127,7 @@
1037
1127
  states except `hover:scale-*`; this is the canonical opt-in that adds the
1038
1128
  spring-hover lift. Consumed by keyframes.js at 7 sites.
1039
1129
 
1040
- AX.W52 §6 — the hover lift is the restrained `--scale-hover-btn` (1.035, NOT
1130
+ AX.W52 §6 — the hover lift is the restrained `--scale-hover-btn` (1.05, NOT
1041
1131
  the emphatic 1.08 `--scale-hover`) routed through `--spring-smooth` — the ONE
1042
1132
  button scale register the §6 doctrine records and the gate asserts across
1043
1133
  `.btn-pill`/`.tap-squish`/`.glass-btn`/`btn-interactive`. The surface legs
@@ -60,5 +60,56 @@
60
60
  @keyframes gl-vt-slide-in { from { opacity: 0; translate: 0 var(--vt-rise, 8px); } }
61
61
  @keyframes gl-vt-slide-out { to { opacity: 0; translate: 0 var(--vt-rise, 8px); } }
62
62
 
63
+ /* ── gl-shared-element — the named-pair MORPH group (D6.c M6) ──────────────────
64
+ * Distinct from `.gl-list-item` (a cross-fade of MANY members in a re-rank): a
65
+ * shared-element morph is ONE element that exists in BOTH the old and new states
66
+ * under the SAME `view-transition-name`, so the browser MORPHS it in place — the
67
+ * group interpolates its size+position (transform) while old/new cross-fade. The
68
+ * editorial case: the gallery card's title IS the dashboard masthead's title, so
69
+ * the name you clicked GROWS into the name you read (the card → masthead route
70
+ * morph). The consumer adds `view-transition-class: gl-shared-element` plus a
71
+ * MATCHED `view-transition-name: <id>` on BOTH the source and destination element
72
+ * (the MANDATORY runtime rule — ≤1 element per name per state, else the morph is
73
+ * skipped silently), and wraps the navigation in `useRouteTransition` /
74
+ * `startViewTransition`.
75
+ *
76
+ * The morph rides the SMOOTH register (the 700ms-class spring, C-ANIM-VIZ-MAP T1
77
+ * §7) — a longer, gentler curve than the list-item cross-fade, because a morph
78
+ * that travels across the viewport wants room to read. The old/new cross-fade is
79
+ * softened so the morphing GROUP is the figure and the rest recedes. */
80
+ ::view-transition-group(.gl-shared-element) {
81
+ animation-duration: var(--vt-morph-duration, var(--duration-slow, 500ms));
82
+ animation-timing-function: var(--vt-morph-ease, var(--spring-smooth, ease-in-out));
83
+ }
84
+ ::view-transition-old(.gl-shared-element),
85
+ ::view-transition-new(.gl-shared-element) {
86
+ /* The image pair cross-fades UNDER the group's size/position morph — softer
87
+ than the group curve so the morph (the size/position travel) is the figure
88
+ and the pixel cross-fade is the recessive fill. */
89
+ animation-duration: var(--vt-morph-duration, var(--duration-slow, 500ms));
90
+ animation-timing-function: var(--vt-ease, var(--spring-smooth, ease-in-out));
91
+ }
92
+
93
+ /* ── gl-expand — the plate → fullscreen MORPH recipe (D6.c M7) ─────────────────
94
+ * The expand verb (a plate growing to fullscreen) is a SHARED-ELEMENT morph, not
95
+ * an opacity jump-cut: the plate IS the fullscreen surface, growing. It is a
96
+ * specialization of `gl-shared-element` — the SAME named-pair morph, with the
97
+ * expand register (a touch snappier than a route morph, because the surface stays
98
+ * in place and only the rect grows, so it wants less travel-room). `ExpandableContainer`
99
+ * wires the `view-transition-name` plumbing on its plate↔fullscreen surfaces (an
100
+ * opt-in `viewTransitionName` prop), so a consumer opts in and the open/close
101
+ * mutation — wrapped in `startViewTransition` — morphs the plate's rect to the
102
+ * fullscreen rect and back. The named pair is the SAME element across states
103
+ * (the slot content), so the browser captures one group and grows it. */
104
+ ::view-transition-group(.gl-expand) {
105
+ animation-duration: var(--vt-expand-duration, var(--duration-normal, 240ms));
106
+ animation-timing-function: var(--vt-expand-ease, var(--spring-bouncy, ease-out));
107
+ }
108
+ ::view-transition-old(.gl-expand),
109
+ ::view-transition-new(.gl-expand) {
110
+ animation-duration: var(--vt-expand-duration, var(--duration-normal, 240ms));
111
+ animation-timing-function: var(--vt-ease, var(--spring-bouncy, ease-out));
112
+ }
113
+
63
114
  /* Keep the non-transitioned UI interactive while the snapshot animates. */
64
115
  ::view-transition { pointer-events: none; }
@@ -1,16 +0,0 @@
1
- import type { GPUCanvasFrame, GPUCanvasSetupArgs } from "../../../../composables/glass/createGPUCanvas";
2
- import type { CursorState } from "./cursorModel";
3
- import type { AuroraConfig } from "../constants/presets";
4
- export interface GPUAuroraDeps {
5
- canvas: HTMLCanvasElement;
6
- getConfig: () => AuroraConfig;
7
- cursor: CursorState;
8
- getReducedMotion: () => boolean;
9
- /** The frozen frame-time offset under reduced motion (matches the WebGL2 path). */
10
- frozenOffset: number;
11
- }
12
- /**
13
- * The `setup` callback for `createGPUCanvas`. Builds the pipeline once on the device,
14
- * returns the frame hooks. The single render pass draws the WGSL aurora base field.
15
- */
16
- export declare function createGPUAuroraSetup(deps: GPUAuroraDeps): ({ device, context, format }: GPUCanvasSetupArgs) => GPUCanvasFrame;
@@ -1,7 +0,0 @@
1
- declare const WGSL_MAX_NUCLEI = 6;
2
- declare const WGSL_MAX_STOPS = 8;
3
- export declare const AURORA_VERTEX_WGSL = "\nstruct VSOut {\n @builtin(position) pos: vec4f,\n @location(0) uv: vec2f,\n};\n\n@vertex\nfn vs_main(@builtin(vertex_index) vi: u32) -> VSOut {\n // Full-screen triangle (matches the WebGL2 [-1,-1, 3,-1, -1,3] quad).\n var p = array<vec2f, 3>(vec2f(-1.0, -1.0), vec2f(3.0, -1.0), vec2f(-1.0, 3.0));\n var out: VSOut;\n let xy = p[vi];\n out.pos = vec4f(xy, 0.0, 1.0);\n out.uv = 0.5 * (xy + vec2f(1.0));\n return out;\n}";
4
- export declare const AURORA_FRAGMENT_WGSL = "\nconst PI: f32 = 3.141592653589793;\nconst W_LUMA: vec3f = vec3f(0.2126, 0.7152, 0.0722);\nconst MAX_NUCLEI: u32 = 6u;\nconst MAX_STOPS: u32 = 8u;\n\n// The uniform struct \u2014 ONLY constant-indexed scalars (safe in the uniform address\n// space). The five count/enum fields are f32 (AX.W07 1a): the WebGPU write path packs\n// them as floats and the shader i32-casts at each use, so WGSL never reads a float\n// bit-pattern as a raw i32. The dynamically-indexed arrays moved OUT to struct Field\n// below (AX.W07 1b).\nstruct Uniforms {\n time: f32,\n stopCount: f32,\n nucleiCount: f32,\n softmaxBeta: f32,\n valueVariance: f32,\n warpAmount: f32,\n warpScale: f32,\n warpDrift: f32,\n warpMode: f32,\n noiseOctaves: f32,\n medium: f32,\n breathDepth: f32,\n breathPeriod: f32,\n saturation: f32,\n paperGrain: f32,\n alpha: f32,\n // AX.W11 \u2014 the huePath enum (f32-packed, i32()-cast in samplePalette) carries the\n // shared ramp's OKLab-rectangular-vs-OKLCh-hue-arc dispatch into the WebGPU twin so\n // a huePath:'increasing' rainbow config arcs identically to the WebGL2 oracle. The\n // CPU pad keeps the buffer vec4-aligned (WGPU_UNIFORM_FLOATS rounds to 20).\n huePath: f32,\n};\n\n// The dynamically-indexed arrays \u2014 bound var<storage, read> (AX.W07 1b). A runtime\n// palette/nuclei index into a var<uniform> array<vec4f> returns [0,0,0,0] on Metal\n// (MSL forbids dynamic indexing of constant); storage is always-legal. The vec4 stride\n// is std140-identical to the uniform layout, so the move is byte-exact.\nstruct Field {\n palette: array<vec4f, 8>, // .xyz = linear-sRGB stop\n nucleiPos: array<vec4f, 6>, // .xy pos, .z radius, .w paletteBias\n nucleiMod: array<vec4f, 6>, // .x valueBias, .y elong, .z angle, .w driftRadius\n};\n\n@group(0) @binding(0) var<uniform> U: Uniforms;\n@group(0) @binding(1) var<storage, read> F: Field;\n\n\nfn srgbToLinearCh(c: f32) -> f32 {\n return select(pow((c + 0.055) / 1.055, 2.4), c / 12.92, c <= 0.04045);\n}\nfn srgbToLinear(c: vec3f) -> vec3f {\n return vec3f(srgbToLinearCh(c.r), srgbToLinearCh(c.g), srgbToLinearCh(c.b));\n}\nfn linearToSrgbCh(c: f32) -> f32 {\n return select(1.055 * pow(c, 1.0 / 2.4) - 0.055, c * 12.92, c <= 0.0031308);\n}\nfn linearToSrgb(c: vec3f) -> vec3f {\n return vec3f(linearToSrgbCh(c.r), linearToSrgbCh(c.g), linearToSrgbCh(c.b));\n}\nconst FBM_ROT: mat2x2f = mat2x2f(0.8, 0.6, -0.6, 0.8);\n\nconst LINEAR_SRGB_TO_LMS: mat3x3f = mat3x3f(\n 0.4122214708, 0.2119034982, 0.0883024619,\n 0.5363325363, 0.6806995451, 0.2817188376,\n 0.0514459929, 0.1073969566, 0.6299787005\n);\nconst LMS_TO_OKLAB: mat3x3f = mat3x3f(\n 0.2104542553, 1.9779984951, 0.0259040371,\n 0.7936177850, -2.4285922050, 0.7827717662,\n -0.0040720468, 0.4505937099, -0.8086757660\n);\nconst OKLAB_TO_LMS: mat3x3f = mat3x3f(\n 1.0, 1.0, 1.0,\n 0.3963377774, -0.1055613458, -0.0894841775,\n 0.2158037573, -0.0638541728, -1.2914855480\n);\nconst LMS_TO_LINEAR_SRGB: mat3x3f = mat3x3f(\n 4.0767416621, -1.2684380046, -0.0041960863,\n -3.3077115913, 2.6097574011, -0.7034186147,\n 0.2309699292, -0.3413193965, 1.7076147010\n);\n\nfn srgbToOklab(c: vec3f) -> vec3f {\n let lin = srgbToLinear(c);\n let lms = LINEAR_SRGB_TO_LMS * lin;\n let lmsCbrt = sign(lms) * pow(abs(lms), vec3f(1.0 / 3.0));\n return LMS_TO_OKLAB * lmsCbrt;\n}\nfn oklabToLinearSrgb(lab: vec3f) -> vec3f {\n let lms_ = OKLAB_TO_LMS * lab;\n let lms = lms_ * lms_ * lms_;\n return LMS_TO_LINEAR_SRGB * lms;\n}\nfn oklabToOklch(lab: vec3f) -> vec3f {\n let C = length(lab.yz);\n var H = atan2(lab.z, lab.y);\n if (H < 0.0) { H = H + 2.0 * PI; }\n return vec3f(lab.x, C, H);\n}\nfn oklchToOklab(lch: vec3f) -> vec3f {\n return vec3f(lch.x, lch.y * cos(lch.z), lch.y * sin(lch.z));\n}\n\nfn interpolateHueTurns(h0: f32, h1: f32, t: f32, method: i32) -> f32 {\n let TAU = 2.0 * PI;\n var a = h0 / TAU;\n var b = h1 / TAU;\n let i = b - a;\n if (method == 0) { // shorter\n if (i > 0.5) { a = a + 1.0; } else if (i < -0.5) { b = b + 1.0; }\n } else if (method == 1) { // longer\n if (i > 0.0 && i < 0.5) { a = a + 1.0; } else if (i > -0.5 && i <= 0.0) { b = b + 1.0; }\n } else if (method == 2) { // increasing\n if (i < 0.0) { b = b + 1.0; }\n } else { // decreasing\n if (i > 0.0) { a = a + 1.0; }\n }\n let r = fract(a + t * (b - a));\n return r * TAU;\n}\n\nfn mixPaletteOklab(linA: vec3f, linB: vec3f, t: f32) -> vec3f {\n let labA = LMS_TO_OKLAB * (sign(LINEAR_SRGB_TO_LMS * linA) * pow(abs(LINEAR_SRGB_TO_LMS * linA), vec3f(1.0 / 3.0)));\n let labB = LMS_TO_OKLAB * (sign(LINEAR_SRGB_TO_LMS * linB) * pow(abs(LINEAR_SRGB_TO_LMS * linB), vec3f(1.0 / 3.0)));\n let lab = mix(labA, labB, vec3f(t));\n return oklabToLinearSrgb(lab);\n}\n\nfn mixPaletteOklchArc(linA: vec3f, linB: vec3f, t: f32, method: i32) -> vec3f {\n let labA = LMS_TO_OKLAB * (sign(LINEAR_SRGB_TO_LMS * linA) * pow(abs(LINEAR_SRGB_TO_LMS * linA), vec3f(1.0 / 3.0)));\n let labB = LMS_TO_OKLAB * (sign(LINEAR_SRGB_TO_LMS * linB) * pow(abs(LINEAR_SRGB_TO_LMS * linB), vec3f(1.0 / 3.0)));\n let lchA = oklabToOklch(labA);\n let lchB = oklabToOklch(labB);\n let L = mix(lchA.x, lchB.x, t);\n let C = mix(lchA.y, lchB.y, t);\n let H = interpolateHueTurns(lchA.z, lchB.z, t, method);\n return oklabToLinearSrgb(oklchToOklab(vec3f(L, C, H)));\n}\n\nfn samplePaletteRamp(a: vec3f, b: vec3f, tRaw: f32, huePath: i32) -> vec3f {\n let t = smoothstep(0.0, 1.0, tRaw);\n if (huePath == 2 || huePath == 3) {\n return mixPaletteOklchArc(a, b, t, huePath);\n }\n return mixPaletteOklab(a, b, t);\n}\n\n// AX.W12 \u2014 the painterly-medium organic noise basis (PCG2D integer-bit hash + 2D\n// simplex gradient noise), SPLICED from the shared procedural-color chunk \u2014 the EXACT\n// twin of the GLSL leaf aurora.frag.ts splices, so the hash can NEVER drift between\n// backends (proof:aurora-noise-hash-equivalence locks the twins to 1e-6). The WebGPU\n// single-pass fs_main below stays the smooth pole (it runs no medium body \u2014 the\n// painterly mediums are the W7c/W14 multi-pass), so the leaf is single-sourced HERE for\n// the twin-equivalence contract; the W14 painterly passes consume it.\n\nfn pcg2d(v0: vec2u) -> vec2u {\n var v = v0 * 1664525u + vec2u(1013904223u);\n v.x = v.x + v.y * 1664525u;\n v.y = v.y + v.x * 1664525u;\n v = v ^ (v >> vec2u(16u));\n v.x = v.x + v.y * 1664525u;\n v.y = v.y + v.x * 1664525u;\n v = v ^ (v >> vec2u(16u));\n return v;\n}\n\nfn pcgHash2(p: vec2f) -> f32 {\n let u = bitcast<vec2u>(p);\n return f32(pcg2d(u).x) * (1.0 / 4294967296.0);\n}\n\nfn pcgGrad2(cell: vec2i) -> vec2f {\n let h = pcg2d(bitcast<vec2u>(cell));\n let ang = f32(h.x) * (1.0 / 4294967296.0) * 6.28318530717958647692;\n return vec2f(cos(ang), sin(ang));\n}\n\nfn gnoise(p: vec2f) -> f32 {\n let F2 = 0.36602540378443864676;\n let G2 = 0.21132486540518711775;\n let s = (p.x + p.y) * F2;\n let i = floor(p + vec2f(s));\n let tt = (i.x + i.y) * G2;\n let x0 = p - (i - vec2f(tt));\n var i1 = vec2i(0, 1);\n if (x0.x > x0.y) { i1 = vec2i(1, 0); }\n let x1 = x0 - vec2f(i1) + vec2f(G2);\n let x2 = x0 - vec2f(1.0) + vec2f(2.0 * G2);\n let ii = vec2i(i);\n var n = 0.0;\n var t0 = 0.5 - dot(x0, x0);\n if (t0 > 0.0) { t0 = t0 * t0; n = n + t0 * t0 * dot(pcgGrad2(ii), x0); }\n var t1 = 0.5 - dot(x1, x1);\n if (t1 > 0.0) { t1 = t1 * t1; n = n + t1 * t1 * dot(pcgGrad2(ii + i1), x1); }\n var t2 = 0.5 - dot(x2, x2);\n if (t2 > 0.0) { t2 = t2 * t2; n = n + t2 * t2 * dot(pcgGrad2(ii + vec2i(1, 1)), x2); }\n return 70.0 * n;\n}\n\nfn hash21(p0: vec2f) -> f32 {\n var p = fract(p0 * vec2f(123.34, 456.21));\n p = p + dot(p, p + 45.32);\n return fract(p.x * p.y);\n}\nfn vnoise(p: vec2f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let a = hash21(i);\n let b = hash21(i + vec2f(1.0, 0.0));\n let c = hash21(i + vec2f(0.0, 1.0));\n let d = hash21(i + vec2f(1.0, 1.0));\n return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);\n}\nfn fbm(p0: vec2f) -> f32 {\n var v = 0.0;\n var a = 0.5;\n var p = p0;\n let octaves = i32(U.noiseOctaves); // AX.W07 1a \u2014 i32()-cast the f32-packed count.\n for (var i = 0; i < 5; i = i + 1) {\n if (i >= octaves) { break; }\n v = v + a * vnoise(p);\n p = FBM_ROT * p * 2.02;\n a = a * 0.5;\n }\n return v;\n}\n\nfn samplePalette(id: f32) -> vec3f {\n // AX.W11 \u2014 splice the SHARED samplePaletteRamp (the smoothstep ease + the OKLab-\n // rectangular-vs-OKLCh-hue-arc huePath dispatch), the EXACT twin the GLSL oracle\n // splices. The body here only selects the bracketing stop pair + the raw inter-stop\n // t (the W07 storage-Field dynamic index), then hands them to the shared ramp.\n let n = i32(U.stopCount); // AX.W07 1a \u2014 i32()-cast the f32-packed count.\n if (n <= 1) { return F.palette[0].xyz; }\n let t = clamp(id, 0.0, 1.0) * f32(n - 1);\n let i0 = i32(floor(t));\n let i1 = min(i0 + 1, n - 1);\n let f = t - f32(i0);\n // AX.W07 1b \u2014 read the palette from the storage Field (dynamic i0/i1 index);\n // AX.W11 \u2014 i32()-cast the f32-packed huePath enum (parity with the count casts).\n return samplePaletteRamp(F.palette[i0].xyz, F.palette[i1].xyz, f, i32(U.huePath));\n}\n\nfn domainWarp(p: vec2f, t: f32) -> vec2f {\n let drift = t * U.warpDrift * 5.0;\n let q = vec2f(\n fbm(p * U.warpScale + vec2f(drift, 0.0)),\n fbm(p * U.warpScale + vec2f(5.2, 1.3 + drift))\n );\n let r = vec2f(\n fbm(p * U.warpScale + 4.0 * q + vec2f(1.7, 9.2)),\n fbm(p * U.warpScale + 4.0 * q + vec2f(8.3, 2.8))\n );\n return p + U.warpAmount * r;\n}\n\nfn nucleiField(p: vec2f, t: f32) -> vec2f {\n // Returns (paletteId, valueMod) \u2014 softmax over the nuclei (isotropic; the WebGPU\n // twin keeps the parity-floor isotropic Gaussian).\n var accumId = 0.0;\n var accumVal = 0.0;\n var w = 0.0;\n let n = i32(U.nucleiCount); // AX.W07 1a \u2014 i32()-cast the f32-packed count.\n for (var i = 0; i < 6; i = i + 1) {\n if (i >= n) { break; }\n // AX.W07 1b \u2014 read the nuclei from the storage Field (dynamic i index).\n let pos = F.nucleiPos[i].xy;\n let rad = max(F.nucleiPos[i].z, 0.01);\n let d2 = dot(p - pos, p - pos);\n let wi = exp((-U.softmaxBeta * d2) / (rad * rad));\n accumId = accumId + wi * F.nucleiPos[i].w;\n accumVal = accumVal + wi * F.nucleiMod[i].x;\n w = w + wi;\n }\n if (w < 1e-4) { return vec2f(0.5, 0.0); }\n return vec2f(accumId / w, accumVal / w);\n}\n\n// ACES (Narkowicz) \u2014 matches the GLSL tonemap.\nfn aces(x: vec3f) -> vec3f {\n let a = 2.51; let b = 0.03; let c = 2.43; let d = 0.59; let e = 0.14;\n return clamp((x * (a * x + b)) / (x * (c * x + d) + e), vec3f(0.0), vec3f(1.0));\n}\n\n@fragment\nfn fs_main(@location(0) uv: vec2f) -> @location(0) vec4f {\n let t = U.time;\n let pw = domainWarp(uv, t);\n let nf = nucleiField(pw, t);\n var col = samplePalette(nf.x);\n col = col * (1.0 + U.valueVariance * nf.y);\n\n // Breath.\n let breath = sin(t * 6.2831 / max(U.breathPeriod, 1.0));\n col = col * (1.0 + U.breathDepth * breath * 0.5);\n\n // (The WebGPU smooth pole; the painterly mediums run the W7c multi-pass passes.)\n col = aces(col);\n col = linearToSrgb(col); // the mandatory OETF (linear \u2192 display).\n return vec4f(col * U.alpha, U.alpha); // premultiply AFTER the OETF (parity).\n}";
5
- /** The assembled WGSL module string (vertex + fragment) for createGPUCanvas. */
6
- export declare const AURORA_WGSL: string;
7
- export { WGSL_MAX_NUCLEI, WGSL_MAX_STOPS };
@@ -1,40 +0,0 @@
1
- /** The per-frame hooks a consumer's WebGPU `setup` returns (mirrors WebGLCanvasFrame). */
2
- export interface GPUCanvasFrame {
3
- frame: (timeSec: number) => void;
4
- shouldContinue: () => boolean;
5
- resize: () => void;
6
- time?: (elapsedSec: number) => number;
7
- teardown?: () => void;
8
- }
9
- export interface GPUCanvasSetupArgs {
10
- device: GPUDevice;
11
- context: GPUCanvasContext;
12
- /** The swapchain format (`navigator.gpu.getPreferredCanvasFormat()`). */
13
- format: GPUTextureFormat;
14
- }
15
- export interface GPUCanvasOptions {
16
- /** The pre-acquired `GPUDevice` (from `resolveRenderModeAsync`'s adapter→device). */
17
- device: GPUDevice;
18
- /** Alpha mode for the swapchain (premultiplied to match the WebGL2 premultiply). */
19
- alphaMode?: GPUCanvasAlphaMode;
20
- mode?: "live" | "capture";
21
- respectReducedMotion?: boolean;
22
- /** Build the pipeline + bind group on the configured context. Returns frame hooks. */
23
- setup: (args: GPUCanvasSetupArgs) => GPUCanvasFrame;
24
- }
25
- export interface GPUCanvasHandle {
26
- arm: () => void;
27
- suspend: (reason?: "tab-hidden" | "off-screen" | "manual") => void;
28
- resume: (reason?: "tab-hidden" | "off-screen" | "manual") => void;
29
- wake: () => void;
30
- renderAt: (timeSec: number) => void;
31
- dispose: () => void;
32
- readonly device: GPUDevice | null;
33
- readonly reducedMotion: boolean;
34
- }
35
- /**
36
- * Build a WebGPU canvas over the shared lifecycle core. The `GPUDevice` is acquired
37
- * by `resolveRenderModeAsync` (the probe) and passed in — `createGPUCanvas` configures
38
- * the canvas context + threads the consumer `setup` through the agnostic schedule.
39
- */
40
- export declare function createGPUCanvas(canvas: HTMLCanvasElement, options: GPUCanvasOptions): GPUCanvasHandle;
@@ -1,44 +0,0 @@
1
- //#region src/utils/moveBefore.ts
2
- function e(e, t, n) {
3
- let r = e;
4
- typeof r.moveBefore == "function" ? r.moveBefore(t, n) : e.insertBefore(t, n);
5
- }
6
- function t() {
7
- return typeof Element < "u" && "moveBefore" in Element.prototype;
8
- }
9
- //#endregion
10
- //#region src/utils/platformSupport.ts
11
- function n() {
12
- return typeof globalThis.scheduler?.postTask == "function";
13
- }
14
- //#endregion
15
- //#region src/composables/motion/useViewTransition.ts
16
- function r() {
17
- return typeof document < "u" && typeof document.startViewTransition == "function";
18
- }
19
- function i(e, t) {
20
- let n = typeof document > "u" ? void 0 : document;
21
- if (!n || typeof n.startViewTransition != "function") return e(), {
22
- finished: Promise.resolve(),
23
- transitioned: !1
24
- };
25
- let r = t?.types && t.types.length > 0 ? n.startViewTransition({
26
- update: () => e(),
27
- types: t.types
28
- }) : n.startViewTransition(() => e());
29
- return r.ready?.catch(() => {}), {
30
- finished: r.finished.then(() => void 0, () => void 0),
31
- transitioned: !0
32
- };
33
- }
34
- //#endregion
35
- //#region src/composables/motion/vReveal.ts
36
- function a(e, t) {
37
- e.setAttribute("data-reveal", t.arg === "fade" ? "fade" : ""), e.style.setProperty("--d", String(t.value ?? 0));
38
- }
39
- var o = {
40
- mounted: a,
41
- updated: a
42
- };
43
- //#endregion
44
- export { e as a, n as i, i as n, t as o, r, o as t };