@salesmind-ai/design-system 0.6.0 → 1.0.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 (257) hide show
  1. package/README.md +2 -0
  2. package/dist/Anton-Regular-MLEXVTB2.woff2 +0 -0
  3. package/dist/admin/index.cjs +5 -61
  4. package/dist/admin/index.css +0 -3918
  5. package/dist/admin/index.css.map +1 -1
  6. package/dist/admin/index.d.cts +1 -422
  7. package/dist/admin/index.d.ts +1 -422
  8. package/dist/admin/index.js +1 -5
  9. package/dist/blog/index.cjs +13 -34
  10. package/dist/blog/index.css +0 -579
  11. package/dist/blog/index.css.map +1 -1
  12. package/dist/blog/index.d.cts +1 -54
  13. package/dist/blog/index.d.ts +1 -54
  14. package/dist/blog/index.js +5 -6
  15. package/dist/charts/index.cjs +0 -46
  16. package/dist/charts/index.d.cts +1 -452
  17. package/dist/charts/index.d.ts +1 -452
  18. package/dist/charts/index.js +1 -3
  19. package/dist/{chunk-YTYDQBVY.cjs → chunk-2VVRZBUR.cjs} +4 -4
  20. package/dist/{chunk-GQELL2MF.cjs → chunk-3NS6X2R4.cjs} +20 -203
  21. package/dist/chunk-3NS6X2R4.cjs.map +1 -0
  22. package/dist/{chunk-XEX2AEZK.cjs → chunk-65DTHLVX.cjs} +66 -186
  23. package/dist/chunk-65DTHLVX.cjs.map +1 -0
  24. package/dist/{chunk-QALDZ7WQ.js → chunk-6BUS7RMS.js} +21 -198
  25. package/dist/chunk-6BUS7RMS.js.map +1 -0
  26. package/dist/{chunk-LTPTW2US.cjs → chunk-6BXKRDK5.cjs} +587 -977
  27. package/dist/chunk-6BXKRDK5.cjs.map +1 -0
  28. package/dist/{chunk-BJZ2DKS5.cjs → chunk-6QIQCUYC.cjs} +11 -10
  29. package/dist/chunk-6QIQCUYC.cjs.map +1 -0
  30. package/dist/{chunk-H2Y6BSTL.cjs → chunk-7EUR3AKV.cjs} +1 -1
  31. package/dist/chunk-7EUR3AKV.cjs.map +1 -0
  32. package/dist/{chunk-VFJZQQZU.js → chunk-AMNY5TS3.js} +11 -10
  33. package/dist/chunk-AMNY5TS3.js.map +1 -0
  34. package/dist/{chunk-YJ6C3EKW.js → chunk-CLXLQCNQ.js} +52 -168
  35. package/dist/chunk-CLXLQCNQ.js.map +1 -0
  36. package/dist/{chunk-H2KQ3WSH.cjs → chunk-CVLD5RQK.cjs} +12 -14
  37. package/dist/chunk-CVLD5RQK.cjs.map +1 -0
  38. package/dist/chunk-EPD4ZEPY.cjs +344 -0
  39. package/dist/chunk-EPD4ZEPY.cjs.map +1 -0
  40. package/dist/{chunk-6D22TFLA.cjs → chunk-FVSL5YMB.cjs} +16 -32
  41. package/dist/chunk-FVSL5YMB.cjs.map +1 -0
  42. package/dist/chunk-FXYOSA4E.cjs +118 -0
  43. package/dist/chunk-FXYOSA4E.cjs.map +1 -0
  44. package/dist/{chunk-ECXBTUH6.cjs → chunk-GPHQGLR5.cjs} +27 -204
  45. package/dist/chunk-GPHQGLR5.cjs.map +1 -0
  46. package/dist/chunk-JPUJWI7F.cjs +73 -0
  47. package/dist/chunk-JPUJWI7F.cjs.map +1 -0
  48. package/dist/{chunk-6UNG76Y2.js → chunk-K526GN7P.js} +2 -2
  49. package/dist/{chunk-SICKWUWB.js → chunk-KJHPOB3J.js} +1 -1
  50. package/dist/chunk-KJHPOB3J.js.map +1 -0
  51. package/dist/{chunk-Y26OHHMX.js → chunk-KSEETC4E.js} +508 -891
  52. package/dist/chunk-KSEETC4E.js.map +1 -0
  53. package/dist/chunk-KXVFFEGD.js +60 -0
  54. package/dist/chunk-KXVFFEGD.js.map +1 -0
  55. package/dist/chunk-LQB7QLD3.js +288 -0
  56. package/dist/chunk-LQB7QLD3.js.map +1 -0
  57. package/dist/chunk-LUD52ZJF.cjs +726 -0
  58. package/dist/chunk-LUD52ZJF.cjs.map +1 -0
  59. package/dist/{chunk-7UZ5DETZ.js → chunk-MBAG654R.js} +4 -216
  60. package/dist/chunk-MBAG654R.js.map +1 -0
  61. package/dist/chunk-OMP6FAZ6.cjs +183 -0
  62. package/dist/chunk-OMP6FAZ6.cjs.map +1 -0
  63. package/dist/{chunk-WYH4TKS5.js → chunk-PBYRTNQ5.js} +6 -8
  64. package/dist/chunk-PBYRTNQ5.js.map +1 -0
  65. package/dist/chunk-PYREXCZK.js +679 -0
  66. package/dist/chunk-PYREXCZK.js.map +1 -0
  67. package/dist/{chunk-P5BOFE5A.js → chunk-RSLA2FJN.js} +28 -183
  68. package/dist/chunk-RSLA2FJN.js.map +1 -0
  69. package/dist/chunk-S46SKHMD.js +173 -0
  70. package/dist/chunk-S46SKHMD.js.map +1 -0
  71. package/dist/chunk-SFXTB7JL.js +190 -0
  72. package/dist/chunk-SFXTB7JL.js.map +1 -0
  73. package/dist/chunk-SGYXYMKZ.cjs +214 -0
  74. package/dist/chunk-SGYXYMKZ.cjs.map +1 -0
  75. package/dist/chunk-UGKYP6F3.cjs +296 -0
  76. package/dist/chunk-UGKYP6F3.cjs.map +1 -0
  77. package/dist/{chunk-HDVAMYSG.js → chunk-VFO2MUPI.js} +14 -29
  78. package/dist/chunk-VFO2MUPI.js.map +1 -0
  79. package/dist/chunk-WB6XDNU7.js +115 -0
  80. package/dist/chunk-WB6XDNU7.js.map +1 -0
  81. package/dist/core/index.cjs +144 -626
  82. package/dist/core/index.css +178 -3567
  83. package/dist/core/index.css.map +1 -1
  84. package/dist/core/index.d.cts +940 -902
  85. package/dist/core/index.d.ts +940 -902
  86. package/dist/core/index.js +6 -12
  87. package/dist/i18n/index.cjs +54 -49
  88. package/dist/i18n/index.d.cts +46 -11
  89. package/dist/i18n/index.d.ts +46 -11
  90. package/dist/i18n/index.js +2 -1
  91. package/dist/index-C8A3X92-.d.cts +1100 -0
  92. package/dist/index-wZPBPkOV.d.ts +1100 -0
  93. package/dist/index.cjs +506 -1004
  94. package/dist/index.cjs.map +1 -1
  95. package/dist/index.css +6026 -16790
  96. package/dist/index.css.map +1 -1
  97. package/dist/index.d.cts +307 -22
  98. package/dist/index.d.ts +307 -22
  99. package/dist/index.js +274 -39
  100. package/dist/index.js.map +1 -1
  101. package/dist/marketing/index.cjs +33 -76
  102. package/dist/marketing/index.css +1896 -3234
  103. package/dist/marketing/index.css.map +1 -1
  104. package/dist/marketing/index.d.cts +3 -1351
  105. package/dist/marketing/index.d.ts +3 -1351
  106. package/dist/marketing/index.js +5 -8
  107. package/dist/motion/index.cjs +3 -20
  108. package/dist/motion/index.css +0 -580
  109. package/dist/motion/index.css.map +1 -1
  110. package/dist/motion/index.d.cts +1 -37
  111. package/dist/motion/index.d.ts +1 -37
  112. package/dist/motion/index.js +1 -2
  113. package/dist/nav/index.cjs +10 -35
  114. package/dist/nav/index.css +28 -580
  115. package/dist/nav/index.css.map +1 -1
  116. package/dist/nav/index.d.cts +2 -60
  117. package/dist/nav/index.d.ts +2 -60
  118. package/dist/nav/index.js +1 -2
  119. package/dist/report/index.cjs +1166 -175
  120. package/dist/report/index.cjs.map +1 -1
  121. package/dist/report/index.d.cts +208 -5
  122. package/dist/report/index.d.ts +208 -5
  123. package/dist/report/index.js +1141 -3
  124. package/dist/report/index.js.map +1 -1
  125. package/dist/sections/index.cjs +8 -10
  126. package/dist/sections/index.cjs.map +1 -1
  127. package/dist/sections/index.css +0 -206
  128. package/dist/sections/index.css.map +1 -1
  129. package/dist/sections/index.js +2 -4
  130. package/dist/sections/index.js.map +1 -1
  131. package/dist/social-media/index.cjs +4 -0
  132. package/dist/social-media/index.cjs.map +1 -0
  133. package/dist/social-media/index.d.cts +2 -0
  134. package/dist/social-media/index.d.ts +2 -0
  135. package/dist/social-media/index.js +3 -0
  136. package/dist/social-media/index.js.map +1 -0
  137. package/dist/social-proof/index.cjs +4 -36
  138. package/dist/social-proof/index.css +3 -1106
  139. package/dist/social-proof/index.css.map +1 -1
  140. package/dist/social-proof/index.d.cts +26 -171
  141. package/dist/social-proof/index.d.ts +26 -171
  142. package/dist/social-proof/index.js +1 -5
  143. package/dist/styles/styles.css +602 -3044
  144. package/dist/theme/index.cjs +11 -19
  145. package/dist/theme/index.css +0 -352
  146. package/dist/theme/index.css.map +1 -1
  147. package/dist/theme/index.d.cts +1 -25
  148. package/dist/theme/index.d.ts +1 -25
  149. package/dist/theme/index.js +2 -2
  150. package/dist/web/client/index.cjs +10 -10
  151. package/dist/web/client/index.css +118 -0
  152. package/dist/web/client/index.css.map +1 -1
  153. package/dist/web/client/index.js +2 -2
  154. package/dist/web/index.cjs +10 -10
  155. package/dist/web/index.css +118 -0
  156. package/dist/web/index.css.map +1 -1
  157. package/dist/web/index.js +2 -2
  158. package/package.json +10 -4
  159. package/dist/AppearancePanel-UT57J69V.d.cts +0 -51
  160. package/dist/AppearancePanel-UT57J69V.d.ts +0 -51
  161. package/dist/ExportMenu-A2TLFiVv.d.cts +0 -311
  162. package/dist/ExportMenu-C8qck5AT.d.ts +0 -311
  163. package/dist/Select-BdZmK0Lt.d.cts +0 -66
  164. package/dist/Select-BdZmK0Lt.d.ts +0 -66
  165. package/dist/chart-types-BGVVO-zl.d.cts +0 -208
  166. package/dist/chart-types-BGVVO-zl.d.ts +0 -208
  167. package/dist/charts/index.css +0 -1167
  168. package/dist/charts/index.css.map +0 -1
  169. package/dist/chunk-3BAQDW3V.cjs +0 -1207
  170. package/dist/chunk-3BAQDW3V.cjs.map +0 -1
  171. package/dist/chunk-3NKRFUAR.js +0 -37
  172. package/dist/chunk-3NKRFUAR.js.map +0 -1
  173. package/dist/chunk-3TGSIILM.cjs +0 -201
  174. package/dist/chunk-3TGSIILM.cjs.map +0 -1
  175. package/dist/chunk-4GM5BGBN.cjs +0 -801
  176. package/dist/chunk-4GM5BGBN.cjs.map +0 -1
  177. package/dist/chunk-5LA3T22E.cjs +0 -562
  178. package/dist/chunk-5LA3T22E.cjs.map +0 -1
  179. package/dist/chunk-5SN66B2X.js +0 -2542
  180. package/dist/chunk-5SN66B2X.js.map +0 -1
  181. package/dist/chunk-6D22TFLA.cjs.map +0 -1
  182. package/dist/chunk-6H4DSTXR.js +0 -786
  183. package/dist/chunk-6H4DSTXR.js.map +0 -1
  184. package/dist/chunk-6HKQ5ILL.cjs +0 -1624
  185. package/dist/chunk-6HKQ5ILL.cjs.map +0 -1
  186. package/dist/chunk-7PX2AZ6Y.js +0 -39
  187. package/dist/chunk-7PX2AZ6Y.js.map +0 -1
  188. package/dist/chunk-7UZ5DETZ.js.map +0 -1
  189. package/dist/chunk-B6AVAX4F.js +0 -1415
  190. package/dist/chunk-B6AVAX4F.js.map +0 -1
  191. package/dist/chunk-BJZ2DKS5.cjs.map +0 -1
  192. package/dist/chunk-BUTQSDQH.js +0 -200
  193. package/dist/chunk-BUTQSDQH.js.map +0 -1
  194. package/dist/chunk-C2BCDNAV.js +0 -24
  195. package/dist/chunk-C2BCDNAV.js.map +0 -1
  196. package/dist/chunk-CJ2MKVAF.cjs +0 -46
  197. package/dist/chunk-CJ2MKVAF.cjs.map +0 -1
  198. package/dist/chunk-E7D6EKJ4.cjs +0 -44
  199. package/dist/chunk-E7D6EKJ4.cjs.map +0 -1
  200. package/dist/chunk-ECXBTUH6.cjs.map +0 -1
  201. package/dist/chunk-FAFAP4L5.js +0 -183
  202. package/dist/chunk-FAFAP4L5.js.map +0 -1
  203. package/dist/chunk-G2XGBO5V.cjs +0 -2565
  204. package/dist/chunk-G2XGBO5V.cjs.map +0 -1
  205. package/dist/chunk-GQELL2MF.cjs.map +0 -1
  206. package/dist/chunk-H2KQ3WSH.cjs.map +0 -1
  207. package/dist/chunk-H2Y6BSTL.cjs.map +0 -1
  208. package/dist/chunk-HCZW5AJN.cjs +0 -234
  209. package/dist/chunk-HCZW5AJN.cjs.map +0 -1
  210. package/dist/chunk-HDVAMYSG.js.map +0 -1
  211. package/dist/chunk-HN4PHABT.js +0 -126
  212. package/dist/chunk-HN4PHABT.js.map +0 -1
  213. package/dist/chunk-LTPTW2US.cjs.map +0 -1
  214. package/dist/chunk-MDB2WCRQ.cjs +0 -137
  215. package/dist/chunk-MDB2WCRQ.cjs.map +0 -1
  216. package/dist/chunk-MQRB634A.cjs +0 -34
  217. package/dist/chunk-MQRB634A.cjs.map +0 -1
  218. package/dist/chunk-NN3TUHIH.js +0 -28
  219. package/dist/chunk-NN3TUHIH.js.map +0 -1
  220. package/dist/chunk-OWS2KAXZ.js +0 -701
  221. package/dist/chunk-OWS2KAXZ.js.map +0 -1
  222. package/dist/chunk-P5BOFE5A.js.map +0 -1
  223. package/dist/chunk-PUPSK3DI.cjs +0 -216
  224. package/dist/chunk-PUPSK3DI.cjs.map +0 -1
  225. package/dist/chunk-Q2MFGYTE.cjs +0 -1449
  226. package/dist/chunk-Q2MFGYTE.cjs.map +0 -1
  227. package/dist/chunk-Q75DBVDY.cjs +0 -68
  228. package/dist/chunk-Q75DBVDY.cjs.map +0 -1
  229. package/dist/chunk-QALDZ7WQ.js.map +0 -1
  230. package/dist/chunk-QWE2RNCS.js +0 -1195
  231. package/dist/chunk-QWE2RNCS.js.map +0 -1
  232. package/dist/chunk-RQUFZAZ7.js +0 -1608
  233. package/dist/chunk-RQUFZAZ7.js.map +0 -1
  234. package/dist/chunk-SICKWUWB.js.map +0 -1
  235. package/dist/chunk-TCFC7XTB.js +0 -212
  236. package/dist/chunk-TCFC7XTB.js.map +0 -1
  237. package/dist/chunk-UTVXGAQP.cjs +0 -2437
  238. package/dist/chunk-UTVXGAQP.cjs.map +0 -1
  239. package/dist/chunk-UVEMY3FQ.cjs +0 -717
  240. package/dist/chunk-UVEMY3FQ.cjs.map +0 -1
  241. package/dist/chunk-VFJZQQZU.js.map +0 -1
  242. package/dist/chunk-WH7PYHZY.cjs +0 -35
  243. package/dist/chunk-WH7PYHZY.cjs.map +0 -1
  244. package/dist/chunk-WYH4TKS5.js.map +0 -1
  245. package/dist/chunk-XEX2AEZK.cjs.map +0 -1
  246. package/dist/chunk-XPTVHPCN.js +0 -2320
  247. package/dist/chunk-XPTVHPCN.js.map +0 -1
  248. package/dist/chunk-XWPDRMZG.js +0 -62
  249. package/dist/chunk-XWPDRMZG.js.map +0 -1
  250. package/dist/chunk-Y26OHHMX.js.map +0 -1
  251. package/dist/chunk-YJ6C3EKW.js.map +0 -1
  252. package/dist/motion-C651Ry6d.d.cts +0 -832
  253. package/dist/motion-C651Ry6d.d.ts +0 -832
  254. package/dist/report/index.css +0 -1239
  255. package/dist/report/index.css.map +0 -1
  256. /package/dist/{chunk-6UNG76Y2.js.map → chunk-2VVRZBUR.cjs.map} +0 -0
  257. /package/dist/{chunk-YTYDQBVY.cjs.map → chunk-K526GN7P.js.map} +0 -0
@@ -0,0 +1,296 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var clsx = require('clsx');
5
+ var jsxRuntime = require('react/jsx-runtime');
6
+ var dialog = require('@base-ui/react/dialog');
7
+
8
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
9
+
10
+ var clsx__default = /*#__PURE__*/_interopDefault(clsx);
11
+
12
+ // src/components/StarRating/StarRating.tsx
13
+ var StarRating = react.forwardRef(
14
+ ({
15
+ value,
16
+ max = 5,
17
+ size = "md",
18
+ animate = false,
19
+ showValue = false,
20
+ valueLabel,
21
+ className,
22
+ ...props
23
+ }, ref) => {
24
+ const [visible, setVisible] = react.useState(!animate);
25
+ const containerRef = react.useRef(null);
26
+ react.useEffect(() => {
27
+ if (!animate) return;
28
+ if (typeof window !== "undefined") {
29
+ const mq = window.matchMedia("(prefers-reduced-motion: reduce)");
30
+ if (mq.matches) {
31
+ setVisible(true);
32
+ return;
33
+ }
34
+ }
35
+ const target = containerRef.current;
36
+ if (!target) return;
37
+ const observer = new IntersectionObserver(
38
+ ([entry]) => {
39
+ if (entry.isIntersecting) {
40
+ setVisible(true);
41
+ observer.disconnect();
42
+ }
43
+ },
44
+ { threshold: 0.5 }
45
+ );
46
+ observer.observe(target);
47
+ return () => observer.disconnect();
48
+ }, [animate]);
49
+ const clampedValue = Math.min(Math.max(value, 0), max);
50
+ const stars = Array.from({ length: max }, (_, i) => {
51
+ const starIndex = i + 1;
52
+ const fillPercent = clampedValue >= starIndex ? 100 : clampedValue > starIndex - 1 ? (clampedValue - (starIndex - 1)) * 100 : 0;
53
+ return /* @__PURE__ */ jsxRuntime.jsxs(
54
+ "span",
55
+ {
56
+ className: clsx__default.default(
57
+ "ds-star-rating__star",
58
+ visible && "ds-star-rating__star--visible"
59
+ ),
60
+ style: {
61
+ "--star-fill": `${fillPercent}%`,
62
+ "--star-delay": animate ? `${i * 100}ms` : "0ms"
63
+ },
64
+ children: [
65
+ /* @__PURE__ */ jsxRuntime.jsx(
66
+ "svg",
67
+ {
68
+ viewBox: "0 0 24 24",
69
+ fill: "none",
70
+ xmlns: "http://www.w3.org/2000/svg",
71
+ className: "ds-star-rating__svg",
72
+ "aria-hidden": "true",
73
+ children: /* @__PURE__ */ jsxRuntime.jsx(
74
+ "path",
75
+ {
76
+ d: "M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z",
77
+ className: "ds-star-rating__empty"
78
+ }
79
+ )
80
+ }
81
+ ),
82
+ /* @__PURE__ */ jsxRuntime.jsx(
83
+ "svg",
84
+ {
85
+ viewBox: "0 0 24 24",
86
+ fill: "none",
87
+ xmlns: "http://www.w3.org/2000/svg",
88
+ className: "ds-star-rating__svg ds-star-rating__svg--filled",
89
+ "aria-hidden": "true",
90
+ style: {
91
+ clipPath: `inset(0 ${100 - fillPercent}% 0 0)`
92
+ },
93
+ children: /* @__PURE__ */ jsxRuntime.jsx(
94
+ "path",
95
+ {
96
+ d: "M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z",
97
+ className: "ds-star-rating__filled"
98
+ }
99
+ )
100
+ }
101
+ )
102
+ ]
103
+ },
104
+ i
105
+ );
106
+ });
107
+ return /* @__PURE__ */ jsxRuntime.jsxs(
108
+ "div",
109
+ {
110
+ ref: (node) => {
111
+ containerRef.current = node;
112
+ if (typeof ref === "function") ref(node);
113
+ else if (ref) ref.current = node;
114
+ },
115
+ className: clsx__default.default("ds-star-rating", `ds-star-rating--${size}`, className),
116
+ role: "img",
117
+ "aria-label": `${clampedValue} out of ${max} stars`,
118
+ ...props,
119
+ children: [
120
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ds-star-rating__stars", children: stars }),
121
+ showValue && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "ds-star-rating__value", children: [
122
+ clampedValue.toLocaleString(void 0, {
123
+ minimumFractionDigits: clampedValue % 1 !== 0 ? 1 : 0,
124
+ maximumFractionDigits: 1
125
+ }),
126
+ valueLabel && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ds-star-rating__value-label", children: valueLabel })
127
+ ] })
128
+ ]
129
+ }
130
+ );
131
+ }
132
+ );
133
+ StarRating.displayName = "StarRating";
134
+ var TrustpilotIcon = ({ size }) => /* @__PURE__ */ jsxRuntime.jsx("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 2l2.9 8.9H24l-7.35 5.34 2.81 8.64L12 19.56l-7.46 5.32 2.81-8.64L0 10.9h9.1L12 2z", fill: "#00B67A" }) });
135
+ var G2Icon = ({ size }) => /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", children: [
136
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2z", fill: "#FF492C" }),
137
+ /* @__PURE__ */ jsxRuntime.jsx("text", { x: "50%", y: "54%", dominantBaseline: "middle", textAnchor: "middle", fill: "white", fontWeight: "bold", fontSize: "11", fontFamily: "Arial, sans-serif", children: "G2" })
138
+ ] });
139
+ var GoogleIcon = ({ size }) => /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", children: [
140
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 01-2.2 3.32v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.1z", fill: "#4285F4" }),
141
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z", fill: "#34A853" }),
142
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18A10.96 10.96 0 001 12c0 1.77.42 3.45 1.18 4.93l3.66-2.84z", fill: "#FBBC05" }),
143
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z", fill: "#EA4335" })
144
+ ] });
145
+ var ChromeWebStoreIcon = ({ size }) => /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", children: [
146
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "10", fill: "#4285F4", opacity: "0.1", stroke: "#4285F4", strokeWidth: "1.5" }),
147
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 7c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5zm0 8.5c-1.93 0-3.5-1.57-3.5-3.5S10.07 8.5 12 8.5s3.5 1.57 3.5 3.5-1.57 3.5-3.5 3.5z", fill: "#4285F4" }),
148
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M7.5 10.5L4 5.5", stroke: "#EA4335", strokeWidth: "1.5", strokeLinecap: "round" }),
149
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M16.5 10.5L20 5.5", stroke: "#FBBC05", strokeWidth: "1.5", strokeLinecap: "round" }),
150
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 17l-3.5 5", stroke: "#34A853", strokeWidth: "1.5", strokeLinecap: "round" })
151
+ ] });
152
+ var PLATFORM_ICONS = {
153
+ trustpilot: TrustpilotIcon,
154
+ g2: G2Icon,
155
+ google: GoogleIcon,
156
+ "chrome-web-store": ChromeWebStoreIcon
157
+ };
158
+ var PLATFORM_LABELS = {
159
+ trustpilot: "Trustpilot",
160
+ g2: "G2",
161
+ google: "Google",
162
+ "chrome-web-store": "Chrome Web Store"
163
+ };
164
+ var PLATFORM_COLORS = {
165
+ trustpilot: "#00B67A",
166
+ g2: "#FF492C",
167
+ google: "#4285F4",
168
+ "chrome-web-store": "#4285F4"
169
+ };
170
+ var PlatformBadge = react.forwardRef(
171
+ ({
172
+ platform,
173
+ variant = "icon-label",
174
+ rating,
175
+ count,
176
+ size = "sm",
177
+ className,
178
+ ...props
179
+ }, ref) => {
180
+ const Icon = PLATFORM_ICONS[platform];
181
+ const label = PLATFORM_LABELS[platform];
182
+ const accentColor = PLATFORM_COLORS[platform];
183
+ const iconSize = size === "sm" ? 18 : 22;
184
+ return /* @__PURE__ */ jsxRuntime.jsxs(
185
+ "div",
186
+ {
187
+ ref,
188
+ role: "img",
189
+ className: clsx__default.default(
190
+ "ds-platform-badge",
191
+ `ds-platform-badge--${variant}`,
192
+ `ds-platform-badge--${size}`,
193
+ className
194
+ ),
195
+ style: { "--platform-accent": accentColor },
196
+ "aria-label": variant === "full" && rating ? `${label}: ${rating} stars${count ? ` from ${count} reviews` : ""}` : label,
197
+ ...props,
198
+ children: [
199
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ds-platform-badge__icon", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { size: iconSize }) }),
200
+ variant !== "icon-only" && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ds-platform-badge__label", children: label }),
201
+ variant === "full" && rating != null && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "ds-platform-badge__rating", children: [
202
+ /* @__PURE__ */ jsxRuntime.jsx(StarRating, { value: rating, size: "sm" }),
203
+ count != null && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "ds-platform-badge__count", children: [
204
+ "(",
205
+ count,
206
+ ")"
207
+ ] })
208
+ ] })
209
+ ]
210
+ }
211
+ );
212
+ }
213
+ );
214
+ PlatformBadge.displayName = "PlatformBadge";
215
+ function getEmbedUrl(src) {
216
+ const ytMatch = src.match(
217
+ /(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([a-zA-Z0-9_-]+)/
218
+ );
219
+ if (ytMatch) return `https://www.youtube-nocookie.com/embed/${ytMatch[1]}?autoplay=1&rel=0`;
220
+ const vimeoMatch = src.match(/(?:vimeo\.com\/)(\d+)/);
221
+ if (vimeoMatch) return `https://player.vimeo.com/video/${vimeoMatch[1]}?autoplay=1`;
222
+ const loomMatch = src.match(/(?:loom\.com\/share\/)([a-zA-Z0-9]+)/);
223
+ if (loomMatch) return `https://www.loom.com/embed/${loomMatch[1]}?autoplay=1`;
224
+ return src;
225
+ }
226
+ var PlayIcon = () => /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "64", height: "64", viewBox: "0 0 64 64", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", children: [
227
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "32", cy: "32", r: "32", className: "ds-video-lightbox__play-bg" }),
228
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M26 20v24l20-12L26 20z", fill: "white" })
229
+ ] });
230
+ var CloseIcon = () => /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
231
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
232
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
233
+ ] });
234
+ var VideoLightbox = react.forwardRef(
235
+ ({
236
+ videoUrl,
237
+ thumbnailSrc,
238
+ thumbnailAlt,
239
+ title = "Video",
240
+ trigger,
241
+ className,
242
+ onPlay
243
+ }, ref) => {
244
+ const [open, setOpen] = react.useState(false);
245
+ const handleOpen = react.useCallback(() => {
246
+ setOpen(true);
247
+ onPlay?.();
248
+ }, [onPlay]);
249
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { ref, className: clsx__default.default("ds-video-lightbox", className), children: /* @__PURE__ */ jsxRuntime.jsxs(dialog.Dialog.Root, { open, onOpenChange: setOpen, children: [
250
+ /* @__PURE__ */ jsxRuntime.jsx(
251
+ dialog.Dialog.Trigger,
252
+ {
253
+ className: "ds-video-lightbox__trigger",
254
+ onClick: handleOpen,
255
+ children: trigger || /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ds-video-lightbox__thumbnail-wrap", children: [
256
+ thumbnailSrc ? /* @__PURE__ */ jsxRuntime.jsx(
257
+ "img",
258
+ {
259
+ src: thumbnailSrc,
260
+ alt: thumbnailAlt || `Play: ${title}`,
261
+ className: "ds-video-lightbox__thumbnail",
262
+ loading: "lazy",
263
+ decoding: "async"
264
+ }
265
+ ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ds-video-lightbox__placeholder" }),
266
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ds-video-lightbox__overlay", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ds-video-lightbox__play-btn", children: /* @__PURE__ */ jsxRuntime.jsx(PlayIcon, {}) }) })
267
+ ] })
268
+ }
269
+ ),
270
+ /* @__PURE__ */ jsxRuntime.jsxs(dialog.Dialog.Portal, { children: [
271
+ /* @__PURE__ */ jsxRuntime.jsx(dialog.Dialog.Backdrop, { className: "ds-video-lightbox__backdrop" }),
272
+ /* @__PURE__ */ jsxRuntime.jsxs(dialog.Dialog.Popup, { className: "ds-video-lightbox__popup", children: [
273
+ /* @__PURE__ */ jsxRuntime.jsx(dialog.Dialog.Close, { className: "ds-video-lightbox__close", "aria-label": "Close video", children: /* @__PURE__ */ jsxRuntime.jsx(CloseIcon, {}) }),
274
+ /* @__PURE__ */ jsxRuntime.jsx(dialog.Dialog.Title, { className: "ds-sr-only", children: title }),
275
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ds-video-lightbox__iframe-wrap", children: open && /* @__PURE__ */ jsxRuntime.jsx(
276
+ "iframe",
277
+ {
278
+ src: getEmbedUrl(videoUrl),
279
+ title,
280
+ className: "ds-video-lightbox__iframe",
281
+ allow: "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",
282
+ allowFullScreen: true
283
+ }
284
+ ) })
285
+ ] })
286
+ ] })
287
+ ] }) });
288
+ }
289
+ );
290
+ VideoLightbox.displayName = "VideoLightbox";
291
+
292
+ exports.PlatformBadge = PlatformBadge;
293
+ exports.StarRating = StarRating;
294
+ exports.VideoLightbox = VideoLightbox;
295
+ //# sourceMappingURL=out.js.map
296
+ //# sourceMappingURL=chunk-UGKYP6F3.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/StarRating/StarRating.tsx","../src/components/PlatformBadge/PlatformBadge.tsx","../src/components/VideoLightbox/VideoLightbox.tsx"],"names":["forwardRef","clsx","jsx","jsxs","useState"],"mappings":"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","sourcesContent":["import React, { forwardRef, useEffect, useRef, useState } from 'react';\nimport clsx from 'clsx';\nimport './StarRating.css';\n\n/* ============================================================================\n STAR RATING\n ============================================================================\n Animated star display with half-star support, configurable sizes, and\n optional viewport-triggered fill animation. Replaces text-character\n star displays across all social proof surfaces.\n\n Strategic objective: Instant visual trust signal\n ============================================================================ */\n\nexport interface StarRatingProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Rating value (0-5, supports decimals for half-stars) */\n value: number;\n /** Maximum number of stars */\n max?: number;\n /** Star size variant */\n size?: 'sm' | 'md' | 'lg';\n /** Animate stars filling on viewport entry */\n animate?: boolean;\n /** Show numeric value next to stars */\n showValue?: boolean;\n /** Label shown after value (e.g., \"/ 5\") */\n valueLabel?: string;\n}\n\n/**\n * Star rating display with gold-fill, half-star support, and optional\n * entrance animation. Uses inline SVGs with clip-path for precise\n * partial fills.\n *\n * @example\n * ```tsx\n * <StarRating value={4.5} size=\"lg\" animate />\n * <StarRating value={5} size=\"sm\" showValue />\n * ```\n */\nexport const StarRating = forwardRef<HTMLDivElement, StarRatingProps>(\n (\n {\n value,\n max = 5,\n size = 'md',\n animate = false,\n showValue = false,\n valueLabel,\n className,\n ...props\n },\n ref,\n ) => {\n const [visible, setVisible] = useState(!animate);\n const containerRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n if (!animate) return;\n\n // Respect reduced motion\n if (typeof window !== 'undefined') {\n const mq = window.matchMedia('(prefers-reduced-motion: reduce)');\n if (mq.matches) {\n setVisible(true);\n return;\n }\n }\n\n const target = containerRef.current;\n if (!target) return;\n\n const observer = new IntersectionObserver(\n ([entry]) => {\n if (entry.isIntersecting) {\n setVisible(true);\n observer.disconnect();\n }\n },\n { threshold: 0.5 },\n );\n\n observer.observe(target);\n return () => observer.disconnect();\n }, [animate]);\n\n const clampedValue = Math.min(Math.max(value, 0), max);\n\n const stars = Array.from({ length: max }, (_, i) => {\n const starIndex = i + 1;\n const fillPercent =\n clampedValue >= starIndex\n ? 100\n : clampedValue > starIndex - 1\n ? (clampedValue - (starIndex - 1)) * 100\n : 0;\n\n return (\n <span\n key={i}\n className={clsx(\n 'ds-star-rating__star',\n visible && 'ds-star-rating__star--visible',\n )}\n style={{\n '--star-fill': `${fillPercent}%`,\n '--star-delay': animate ? `${i * 100}ms` : '0ms',\n } as React.CSSProperties}\n >\n {/* Background (empty) star */}\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className=\"ds-star-rating__svg\"\n aria-hidden=\"true\"\n >\n <path\n d=\"M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z\"\n className=\"ds-star-rating__empty\"\n />\n </svg>\n {/* Foreground (filled) star with clip */}\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className=\"ds-star-rating__svg ds-star-rating__svg--filled\"\n aria-hidden=\"true\"\n style={{\n clipPath: `inset(0 ${100 - fillPercent}% 0 0)`,\n }}\n >\n <path\n d=\"M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z\"\n className=\"ds-star-rating__filled\"\n />\n </svg>\n </span>\n );\n });\n\n return (\n <div\n ref={(node) => {\n (containerRef as React.MutableRefObject<HTMLDivElement | null>).current = node;\n if (typeof ref === 'function') ref(node);\n else if (ref) (ref as React.MutableRefObject<HTMLDivElement | null>).current = node;\n }}\n className={clsx('ds-star-rating', `ds-star-rating--${size}`, className)}\n role=\"img\"\n aria-label={`${clampedValue} out of ${max} stars`}\n {...props}\n >\n <span className=\"ds-star-rating__stars\">{stars}</span>\n {showValue && (\n <span className=\"ds-star-rating__value\">\n {clampedValue.toLocaleString(undefined, {\n minimumFractionDigits: clampedValue % 1 !== 0 ? 1 : 0,\n maximumFractionDigits: 1,\n })}\n {valueLabel && (\n <span className=\"ds-star-rating__value-label\">{valueLabel}</span>\n )}\n </span>\n )}\n </div>\n );\n },\n);\n\nStarRating.displayName = 'StarRating';\n","import React, { forwardRef } from 'react';\nimport clsx from 'clsx';\nimport { StarRating } from '../StarRating/StarRating';\nimport './PlatformBadge.css';\n\n/* ============================================================================\n PLATFORM BADGE\n ============================================================================\n Recognizable review platform icons (Trustpilot, G2, Google, Chrome Web\n Store) with optional rating display. Builds instant trust through visual\n brand recognition.\n\n Strategic objective: Third-party authority signal\n ============================================================================ */\n\nexport type Platform = 'trustpilot' | 'g2' | 'google' | 'chrome-web-store';\n\nexport interface PlatformBadgeProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Review platform */\n platform: Platform;\n /** Display mode */\n variant?: 'icon-only' | 'icon-label' | 'full';\n /** Platform rating (used in 'full' variant) */\n rating?: number;\n /** Number of reviews (used in 'full' variant) */\n count?: number;\n /** Size variant */\n size?: 'sm' | 'md';\n}\n\n/* --------------------------------------------------------------------------\n Platform SVG Icons — inline for zero-latency rendering\n -------------------------------------------------------------------------- */\n\nconst TrustpilotIcon = ({ size }: { size: number }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path d=\"M12 2l2.9 8.9H24l-7.35 5.34 2.81 8.64L12 19.56l-7.46 5.32 2.81-8.64L0 10.9h9.1L12 2z\" fill=\"#00B67A\" />\n </svg>\n);\n\nconst G2Icon = ({ size }: { size: number }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2z\" fill=\"#FF492C\" />\n <text x=\"50%\" y=\"54%\" dominantBaseline=\"middle\" textAnchor=\"middle\" fill=\"white\" fontWeight=\"bold\" fontSize=\"11\" fontFamily=\"Arial, sans-serif\">G2</text>\n </svg>\n);\n\nconst GoogleIcon = ({ size }: { size: number }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path d=\"M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 01-2.2 3.32v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.1z\" fill=\"#4285F4\"/>\n <path d=\"M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z\" fill=\"#34A853\"/>\n <path d=\"M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18A10.96 10.96 0 001 12c0 1.77.42 3.45 1.18 4.93l3.66-2.84z\" fill=\"#FBBC05\"/>\n <path d=\"M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z\" fill=\"#EA4335\"/>\n </svg>\n);\n\nconst ChromeWebStoreIcon = ({ size }: { size: number }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" fill=\"#4285F4\" opacity=\"0.1\" stroke=\"#4285F4\" strokeWidth=\"1.5\"/>\n <path d=\"M12 7c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5zm0 8.5c-1.93 0-3.5-1.57-3.5-3.5S10.07 8.5 12 8.5s3.5 1.57 3.5 3.5-1.57 3.5-3.5 3.5z\" fill=\"#4285F4\"/>\n <path d=\"M7.5 10.5L4 5.5\" stroke=\"#EA4335\" strokeWidth=\"1.5\" strokeLinecap=\"round\"/>\n <path d=\"M16.5 10.5L20 5.5\" stroke=\"#FBBC05\" strokeWidth=\"1.5\" strokeLinecap=\"round\"/>\n <path d=\"M12 17l-3.5 5\" stroke=\"#34A853\" strokeWidth=\"1.5\" strokeLinecap=\"round\"/>\n </svg>\n);\n\nconst PLATFORM_ICONS: Record<Platform, React.FC<{ size: number }>> = {\n trustpilot: TrustpilotIcon,\n g2: G2Icon,\n google: GoogleIcon,\n 'chrome-web-store': ChromeWebStoreIcon,\n};\n\nconst PLATFORM_LABELS: Record<Platform, string> = {\n trustpilot: 'Trustpilot',\n g2: 'G2',\n google: 'Google',\n 'chrome-web-store': 'Chrome Web Store',\n};\n\nconst PLATFORM_COLORS: Record<Platform, string> = {\n trustpilot: '#00B67A',\n g2: '#FF492C',\n google: '#4285F4',\n 'chrome-web-store': '#4285F4',\n};\n\n/**\n * Review platform badge with recognizable brand icon, optional label,\n * and optional rating display.\n *\n * @example\n * ```tsx\n * <PlatformBadge platform=\"trustpilot\" variant=\"icon-only\" />\n * <PlatformBadge platform=\"g2\" variant=\"icon-label\" />\n * <PlatformBadge platform=\"google\" variant=\"full\" rating={4.9} count={42} />\n * ```\n */\nexport const PlatformBadge = forwardRef<HTMLDivElement, PlatformBadgeProps>(\n (\n {\n platform,\n variant = 'icon-label',\n rating,\n count,\n size = 'sm',\n className,\n ...props\n },\n ref,\n ) => {\n const Icon = PLATFORM_ICONS[platform];\n const label = PLATFORM_LABELS[platform];\n const accentColor = PLATFORM_COLORS[platform];\n const iconSize = size === 'sm' ? 18 : 22;\n\n return (\n <div\n ref={ref}\n role=\"img\"\n className={clsx(\n 'ds-platform-badge',\n `ds-platform-badge--${variant}`,\n `ds-platform-badge--${size}`,\n className,\n )}\n style={{ '--platform-accent': accentColor } as React.CSSProperties}\n aria-label={\n variant === 'full' && rating\n ? `${label}: ${rating} stars${count ? ` from ${count} reviews` : ''}`\n : label\n }\n {...props}\n >\n <span className=\"ds-platform-badge__icon\">\n <Icon size={iconSize} />\n </span>\n\n {variant !== 'icon-only' && (\n <span className=\"ds-platform-badge__label\">{label}</span>\n )}\n\n {variant === 'full' && rating != null && (\n <span className=\"ds-platform-badge__rating\">\n <StarRating value={rating} size=\"sm\" />\n {count != null && (\n <span className=\"ds-platform-badge__count\">({count})</span>\n )}\n </span>\n )}\n </div>\n );\n },\n);\n\nPlatformBadge.displayName = 'PlatformBadge';\n","import React, { forwardRef, useState, useCallback } from 'react';\nimport { Dialog as BaseDialog } from '@base-ui/react/dialog';\nimport clsx from 'clsx';\nimport './VideoLightbox.css';\n\n/* ============================================================================\n VIDEO LIGHTBOX\n ============================================================================\n Modal overlay video player built on @base-ui/react Dialog. Opens a lazy\n iframe embed (YouTube/Vimeo/Loom) with backdrop blur, keyboard navigation,\n and focus trap. Designed for inline playback from thumbnail cards without\n navigating away from the current page.\n\n Strategic objective: Engagement — keep users on the Wall of Love\n ============================================================================ */\n\nexport interface VideoLightboxProps {\n /** Video embed URL (YouTube, Vimeo, Loom) */\n videoUrl: string;\n /** Poster image shown as thumbnail trigger */\n thumbnailSrc?: string;\n /** Alt text for thumbnail */\n thumbnailAlt?: string;\n /** Video title for accessibility */\n title?: string;\n /** Custom trigger element (overrides default thumbnail + play button) */\n trigger?: React.ReactNode;\n /** Additional class name for the trigger wrapper */\n className?: string;\n /** Callback when video starts playing */\n onPlay?: () => void;\n}\n\n/* --------------------------------------------------------------------------\n Helpers\n -------------------------------------------------------------------------- */\n\nfunction getEmbedUrl(src: string): string {\n // YouTube\n const ytMatch = src.match(\n /(?:youtube\\.com\\/watch\\?v=|youtu\\.be\\/|youtube\\.com\\/embed\\/)([a-zA-Z0-9_-]+)/,\n );\n if (ytMatch) return `https://www.youtube-nocookie.com/embed/${ytMatch[1]}?autoplay=1&rel=0`;\n\n // Vimeo\n const vimeoMatch = src.match(/(?:vimeo\\.com\\/)(\\d+)/);\n if (vimeoMatch) return `https://player.vimeo.com/video/${vimeoMatch[1]}?autoplay=1`;\n\n // Loom\n const loomMatch = src.match(/(?:loom\\.com\\/share\\/)([a-zA-Z0-9]+)/);\n if (loomMatch) return `https://www.loom.com/embed/${loomMatch[1]}?autoplay=1`;\n\n return src;\n}\n\nconst PlayIcon = () => (\n <svg width=\"64\" height=\"64\" viewBox=\"0 0 64 64\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <circle cx=\"32\" cy=\"32\" r=\"32\" className=\"ds-video-lightbox__play-bg\" />\n <path d=\"M26 20v24l20-12L26 20z\" fill=\"white\" />\n </svg>\n);\n\nconst CloseIcon = () => (\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n);\n\n/**\n * Modal video player triggered by a thumbnail or custom element.\n * Opens a full-screen overlay with lazy iframe injection.\n *\n * @example\n * ```tsx\n * <VideoLightbox\n * videoUrl=\"https://www.youtube.com/watch?v=dQw4w9WgXcQ\"\n * thumbnailSrc=\"/thumbnails/demo.jpg\"\n * title=\"Customer testimonial\"\n * />\n *\n * <VideoLightbox\n * videoUrl=\"https://vimeo.com/123456789\"\n * trigger={<Button>Watch Video</Button>}\n * />\n * ```\n */\nexport const VideoLightbox = forwardRef<HTMLDivElement, VideoLightboxProps>(\n (\n {\n videoUrl,\n thumbnailSrc,\n thumbnailAlt,\n title = 'Video',\n trigger,\n className,\n onPlay,\n },\n ref,\n ) => {\n const [open, setOpen] = useState(false);\n\n const handleOpen = useCallback(() => {\n setOpen(true);\n onPlay?.();\n }, [onPlay]);\n\n return (\n <div ref={ref} className={clsx('ds-video-lightbox', className)}>\n <BaseDialog.Root open={open} onOpenChange={setOpen}>\n <BaseDialog.Trigger\n className=\"ds-video-lightbox__trigger\"\n onClick={handleOpen}\n >\n {trigger || (\n <div className=\"ds-video-lightbox__thumbnail-wrap\">\n {thumbnailSrc ? (\n <img\n src={thumbnailSrc}\n alt={thumbnailAlt || `Play: ${title}`}\n className=\"ds-video-lightbox__thumbnail\"\n loading=\"lazy\"\n decoding=\"async\"\n />\n ) : (\n <div className=\"ds-video-lightbox__placeholder\" />\n )}\n <div className=\"ds-video-lightbox__overlay\">\n <div className=\"ds-video-lightbox__play-btn\">\n <PlayIcon />\n </div>\n </div>\n </div>\n )}\n </BaseDialog.Trigger>\n\n <BaseDialog.Portal>\n <BaseDialog.Backdrop className=\"ds-video-lightbox__backdrop\" />\n <BaseDialog.Popup className=\"ds-video-lightbox__popup\">\n <BaseDialog.Close className=\"ds-video-lightbox__close\" aria-label=\"Close video\">\n <CloseIcon />\n </BaseDialog.Close>\n <BaseDialog.Title className=\"ds-sr-only\">{title}</BaseDialog.Title>\n <div className=\"ds-video-lightbox__iframe-wrap\">\n {open && (\n <iframe\n src={getEmbedUrl(videoUrl)}\n title={title}\n className=\"ds-video-lightbox__iframe\"\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\"\n allowFullScreen\n />\n )}\n </div>\n </BaseDialog.Popup>\n </BaseDialog.Portal>\n </BaseDialog.Root>\n </div>\n );\n },\n);\n\nVideoLightbox.displayName = 'VideoLightbox';\n"]}
@@ -1,35 +1,20 @@
1
- import { useAppearance } from './chunk-7UZ5DETZ.js';
2
- import React from 'react';
3
- import clsx2 from 'clsx';
4
- import { jsxs, jsx } from 'react/jsx-runtime';
1
+ import { useAppearance } from './chunk-MBAG654R.js';
5
2
  import { Sun, Moon } from 'lucide-react';
3
+ import clsx from 'clsx';
4
+ import { jsxs, jsx } from 'react/jsx-runtime';
5
+ import React from 'react';
6
6
 
7
- var VoidBackground = React.forwardRef(
8
- ({ showGrid = true, showGrain = true, className, children }, ref) => {
9
- return /* @__PURE__ */ jsxs("div", { ref, className: clsx2("void", className), children: [
10
- /* @__PURE__ */ jsx("div", { className: "void__base", "aria-hidden": "true" }),
11
- /* @__PURE__ */ jsxs("div", { className: "void__orbs", "aria-hidden": "true", children: [
12
- /* @__PURE__ */ jsx("div", { className: "void__orb void__orb--warm" }),
13
- /* @__PURE__ */ jsx("div", { className: "void__orb void__orb--purple" })
14
- ] }),
15
- showGrid && /* @__PURE__ */ jsx("div", { className: "void__grid", "aria-hidden": "true" }),
16
- showGrain && /* @__PURE__ */ jsx("div", { className: "void__grain", "aria-hidden": "true" }),
17
- children && /* @__PURE__ */ jsx("div", { className: "void__content", children })
18
- ] });
19
- }
20
- );
21
- VoidBackground.displayName = "VoidBackground";
22
7
  var ThemeSelector = ({ className, style }) => {
23
8
  const { theme, setTheme } = useAppearance();
24
9
  const handleThemeChange = (newTheme) => {
25
10
  setTheme(newTheme);
26
11
  };
27
- return /* @__PURE__ */ jsxs("div", { className: clsx2("ds-theme-selector", className), style, role: "group", "aria-label": "Theme Selector", children: [
12
+ return /* @__PURE__ */ jsxs("div", { className: clsx("ds-theme-selector", className), style, role: "group", "aria-label": "Theme Selector", children: [
28
13
  /* @__PURE__ */ jsxs(
29
14
  "button",
30
15
  {
31
16
  type: "button",
32
- className: clsx2("ds-theme-selector__btn", { "ds-theme-selector__btn--active": theme === "light" }),
17
+ className: clsx("ds-theme-selector__btn", { "ds-theme-selector__btn--active": theme === "light" }),
33
18
  onClick: () => handleThemeChange("light"),
34
19
  "aria-pressed": theme === "light",
35
20
  children: [
@@ -42,7 +27,7 @@ var ThemeSelector = ({ className, style }) => {
42
27
  "button",
43
28
  {
44
29
  type: "button",
45
- className: clsx2("ds-theme-selector__btn", { "ds-theme-selector__btn--active": theme === "light-contrast" }),
30
+ className: clsx("ds-theme-selector__btn", { "ds-theme-selector__btn--active": theme === "light-contrast" }),
46
31
  onClick: () => handleThemeChange("light-contrast"),
47
32
  "aria-pressed": theme === "light-contrast",
48
33
  children: [
@@ -55,7 +40,7 @@ var ThemeSelector = ({ className, style }) => {
55
40
  "button",
56
41
  {
57
42
  type: "button",
58
- className: clsx2("ds-theme-selector__btn", { "ds-theme-selector__btn--active": theme === "dark" }),
43
+ className: clsx("ds-theme-selector__btn", { "ds-theme-selector__btn--active": theme === "dark" }),
59
44
  onClick: () => handleThemeChange("dark"),
60
45
  "aria-pressed": theme === "dark",
61
46
  children: [
@@ -68,7 +53,7 @@ var ThemeSelector = ({ className, style }) => {
68
53
  "button",
69
54
  {
70
55
  type: "button",
71
- className: clsx2("ds-theme-selector__btn", { "ds-theme-selector__btn--active": theme === "dark-contrast" }),
56
+ className: clsx("ds-theme-selector__btn", { "ds-theme-selector__btn--active": theme === "dark-contrast" }),
72
57
  onClick: () => handleThemeChange("dark-contrast"),
73
58
  "aria-pressed": theme === "dark-contrast",
74
59
  children: [
@@ -91,7 +76,7 @@ var ColorPicker = React.forwardRef(
91
76
  "div",
92
77
  {
93
78
  ref,
94
- className: clsx2("ds-color-picker", className),
79
+ className: clsx("ds-color-picker", className),
95
80
  style,
96
81
  role: "group",
97
82
  "aria-label": "Brand Color",
@@ -100,7 +85,7 @@ var ColorPicker = React.forwardRef(
100
85
  "button",
101
86
  {
102
87
  type: "button",
103
- className: clsx2("ds-color-picker__swatch", {
88
+ className: clsx("ds-color-picker__swatch", {
104
89
  "ds-color-picker__swatch--active": brand === value
105
90
  }),
106
91
  onClick: () => setBrand(value),
@@ -120,7 +105,7 @@ var ColorPicker = React.forwardRef(
120
105
  !hideCustom && /* @__PURE__ */ jsxs(
121
106
  "label",
122
107
  {
123
- className: clsx2("ds-color-picker__swatch", "ds-color-picker__swatch--custom", {
108
+ className: clsx("ds-color-picker__swatch", "ds-color-picker__swatch--custom", {
124
109
  "ds-color-picker__swatch--active": brand === "custom"
125
110
  }),
126
111
  title: `Custom: ${customColor}`,
@@ -152,6 +137,6 @@ var ColorPicker = React.forwardRef(
152
137
  );
153
138
  ColorPicker.displayName = "ColorPicker";
154
139
 
155
- export { ColorPicker, ThemeSelector, VoidBackground };
140
+ export { ColorPicker, ThemeSelector };
156
141
  //# sourceMappingURL=out.js.map
157
- //# sourceMappingURL=chunk-HDVAMYSG.js.map
142
+ //# sourceMappingURL=chunk-VFO2MUPI.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/ThemeSelector/ThemeSelector.tsx","../src/components/ColorPicker/ColorPicker.tsx"],"names":["clsx","jsx","jsxs"],"mappings":"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","sourcesContent":["import React from 'react';\nimport { Sun, Moon } from 'lucide-react';\nimport clsx from 'clsx';\nimport { useAppearance, Theme } from '../../theme/AppearanceProvider';\nimport './ThemeSelector.css';\n\nexport interface ThemeSelectorProps {\n className?: string;\n style?: React.CSSProperties;\n}\n\nexport const ThemeSelector: React.FC<ThemeSelectorProps> = ({ className, style }) => {\n const { theme, setTheme } = useAppearance();\n\n const handleThemeChange = (newTheme: Theme) => {\n setTheme(newTheme);\n };\n\n return (\n <div className={clsx('ds-theme-selector', className)} style={style} role=\"group\" aria-label=\"Theme Selector\">\n <button\n type=\"button\"\n className={clsx('ds-theme-selector__btn', { 'ds-theme-selector__btn--active': theme === 'light' })}\n onClick={() => handleThemeChange('light')}\n aria-pressed={theme === 'light'}\n >\n <Sun className=\"ds-theme-selector__icon\" />\n <span className=\"ds-theme-selector__label\">Light</span>\n </button>\n\n <button\n type=\"button\"\n className={clsx('ds-theme-selector__btn', { 'ds-theme-selector__btn--active': theme === 'light-contrast' })}\n onClick={() => handleThemeChange('light-contrast')}\n aria-pressed={theme === 'light-contrast'}\n >\n <Sun className=\"ds-theme-selector__icon\" />\n <span className=\"ds-theme-selector__label\">Light HC</span>\n </button>\n\n <button\n type=\"button\"\n className={clsx('ds-theme-selector__btn', { 'ds-theme-selector__btn--active': theme === 'dark' })}\n onClick={() => handleThemeChange('dark')}\n aria-pressed={theme === 'dark'}\n >\n <Moon className=\"ds-theme-selector__icon\" />\n <span className=\"ds-theme-selector__label\">Dark</span>\n </button>\n\n <button\n type=\"button\"\n className={clsx('ds-theme-selector__btn', { 'ds-theme-selector__btn--active': theme === 'dark-contrast' })}\n onClick={() => handleThemeChange('dark-contrast')}\n aria-pressed={theme === 'dark-contrast'}\n >\n <Moon className=\"ds-theme-selector__icon\" />\n <span className=\"ds-theme-selector__label\">Dark HC</span>\n </button>\n </div>\n );\n};\n\nThemeSelector.displayName = 'ThemeSelector';\n","import React from 'react';\nimport clsx from 'clsx';\nimport { useAppearance, Brand } from '../../theme/AppearanceProvider';\nimport './ColorPicker.css';\n\n/** Pre-configured brand presets shown as swatches. */\nconst BRAND_PRESETS: { value: Brand; label: string; color: string }[] = [\n { value: 'default', label: 'Warm Intelligence', color: '#f97316' },\n { value: 'salesmind', label: 'SalesMind', color: '#ff005a' },\n];\n\nexport interface ColorPickerProps {\n /** Additional CSS class name. */\n className?: string;\n /** Inline styles. */\n style?: React.CSSProperties;\n /**\n * Hide the custom color swatch and native color input.\n * When `true`, only the curated brand presets are shown.\n * Use this on production surfaces (e.g., marketing website footer)\n * where arbitrary custom colors would undermine brand consistency.\n * @default false\n */\n hideCustom?: boolean;\n}\n\n/**\n * Compact brand/accent color picker.\n * Renders preset brand swatches and an optional custom color input.\n * Consumes the `useAppearance()` context for state.\n */\nexport const ColorPicker = React.forwardRef<HTMLDivElement, ColorPickerProps>(\n ({ className, style, hideCustom = false }, ref) => {\n const { brand, setBrand, customColor, setCustomColor } = useAppearance();\n\n return (\n <div\n ref={ref}\n className={clsx('ds-color-picker', className)}\n style={style}\n role=\"group\"\n aria-label=\"Brand Color\"\n >\n {BRAND_PRESETS.map(({ value, label, color }) => (\n <button\n key={value}\n type=\"button\"\n className={clsx('ds-color-picker__swatch', {\n 'ds-color-picker__swatch--active': brand === value,\n })}\n onClick={() => setBrand(value)}\n aria-label={`Switch to ${label} color scheme`}\n aria-pressed={brand === value}\n title={label}\n >\n <span\n className=\"ds-color-picker__dot\"\n style={{ '--swatch-color': color } as React.CSSProperties}\n />\n </button>\n ))}\n\n {/* Custom color — clicking the dot opens the native color picker */}\n {!hideCustom && (\n <label\n className={clsx('ds-color-picker__swatch', 'ds-color-picker__swatch--custom', {\n 'ds-color-picker__swatch--active': brand === 'custom',\n })}\n title={`Custom: ${customColor}`}\n >\n <span\n className=\"ds-color-picker__dot\"\n style={{ '--swatch-color': customColor } as React.CSSProperties}\n />\n <input\n type=\"color\"\n value={customColor}\n onChange={(e) => setCustomColor(e.target.value)}\n className=\"ds-color-picker__native-input\"\n aria-label=\"Pick a custom accent color\"\n />\n </label>\n )}\n </div>\n );\n },\n);\n\nColorPicker.displayName = 'ColorPicker';\n"]}
@@ -0,0 +1,115 @@
1
+ import { useRef, useState, useEffect, useCallback } from 'react';
2
+ import { jsxs, jsx } from 'react/jsx-runtime';
3
+
4
+ // src/components/CursorSpotlight/CursorSpotlight.tsx
5
+ var CursorSpotlight = ({
6
+ children,
7
+ color = "var(--accent-rgb)",
8
+ size = 400,
9
+ opacity = 0.15,
10
+ borderGlow = true,
11
+ className,
12
+ disabled = false
13
+ }) => {
14
+ const containerRef = useRef(null);
15
+ const [position, setPosition] = useState({ x: 0, y: 0 });
16
+ const [isHovering, setIsHovering] = useState(false);
17
+ const [reducedMotion, setReducedMotion] = useState(false);
18
+ useEffect(() => {
19
+ const mediaQuery = window.matchMedia("(prefers-reduced-motion: reduce)");
20
+ setReducedMotion(mediaQuery.matches);
21
+ const handler = (e) => setReducedMotion(e.matches);
22
+ mediaQuery.addEventListener("change", handler);
23
+ return () => mediaQuery.removeEventListener("change", handler);
24
+ }, []);
25
+ const isDisabled = disabled || reducedMotion;
26
+ const handleMouseMove = useCallback(
27
+ (e) => {
28
+ if (!containerRef.current || isDisabled) return;
29
+ const rect = containerRef.current.getBoundingClientRect();
30
+ setPosition({
31
+ x: e.clientX - rect.left,
32
+ y: e.clientY - rect.top
33
+ });
34
+ },
35
+ [isDisabled]
36
+ );
37
+ const handleMouseEnter = useCallback(() => {
38
+ if (!isDisabled) setIsHovering(true);
39
+ }, [isDisabled]);
40
+ const handleMouseLeave = useCallback(() => {
41
+ setIsHovering(false);
42
+ }, []);
43
+ useEffect(() => {
44
+ const container = containerRef.current;
45
+ if (!container || isDisabled) return;
46
+ container.addEventListener("mousemove", handleMouseMove);
47
+ container.addEventListener("mouseenter", handleMouseEnter);
48
+ container.addEventListener("mouseleave", handleMouseLeave);
49
+ return () => {
50
+ container.removeEventListener("mousemove", handleMouseMove);
51
+ container.removeEventListener("mouseenter", handleMouseEnter);
52
+ container.removeEventListener("mouseleave", handleMouseLeave);
53
+ };
54
+ }, [handleMouseMove, handleMouseEnter, handleMouseLeave, isDisabled]);
55
+ const spotlightColor = color.startsWith("var(") ? `rgba(${color.replace("var(", "").replace(")", "")}, ${opacity})` : color.includes(",") ? `rgba(${color}, ${opacity})` : color;
56
+ const borderColor = color.startsWith("var(") ? `rgba(${color.replace("var(", "").replace(")", "")}, 0.5)` : color.includes(",") ? `rgba(${color}, 0.5)` : color;
57
+ return /* @__PURE__ */ jsxs(
58
+ "div",
59
+ {
60
+ ref: containerRef,
61
+ className: `ds-cursor-spotlight ${className || ""}`,
62
+ style: {
63
+ "--spotlight-x": `${position.x}px`,
64
+ "--spotlight-y": `${position.y}px`,
65
+ "--spotlight-size": `${size}px`,
66
+ "--spotlight-color": spotlightColor,
67
+ "--spotlight-border-color": borderColor,
68
+ "--spotlight-opacity": isHovering ? 1 : 0
69
+ },
70
+ "data-hovering": isHovering,
71
+ "data-border-glow": borderGlow,
72
+ children: [
73
+ /* @__PURE__ */ jsx("div", { className: "ds-cursor-spotlight__glow", "aria-hidden": "true" }),
74
+ borderGlow && /* @__PURE__ */ jsx("div", { className: "ds-cursor-spotlight__border", "aria-hidden": "true" }),
75
+ /* @__PURE__ */ jsx("div", { className: "ds-cursor-spotlight__content", children })
76
+ ]
77
+ }
78
+ );
79
+ };
80
+ CursorSpotlight.displayName = "CursorSpotlight";
81
+ var SpotlightCard = ({
82
+ title,
83
+ description,
84
+ icon,
85
+ children,
86
+ onClick,
87
+ ...spotlightProps
88
+ }) => {
89
+ return /* @__PURE__ */ jsx(CursorSpotlight, { ...spotlightProps, className: "ds-spotlight-card", children: /* @__PURE__ */ jsxs(
90
+ "div",
91
+ {
92
+ className: "ds-spotlight-card__inner",
93
+ onClick,
94
+ role: onClick ? "button" : void 0,
95
+ tabIndex: onClick ? 0 : void 0,
96
+ onKeyDown: onClick ? (e) => {
97
+ if (e.key === "Enter" || e.key === " ") {
98
+ e.preventDefault();
99
+ onClick();
100
+ }
101
+ } : void 0,
102
+ children: [
103
+ icon && /* @__PURE__ */ jsx("div", { className: "ds-spotlight-card__icon", children: icon }),
104
+ title && /* @__PURE__ */ jsx("h3", { className: "ds-spotlight-card__title", children: title }),
105
+ description && /* @__PURE__ */ jsx("p", { className: "ds-spotlight-card__description", children: description }),
106
+ children
107
+ ]
108
+ }
109
+ ) });
110
+ };
111
+ SpotlightCard.displayName = "SpotlightCard";
112
+
113
+ export { CursorSpotlight, SpotlightCard };
114
+ //# sourceMappingURL=out.js.map
115
+ //# sourceMappingURL=chunk-WB6XDNU7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/CursorSpotlight/CursorSpotlight.tsx"],"names":[],"mappings":";AAAA,SAAgB,WAAW,QAAQ,UAAU,mBAAmB;AA8G5D,SAiBE,KAjBF;AA3EG,IAAM,kBAAkD,CAAC;AAAA,EAC9D;AAAA,EACA,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,UAAU;AAAA,EACV,aAAa;AAAA,EACb;AAAA,EACA,WAAW;AACb,MAAM;AACJ,QAAM,eAAe,OAAuB,IAAI;AAChD,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,EAAE,GAAG,GAAG,GAAG,EAAE,CAAC;AACvD,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,KAAK;AAClD,QAAM,CAAC,eAAe,gBAAgB,IAAI,SAAS,KAAK;AAExD,YAAU,MAAM;AACd,UAAM,aAAa,OAAO,WAAW,kCAAkC;AACvE,qBAAiB,WAAW,OAAO;AAEnC,UAAM,UAAU,CAAC,MAA2B,iBAAiB,EAAE,OAAO;AACtE,eAAW,iBAAiB,UAAU,OAAO;AAC7C,WAAO,MAAM,WAAW,oBAAoB,UAAU,OAAO;AAAA,EAC/D,GAAG,CAAC,CAAC;AAEL,QAAM,aAAa,YAAY;AAE/B,QAAM,kBAAkB;AAAA,IACtB,CAAC,MAAkB;AACjB,UAAI,CAAC,aAAa,WAAW,WAAY;AAEzC,YAAM,OAAO,aAAa,QAAQ,sBAAsB;AACxD,kBAAY;AAAA,QACV,GAAG,EAAE,UAAU,KAAK;AAAA,QACpB,GAAG,EAAE,UAAU,KAAK;AAAA,MACtB,CAAC;AAAA,IACH;AAAA,IACA,CAAC,UAAU;AAAA,EACb;AAEA,QAAM,mBAAmB,YAAY,MAAM;AACzC,QAAI,CAAC,WAAY,eAAc,IAAI;AAAA,EACrC,GAAG,CAAC,UAAU,CAAC;AAEf,QAAM,mBAAmB,YAAY,MAAM;AACzC,kBAAc,KAAK;AAAA,EACrB,GAAG,CAAC,CAAC;AAEL,YAAU,MAAM;AACd,UAAM,YAAY,aAAa;AAC/B,QAAI,CAAC,aAAa,WAAY;AAE9B,cAAU,iBAAiB,aAAa,eAAe;AACvD,cAAU,iBAAiB,cAAc,gBAAgB;AACzD,cAAU,iBAAiB,cAAc,gBAAgB;AAEzD,WAAO,MAAM;AACX,gBAAU,oBAAoB,aAAa,eAAe;AAC1D,gBAAU,oBAAoB,cAAc,gBAAgB;AAC5D,gBAAU,oBAAoB,cAAc,gBAAgB;AAAA,IAC9D;AAAA,EACF,GAAG,CAAC,iBAAiB,kBAAkB,kBAAkB,UAAU,CAAC;AAGpE,QAAM,iBAAiB,MAAM,WAAW,MAAM,IAC1C,QAAQ,MAAM,QAAQ,QAAQ,EAAE,EAAE,QAAQ,KAAK,EAAE,CAAC,KAAK,OAAO,MAC9D,MAAM,SAAS,GAAG,IAChB,QAAQ,KAAK,KAAK,OAAO,MACzB;AAEN,QAAM,cAAc,MAAM,WAAW,MAAM,IACvC,QAAQ,MAAM,QAAQ,QAAQ,EAAE,EAAE,QAAQ,KAAK,EAAE,CAAC,WAClD,MAAM,SAAS,GAAG,IAChB,QAAQ,KAAK,WACb;AAEN,SACE;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL,WAAW,uBAAuB,aAAa,EAAE;AAAA,MACjD,OACE;AAAA,QACE,iBAAiB,GAAG,SAAS,CAAC;AAAA,QAC9B,iBAAiB,GAAG,SAAS,CAAC;AAAA,QAC9B,oBAAoB,GAAG,IAAI;AAAA,QAC3B,qBAAqB;AAAA,QACrB,4BAA4B;AAAA,QAC5B,uBAAuB,aAAa,IAAI;AAAA,MAC1C;AAAA,MAEF,iBAAe;AAAA,MACf,oBAAkB;AAAA,MAGlB;AAAA,4BAAC,SAAI,WAAU,6BAA4B,eAAY,QAAO;AAAA,QAG7D,cAAc,oBAAC,SAAI,WAAU,+BAA8B,eAAY,QAAO;AAAA,QAG/E,oBAAC,SAAI,WAAU,gCAAgC,UAAS;AAAA;AAAA;AAAA,EAC1D;AAEJ;AAEA,gBAAgB,cAAc;AAmBvB,IAAM,gBAA8C,CAAC;AAAA,EAC1D;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,MAAM;AACJ,SACE,oBAAC,mBAAiB,GAAG,gBAAgB,WAAU,qBAC7C;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MACV;AAAA,MACA,MAAM,UAAU,WAAW;AAAA,MAC3B,UAAU,UAAU,IAAI;AAAA,MACxB,WACE,UACI,CAAC,MAAM;AACL,YAAI,EAAE,QAAQ,WAAW,EAAE,QAAQ,KAAK;AACtC,YAAE,eAAe;AACjB,kBAAQ;AAAA,QACV;AAAA,MACF,IACA;AAAA,MAGL;AAAA,gBAAQ,oBAAC,SAAI,WAAU,2BAA2B,gBAAK;AAAA,QACvD,SAAS,oBAAC,QAAG,WAAU,4BAA4B,iBAAM;AAAA,QACzD,eAAe,oBAAC,OAAE,WAAU,kCAAkC,uBAAY;AAAA,QAC1E;AAAA;AAAA;AAAA,EACH,GACF;AAEJ;AAEA,cAAc,cAAc","sourcesContent":["import React, { useEffect, useRef, useState, useCallback } from 'react';\nimport './CursorSpotlight.css';\n\n/* ============================================================================\n CURSOR SPOTLIGHT — \"The Glow\" Effect\n ============================================================================\n \n A mouse-following spotlight effect that creates a premium, interactive feel\n on cards, buttons, and other surfaces. The spotlight reveals a subtle glow\n that follows the cursor position.\n \n Usage:\n <CursorSpotlight>\n <YourCard />\n </CursorSpotlight>\n \n ============================================================================ */\n\nexport interface CursorSpotlightProps {\n /** Children to wrap with spotlight effect */\n children: React.ReactNode;\n /** Spotlight color (CSS color or rgb values) */\n color?: string;\n /** Spotlight size in pixels */\n size?: number;\n /** Spotlight opacity (0-1) */\n opacity?: number;\n /** Enable border glow effect */\n borderGlow?: boolean;\n /** Custom class name */\n className?: string;\n /** Disable the effect */\n disabled?: boolean;\n}\n\nexport const CursorSpotlight: React.FC<CursorSpotlightProps> = ({\n children,\n color = 'var(--accent-rgb)',\n size = 400,\n opacity = 0.15,\n borderGlow = true,\n className,\n disabled = false,\n}) => {\n const containerRef = useRef<HTMLDivElement>(null);\n const [position, setPosition] = useState({ x: 0, y: 0 });\n const [isHovering, setIsHovering] = useState(false);\n const [reducedMotion, setReducedMotion] = useState(false);\n\n useEffect(() => {\n const mediaQuery = window.matchMedia('(prefers-reduced-motion: reduce)');\n setReducedMotion(mediaQuery.matches);\n\n const handler = (e: MediaQueryListEvent) => setReducedMotion(e.matches);\n mediaQuery.addEventListener('change', handler);\n return () => mediaQuery.removeEventListener('change', handler);\n }, []);\n\n const isDisabled = disabled || reducedMotion;\n\n const handleMouseMove = useCallback(\n (e: MouseEvent) => {\n if (!containerRef.current || isDisabled) return;\n\n const rect = containerRef.current.getBoundingClientRect();\n setPosition({\n x: e.clientX - rect.left,\n y: e.clientY - rect.top,\n });\n },\n [isDisabled],\n );\n\n const handleMouseEnter = useCallback(() => {\n if (!isDisabled) setIsHovering(true);\n }, [isDisabled]);\n\n const handleMouseLeave = useCallback(() => {\n setIsHovering(false);\n }, []);\n\n useEffect(() => {\n const container = containerRef.current;\n if (!container || isDisabled) return;\n\n container.addEventListener('mousemove', handleMouseMove);\n container.addEventListener('mouseenter', handleMouseEnter);\n container.addEventListener('mouseleave', handleMouseLeave);\n\n return () => {\n container.removeEventListener('mousemove', handleMouseMove);\n container.removeEventListener('mouseenter', handleMouseEnter);\n container.removeEventListener('mouseleave', handleMouseLeave);\n };\n }, [handleMouseMove, handleMouseEnter, handleMouseLeave, isDisabled]);\n\n // Parse color to handle both CSS variables and rgb values\n const spotlightColor = color.startsWith('var(')\n ? `rgba(${color.replace('var(', '').replace(')', '')}, ${opacity})`\n : color.includes(',')\n ? `rgba(${color}, ${opacity})`\n : color;\n\n const borderColor = color.startsWith('var(')\n ? `rgba(${color.replace('var(', '').replace(')', '')}, 0.5)`\n : color.includes(',')\n ? `rgba(${color}, 0.5)`\n : color;\n\n return (\n <div\n ref={containerRef}\n className={`ds-cursor-spotlight ${className || ''}`}\n style={\n {\n '--spotlight-x': `${position.x}px`,\n '--spotlight-y': `${position.y}px`,\n '--spotlight-size': `${size}px`,\n '--spotlight-color': spotlightColor,\n '--spotlight-border-color': borderColor,\n '--spotlight-opacity': isHovering ? 1 : 0,\n } as React.CSSProperties\n }\n data-hovering={isHovering}\n data-border-glow={borderGlow}\n >\n {/* Spotlight overlay */}\n <div className=\"ds-cursor-spotlight__glow\" aria-hidden=\"true\" />\n\n {/* Border glow */}\n {borderGlow && <div className=\"ds-cursor-spotlight__border\" aria-hidden=\"true\" />}\n\n {/* Content */}\n <div className=\"ds-cursor-spotlight__content\">{children}</div>\n </div>\n );\n};\n\nCursorSpotlight.displayName = 'CursorSpotlight';\n\n/* ============================================================================\n SPOTLIGHT CARD — Pre-styled card with spotlight effect\n ============================================================================ */\n\nexport interface SpotlightCardProps extends Omit<CursorSpotlightProps, 'children'> {\n /** Card title */\n title?: string;\n /** Card description */\n description?: string;\n /** Card icon */\n icon?: React.ReactNode;\n /** Card children */\n children?: React.ReactNode;\n /** Click handler */\n onClick?: () => void;\n}\n\nexport const SpotlightCard: React.FC<SpotlightCardProps> = ({\n title,\n description,\n icon,\n children,\n onClick,\n ...spotlightProps\n}) => {\n return (\n <CursorSpotlight {...spotlightProps} className=\"ds-spotlight-card\">\n <div\n className=\"ds-spotlight-card__inner\"\n onClick={onClick}\n role={onClick ? 'button' : undefined}\n tabIndex={onClick ? 0 : undefined}\n onKeyDown={\n onClick\n ? (e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n onClick();\n }\n }\n : undefined\n }\n >\n {icon && <div className=\"ds-spotlight-card__icon\">{icon}</div>}\n {title && <h3 className=\"ds-spotlight-card__title\">{title}</h3>}\n {description && <p className=\"ds-spotlight-card__description\">{description}</p>}\n {children}\n </div>\n </CursorSpotlight>\n );\n};\n\nSpotlightCard.displayName = 'SpotlightCard';\n"]}