@mieweb/ui 0.8.0-dev.21 → 0.8.0-dev.23

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 (196) hide show
  1. package/dist/Animated-Cs-naa2g.d.cts +175 -0
  2. package/dist/Animated-Cs-naa2g.d.ts +175 -0
  3. package/dist/brands/index.cjs +7 -7
  4. package/dist/brands/index.js +2 -2
  5. package/dist/{chunk-YIOZQEMV.cjs → chunk-3LHICE4I.cjs} +3 -3
  6. package/dist/{chunk-YIOZQEMV.cjs.map → chunk-3LHICE4I.cjs.map} +1 -1
  7. package/dist/{chunk-DWRIU4MW.js → chunk-3RB35GYK.js} +3 -3
  8. package/dist/{chunk-DWRIU4MW.js.map → chunk-3RB35GYK.js.map} +1 -1
  9. package/dist/{chunk-BT7SXNZU.cjs → chunk-4SM3I7WE.cjs} +3 -3
  10. package/dist/{chunk-BT7SXNZU.cjs.map → chunk-4SM3I7WE.cjs.map} +1 -1
  11. package/dist/{chunk-62FT5JRH.js → chunk-5H7SAEQO.js} +4 -4
  12. package/dist/{chunk-62FT5JRH.js.map → chunk-5H7SAEQO.js.map} +1 -1
  13. package/dist/{chunk-L2LH4CEN.js → chunk-5KJYGLVU.js} +3 -3
  14. package/dist/{chunk-L2LH4CEN.js.map → chunk-5KJYGLVU.js.map} +1 -1
  15. package/dist/{chunk-KRYJJMYN.js → chunk-6SAYPXHX.js} +3 -3
  16. package/dist/{chunk-KRYJJMYN.js.map → chunk-6SAYPXHX.js.map} +1 -1
  17. package/dist/{chunk-KTJETOOH.cjs → chunk-6XDPJWSC.cjs} +36 -36
  18. package/dist/{chunk-KTJETOOH.cjs.map → chunk-6XDPJWSC.cjs.map} +1 -1
  19. package/dist/{chunk-WUKXQZZR.cjs → chunk-7KN6J7XV.cjs} +8 -8
  20. package/dist/{chunk-WUKXQZZR.cjs.map → chunk-7KN6J7XV.cjs.map} +1 -1
  21. package/dist/{chunk-R4E6DMZS.js → chunk-AZLDTPYK.js} +3 -3
  22. package/dist/{chunk-R4E6DMZS.js.map → chunk-AZLDTPYK.js.map} +1 -1
  23. package/dist/chunk-BN75TLUN.cjs +163 -0
  24. package/dist/chunk-BN75TLUN.cjs.map +1 -0
  25. package/dist/{chunk-WWJIW2IH.cjs → chunk-BVMABVC4.cjs} +3 -3
  26. package/dist/{chunk-WWJIW2IH.cjs.map → chunk-BVMABVC4.cjs.map} +1 -1
  27. package/dist/{chunk-RS5VA6QU.cjs → chunk-C3DUS2VE.cjs} +3 -3
  28. package/dist/{chunk-RS5VA6QU.cjs.map → chunk-C3DUS2VE.cjs.map} +1 -1
  29. package/dist/{chunk-VIE7UI2R.cjs → chunk-CXDMZT45.cjs} +4 -2
  30. package/dist/chunk-CXDMZT45.cjs.map +1 -0
  31. package/dist/{chunk-TXRQQMG5.js → chunk-CYMZ2QS5.js} +2 -2
  32. package/dist/{chunk-TXRQQMG5.js.map → chunk-CYMZ2QS5.js.map} +1 -1
  33. package/dist/{chunk-KY2CZK5F.js → chunk-D25WH7MH.js} +2 -2
  34. package/dist/{chunk-KY2CZK5F.js.map → chunk-D25WH7MH.js.map} +1 -1
  35. package/dist/{chunk-ATOWMVZH.js → chunk-DFYMIRMW.js} +3 -3
  36. package/dist/{chunk-ATOWMVZH.js.map → chunk-DFYMIRMW.js.map} +1 -1
  37. package/dist/{chunk-PDS7OM73.js → chunk-DGRXAIWK.js} +4 -2
  38. package/dist/chunk-DGRXAIWK.js.map +1 -0
  39. package/dist/{chunk-W3N5XENB.cjs → chunk-FUFBRKOG.cjs} +24 -24
  40. package/dist/{chunk-W3N5XENB.cjs.map → chunk-FUFBRKOG.cjs.map} +1 -1
  41. package/dist/{chunk-UEDTZYGI.cjs → chunk-H4AVMUOC.cjs} +3 -3
  42. package/dist/{chunk-UEDTZYGI.cjs.map → chunk-H4AVMUOC.cjs.map} +1 -1
  43. package/dist/{chunk-JHR4PNUA.cjs → chunk-HIMVH5OH.cjs} +5 -5
  44. package/dist/{chunk-JHR4PNUA.cjs.map → chunk-HIMVH5OH.cjs.map} +1 -1
  45. package/dist/{chunk-X5ZPYYMS.js → chunk-I3DQXUSC.js} +3 -3
  46. package/dist/{chunk-X5ZPYYMS.js.map → chunk-I3DQXUSC.js.map} +1 -1
  47. package/dist/{chunk-LZVPVY5P.js → chunk-KU3XRHWA.js} +3 -3
  48. package/dist/{chunk-LZVPVY5P.js.map → chunk-KU3XRHWA.js.map} +1 -1
  49. package/dist/{chunk-P2VTQ5AR.cjs → chunk-L52N6BEN.cjs} +39 -23
  50. package/dist/chunk-L52N6BEN.cjs.map +1 -0
  51. package/dist/{chunk-SRMTRD7F.js → chunk-L5W7TJQ5.js} +3 -3
  52. package/dist/{chunk-SRMTRD7F.js.map → chunk-L5W7TJQ5.js.map} +1 -1
  53. package/dist/{chunk-Z5KFXHDP.cjs → chunk-MM7QKUE3.cjs} +5 -5
  54. package/dist/{chunk-Z5KFXHDP.cjs.map → chunk-MM7QKUE3.cjs.map} +1 -1
  55. package/dist/{chunk-FJ7YPVKV.js → chunk-MUIDWFYJ.js} +3 -3
  56. package/dist/{chunk-FJ7YPVKV.js.map → chunk-MUIDWFYJ.js.map} +1 -1
  57. package/dist/{chunk-MX46D4UJ.cjs → chunk-O7XHHTTP.cjs} +2 -2
  58. package/dist/{chunk-MX46D4UJ.cjs.map → chunk-O7XHHTTP.cjs.map} +1 -1
  59. package/dist/{chunk-7IBZA7G6.js → chunk-OAZUDUGX.js} +4 -4
  60. package/dist/{chunk-7IBZA7G6.js.map → chunk-OAZUDUGX.js.map} +1 -1
  61. package/dist/{chunk-NFKX6QJP.cjs → chunk-OCV33S5Q.cjs} +3 -3
  62. package/dist/{chunk-NFKX6QJP.cjs.map → chunk-OCV33S5Q.cjs.map} +1 -1
  63. package/dist/{chunk-334BUQMR.js → chunk-ODYQYLO2.js} +7 -7
  64. package/dist/{chunk-334BUQMR.js.map → chunk-ODYQYLO2.js.map} +1 -1
  65. package/dist/{chunk-FK2WTX7D.js → chunk-OHDIJOGG.js} +3 -3
  66. package/dist/{chunk-FK2WTX7D.js.map → chunk-OHDIJOGG.js.map} +1 -1
  67. package/dist/{chunk-GQVL2JKQ.cjs → chunk-OHKO5THF.cjs} +3 -3
  68. package/dist/{chunk-GQVL2JKQ.cjs.map → chunk-OHKO5THF.cjs.map} +1 -1
  69. package/dist/{chunk-LMBXJZLG.js → chunk-ON4JULCA.js} +3 -3
  70. package/dist/{chunk-LMBXJZLG.js.map → chunk-ON4JULCA.js.map} +1 -1
  71. package/dist/{chunk-57DPXKUV.js → chunk-ONNY7UO4.js} +4 -4
  72. package/dist/{chunk-57DPXKUV.js.map → chunk-ONNY7UO4.js.map} +1 -1
  73. package/dist/{chunk-KFEN2SRZ.js → chunk-PHK4XBO5.js} +3 -3
  74. package/dist/{chunk-KFEN2SRZ.js.map → chunk-PHK4XBO5.js.map} +1 -1
  75. package/dist/{chunk-XMCIXHGH.js → chunk-Q7G56ZIO.js} +39 -23
  76. package/dist/chunk-Q7G56ZIO.js.map +1 -0
  77. package/dist/{chunk-T5BBZQ66.js → chunk-QAKASRE6.js} +3 -3
  78. package/dist/{chunk-T5BBZQ66.js.map → chunk-QAKASRE6.js.map} +1 -1
  79. package/dist/{chunk-UBOVDAD7.js → chunk-QAQOCS2J.js} +3 -3
  80. package/dist/{chunk-UBOVDAD7.js.map → chunk-QAQOCS2J.js.map} +1 -1
  81. package/dist/{chunk-JWB4UA4J.cjs → chunk-QEJHAU7N.cjs} +3 -3
  82. package/dist/{chunk-JWB4UA4J.cjs.map → chunk-QEJHAU7N.cjs.map} +1 -1
  83. package/dist/{chunk-GLEIVJQB.cjs → chunk-QKAYRHWP.cjs} +9 -9
  84. package/dist/{chunk-GLEIVJQB.cjs.map → chunk-QKAYRHWP.cjs.map} +1 -1
  85. package/dist/{chunk-JWTCEWQ4.cjs → chunk-SNEQXSC5.cjs} +2 -2
  86. package/dist/{chunk-JWTCEWQ4.cjs.map → chunk-SNEQXSC5.cjs.map} +1 -1
  87. package/dist/{chunk-ZSGEGISB.cjs → chunk-SRA2L3SQ.cjs} +4 -4
  88. package/dist/{chunk-ZSGEGISB.cjs.map → chunk-SRA2L3SQ.cjs.map} +1 -1
  89. package/dist/{chunk-3SHLOF4V.cjs → chunk-VTR2KGLY.cjs} +9 -9
  90. package/dist/{chunk-3SHLOF4V.cjs.map → chunk-VTR2KGLY.cjs.map} +1 -1
  91. package/dist/chunk-WYMO3IIK.js +136 -0
  92. package/dist/chunk-WYMO3IIK.js.map +1 -0
  93. package/dist/{chunk-CFI3TQMA.cjs → chunk-X5QRRMGB.cjs} +3 -3
  94. package/dist/{chunk-CFI3TQMA.cjs.map → chunk-X5QRRMGB.cjs.map} +1 -1
  95. package/dist/{chunk-SH6B4MG5.js → chunk-XBXDUAIM.js} +4 -4
  96. package/dist/{chunk-SH6B4MG5.js.map → chunk-XBXDUAIM.js.map} +1 -1
  97. package/dist/{chunk-K5GQPTTA.cjs → chunk-XEEVDZDK.cjs} +3 -3
  98. package/dist/{chunk-K5GQPTTA.cjs.map → chunk-XEEVDZDK.cjs.map} +1 -1
  99. package/dist/{chunk-GXODR6V4.js → chunk-XOIIUO6O.js} +3 -3
  100. package/dist/{chunk-GXODR6V4.js.map → chunk-XOIIUO6O.js.map} +1 -1
  101. package/dist/{chunk-AHHFFXJR.cjs → chunk-XTHMC76F.cjs} +3 -3
  102. package/dist/{chunk-AHHFFXJR.cjs.map → chunk-XTHMC76F.cjs.map} +1 -1
  103. package/dist/{chunk-64SSWBJN.cjs → chunk-YJP6DUFO.cjs} +6 -6
  104. package/dist/{chunk-64SSWBJN.cjs.map → chunk-YJP6DUFO.cjs.map} +1 -1
  105. package/dist/components/Alert/index.d.cts +1 -1
  106. package/dist/components/Alert/index.d.ts +1 -1
  107. package/dist/components/AlertDialog/index.cjs +5 -4
  108. package/dist/components/AlertDialog/index.js +4 -3
  109. package/dist/components/Autocomplete/index.cjs +3 -3
  110. package/dist/components/Autocomplete/index.js +2 -2
  111. package/dist/components/Badge/index.d.cts +1 -1
  112. package/dist/components/Badge/index.d.ts +1 -1
  113. package/dist/components/Card/index.d.cts +2 -2
  114. package/dist/components/Card/index.d.ts +2 -2
  115. package/dist/components/CountryCodeDropdown/index.cjs +5 -5
  116. package/dist/components/CountryCodeDropdown/index.js +2 -2
  117. package/dist/components/CustomizableDashboard/index.cjs +10 -10
  118. package/dist/components/CustomizableDashboard/index.js +3 -3
  119. package/dist/components/DateInput/index.cjs +4 -4
  120. package/dist/components/DateInput/index.js +3 -3
  121. package/dist/components/Dropdown/index.cjs +9 -9
  122. package/dist/components/Dropdown/index.js +2 -2
  123. package/dist/components/GlossaryTooltip/index.cjs +3 -3
  124. package/dist/components/GlossaryTooltip/index.js +2 -2
  125. package/dist/components/KeyboardShortcutsOverlay/index.cjs +6 -5
  126. package/dist/components/KeyboardShortcutsOverlay/index.js +4 -3
  127. package/dist/components/Markdown/index.cjs +10 -10
  128. package/dist/components/Markdown/index.js +2 -2
  129. package/dist/components/MediaEditor/index.cjs +11 -10
  130. package/dist/components/MediaEditor/index.js +6 -5
  131. package/dist/components/MediaPlayer/index.cjs +1 -1
  132. package/dist/components/MediaPlayer/index.js +1 -1
  133. package/dist/components/Modal/index.cjs +11 -10
  134. package/dist/components/Modal/index.d.cts +1 -1
  135. package/dist/components/Modal/index.d.ts +1 -1
  136. package/dist/components/Modal/index.js +3 -2
  137. package/dist/components/Pagination/index.d.cts +1 -1
  138. package/dist/components/Pagination/index.d.ts +1 -1
  139. package/dist/components/PhoneInput/index.cjs +4 -4
  140. package/dist/components/PhoneInput/index.js +2 -2
  141. package/dist/components/Progress/index.d.cts +1 -1
  142. package/dist/components/Progress/index.d.ts +1 -1
  143. package/dist/components/RecordButton/index.d.cts +1 -1
  144. package/dist/components/RecordButton/index.d.ts +1 -1
  145. package/dist/components/RichTextEditor/index.cjs +7 -7
  146. package/dist/components/RichTextEditor/index.js +4 -4
  147. package/dist/components/SectionSpyNav/index.cjs +3 -3
  148. package/dist/components/SectionSpyNav/index.js +2 -2
  149. package/dist/components/Select/index.cjs +4 -4
  150. package/dist/components/Select/index.js +2 -2
  151. package/dist/components/Sheet/index.cjs +11 -11
  152. package/dist/components/Sheet/index.js +2 -2
  153. package/dist/components/Slider/index.d.cts +2 -2
  154. package/dist/components/Slider/index.d.ts +2 -2
  155. package/dist/components/SourceTip/index.cjs +3 -3
  156. package/dist/components/SourceTip/index.js +2 -2
  157. package/dist/components/SuperChat/index.cjs +10 -10
  158. package/dist/components/SuperChat/index.js +4 -4
  159. package/dist/components/SuperChat/plugins/index.cjs +4 -4
  160. package/dist/components/SuperChat/plugins/index.js +3 -3
  161. package/dist/components/Text/index.d.cts +1 -1
  162. package/dist/components/Text/index.d.ts +1 -1
  163. package/dist/components/ThemeProvider/index.cjs +9 -9
  164. package/dist/components/ThemeProvider/index.d.cts +1 -1
  165. package/dist/components/ThemeProvider/index.d.ts +1 -1
  166. package/dist/components/ThemeProvider/index.js +3 -3
  167. package/dist/components/Toggle/index.d.cts +1 -1
  168. package/dist/components/Toggle/index.d.ts +1 -1
  169. package/dist/components/Tooltip/index.cjs +3 -3
  170. package/dist/components/Tooltip/index.js +2 -2
  171. package/dist/esheet.cjs +12 -11
  172. package/dist/esheet.cjs.map +1 -1
  173. package/dist/esheet.js +9 -8
  174. package/dist/esheet.js.map +1 -1
  175. package/dist/hooks/index.cjs +1 -1
  176. package/dist/hooks/index.js +1 -1
  177. package/dist/index.cjs +631 -570
  178. package/dist/index.cjs.map +1 -1
  179. package/dist/index.d.cts +5 -4
  180. package/dist/index.d.ts +5 -4
  181. package/dist/index.js +125 -79
  182. package/dist/index.js.map +1 -1
  183. package/dist/motion.cjs +87 -0
  184. package/dist/motion.cjs.map +1 -0
  185. package/dist/motion.d.cts +52 -0
  186. package/dist/motion.d.ts +52 -0
  187. package/dist/motion.js +50 -0
  188. package/dist/motion.js.map +1 -0
  189. package/dist/styles.css +1 -1
  190. package/dist/tailwind-preset.cjs +4 -4
  191. package/dist/tailwind-preset.js +1 -1
  192. package/package.json +16 -1
  193. package/dist/chunk-P2VTQ5AR.cjs.map +0 -1
  194. package/dist/chunk-PDS7OM73.js.map +0 -1
  195. package/dist/chunk-VIE7UI2R.cjs.map +0 -1
  196. package/dist/chunk-XMCIXHGH.js.map +0 -1
@@ -0,0 +1,175 @@
1
+ import * as React from 'react';
2
+
3
+ /**
4
+ * Motion presets — the shared vocabulary of animations.
5
+ *
6
+ * This module is intentionally **free of any `motion` import**. It ships in the
7
+ * main `@mieweb/ui` entry so components can describe *what* they animate
8
+ * without pulling in a runtime to actually do it. The `@mieweb/ui/motion` entry
9
+ * is what turns these descriptions into real animations.
10
+ *
11
+ * Every preset is expressed as an open/closed variant pair so the same
12
+ * definition serves both usages:
13
+ *
14
+ * - **toggle** — the element stays mounted and animates between `open` and
15
+ * `closed` (a drawer sliding in and out).
16
+ * - **presence** — the element mounts and unmounts, entering on `open` and
17
+ * exiting back to `closed` (a modal).
18
+ */
19
+ /** Named animation recipes shared by components. */
20
+ type MotionPreset = 'drawerStart' | 'overlay' | 'modalContent' | 'sidebarLabel';
21
+ /**
22
+ * A variant value. Either a plain target object or a function of the element's
23
+ * `custom` value — the latter lets a single preset adapt to context, e.g. a
24
+ * drawer flipping its off-canvas direction in RTL.
25
+ */
26
+ type MotionVariant = Record<string, unknown> | ((custom: unknown) => Record<string, unknown>);
27
+ interface MotionPresetDefinition {
28
+ variants: {
29
+ open: MotionVariant;
30
+ closed: MotionVariant;
31
+ };
32
+ transition: Record<string, unknown>;
33
+ }
34
+ /**
35
+ * `custom` contract per preset:
36
+ *
37
+ * - `drawerStart` — `boolean`, whether the document direction is RTL.
38
+ * - `overlay`, `modalContent`, `sidebarLabel` — unused.
39
+ */
40
+ declare const motionPresets: Record<MotionPreset, MotionPresetDefinition>;
41
+
42
+ /**
43
+ * Motion runtime — the seam between components and an optional animation library.
44
+ *
45
+ * Components in the main entry never import `motion`. They ask this module for
46
+ * a runtime; if an app has opted in by rendering `<MotionProvider>` from
47
+ * `@mieweb/ui/motion`, they get one and animate. If not, `useMotionRuntime()`
48
+ * returns `null` and they fall back to their existing CSS transitions. That is
49
+ * what keeps `motion` out of the bundle of every app that hasn't asked for it —
50
+ * the opt-in is resolved in the *module graph*, not by a prop.
51
+ *
52
+ * Like `presets`, this module must stay free of any `motion` import.
53
+ */
54
+
55
+ interface MotionPresenceProps {
56
+ children?: React.ReactNode;
57
+ /** Suppress the initial mount animation. */
58
+ initial?: boolean;
59
+ /**
60
+ * Fires once every exiting child has finished and been removed.
61
+ *
62
+ * Needed by anything that guards the DOM while it is open — a focus trap, a
63
+ * scroll lock — because those guards must outlive `open` for as long as the
64
+ * subtree is still on screen. Never fires when no runtime is active, since
65
+ * there is no exit to wait for.
66
+ */
67
+ onExitComplete?: () => void;
68
+ }
69
+ /**
70
+ * The capabilities a motion library must supply. Deliberately small: element
71
+ * factories, a presence wrapper, and prop builders. Components depend on this
72
+ * interface, never on a concrete library, so swapping or dropping the library
73
+ * stays a one-file change.
74
+ */
75
+ interface MotionRuntime {
76
+ /**
77
+ * Whether animations should actually run.
78
+ *
79
+ * A disabled provider still supplies a runtime rather than `null` so that
80
+ * consumers can tell "disabled" apart from "never opted in": `Modal` uses it
81
+ * to decide whether an exit animation will ever report completion, and
82
+ * `AnimatedPresence` keeps its wrapper element stable across the flip.
83
+ * `Animated` itself renders the plain fallback tag whenever this is false —
84
+ * withholding animation props from a mounted motion component corrupts
85
+ * `toggle`-mode elements, so the swap (and the remount it implies) is the
86
+ * correct behaviour. See the render branches in `Animated`.
87
+ */
88
+ enabled: boolean;
89
+ /** Animated `div`. */
90
+ Div: React.ElementType;
91
+ /** Animated `nav`. */
92
+ Nav: React.ElementType;
93
+ /** Animated `span`, for inline content that must not become a block. */
94
+ Span: React.ElementType;
95
+ /** Enables exit animations for conditionally rendered children. */
96
+ Presence: React.ComponentType<MotionPresenceProps>;
97
+ /**
98
+ * Props for an element that stays mounted and animates between states.
99
+ * Skips the initial animation so a closed drawer doesn't slide out on load.
100
+ */
101
+ toggle: (preset: MotionPreset, open: boolean, custom?: unknown) => Record<string, unknown>;
102
+ /** Props for an element that mounts and unmounts inside `Presence`. */
103
+ presence: (preset: MotionPreset, custom?: unknown) => Record<string, unknown>;
104
+ }
105
+ /**
106
+ * The active motion runtime, or `null` when the app has not opted in.
107
+ *
108
+ * Components must treat `null` as the normal case and keep a CSS path that
109
+ * looks good on its own — motion is an enhancement, never a requirement.
110
+ */
111
+ declare function useMotionRuntime(): MotionRuntime | null;
112
+
113
+ /**
114
+ * `Animated` / `AnimatedPresence` — the ergonomic layer components use.
115
+ *
116
+ * These exist so an individual component never has to branch on whether motion
117
+ * is available. It declares the preset it wants and the CSS classes that stand
118
+ * in when motion is absent; this module picks the element to render.
119
+ *
120
+ * Adding motion to a further component should be a matter of swapping its
121
+ * root `<div>` for an `<Animated>` and naming a preset — no new imports, no
122
+ * new dependency, and no behavior change for apps that have not opted in.
123
+ */
124
+
125
+ interface AnimatedProps extends React.HTMLAttributes<HTMLElement> {
126
+ /** Intrinsic element to render. */
127
+ as?: 'div' | 'nav' | 'span';
128
+ /** Which shared animation recipe to use. */
129
+ preset: MotionPreset;
130
+ /**
131
+ * - `toggle` — element stays mounted and animates between open and closed.
132
+ * - `presence` — element mounts and unmounts; wrap it in `<AnimatedPresence>`
133
+ * so the exit animation gets a chance to run.
134
+ */
135
+ mode: 'toggle' | 'presence';
136
+ /** Current state. Only read in `toggle` mode. */
137
+ open?: boolean;
138
+ /** Per-preset context value — see the `custom` contract in `presets.ts`. */
139
+ custom?: unknown;
140
+ /**
141
+ * Classes applied whenever this element is **not** animating — no provider,
142
+ * a disabled provider, or `enabled={false}`. This is where a component's
143
+ * existing CSS transition lives, so the two paths never both try to drive
144
+ * the same property.
145
+ */
146
+ fallbackClassName?: string;
147
+ /**
148
+ * Set `false` to stop this element animating even when a runtime is active.
149
+ *
150
+ * For states where an animated element would be wrong rather than merely
151
+ * unnecessary. Motion writes a `transform` to hold an element at its resting
152
+ * position, and a transformed ancestor becomes the containing block for any
153
+ * `position: fixed` descendant — so an element that is only sometimes
154
+ * animated should opt out the rest of the time.
155
+ *
156
+ * Flipping this (or the provider's `disabled`) swaps the element back to a
157
+ * plain tag, which **remounts** it — deliberately. See the note on the
158
+ * render branches below.
159
+ */
160
+ enabled?: boolean;
161
+ }
162
+ declare const Animated: React.ForwardRefExoticComponent<AnimatedProps & React.RefAttributes<HTMLElement>>;
163
+ /**
164
+ * Keeps unmounting children on screen long enough to animate out.
165
+ *
166
+ * Without a runtime this is a pass-through, so children disappear immediately —
167
+ * which is exactly what they do today. Children rendered conditionally inside
168
+ * it need a stable `key`.
169
+ */
170
+ declare function AnimatedPresence({ children, initial, onExitComplete, }: MotionPresenceProps): React.JSX.Element;
171
+ declare namespace AnimatedPresence {
172
+ var displayName: string;
173
+ }
174
+
175
+ export { Animated as A, type MotionPreset as M, AnimatedPresence as a, type AnimatedProps as b, type MotionRuntime as c, type MotionPresenceProps as d, type MotionPresetDefinition as e, type MotionVariant as f, motionPresets as m, useMotionRuntime as u };
@@ -0,0 +1,175 @@
1
+ import * as React from 'react';
2
+
3
+ /**
4
+ * Motion presets — the shared vocabulary of animations.
5
+ *
6
+ * This module is intentionally **free of any `motion` import**. It ships in the
7
+ * main `@mieweb/ui` entry so components can describe *what* they animate
8
+ * without pulling in a runtime to actually do it. The `@mieweb/ui/motion` entry
9
+ * is what turns these descriptions into real animations.
10
+ *
11
+ * Every preset is expressed as an open/closed variant pair so the same
12
+ * definition serves both usages:
13
+ *
14
+ * - **toggle** — the element stays mounted and animates between `open` and
15
+ * `closed` (a drawer sliding in and out).
16
+ * - **presence** — the element mounts and unmounts, entering on `open` and
17
+ * exiting back to `closed` (a modal).
18
+ */
19
+ /** Named animation recipes shared by components. */
20
+ type MotionPreset = 'drawerStart' | 'overlay' | 'modalContent' | 'sidebarLabel';
21
+ /**
22
+ * A variant value. Either a plain target object or a function of the element's
23
+ * `custom` value — the latter lets a single preset adapt to context, e.g. a
24
+ * drawer flipping its off-canvas direction in RTL.
25
+ */
26
+ type MotionVariant = Record<string, unknown> | ((custom: unknown) => Record<string, unknown>);
27
+ interface MotionPresetDefinition {
28
+ variants: {
29
+ open: MotionVariant;
30
+ closed: MotionVariant;
31
+ };
32
+ transition: Record<string, unknown>;
33
+ }
34
+ /**
35
+ * `custom` contract per preset:
36
+ *
37
+ * - `drawerStart` — `boolean`, whether the document direction is RTL.
38
+ * - `overlay`, `modalContent`, `sidebarLabel` — unused.
39
+ */
40
+ declare const motionPresets: Record<MotionPreset, MotionPresetDefinition>;
41
+
42
+ /**
43
+ * Motion runtime — the seam between components and an optional animation library.
44
+ *
45
+ * Components in the main entry never import `motion`. They ask this module for
46
+ * a runtime; if an app has opted in by rendering `<MotionProvider>` from
47
+ * `@mieweb/ui/motion`, they get one and animate. If not, `useMotionRuntime()`
48
+ * returns `null` and they fall back to their existing CSS transitions. That is
49
+ * what keeps `motion` out of the bundle of every app that hasn't asked for it —
50
+ * the opt-in is resolved in the *module graph*, not by a prop.
51
+ *
52
+ * Like `presets`, this module must stay free of any `motion` import.
53
+ */
54
+
55
+ interface MotionPresenceProps {
56
+ children?: React.ReactNode;
57
+ /** Suppress the initial mount animation. */
58
+ initial?: boolean;
59
+ /**
60
+ * Fires once every exiting child has finished and been removed.
61
+ *
62
+ * Needed by anything that guards the DOM while it is open — a focus trap, a
63
+ * scroll lock — because those guards must outlive `open` for as long as the
64
+ * subtree is still on screen. Never fires when no runtime is active, since
65
+ * there is no exit to wait for.
66
+ */
67
+ onExitComplete?: () => void;
68
+ }
69
+ /**
70
+ * The capabilities a motion library must supply. Deliberately small: element
71
+ * factories, a presence wrapper, and prop builders. Components depend on this
72
+ * interface, never on a concrete library, so swapping or dropping the library
73
+ * stays a one-file change.
74
+ */
75
+ interface MotionRuntime {
76
+ /**
77
+ * Whether animations should actually run.
78
+ *
79
+ * A disabled provider still supplies a runtime rather than `null` so that
80
+ * consumers can tell "disabled" apart from "never opted in": `Modal` uses it
81
+ * to decide whether an exit animation will ever report completion, and
82
+ * `AnimatedPresence` keeps its wrapper element stable across the flip.
83
+ * `Animated` itself renders the plain fallback tag whenever this is false —
84
+ * withholding animation props from a mounted motion component corrupts
85
+ * `toggle`-mode elements, so the swap (and the remount it implies) is the
86
+ * correct behaviour. See the render branches in `Animated`.
87
+ */
88
+ enabled: boolean;
89
+ /** Animated `div`. */
90
+ Div: React.ElementType;
91
+ /** Animated `nav`. */
92
+ Nav: React.ElementType;
93
+ /** Animated `span`, for inline content that must not become a block. */
94
+ Span: React.ElementType;
95
+ /** Enables exit animations for conditionally rendered children. */
96
+ Presence: React.ComponentType<MotionPresenceProps>;
97
+ /**
98
+ * Props for an element that stays mounted and animates between states.
99
+ * Skips the initial animation so a closed drawer doesn't slide out on load.
100
+ */
101
+ toggle: (preset: MotionPreset, open: boolean, custom?: unknown) => Record<string, unknown>;
102
+ /** Props for an element that mounts and unmounts inside `Presence`. */
103
+ presence: (preset: MotionPreset, custom?: unknown) => Record<string, unknown>;
104
+ }
105
+ /**
106
+ * The active motion runtime, or `null` when the app has not opted in.
107
+ *
108
+ * Components must treat `null` as the normal case and keep a CSS path that
109
+ * looks good on its own — motion is an enhancement, never a requirement.
110
+ */
111
+ declare function useMotionRuntime(): MotionRuntime | null;
112
+
113
+ /**
114
+ * `Animated` / `AnimatedPresence` — the ergonomic layer components use.
115
+ *
116
+ * These exist so an individual component never has to branch on whether motion
117
+ * is available. It declares the preset it wants and the CSS classes that stand
118
+ * in when motion is absent; this module picks the element to render.
119
+ *
120
+ * Adding motion to a further component should be a matter of swapping its
121
+ * root `<div>` for an `<Animated>` and naming a preset — no new imports, no
122
+ * new dependency, and no behavior change for apps that have not opted in.
123
+ */
124
+
125
+ interface AnimatedProps extends React.HTMLAttributes<HTMLElement> {
126
+ /** Intrinsic element to render. */
127
+ as?: 'div' | 'nav' | 'span';
128
+ /** Which shared animation recipe to use. */
129
+ preset: MotionPreset;
130
+ /**
131
+ * - `toggle` — element stays mounted and animates between open and closed.
132
+ * - `presence` — element mounts and unmounts; wrap it in `<AnimatedPresence>`
133
+ * so the exit animation gets a chance to run.
134
+ */
135
+ mode: 'toggle' | 'presence';
136
+ /** Current state. Only read in `toggle` mode. */
137
+ open?: boolean;
138
+ /** Per-preset context value — see the `custom` contract in `presets.ts`. */
139
+ custom?: unknown;
140
+ /**
141
+ * Classes applied whenever this element is **not** animating — no provider,
142
+ * a disabled provider, or `enabled={false}`. This is where a component's
143
+ * existing CSS transition lives, so the two paths never both try to drive
144
+ * the same property.
145
+ */
146
+ fallbackClassName?: string;
147
+ /**
148
+ * Set `false` to stop this element animating even when a runtime is active.
149
+ *
150
+ * For states where an animated element would be wrong rather than merely
151
+ * unnecessary. Motion writes a `transform` to hold an element at its resting
152
+ * position, and a transformed ancestor becomes the containing block for any
153
+ * `position: fixed` descendant — so an element that is only sometimes
154
+ * animated should opt out the rest of the time.
155
+ *
156
+ * Flipping this (or the provider's `disabled`) swaps the element back to a
157
+ * plain tag, which **remounts** it — deliberately. See the note on the
158
+ * render branches below.
159
+ */
160
+ enabled?: boolean;
161
+ }
162
+ declare const Animated: React.ForwardRefExoticComponent<AnimatedProps & React.RefAttributes<HTMLElement>>;
163
+ /**
164
+ * Keeps unmounting children on screen long enough to animate out.
165
+ *
166
+ * Without a runtime this is a pass-through, so children disappear immediately —
167
+ * which is exactly what they do today. Children rendered conditionally inside
168
+ * it need a stable `key`.
169
+ */
170
+ declare function AnimatedPresence({ children, initial, onExitComplete, }: MotionPresenceProps): React.JSX.Element;
171
+ declare namespace AnimatedPresence {
172
+ var displayName: string;
173
+ }
174
+
175
+ export { Animated as A, type MotionPreset as M, AnimatedPresence as a, type AnimatedProps as b, type MotionRuntime as c, type MotionPresenceProps as d, type MotionPresetDefinition as e, type MotionVariant as f, motionPresets as m, useMotionRuntime as u };
@@ -1,11 +1,11 @@
1
1
  'use strict';
2
2
 
3
- var chunkJWTCEWQ4_cjs = require('../chunk-JWTCEWQ4.cjs');
3
+ var chunkSNEQXSC5_cjs = require('../chunk-SNEQXSC5.cjs');
4
+ var chunkU7VQUOLS_cjs = require('../chunk-U7VQUOLS.cjs');
4
5
  var chunkX7S76WB7_cjs = require('../chunk-X7S76WB7.cjs');
5
6
  var chunkP52GA3GJ_cjs = require('../chunk-P52GA3GJ.cjs');
6
7
  var chunkLCQGAOAZ_cjs = require('../chunk-LCQGAOAZ.cjs');
7
8
  var chunkZ5J4NTPL_cjs = require('../chunk-Z5J4NTPL.cjs');
8
- var chunkU7VQUOLS_cjs = require('../chunk-U7VQUOLS.cjs');
9
9
  var chunkJ644FU54_cjs = require('../chunk-J644FU54.cjs');
10
10
  var chunkWNJRS7Y7_cjs = require('../chunk-WNJRS7Y7.cjs');
11
11
  var chunkUCGUA3QV_cjs = require('../chunk-UCGUA3QV.cjs');
@@ -15,7 +15,11 @@ var chunkRR2MJBGV_cjs = require('../chunk-RR2MJBGV.cjs');
15
15
 
16
16
  Object.defineProperty(exports, "brands", {
17
17
  enumerable: true,
18
- get: function () { return chunkJWTCEWQ4_cjs.brands; }
18
+ get: function () { return chunkSNEQXSC5_cjs.brands; }
19
+ });
20
+ Object.defineProperty(exports, "webchartBrand", {
21
+ enumerable: true,
22
+ get: function () { return chunkU7VQUOLS_cjs.webchartBrand; }
19
23
  });
20
24
  Object.defineProperty(exports, "defaultBrand", {
21
25
  enumerable: true,
@@ -33,10 +37,6 @@ Object.defineProperty(exports, "wagglelineBrand", {
33
37
  enumerable: true,
34
38
  get: function () { return chunkZ5J4NTPL_cjs.wagglelineBrand; }
35
39
  });
36
- Object.defineProperty(exports, "webchartBrand", {
37
- enumerable: true,
38
- get: function () { return chunkU7VQUOLS_cjs.webchartBrand; }
39
- });
40
40
  Object.defineProperty(exports, "createBrandPreset", {
41
41
  enumerable: true,
42
42
  get: function () { return chunkJ644FU54_cjs.createBrandPreset; }
@@ -1,9 +1,9 @@
1
- export { brands } from '../chunk-TXRQQMG5.js';
1
+ export { brands } from '../chunk-CYMZ2QS5.js';
2
+ export { webchartBrand } from '../chunk-CW66SO5O.js';
2
3
  export { defaultBrand } from '../chunk-PSTGFXQG.js';
3
4
  export { enterpriseHealthBrand } from '../chunk-MTZPVOP6.js';
4
5
  export { miewebBrand } from '../chunk-NERS5ZT6.js';
5
6
  export { wagglelineBrand } from '../chunk-2NTYAALC.js';
6
- export { webchartBrand } from '../chunk-CW66SO5O.js';
7
7
  export { createBrandPreset, generateBrandCSS, generateTailwindTheme } from '../chunk-WOYUQ4AT.js';
8
8
  export { bluehiveBrand } from '../chunk-3T7QGLNB.js';
9
9
  export { ccmeBrand } from '../chunk-SCQRJPSV.js';
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
3
  var chunkIINZWUGK_cjs = require('./chunk-IINZWUGK.cjs');
4
- var chunk7IKLGH3Z_cjs = require('./chunk-7IKLGH3Z.cjs');
5
4
  var chunkS5RVSXDV_cjs = require('./chunk-S5RVSXDV.cjs');
5
+ var chunk7IKLGH3Z_cjs = require('./chunk-7IKLGH3Z.cjs');
6
6
  var React = require('react');
7
7
  var lucideReact = require('lucide-react');
8
8
  var jsxRuntime = require('react/jsx-runtime');
@@ -160,5 +160,5 @@ function SectionSpyNav({
160
160
  }
161
161
 
162
162
  exports.SectionSpyNav = SectionSpyNav;
163
- //# sourceMappingURL=chunk-YIOZQEMV.cjs.map
164
- //# sourceMappingURL=chunk-YIOZQEMV.cjs.map
163
+ //# sourceMappingURL=chunk-3LHICE4I.cjs.map
164
+ //# sourceMappingURL=chunk-3LHICE4I.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/SectionSpyNav/SectionSpyNav.tsx"],"names":["ArrowDown","ArrowRight","ArrowUpRight","jsx","React","useScrollSpy","cn","jsxs","buttonVariants"],"mappings":"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","file":"chunk-YIOZQEMV.cjs","sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { ArrowDown, ArrowRight, ArrowUpRight } from 'lucide-react';\nimport { cn } from '../../utils/cn';\nimport { useScrollSpy } from '../../hooks/useScrollSpy';\nimport { buttonVariants } from '../Button';\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/** One section the nav links to. The id must match a `section[id]` on the page. */\nexport interface SectionSpyItem {\n id: string;\n label: string;\n}\n\n/**\n * Funnel intent for the bar's single contextual CTA — drives its visual\n * weight so the in-page strip never out-shouts a page's primary CTA:\n * `explore` (quiet next step) → `evaluate` (sales-ready ask) → `commit`\n * (high-intent action).\n */\nexport type SectionSpyCtaTier = 'explore' | 'evaluate' | 'commit';\n\nexport interface SectionSpyCta {\n label: string;\n href: string;\n tier?: SectionSpyCtaTier;\n}\n\nexport interface SectionSpyNavProps {\n /** Sections to link to, in page order. */\n items: SectionSpyItem[];\n /** Optional single page-specific next-step CTA at the end of the bar. */\n cta?: SectionSpyCta;\n /** Eyebrow before the links (default \"On this page\"). */\n label?: string;\n /**\n * Visual tone: `surface` sits on the page background; `brand` renders the\n * inverted primary band.\n */\n tone?: 'surface' | 'brand';\n /** IntersectionObserver root margin tuning for the scroll spy. */\n rootMargin?: string;\n /** Called with the section id when a nav link is clicked. */\n onItemClick?: (id: string) => void;\n /** Called when the CTA is clicked (e.g. for analytics). */\n onCtaClick?: (cta: SectionSpyCta) => void;\n className?: string;\n}\n\n// =============================================================================\n// Internals\n// =============================================================================\n\n/** Directional glyph inferred from the destination. */\nfunction CtaArrow({ href }: { href: string }) {\n const Icon = href.startsWith('#')\n ? ArrowDown\n : href.startsWith('/')\n ? ArrowRight\n : ArrowUpRight;\n // rtl:-scale-x-100 mirrors ArrowRight and ArrowUpRight for RTL reading\n // direction; ArrowDown is horizontally symmetric so the flip is a no-op.\n return <Icon aria-hidden=\"true\" className=\"h-3.5 w-3.5 rtl:-scale-x-100\" />;\n}\n\nconst CTA_TIER_VARIANT = {\n explore: 'ghost',\n evaluate: 'outline',\n commit: 'primary',\n} as const;\n\n// =============================================================================\n// SectionSpyNav\n// =============================================================================\n\n/**\n * Sticky horizontal in-page wayfinding: jumps between a page's major\n * sections, highlights the one in view with a sliding underline, and can\n * carry a single page-specific next-step CTA. Pure anchor links, so it\n * still works if the scroll spy never runs.\n *\n * The horizontal complement to `TableOfContents` — use that for a sidebar\n * outline, this for a band under the page header. Section elements need\n * matching `id`s.\n *\n * @example\n * ```tsx\n * <SectionSpyNav\n * items={[\n * { id: 'overview', label: 'Overview' },\n * { id: 'pricing', label: 'Pricing' },\n * ]}\n * cta={{ label: 'Book a demo', href: '/demo', tier: 'evaluate' }}\n * />\n * ```\n */\nexport function SectionSpyNav({\n items,\n cta,\n label = 'On this page',\n tone = 'surface',\n rootMargin = '-22% 0px -68% 0px',\n onItemClick,\n onCtaClick,\n className,\n}: SectionSpyNavProps) {\n const ids = React.useMemo(() => items.map((it) => it.id), [items]);\n const { activeId } = useScrollSpy({ ids, rootMargin });\n const active = activeId ?? items[0]?.id ?? '';\n\n const railRef = React.useRef<HTMLDivElement>(null);\n const markRef = React.useRef<HTMLSpanElement>(null);\n\n // Find the active link by dataset rather than a CSS selector, so ids\n // never need escaping and CSS.escape availability doesn't matter.\n const findActiveLink = React.useCallback((): HTMLAnchorElement | null => {\n const links = railRef.current?.querySelectorAll<HTMLAnchorElement>('a');\n if (!links) return null;\n for (const link of links) {\n if (link.dataset.id === active) return link;\n }\n return null;\n }, [active]);\n\n // Slide the underline beneath the active link. offsetLeft/offsetWidth are\n // physical and measured against the rail, so the marker tracks the link\n // in both directions and while the rail is scrolled horizontally.\n const syncMarker = React.useCallback(() => {\n const link = findActiveLink();\n const mark = markRef.current;\n if (!link || !mark) return;\n mark.style.left = `${link.offsetLeft}px`;\n mark.style.width = `${link.offsetWidth}px`;\n }, [findActiveLink]);\n\n React.useEffect(() => {\n const link = findActiveLink();\n link?.scrollIntoView?.({ inline: 'center', block: 'nearest' });\n syncMarker();\n }, [findActiveLink, syncMarker]);\n\n React.useEffect(() => {\n window.addEventListener('resize', syncMarker);\n return () => window.removeEventListener('resize', syncMarker);\n }, [syncMarker]);\n\n const brand = tone === 'brand';\n\n return (\n <nav\n className={cn(\n 'sticky top-0 z-30 w-full',\n brand\n ? 'bg-primary-900 text-white'\n : 'border-border bg-card/95 border-b backdrop-blur',\n className\n )}\n aria-label={label}\n >\n <div className=\"flex items-center gap-4 px-4 py-0.5\">\n <span\n className={cn(\n 'shrink-0 text-[11px] font-semibold tracking-wide uppercase max-md:hidden',\n brand ? 'text-white/85' : 'text-muted-foreground'\n )}\n aria-hidden=\"true\"\n >\n {label}\n </span>\n <div\n ref={railRef}\n className=\"relative flex min-w-0 flex-1 gap-1 overflow-x-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden\"\n >\n {items.map((it) => {\n const isActive = active === it.id;\n return (\n <a\n key={it.id}\n href={`#${it.id}`}\n data-id={it.id}\n onClick={() => onItemClick?.(it.id)}\n className={cn(\n 'shrink-0 rounded px-2.5 py-2 text-sm whitespace-nowrap transition-colors',\n brand\n ? isActive\n ? 'font-semibold text-white'\n : 'text-white/85 hover:text-white'\n : isActive\n ? 'text-foreground font-semibold'\n : 'text-muted-foreground hover:text-foreground'\n )}\n aria-current={isActive ? 'location' : undefined}\n >\n {it.label}\n </a>\n );\n })}\n <span\n ref={markRef}\n className={cn(\n 'absolute bottom-0 h-0.5 rounded-full transition-[left,width] duration-300',\n brand ? 'bg-primary-400' : 'bg-primary-500'\n )}\n aria-hidden=\"true\"\n />\n </div>\n {cta && (\n <a\n href={cta.href}\n onClick={() => onCtaClick?.(cta)}\n className={cn(\n buttonVariants({\n variant: CTA_TIER_VARIANT[cta.tier ?? 'explore'],\n size: 'sm',\n }),\n 'shrink-0',\n brand &&\n (cta.tier ?? 'explore') !== 'commit' &&\n 'text-white hover:bg-white/10 hover:text-white'\n )}\n >\n {cta.label}\n <CtaArrow href={cta.href} />\n </a>\n )}\n </div>\n </nav>\n );\n}\n"]}
1
+ {"version":3,"sources":["../src/components/SectionSpyNav/SectionSpyNav.tsx"],"names":["ArrowDown","ArrowRight","ArrowUpRight","jsx","React","useScrollSpy","cn","jsxs","buttonVariants"],"mappings":"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","file":"chunk-3LHICE4I.cjs","sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { ArrowDown, ArrowRight, ArrowUpRight } from 'lucide-react';\nimport { cn } from '../../utils/cn';\nimport { useScrollSpy } from '../../hooks/useScrollSpy';\nimport { buttonVariants } from '../Button';\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/** One section the nav links to. The id must match a `section[id]` on the page. */\nexport interface SectionSpyItem {\n id: string;\n label: string;\n}\n\n/**\n * Funnel intent for the bar's single contextual CTA — drives its visual\n * weight so the in-page strip never out-shouts a page's primary CTA:\n * `explore` (quiet next step) → `evaluate` (sales-ready ask) → `commit`\n * (high-intent action).\n */\nexport type SectionSpyCtaTier = 'explore' | 'evaluate' | 'commit';\n\nexport interface SectionSpyCta {\n label: string;\n href: string;\n tier?: SectionSpyCtaTier;\n}\n\nexport interface SectionSpyNavProps {\n /** Sections to link to, in page order. */\n items: SectionSpyItem[];\n /** Optional single page-specific next-step CTA at the end of the bar. */\n cta?: SectionSpyCta;\n /** Eyebrow before the links (default \"On this page\"). */\n label?: string;\n /**\n * Visual tone: `surface` sits on the page background; `brand` renders the\n * inverted primary band.\n */\n tone?: 'surface' | 'brand';\n /** IntersectionObserver root margin tuning for the scroll spy. */\n rootMargin?: string;\n /** Called with the section id when a nav link is clicked. */\n onItemClick?: (id: string) => void;\n /** Called when the CTA is clicked (e.g. for analytics). */\n onCtaClick?: (cta: SectionSpyCta) => void;\n className?: string;\n}\n\n// =============================================================================\n// Internals\n// =============================================================================\n\n/** Directional glyph inferred from the destination. */\nfunction CtaArrow({ href }: { href: string }) {\n const Icon = href.startsWith('#')\n ? ArrowDown\n : href.startsWith('/')\n ? ArrowRight\n : ArrowUpRight;\n // rtl:-scale-x-100 mirrors ArrowRight and ArrowUpRight for RTL reading\n // direction; ArrowDown is horizontally symmetric so the flip is a no-op.\n return <Icon aria-hidden=\"true\" className=\"h-3.5 w-3.5 rtl:-scale-x-100\" />;\n}\n\nconst CTA_TIER_VARIANT = {\n explore: 'ghost',\n evaluate: 'outline',\n commit: 'primary',\n} as const;\n\n// =============================================================================\n// SectionSpyNav\n// =============================================================================\n\n/**\n * Sticky horizontal in-page wayfinding: jumps between a page's major\n * sections, highlights the one in view with a sliding underline, and can\n * carry a single page-specific next-step CTA. Pure anchor links, so it\n * still works if the scroll spy never runs.\n *\n * The horizontal complement to `TableOfContents` — use that for a sidebar\n * outline, this for a band under the page header. Section elements need\n * matching `id`s.\n *\n * @example\n * ```tsx\n * <SectionSpyNav\n * items={[\n * { id: 'overview', label: 'Overview' },\n * { id: 'pricing', label: 'Pricing' },\n * ]}\n * cta={{ label: 'Book a demo', href: '/demo', tier: 'evaluate' }}\n * />\n * ```\n */\nexport function SectionSpyNav({\n items,\n cta,\n label = 'On this page',\n tone = 'surface',\n rootMargin = '-22% 0px -68% 0px',\n onItemClick,\n onCtaClick,\n className,\n}: SectionSpyNavProps) {\n const ids = React.useMemo(() => items.map((it) => it.id), [items]);\n const { activeId } = useScrollSpy({ ids, rootMargin });\n const active = activeId ?? items[0]?.id ?? '';\n\n const railRef = React.useRef<HTMLDivElement>(null);\n const markRef = React.useRef<HTMLSpanElement>(null);\n\n // Find the active link by dataset rather than a CSS selector, so ids\n // never need escaping and CSS.escape availability doesn't matter.\n const findActiveLink = React.useCallback((): HTMLAnchorElement | null => {\n const links = railRef.current?.querySelectorAll<HTMLAnchorElement>('a');\n if (!links) return null;\n for (const link of links) {\n if (link.dataset.id === active) return link;\n }\n return null;\n }, [active]);\n\n // Slide the underline beneath the active link. offsetLeft/offsetWidth are\n // physical and measured against the rail, so the marker tracks the link\n // in both directions and while the rail is scrolled horizontally.\n const syncMarker = React.useCallback(() => {\n const link = findActiveLink();\n const mark = markRef.current;\n if (!link || !mark) return;\n mark.style.left = `${link.offsetLeft}px`;\n mark.style.width = `${link.offsetWidth}px`;\n }, [findActiveLink]);\n\n React.useEffect(() => {\n const link = findActiveLink();\n link?.scrollIntoView?.({ inline: 'center', block: 'nearest' });\n syncMarker();\n }, [findActiveLink, syncMarker]);\n\n React.useEffect(() => {\n window.addEventListener('resize', syncMarker);\n return () => window.removeEventListener('resize', syncMarker);\n }, [syncMarker]);\n\n const brand = tone === 'brand';\n\n return (\n <nav\n className={cn(\n 'sticky top-0 z-30 w-full',\n brand\n ? 'bg-primary-900 text-white'\n : 'border-border bg-card/95 border-b backdrop-blur',\n className\n )}\n aria-label={label}\n >\n <div className=\"flex items-center gap-4 px-4 py-0.5\">\n <span\n className={cn(\n 'shrink-0 text-[11px] font-semibold tracking-wide uppercase max-md:hidden',\n brand ? 'text-white/85' : 'text-muted-foreground'\n )}\n aria-hidden=\"true\"\n >\n {label}\n </span>\n <div\n ref={railRef}\n className=\"relative flex min-w-0 flex-1 gap-1 overflow-x-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden\"\n >\n {items.map((it) => {\n const isActive = active === it.id;\n return (\n <a\n key={it.id}\n href={`#${it.id}`}\n data-id={it.id}\n onClick={() => onItemClick?.(it.id)}\n className={cn(\n 'shrink-0 rounded px-2.5 py-2 text-sm whitespace-nowrap transition-colors',\n brand\n ? isActive\n ? 'font-semibold text-white'\n : 'text-white/85 hover:text-white'\n : isActive\n ? 'text-foreground font-semibold'\n : 'text-muted-foreground hover:text-foreground'\n )}\n aria-current={isActive ? 'location' : undefined}\n >\n {it.label}\n </a>\n );\n })}\n <span\n ref={markRef}\n className={cn(\n 'absolute bottom-0 h-0.5 rounded-full transition-[left,width] duration-300',\n brand ? 'bg-primary-400' : 'bg-primary-500'\n )}\n aria-hidden=\"true\"\n />\n </div>\n {cta && (\n <a\n href={cta.href}\n onClick={() => onCtaClick?.(cta)}\n className={cn(\n buttonVariants({\n variant: CTA_TIER_VARIANT[cta.tier ?? 'explore'],\n size: 'sm',\n }),\n 'shrink-0',\n brand &&\n (cta.tier ?? 'explore') !== 'commit' &&\n 'text-white hover:bg-white/10 hover:text-white'\n )}\n >\n {cta.label}\n <CtaArrow href={cta.href} />\n </a>\n )}\n </div>\n </nav>\n );\n}\n"]}
@@ -1,6 +1,6 @@
1
1
  import { buttonVariants } from './chunk-KREUYVMZ.js';
2
- import { useScrollSpy } from './chunk-WBGUYSWY.js';
3
2
  import { cn } from './chunk-TQONQSJO.js';
3
+ import { useScrollSpy } from './chunk-WBGUYSWY.js';
4
4
  import * as React from 'react';
5
5
  import { ArrowDown, ArrowRight, ArrowUpRight } from 'lucide-react';
6
6
  import { jsx, jsxs } from 'react/jsx-runtime';
@@ -138,5 +138,5 @@ function SectionSpyNav({
138
138
  }
139
139
 
140
140
  export { SectionSpyNav };
141
- //# sourceMappingURL=chunk-DWRIU4MW.js.map
142
- //# sourceMappingURL=chunk-DWRIU4MW.js.map
141
+ //# sourceMappingURL=chunk-3RB35GYK.js.map
142
+ //# sourceMappingURL=chunk-3RB35GYK.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/SectionSpyNav/SectionSpyNav.tsx"],"names":[],"mappings":";;;;;;;AA0DA,SAAS,QAAA,CAAS,EAAE,IAAA,EAAK,EAAqB;AAC5C,EAAA,MAAM,IAAA,GAAO,IAAA,CAAK,UAAA,CAAW,GAAG,CAAA,GAC5B,YACA,IAAA,CAAK,UAAA,CAAW,GAAG,CAAA,GACjB,UAAA,GACA,YAAA;AAGN,EAAA,uBAAO,GAAA,CAAC,IAAA,EAAA,EAAK,aAAA,EAAY,MAAA,EAAO,WAAU,8BAAA,EAA+B,CAAA;AAC3E;AAEA,IAAM,gBAAA,GAAmB;AAAA,EACvB,OAAA,EAAS,OAAA;AAAA,EACT,QAAA,EAAU,SAAA;AAAA,EACV,MAAA,EAAQ;AACV,CAAA;AA2BO,SAAS,aAAA,CAAc;AAAA,EAC5B,KAAA;AAAA,EACA,GAAA;AAAA,EACA,KAAA,GAAQ,cAAA;AAAA,EACR,IAAA,GAAO,SAAA;AAAA,EACP,UAAA,GAAa,mBAAA;AAAA,EACb,WAAA;AAAA,EACA,UAAA;AAAA,EACA;AACF,CAAA,EAAuB;AACrB,EAAA,MAAM,GAAA,GAAY,KAAA,CAAA,OAAA,CAAQ,MAAM,KAAA,CAAM,GAAA,CAAI,CAAC,EAAA,KAAO,EAAA,CAAG,EAAE,CAAA,EAAG,CAAC,KAAK,CAAC,CAAA;AACjE,EAAA,MAAM,EAAE,QAAA,EAAS,GAAI,aAAa,EAAE,GAAA,EAAK,YAAY,CAAA;AACrD,EAAA,MAAM,MAAA,GAAS,QAAA,IAAY,KAAA,CAAM,CAAC,GAAG,EAAA,IAAM,EAAA;AAE3C,EAAA,MAAM,OAAA,GAAgB,aAAuB,IAAI,CAAA;AACjD,EAAA,MAAM,OAAA,GAAgB,aAAwB,IAAI,CAAA;AAIlD,EAAA,MAAM,cAAA,GAAuB,kBAAY,MAAgC;AACvE,IAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,OAAA,EAAS,gBAAA,CAAoC,GAAG,CAAA;AACtE,IAAA,IAAI,CAAC,OAAO,OAAO,IAAA;AACnB,IAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,MAAA,IAAI,IAAA,CAAK,OAAA,CAAQ,EAAA,KAAO,MAAA,EAAQ,OAAO,IAAA;AAAA,IACzC;AACA,IAAA,OAAO,IAAA;AAAA,EACT,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AAKX,EAAA,MAAM,UAAA,GAAmB,kBAAY,MAAM;AACzC,IAAA,MAAM,OAAO,cAAA,EAAe;AAC5B,IAAA,MAAM,OAAO,OAAA,CAAQ,OAAA;AACrB,IAAA,IAAI,CAAC,IAAA,IAAQ,CAAC,IAAA,EAAM;AACpB,IAAA,IAAA,CAAK,KAAA,CAAM,IAAA,GAAO,CAAA,EAAG,IAAA,CAAK,UAAU,CAAA,EAAA,CAAA;AACpC,IAAA,IAAA,CAAK,KAAA,CAAM,KAAA,GAAQ,CAAA,EAAG,IAAA,CAAK,WAAW,CAAA,EAAA,CAAA;AAAA,EACxC,CAAA,EAAG,CAAC,cAAc,CAAC,CAAA;AAEnB,EAAM,gBAAU,MAAM;AACpB,IAAA,MAAM,OAAO,cAAA,EAAe;AAC5B,IAAA,IAAA,EAAM,iBAAiB,EAAE,MAAA,EAAQ,QAAA,EAAU,KAAA,EAAO,WAAW,CAAA;AAC7D,IAAA,UAAA,EAAW;AAAA,EACb,CAAA,EAAG,CAAC,cAAA,EAAgB,UAAU,CAAC,CAAA;AAE/B,EAAM,gBAAU,MAAM;AACpB,IAAA,MAAA,CAAO,gBAAA,CAAiB,UAAU,UAAU,CAAA;AAC5C,IAAA,OAAO,MAAM,MAAA,CAAO,mBAAA,CAAoB,QAAA,EAAU,UAAU,CAAA;AAAA,EAC9D,CAAA,EAAG,CAAC,UAAU,CAAC,CAAA;AAEf,EAAA,MAAM,QAAQ,IAAA,KAAS,OAAA;AAEvB,EAAA,uBACE,GAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAW,EAAA;AAAA,QACT,0BAAA;AAAA,QACA,QACI,2BAAA,GACA,iDAAA;AAAA,QACJ;AAAA,OACF;AAAA,MACA,YAAA,EAAY,KAAA;AAAA,MAEZ,QAAA,kBAAA,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,qCAAA,EACb,QAAA,EAAA;AAAA,wBAAA,GAAA;AAAA,UAAC,MAAA;AAAA,UAAA;AAAA,YACC,SAAA,EAAW,EAAA;AAAA,cACT,0EAAA;AAAA,cACA,QAAQ,eAAA,GAAkB;AAAA,aAC5B;AAAA,YACA,aAAA,EAAY,MAAA;AAAA,YAEX,QAAA,EAAA;AAAA;AAAA,SACH;AAAA,wBACA,IAAA;AAAA,UAAC,KAAA;AAAA,UAAA;AAAA,YACC,GAAA,EAAK,OAAA;AAAA,YACL,SAAA,EAAU,yGAAA;AAAA,YAET,QAAA,EAAA;AAAA,cAAA,KAAA,CAAM,GAAA,CAAI,CAAC,EAAA,KAAO;AACjB,gBAAA,MAAM,QAAA,GAAW,WAAW,EAAA,CAAG,EAAA;AAC/B,gBAAA,uBACE,GAAA;AAAA,kBAAC,GAAA;AAAA,kBAAA;AAAA,oBAEC,IAAA,EAAM,CAAA,CAAA,EAAI,EAAA,CAAG,EAAE,CAAA,CAAA;AAAA,oBACf,WAAS,EAAA,CAAG,EAAA;AAAA,oBACZ,OAAA,EAAS,MAAM,WAAA,GAAc,EAAA,CAAG,EAAE,CAAA;AAAA,oBAClC,SAAA,EAAW,EAAA;AAAA,sBACT,0EAAA;AAAA,sBACA,KAAA,GACI,QAAA,GACE,0BAAA,GACA,gCAAA,GACF,WACE,+BAAA,GACA;AAAA,qBACR;AAAA,oBACA,cAAA,EAAc,WAAW,UAAA,GAAa,MAAA;AAAA,oBAErC,QAAA,EAAA,EAAA,CAAG;AAAA,mBAAA;AAAA,kBAhBC,EAAA,CAAG;AAAA,iBAiBV;AAAA,cAEJ,CAAC,CAAA;AAAA,8BACD,GAAA;AAAA,gBAAC,MAAA;AAAA,gBAAA;AAAA,kBACC,GAAA,EAAK,OAAA;AAAA,kBACL,SAAA,EAAW,EAAA;AAAA,oBACT,2EAAA;AAAA,oBACA,QAAQ,gBAAA,GAAmB;AAAA,mBAC7B;AAAA,kBACA,aAAA,EAAY;AAAA;AAAA;AACd;AAAA;AAAA,SACF;AAAA,QACC,GAAA,oBACC,IAAA;AAAA,UAAC,GAAA;AAAA,UAAA;AAAA,YACC,MAAM,GAAA,CAAI,IAAA;AAAA,YACV,OAAA,EAAS,MAAM,UAAA,GAAa,GAAG,CAAA;AAAA,YAC/B,SAAA,EAAW,EAAA;AAAA,cACT,cAAA,CAAe;AAAA,gBACb,OAAA,EAAS,gBAAA,CAAiB,GAAA,CAAI,IAAA,IAAQ,SAAS,CAAA;AAAA,gBAC/C,IAAA,EAAM;AAAA,eACP,CAAA;AAAA,cACD,UAAA;AAAA,cACA,KAAA,IAAA,CACG,GAAA,CAAI,IAAA,IAAQ,SAAA,MAAe,QAAA,IAC5B;AAAA,aACJ;AAAA,YAEC,QAAA,EAAA;AAAA,cAAA,GAAA,CAAI,KAAA;AAAA,8BACL,GAAA,CAAC,QAAA,EAAA,EAAS,IAAA,EAAM,GAAA,CAAI,IAAA,EAAM;AAAA;AAAA;AAAA;AAC5B,OAAA,EAEJ;AAAA;AAAA,GACF;AAEJ","file":"chunk-DWRIU4MW.js","sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { ArrowDown, ArrowRight, ArrowUpRight } from 'lucide-react';\nimport { cn } from '../../utils/cn';\nimport { useScrollSpy } from '../../hooks/useScrollSpy';\nimport { buttonVariants } from '../Button';\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/** One section the nav links to. The id must match a `section[id]` on the page. */\nexport interface SectionSpyItem {\n id: string;\n label: string;\n}\n\n/**\n * Funnel intent for the bar's single contextual CTA — drives its visual\n * weight so the in-page strip never out-shouts a page's primary CTA:\n * `explore` (quiet next step) → `evaluate` (sales-ready ask) → `commit`\n * (high-intent action).\n */\nexport type SectionSpyCtaTier = 'explore' | 'evaluate' | 'commit';\n\nexport interface SectionSpyCta {\n label: string;\n href: string;\n tier?: SectionSpyCtaTier;\n}\n\nexport interface SectionSpyNavProps {\n /** Sections to link to, in page order. */\n items: SectionSpyItem[];\n /** Optional single page-specific next-step CTA at the end of the bar. */\n cta?: SectionSpyCta;\n /** Eyebrow before the links (default \"On this page\"). */\n label?: string;\n /**\n * Visual tone: `surface` sits on the page background; `brand` renders the\n * inverted primary band.\n */\n tone?: 'surface' | 'brand';\n /** IntersectionObserver root margin tuning for the scroll spy. */\n rootMargin?: string;\n /** Called with the section id when a nav link is clicked. */\n onItemClick?: (id: string) => void;\n /** Called when the CTA is clicked (e.g. for analytics). */\n onCtaClick?: (cta: SectionSpyCta) => void;\n className?: string;\n}\n\n// =============================================================================\n// Internals\n// =============================================================================\n\n/** Directional glyph inferred from the destination. */\nfunction CtaArrow({ href }: { href: string }) {\n const Icon = href.startsWith('#')\n ? ArrowDown\n : href.startsWith('/')\n ? ArrowRight\n : ArrowUpRight;\n // rtl:-scale-x-100 mirrors ArrowRight and ArrowUpRight for RTL reading\n // direction; ArrowDown is horizontally symmetric so the flip is a no-op.\n return <Icon aria-hidden=\"true\" className=\"h-3.5 w-3.5 rtl:-scale-x-100\" />;\n}\n\nconst CTA_TIER_VARIANT = {\n explore: 'ghost',\n evaluate: 'outline',\n commit: 'primary',\n} as const;\n\n// =============================================================================\n// SectionSpyNav\n// =============================================================================\n\n/**\n * Sticky horizontal in-page wayfinding: jumps between a page's major\n * sections, highlights the one in view with a sliding underline, and can\n * carry a single page-specific next-step CTA. Pure anchor links, so it\n * still works if the scroll spy never runs.\n *\n * The horizontal complement to `TableOfContents` — use that for a sidebar\n * outline, this for a band under the page header. Section elements need\n * matching `id`s.\n *\n * @example\n * ```tsx\n * <SectionSpyNav\n * items={[\n * { id: 'overview', label: 'Overview' },\n * { id: 'pricing', label: 'Pricing' },\n * ]}\n * cta={{ label: 'Book a demo', href: '/demo', tier: 'evaluate' }}\n * />\n * ```\n */\nexport function SectionSpyNav({\n items,\n cta,\n label = 'On this page',\n tone = 'surface',\n rootMargin = '-22% 0px -68% 0px',\n onItemClick,\n onCtaClick,\n className,\n}: SectionSpyNavProps) {\n const ids = React.useMemo(() => items.map((it) => it.id), [items]);\n const { activeId } = useScrollSpy({ ids, rootMargin });\n const active = activeId ?? items[0]?.id ?? '';\n\n const railRef = React.useRef<HTMLDivElement>(null);\n const markRef = React.useRef<HTMLSpanElement>(null);\n\n // Find the active link by dataset rather than a CSS selector, so ids\n // never need escaping and CSS.escape availability doesn't matter.\n const findActiveLink = React.useCallback((): HTMLAnchorElement | null => {\n const links = railRef.current?.querySelectorAll<HTMLAnchorElement>('a');\n if (!links) return null;\n for (const link of links) {\n if (link.dataset.id === active) return link;\n }\n return null;\n }, [active]);\n\n // Slide the underline beneath the active link. offsetLeft/offsetWidth are\n // physical and measured against the rail, so the marker tracks the link\n // in both directions and while the rail is scrolled horizontally.\n const syncMarker = React.useCallback(() => {\n const link = findActiveLink();\n const mark = markRef.current;\n if (!link || !mark) return;\n mark.style.left = `${link.offsetLeft}px`;\n mark.style.width = `${link.offsetWidth}px`;\n }, [findActiveLink]);\n\n React.useEffect(() => {\n const link = findActiveLink();\n link?.scrollIntoView?.({ inline: 'center', block: 'nearest' });\n syncMarker();\n }, [findActiveLink, syncMarker]);\n\n React.useEffect(() => {\n window.addEventListener('resize', syncMarker);\n return () => window.removeEventListener('resize', syncMarker);\n }, [syncMarker]);\n\n const brand = tone === 'brand';\n\n return (\n <nav\n className={cn(\n 'sticky top-0 z-30 w-full',\n brand\n ? 'bg-primary-900 text-white'\n : 'border-border bg-card/95 border-b backdrop-blur',\n className\n )}\n aria-label={label}\n >\n <div className=\"flex items-center gap-4 px-4 py-0.5\">\n <span\n className={cn(\n 'shrink-0 text-[11px] font-semibold tracking-wide uppercase max-md:hidden',\n brand ? 'text-white/85' : 'text-muted-foreground'\n )}\n aria-hidden=\"true\"\n >\n {label}\n </span>\n <div\n ref={railRef}\n className=\"relative flex min-w-0 flex-1 gap-1 overflow-x-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden\"\n >\n {items.map((it) => {\n const isActive = active === it.id;\n return (\n <a\n key={it.id}\n href={`#${it.id}`}\n data-id={it.id}\n onClick={() => onItemClick?.(it.id)}\n className={cn(\n 'shrink-0 rounded px-2.5 py-2 text-sm whitespace-nowrap transition-colors',\n brand\n ? isActive\n ? 'font-semibold text-white'\n : 'text-white/85 hover:text-white'\n : isActive\n ? 'text-foreground font-semibold'\n : 'text-muted-foreground hover:text-foreground'\n )}\n aria-current={isActive ? 'location' : undefined}\n >\n {it.label}\n </a>\n );\n })}\n <span\n ref={markRef}\n className={cn(\n 'absolute bottom-0 h-0.5 rounded-full transition-[left,width] duration-300',\n brand ? 'bg-primary-400' : 'bg-primary-500'\n )}\n aria-hidden=\"true\"\n />\n </div>\n {cta && (\n <a\n href={cta.href}\n onClick={() => onCtaClick?.(cta)}\n className={cn(\n buttonVariants({\n variant: CTA_TIER_VARIANT[cta.tier ?? 'explore'],\n size: 'sm',\n }),\n 'shrink-0',\n brand &&\n (cta.tier ?? 'explore') !== 'commit' &&\n 'text-white hover:bg-white/10 hover:text-white'\n )}\n >\n {cta.label}\n <CtaArrow href={cta.href} />\n </a>\n )}\n </div>\n </nav>\n );\n}\n"]}
1
+ {"version":3,"sources":["../src/components/SectionSpyNav/SectionSpyNav.tsx"],"names":[],"mappings":"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","file":"chunk-3RB35GYK.js","sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { ArrowDown, ArrowRight, ArrowUpRight } from 'lucide-react';\nimport { cn } from '../../utils/cn';\nimport { useScrollSpy } from '../../hooks/useScrollSpy';\nimport { buttonVariants } from '../Button';\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/** One section the nav links to. The id must match a `section[id]` on the page. */\nexport interface SectionSpyItem {\n id: string;\n label: string;\n}\n\n/**\n * Funnel intent for the bar's single contextual CTA — drives its visual\n * weight so the in-page strip never out-shouts a page's primary CTA:\n * `explore` (quiet next step) → `evaluate` (sales-ready ask) → `commit`\n * (high-intent action).\n */\nexport type SectionSpyCtaTier = 'explore' | 'evaluate' | 'commit';\n\nexport interface SectionSpyCta {\n label: string;\n href: string;\n tier?: SectionSpyCtaTier;\n}\n\nexport interface SectionSpyNavProps {\n /** Sections to link to, in page order. */\n items: SectionSpyItem[];\n /** Optional single page-specific next-step CTA at the end of the bar. */\n cta?: SectionSpyCta;\n /** Eyebrow before the links (default \"On this page\"). */\n label?: string;\n /**\n * Visual tone: `surface` sits on the page background; `brand` renders the\n * inverted primary band.\n */\n tone?: 'surface' | 'brand';\n /** IntersectionObserver root margin tuning for the scroll spy. */\n rootMargin?: string;\n /** Called with the section id when a nav link is clicked. */\n onItemClick?: (id: string) => void;\n /** Called when the CTA is clicked (e.g. for analytics). */\n onCtaClick?: (cta: SectionSpyCta) => void;\n className?: string;\n}\n\n// =============================================================================\n// Internals\n// =============================================================================\n\n/** Directional glyph inferred from the destination. */\nfunction CtaArrow({ href }: { href: string }) {\n const Icon = href.startsWith('#')\n ? ArrowDown\n : href.startsWith('/')\n ? ArrowRight\n : ArrowUpRight;\n // rtl:-scale-x-100 mirrors ArrowRight and ArrowUpRight for RTL reading\n // direction; ArrowDown is horizontally symmetric so the flip is a no-op.\n return <Icon aria-hidden=\"true\" className=\"h-3.5 w-3.5 rtl:-scale-x-100\" />;\n}\n\nconst CTA_TIER_VARIANT = {\n explore: 'ghost',\n evaluate: 'outline',\n commit: 'primary',\n} as const;\n\n// =============================================================================\n// SectionSpyNav\n// =============================================================================\n\n/**\n * Sticky horizontal in-page wayfinding: jumps between a page's major\n * sections, highlights the one in view with a sliding underline, and can\n * carry a single page-specific next-step CTA. Pure anchor links, so it\n * still works if the scroll spy never runs.\n *\n * The horizontal complement to `TableOfContents` — use that for a sidebar\n * outline, this for a band under the page header. Section elements need\n * matching `id`s.\n *\n * @example\n * ```tsx\n * <SectionSpyNav\n * items={[\n * { id: 'overview', label: 'Overview' },\n * { id: 'pricing', label: 'Pricing' },\n * ]}\n * cta={{ label: 'Book a demo', href: '/demo', tier: 'evaluate' }}\n * />\n * ```\n */\nexport function SectionSpyNav({\n items,\n cta,\n label = 'On this page',\n tone = 'surface',\n rootMargin = '-22% 0px -68% 0px',\n onItemClick,\n onCtaClick,\n className,\n}: SectionSpyNavProps) {\n const ids = React.useMemo(() => items.map((it) => it.id), [items]);\n const { activeId } = useScrollSpy({ ids, rootMargin });\n const active = activeId ?? items[0]?.id ?? '';\n\n const railRef = React.useRef<HTMLDivElement>(null);\n const markRef = React.useRef<HTMLSpanElement>(null);\n\n // Find the active link by dataset rather than a CSS selector, so ids\n // never need escaping and CSS.escape availability doesn't matter.\n const findActiveLink = React.useCallback((): HTMLAnchorElement | null => {\n const links = railRef.current?.querySelectorAll<HTMLAnchorElement>('a');\n if (!links) return null;\n for (const link of links) {\n if (link.dataset.id === active) return link;\n }\n return null;\n }, [active]);\n\n // Slide the underline beneath the active link. offsetLeft/offsetWidth are\n // physical and measured against the rail, so the marker tracks the link\n // in both directions and while the rail is scrolled horizontally.\n const syncMarker = React.useCallback(() => {\n const link = findActiveLink();\n const mark = markRef.current;\n if (!link || !mark) return;\n mark.style.left = `${link.offsetLeft}px`;\n mark.style.width = `${link.offsetWidth}px`;\n }, [findActiveLink]);\n\n React.useEffect(() => {\n const link = findActiveLink();\n link?.scrollIntoView?.({ inline: 'center', block: 'nearest' });\n syncMarker();\n }, [findActiveLink, syncMarker]);\n\n React.useEffect(() => {\n window.addEventListener('resize', syncMarker);\n return () => window.removeEventListener('resize', syncMarker);\n }, [syncMarker]);\n\n const brand = tone === 'brand';\n\n return (\n <nav\n className={cn(\n 'sticky top-0 z-30 w-full',\n brand\n ? 'bg-primary-900 text-white'\n : 'border-border bg-card/95 border-b backdrop-blur',\n className\n )}\n aria-label={label}\n >\n <div className=\"flex items-center gap-4 px-4 py-0.5\">\n <span\n className={cn(\n 'shrink-0 text-[11px] font-semibold tracking-wide uppercase max-md:hidden',\n brand ? 'text-white/85' : 'text-muted-foreground'\n )}\n aria-hidden=\"true\"\n >\n {label}\n </span>\n <div\n ref={railRef}\n className=\"relative flex min-w-0 flex-1 gap-1 overflow-x-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden\"\n >\n {items.map((it) => {\n const isActive = active === it.id;\n return (\n <a\n key={it.id}\n href={`#${it.id}`}\n data-id={it.id}\n onClick={() => onItemClick?.(it.id)}\n className={cn(\n 'shrink-0 rounded px-2.5 py-2 text-sm whitespace-nowrap transition-colors',\n brand\n ? isActive\n ? 'font-semibold text-white'\n : 'text-white/85 hover:text-white'\n : isActive\n ? 'text-foreground font-semibold'\n : 'text-muted-foreground hover:text-foreground'\n )}\n aria-current={isActive ? 'location' : undefined}\n >\n {it.label}\n </a>\n );\n })}\n <span\n ref={markRef}\n className={cn(\n 'absolute bottom-0 h-0.5 rounded-full transition-[left,width] duration-300',\n brand ? 'bg-primary-400' : 'bg-primary-500'\n )}\n aria-hidden=\"true\"\n />\n </div>\n {cta && (\n <a\n href={cta.href}\n onClick={() => onCtaClick?.(cta)}\n className={cn(\n buttonVariants({\n variant: CTA_TIER_VARIANT[cta.tier ?? 'explore'],\n size: 'sm',\n }),\n 'shrink-0',\n brand &&\n (cta.tier ?? 'explore') !== 'commit' &&\n 'text-white hover:bg-white/10 hover:text-white'\n )}\n >\n {cta.label}\n <CtaArrow href={cta.href} />\n </a>\n )}\n </div>\n </nav>\n );\n}\n"]}
@@ -1,10 +1,10 @@
1
1
  'use strict';
2
2
 
3
3
  var chunkZ5S5JEJD_cjs = require('./chunk-Z5S5JEJD.cjs');
4
+ var chunkS5RVSXDV_cjs = require('./chunk-S5RVSXDV.cjs');
4
5
  var chunkFHY3K6PL_cjs = require('./chunk-FHY3K6PL.cjs');
5
6
  var chunkFSGEDDF2_cjs = require('./chunk-FSGEDDF2.cjs');
6
7
  var chunkAOHJILLU_cjs = require('./chunk-AOHJILLU.cjs');
7
- var chunkS5RVSXDV_cjs = require('./chunk-S5RVSXDV.cjs');
8
8
  var React = require('react');
9
9
  var reactDom = require('react-dom');
10
10
  var lucideReact = require('lucide-react');
@@ -794,5 +794,5 @@ exports.DropdownItem = DropdownItem;
794
794
  exports.DropdownLabel = DropdownLabel;
795
795
  exports.DropdownSeparator = DropdownSeparator;
796
796
  exports.DropdownSubmenu = DropdownSubmenu;
797
- //# sourceMappingURL=chunk-BT7SXNZU.cjs.map
798
- //# sourceMappingURL=chunk-BT7SXNZU.cjs.map
797
+ //# sourceMappingURL=chunk-4SM3I7WE.cjs.map
798
+ //# sourceMappingURL=chunk-4SM3I7WE.cjs.map