@sonordev/site-kit 4.0.0 → 4.0.2

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 (263) hide show
  1. package/agent-manifest.json +6 -1
  2. package/dist/AnalyticsProvider-5SX4ZRGN.js +31 -0
  3. package/dist/{AnalyticsProvider-HZTP36TY.js.map → AnalyticsProvider-5SX4ZRGN.js.map} +1 -1
  4. package/dist/AnalyticsProvider-6DIRTRRA.mjs +10 -0
  5. package/dist/{AnalyticsProvider-P6J5ZGZB.mjs.map → AnalyticsProvider-6DIRTRRA.mjs.map} +1 -1
  6. package/dist/ChatWidget-3SUC7HZA.js +20 -0
  7. package/dist/{ChatWidget-XJFFNLTK.js.map → ChatWidget-3SUC7HZA.js.map} +1 -1
  8. package/dist/ChatWidget-JTTERPI5.mjs +11 -0
  9. package/dist/{ChatWidget-Q4YJUQ36.mjs.map → ChatWidget-JTTERPI5.mjs.map} +1 -1
  10. package/dist/DatePicker-4WNKNNCV.mjs +4 -0
  11. package/dist/DatePicker-4WNKNNCV.mjs.map +1 -0
  12. package/dist/DatePicker-VPZR7RU3.js +13 -0
  13. package/dist/DatePicker-VPZR7RU3.js.map +1 -0
  14. package/dist/EngageWidget-3MDY2W5G.mjs +9 -0
  15. package/dist/{EngageWidget-IMQBLBII.mjs.map → EngageWidget-3MDY2W5G.mjs.map} +1 -1
  16. package/dist/EngageWidget-YVAIP2NW.js +18 -0
  17. package/dist/{EngageWidget-NGPAVLLE.js.map → EngageWidget-YVAIP2NW.js.map} +1 -1
  18. package/dist/FileField-PD52URLX.js +91 -0
  19. package/dist/FileField-PD52URLX.js.map +1 -0
  20. package/dist/FileField-XMBF3XCG.mjs +85 -0
  21. package/dist/FileField-XMBF3XCG.mjs.map +1 -0
  22. package/dist/{FormSpotlight-5YFKFZYS.mjs → FormSpotlight-72TYHDPV.mjs} +8 -7
  23. package/dist/FormSpotlight-72TYHDPV.mjs.map +1 -0
  24. package/dist/{FormSpotlight-7BH6QE6T.js → FormSpotlight-AL6KGMGG.js} +23 -22
  25. package/dist/FormSpotlight-AL6KGMGG.js.map +1 -0
  26. package/dist/{FormStage-KB4GYL5K.mjs → FormStage-GNBPJ7WX.mjs} +7 -7
  27. package/dist/FormStage-GNBPJ7WX.mjs.map +1 -0
  28. package/dist/{FormStage-7BVPTSSE.js → FormStage-NLO52ER2.js} +19 -19
  29. package/dist/FormStage-NLO52ER2.js.map +1 -0
  30. package/dist/ManagedForm-2SYVMJY6.mjs +14 -0
  31. package/dist/ManagedForm-2SYVMJY6.mjs.map +1 -0
  32. package/dist/ManagedForm-XWN3TVQQ.js +23 -0
  33. package/dist/ManagedForm-XWN3TVQQ.js.map +1 -0
  34. package/dist/{SignalCore-6GXPHNFA.mjs → SignalCore-4NOVH33M.mjs} +5 -5
  35. package/dist/{SignalCore-6GXPHNFA.mjs.map → SignalCore-4NOVH33M.mjs.map} +1 -1
  36. package/dist/{SignalCore-LJ3NUEU4.js → SignalCore-PD75O7GB.js} +11 -11
  37. package/dist/{SignalCore-LJ3NUEU4.js.map → SignalCore-PD75O7GB.js.map} +1 -1
  38. package/dist/SitemapSync-KKUF43DS.js +17 -0
  39. package/dist/{SitemapSync-BG2ZWSM6.js.map → SitemapSync-KKUF43DS.js.map} +1 -1
  40. package/dist/SitemapSync-UH6VZ55T.mjs +8 -0
  41. package/dist/{SitemapSync-5DGS2XLM.mjs.map → SitemapSync-UH6VZ55T.mjs.map} +1 -1
  42. package/dist/analytics/index.js +9 -9
  43. package/dist/analytics/index.mjs +4 -4
  44. package/dist/{chunk-BFADBU2B.js → chunk-2ACKOMAL.js} +105 -514
  45. package/dist/chunk-2ACKOMAL.js.map +1 -0
  46. package/dist/{chunk-64DQANST.js → chunk-2MIOQYF5.js} +6 -6
  47. package/dist/{chunk-64DQANST.js.map → chunk-2MIOQYF5.js.map} +1 -1
  48. package/dist/{chunk-FLTTRX4O.mjs → chunk-53RHVKTD.mjs} +5 -5
  49. package/dist/{chunk-FLTTRX4O.mjs.map → chunk-53RHVKTD.mjs.map} +1 -1
  50. package/dist/{chunk-NQRUCOI7.js → chunk-5QVXHD6V.js} +14 -14
  51. package/dist/{chunk-NQRUCOI7.js.map → chunk-5QVXHD6V.js.map} +1 -1
  52. package/dist/{chunk-J6UKM476.js → chunk-62Z4D3QR.js} +5 -5
  53. package/dist/{chunk-J6UKM476.js.map → chunk-62Z4D3QR.js.map} +1 -1
  54. package/dist/{chunk-744TJ7SN.mjs → chunk-6PNWJXAE.mjs} +3 -3
  55. package/dist/{chunk-744TJ7SN.mjs.map → chunk-6PNWJXAE.mjs.map} +1 -1
  56. package/dist/{chunk-NKKXKN5L.mjs → chunk-6ZFXASW6.mjs} +4 -4
  57. package/dist/{chunk-NKKXKN5L.mjs.map → chunk-6ZFXASW6.mjs.map} +1 -1
  58. package/dist/{chunk-YNIZKOEW.js → chunk-A624YYDG.js} +2 -2
  59. package/dist/{chunk-YNIZKOEW.js.map → chunk-A624YYDG.js.map} +1 -1
  60. package/dist/chunk-AJNT5GCU.mjs +183 -0
  61. package/dist/chunk-AJNT5GCU.mjs.map +1 -0
  62. package/dist/chunk-AQA4PFMJ.mjs +44 -0
  63. package/dist/chunk-AQA4PFMJ.mjs.map +1 -0
  64. package/dist/{chunk-AVHXVWWG.mjs → chunk-B7J7SU5A.mjs} +8 -8
  65. package/dist/{chunk-AVHXVWWG.mjs.map → chunk-B7J7SU5A.mjs.map} +1 -1
  66. package/dist/chunk-DVYYGHSG.js +285 -0
  67. package/dist/chunk-DVYYGHSG.js.map +1 -0
  68. package/dist/{chunk-HUYPO33T.mjs → chunk-FGN27J35.mjs} +4 -4
  69. package/dist/{chunk-HUYPO33T.mjs.map → chunk-FGN27J35.mjs.map} +1 -1
  70. package/dist/{chunk-O5Z7ZKYS.mjs → chunk-FZ643CZK.mjs} +4 -4
  71. package/dist/{chunk-O5Z7ZKYS.mjs.map → chunk-FZ643CZK.mjs.map} +1 -1
  72. package/dist/{chunk-3IXGYUUR.mjs → chunk-GCS4XZHC.mjs} +3 -3
  73. package/dist/{chunk-3IXGYUUR.mjs.map → chunk-GCS4XZHC.mjs.map} +1 -1
  74. package/dist/{chunk-BH3XILNX.mjs → chunk-HOB2A6JM.mjs} +3 -3
  75. package/dist/{chunk-BH3XILNX.mjs.map → chunk-HOB2A6JM.mjs.map} +1 -1
  76. package/dist/{chunk-U3OU4V56.mjs → chunk-JFVEKSZT.mjs} +5 -182
  77. package/dist/chunk-JFVEKSZT.mjs.map +1 -0
  78. package/dist/{chunk-PMCNKBYS.mjs → chunk-KNYK2VLX.mjs} +3 -3
  79. package/dist/{chunk-PMCNKBYS.mjs.map → chunk-KNYK2VLX.mjs.map} +1 -1
  80. package/dist/{chunk-6DJEXHZE.mjs → chunk-LB7LHVWI.mjs} +107 -503
  81. package/dist/chunk-LB7LHVWI.mjs.map +1 -0
  82. package/dist/{chunk-YOTQH4X4.js → chunk-LP6MC2RH.js} +14 -14
  83. package/dist/{chunk-YOTQH4X4.js.map → chunk-LP6MC2RH.js.map} +1 -1
  84. package/dist/chunk-LVCUT2OX.js +148 -0
  85. package/dist/chunk-LVCUT2OX.js.map +1 -0
  86. package/dist/chunk-LYAU5PRS.js +8 -0
  87. package/dist/{chunk-T7QIXFFM.js.map → chunk-LYAU5PRS.js.map} +1 -1
  88. package/dist/{chunk-OTAIUZED.js → chunk-MPACJDDG.js} +8 -8
  89. package/dist/{chunk-OTAIUZED.js.map → chunk-MPACJDDG.js.map} +1 -1
  90. package/dist/{chunk-W4WGNWQG.js → chunk-MRRTAJWQ.js} +3 -3
  91. package/dist/{chunk-W4WGNWQG.js.map → chunk-MRRTAJWQ.js.map} +1 -1
  92. package/dist/chunk-MU3D5IO7.mjs +283 -0
  93. package/dist/chunk-MU3D5IO7.mjs.map +1 -0
  94. package/dist/{chunk-L5NLP3EW.js → chunk-MVHR77V4.js} +11 -2
  95. package/dist/chunk-MVHR77V4.js.map +1 -0
  96. package/dist/chunk-MZY3MEUO.mjs +138 -0
  97. package/dist/chunk-MZY3MEUO.mjs.map +1 -0
  98. package/dist/chunk-NCWVR4BU.mjs +508 -0
  99. package/dist/chunk-NCWVR4BU.mjs.map +1 -0
  100. package/dist/{chunk-LKZF7343.js → chunk-NJN467OM.js} +14 -14
  101. package/dist/{chunk-LKZF7343.js.map → chunk-NJN467OM.js.map} +1 -1
  102. package/dist/{chunk-EZCDDRXA.mjs → chunk-NWJX6NHR.mjs} +4 -4
  103. package/dist/{chunk-EZCDDRXA.mjs.map → chunk-NWJX6NHR.mjs.map} +1 -1
  104. package/dist/chunk-NWPNFCNY.js +511 -0
  105. package/dist/chunk-NWPNFCNY.js.map +1 -0
  106. package/dist/{chunk-P64SKDBS.js → chunk-OP5FYTWJ.js} +12 -191
  107. package/dist/chunk-OP5FYTWJ.js.map +1 -0
  108. package/dist/{chunk-JM5JCIAY.js → chunk-PGND73EV.js} +4 -4
  109. package/dist/{chunk-JM5JCIAY.js.map → chunk-PGND73EV.js.map} +1 -1
  110. package/dist/chunk-QO5NS62X.js +46 -0
  111. package/dist/chunk-QO5NS62X.js.map +1 -0
  112. package/dist/chunk-QTN3F7D7.mjs +6 -0
  113. package/dist/{chunk-LLQEJDCA.mjs.map → chunk-QTN3F7D7.mjs.map} +1 -1
  114. package/dist/chunk-R4PCXP3F.mjs +60 -0
  115. package/dist/chunk-R4PCXP3F.mjs.map +1 -0
  116. package/dist/{chunk-BOWEGDKJ.js → chunk-RAX3HS4T.js} +15 -15
  117. package/dist/{chunk-BOWEGDKJ.js.map → chunk-RAX3HS4T.js.map} +1 -1
  118. package/dist/{chunk-SXE2TRRU.js → chunk-T2YAEBEY.js} +4 -4
  119. package/dist/{chunk-SXE2TRRU.js.map → chunk-T2YAEBEY.js.map} +1 -1
  120. package/dist/{chunk-I76F26IS.mjs → chunk-T5DXYGWK.mjs} +3 -3
  121. package/dist/{chunk-I76F26IS.mjs.map → chunk-T5DXYGWK.mjs.map} +1 -1
  122. package/dist/{chunk-KHJYA64F.mjs → chunk-U2CJZ6O5.mjs} +2 -2
  123. package/dist/{chunk-KHJYA64F.mjs.map → chunk-U2CJZ6O5.mjs.map} +1 -1
  124. package/dist/{chunk-TUVJQ6FS.mjs → chunk-UBNVD567.mjs} +3 -3
  125. package/dist/{chunk-TUVJQ6FS.mjs.map → chunk-UBNVD567.mjs.map} +1 -1
  126. package/dist/chunk-UQPUFRXF.mjs +104 -0
  127. package/dist/chunk-UQPUFRXF.mjs.map +1 -0
  128. package/dist/chunk-VLY3SFNH.js +64 -0
  129. package/dist/chunk-VLY3SFNH.js.map +1 -0
  130. package/dist/{chunk-MVMORDOM.mjs → chunk-VUIQLKJB.mjs} +3 -3
  131. package/dist/{chunk-MVMORDOM.mjs.map → chunk-VUIQLKJB.mjs.map} +1 -1
  132. package/dist/chunk-WHVUSVHA.js +110 -0
  133. package/dist/chunk-WHVUSVHA.js.map +1 -0
  134. package/dist/{chunk-H4F557CO.js → chunk-WNNY2BCV.js} +8 -8
  135. package/dist/{chunk-H4F557CO.js.map → chunk-WNNY2BCV.js.map} +1 -1
  136. package/dist/{chunk-KN2EYZYV.mjs → chunk-WSPGXSBU.mjs} +11 -2
  137. package/dist/chunk-WSPGXSBU.mjs.map +1 -0
  138. package/dist/{chunk-ZLVZOAJG.js → chunk-WXKEYKE6.js} +15 -15
  139. package/dist/{chunk-ZLVZOAJG.js.map → chunk-WXKEYKE6.js.map} +1 -1
  140. package/dist/{chunk-NWYXTJNW.mjs → chunk-XNPITFJ5.mjs} +3 -3
  141. package/dist/{chunk-NWYXTJNW.mjs.map → chunk-XNPITFJ5.mjs.map} +1 -1
  142. package/dist/{chunk-ZBFBNGQK.mjs → chunk-XRC3JZAK.mjs} +3 -3
  143. package/dist/{chunk-ZBFBNGQK.mjs.map → chunk-XRC3JZAK.mjs.map} +1 -1
  144. package/dist/{chunk-OWQFWDE2.js → chunk-Y26LKDE4.js} +9 -9
  145. package/dist/{chunk-OWQFWDE2.js.map → chunk-Y26LKDE4.js.map} +1 -1
  146. package/dist/{chunk-2OD3EGPO.js → chunk-ZK5VSIRO.js} +12 -12
  147. package/dist/{chunk-2OD3EGPO.js.map → chunk-ZK5VSIRO.js.map} +1 -1
  148. package/dist/{chunk-IPQ7Z5VT.js → chunk-ZPG4GTU7.js} +5 -5
  149. package/dist/{chunk-IPQ7Z5VT.js.map → chunk-ZPG4GTU7.js.map} +1 -1
  150. package/dist/chunk-ZRGCKWIT.js +188 -0
  151. package/dist/chunk-ZRGCKWIT.js.map +1 -0
  152. package/dist/cli/index.js +3 -3
  153. package/dist/cli/index.mjs +1 -1
  154. package/dist/client/index.js +5 -5
  155. package/dist/client/index.mjs +4 -4
  156. package/dist/commerce/index.js +56 -56
  157. package/dist/commerce/index.mjs +5 -5
  158. package/dist/engage/index.js +10 -9
  159. package/dist/engage/index.js.map +1 -1
  160. package/dist/engage/index.mjs +7 -6
  161. package/dist/engage/index.mjs.map +1 -1
  162. package/dist/fleet/index.js +9 -9
  163. package/dist/fleet/index.mjs +5 -5
  164. package/dist/forms/FileField.d.ts +31 -0
  165. package/dist/forms/FileField.d.ts.map +1 -0
  166. package/dist/forms/FormClient.d.ts +12 -1
  167. package/dist/forms/FormClient.d.ts.map +1 -1
  168. package/dist/forms/FormEnhancer.d.ts +72 -0
  169. package/dist/forms/FormEnhancer.d.ts.map +1 -0
  170. package/dist/forms/FormField.d.ts +26 -3
  171. package/dist/forms/FormField.d.ts.map +1 -1
  172. package/dist/forms/FormSpotlight.d.ts.map +1 -1
  173. package/dist/forms/FormStage.d.ts.map +1 -1
  174. package/dist/forms/HoneypotField.d.ts +8 -2
  175. package/dist/forms/HoneypotField.d.ts.map +1 -1
  176. package/dist/forms/ManagedForm.d.ts +1 -1
  177. package/dist/forms/ManagedForm.d.ts.map +1 -1
  178. package/dist/forms/ServerForm.d.ts +73 -0
  179. package/dist/forms/ServerForm.d.ts.map +1 -0
  180. package/dist/forms/StaticForm.d.ts +50 -0
  181. package/dist/forms/StaticForm.d.ts.map +1 -0
  182. package/dist/forms/experience-chrome.d.ts.map +1 -1
  183. package/dist/forms/field-controls.d.ts +7 -0
  184. package/dist/forms/field-controls.d.ts.map +1 -0
  185. package/dist/forms/index.d.ts +7 -0
  186. package/dist/forms/index.d.ts.map +1 -1
  187. package/dist/forms/index.js +33 -537
  188. package/dist/forms/index.js.map +1 -1
  189. package/dist/forms/index.mjs +14 -530
  190. package/dist/forms/index.mjs.map +1 -1
  191. package/dist/forms/native-fallback.d.ts +41 -0
  192. package/dist/forms/native-fallback.d.ts.map +1 -0
  193. package/dist/forms/native-fallback.test.d.ts +2 -0
  194. package/dist/forms/native-fallback.test.d.ts.map +1 -0
  195. package/dist/forms/normalize-config.d.ts +18 -0
  196. package/dist/forms/normalize-config.d.ts.map +1 -0
  197. package/dist/forms/server.d.ts +5 -0
  198. package/dist/forms/server.d.ts.map +1 -1
  199. package/dist/forms/server.js +47 -0
  200. package/dist/forms/server.js.map +1 -1
  201. package/dist/forms/server.mjs +44 -1
  202. package/dist/forms/server.mjs.map +1 -1
  203. package/dist/forms/static-form.test.d.ts +2 -0
  204. package/dist/forms/static-form.test.d.ts.map +1 -0
  205. package/dist/forms/static.d.ts +17 -0
  206. package/dist/forms/static.d.ts.map +1 -0
  207. package/dist/forms/static.js +20 -0
  208. package/dist/forms/static.js.map +1 -0
  209. package/dist/forms/static.mjs +11 -0
  210. package/dist/forms/static.mjs.map +1 -0
  211. package/dist/forms/submitForm.d.ts +4 -0
  212. package/dist/forms/submitForm.d.ts.map +1 -1
  213. package/dist/forms/types.d.ts +19 -0
  214. package/dist/forms/types.d.ts.map +1 -1
  215. package/dist/forms/useForm.d.ts.map +1 -1
  216. package/dist/images/index.js +11 -11
  217. package/dist/images/index.mjs +4 -4
  218. package/dist/index.js +109 -109
  219. package/dist/index.mjs +9 -9
  220. package/dist/layout/client.js +9 -9
  221. package/dist/layout/client.mjs +8 -8
  222. package/dist/layout/index.js +11 -11
  223. package/dist/layout/index.mjs +9 -9
  224. package/dist/maps/index.js +5 -5
  225. package/dist/maps/index.mjs +3 -3
  226. package/dist/search/index.js +4 -4
  227. package/dist/search/index.mjs +3 -3
  228. package/dist/seo/client.js +5 -5
  229. package/dist/seo/client.mjs +4 -4
  230. package/dist/seo/index.js +14 -14
  231. package/dist/seo/index.mjs +6 -6
  232. package/dist/seo/server.js +5 -5
  233. package/dist/seo/server.mjs +1 -1
  234. package/dist/server/index.js +3 -3
  235. package/dist/server/index.mjs +2 -2
  236. package/dist/shared/build-entries.d.ts +6 -0
  237. package/dist/shared/build-entries.d.ts.map +1 -1
  238. package/dist/shared/version.d.ts +1 -1
  239. package/dist/signal/index.js +11 -11
  240. package/dist/signal/index.mjs +2 -2
  241. package/dist/sync/index.js +16 -16
  242. package/dist/sync/index.mjs +4 -4
  243. package/package.json +9 -4
  244. package/dist/AnalyticsProvider-HZTP36TY.js +0 -31
  245. package/dist/AnalyticsProvider-P6J5ZGZB.mjs +0 -10
  246. package/dist/ChatWidget-Q4YJUQ36.mjs +0 -10
  247. package/dist/ChatWidget-XJFFNLTK.js +0 -19
  248. package/dist/EngageWidget-IMQBLBII.mjs +0 -9
  249. package/dist/EngageWidget-NGPAVLLE.js +0 -18
  250. package/dist/FormSpotlight-5YFKFZYS.mjs.map +0 -1
  251. package/dist/FormSpotlight-7BH6QE6T.js.map +0 -1
  252. package/dist/FormStage-7BVPTSSE.js.map +0 -1
  253. package/dist/FormStage-KB4GYL5K.mjs.map +0 -1
  254. package/dist/SitemapSync-5DGS2XLM.mjs +0 -8
  255. package/dist/SitemapSync-BG2ZWSM6.js +0 -17
  256. package/dist/chunk-6DJEXHZE.mjs.map +0 -1
  257. package/dist/chunk-BFADBU2B.js.map +0 -1
  258. package/dist/chunk-KN2EYZYV.mjs.map +0 -1
  259. package/dist/chunk-L5NLP3EW.js.map +0 -1
  260. package/dist/chunk-LLQEJDCA.mjs +0 -6
  261. package/dist/chunk-P64SKDBS.js.map +0 -1
  262. package/dist/chunk-T7QIXFFM.js +0 -8
  263. package/dist/chunk-U3OU4V56.mjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/shared/version.ts"],"names":[],"mappings":";;;AASO,IAAM,gBAAA,GAAmB","file":"chunk-T7QIXFFM.js","sourcesContent":["/**\n * @sonordev/site-kit shared - Package version constant\n *\n * Single source of truth for the version the kit reports at runtime (the\n * x-sitekit-version request header set by sonorFetch). Kept as a plain const\n * so client bundles don't need JSON imports; scripts/verify-dts.cjs asserts\n * it matches package.json at publish time, and version.test.ts asserts it in\n * dev, so it can't silently drift.\n */\nexport const SITE_KIT_VERSION = '4.0.0'\n"]}
1
+ {"version":3,"sources":["../src/shared/version.ts"],"names":[],"mappings":";;;AASO,IAAM,gBAAA,GAAmB","file":"chunk-LYAU5PRS.js","sourcesContent":["/**\n * @sonordev/site-kit shared - Package version constant\n *\n * Single source of truth for the version the kit reports at runtime (the\n * x-sitekit-version request header set by sonorFetch). Kept as a plain const\n * so client bundles don't need JSON imports; scripts/verify-dts.cjs asserts\n * it matches package.json at publish time, and version.test.ts asserts it in\n * dev, so it can't silently drift.\n */\nexport const SITE_KIT_VERSION = '4.0.2'\n"]}
@@ -1,10 +1,10 @@
1
1
  'use client';
2
2
  'use strict';
3
3
 
4
- var chunkHGNSJXWT_js = require('./chunk-HGNSJXWT.js');
5
4
  var chunkQ2NBD54V_js = require('./chunk-Q2NBD54V.js');
6
- var chunkZLVZOAJG_js = require('./chunk-ZLVZOAJG.js');
7
- var chunkT7QIXFFM_js = require('./chunk-T7QIXFFM.js');
5
+ var chunkHGNSJXWT_js = require('./chunk-HGNSJXWT.js');
6
+ var chunkWXKEYKE6_js = require('./chunk-WXKEYKE6.js');
7
+ var chunkLYAU5PRS_js = require('./chunk-LYAU5PRS.js');
8
8
  var react = require('react');
9
9
 
10
10
  // src/fleet/heartbeat.ts
@@ -18,14 +18,14 @@ async function sendFleetHeartbeat(opts) {
18
18
  const { apiUrl, apiKey } = opts;
19
19
  if (!apiUrl || !apiKey) return false;
20
20
  const payload = chunkQ2NBD54V_js.buildFleetHeartbeatPayload({
21
- kitVersion: opts.kitVersion || chunkT7QIXFFM_js.SITE_KIT_VERSION,
21
+ kitVersion: opts.kitVersion || chunkLYAU5PRS_js.SITE_KIT_VERSION,
22
22
  nextVersion: opts.nextVersion,
23
23
  modules: opts.modules,
24
24
  site: opts.site,
25
25
  source: opts.source
26
26
  });
27
27
  try {
28
- const res = await chunkZLVZOAJG_js.sonorFetch(
28
+ const res = await chunkWXKEYKE6_js.sonorFetch(
29
29
  `${apiUrl.replace(/\/$/, "")}${FLEET_HEARTBEAT_PATH}`,
30
30
  {
31
31
  method: "POST",
@@ -70,7 +70,7 @@ function FleetHeartbeat({
70
70
  if (!key) return;
71
71
  const resolvedSite = site ?? w.__SITE_KIT_SITE__ ?? window.location.host ?? "";
72
72
  if (oncePerSession) {
73
- const stamp = `${chunkT7QIXFFM_js.SITE_KIT_VERSION}:${resolvedSite}`;
73
+ const stamp = `${chunkLYAU5PRS_js.SITE_KIT_VERSION}:${resolvedSite}`;
74
74
  try {
75
75
  if (sessionStorage.getItem(SESSION_GUARD_KEY) === stamp) {
76
76
  sentRef.current = true;
@@ -98,5 +98,5 @@ exports.FLEET_HEARTBEAT_PATH = FLEET_HEARTBEAT_PATH;
98
98
  exports.FleetHeartbeat = FleetHeartbeat;
99
99
  exports.detectNextVersion = detectNextVersion;
100
100
  exports.sendFleetHeartbeat = sendFleetHeartbeat;
101
- //# sourceMappingURL=chunk-OTAIUZED.js.map
102
- //# sourceMappingURL=chunk-OTAIUZED.js.map
101
+ //# sourceMappingURL=chunk-MPACJDDG.js.map
102
+ //# sourceMappingURL=chunk-MPACJDDG.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/fleet/heartbeat.ts","../src/fleet/FleetHeartbeat.tsx"],"names":["sanitizeFleetVersion","buildFleetHeartbeatPayload","SITE_KIT_VERSION","sonorFetch","useDeferredActivation","useRef","useEffect"],"mappings":"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","file":"chunk-OTAIUZED.js","sourcesContent":["/**\n * @sonordev/site-kit/fleet — heartbeat sender (single source of truth for the POST)\n *\n * One place that turns fleet inputs into a validated payload and ships it to\n * `/api/public/fleet/heartbeat`. Used by BOTH the client component\n * (FleetHeartbeat) and any build-time caller, so the send path never forks.\n *\n * It routes through the canonical `sonorFetch` transport, which already stamps\n * the x-sitekit-version header, attaches x-api-key, and honors the auth\n * circuit breaker — a site on a stale/canceled key will not spam the API.\n */\n\nimport { sonorFetch } from '../shared/sonorFetch'\nimport { SITE_KIT_VERSION } from '../shared/version'\nimport {\n buildFleetHeartbeatPayload,\n sanitizeFleetVersion,\n type FleetHeartbeatPayload,\n type FleetHeartbeatSource,\n type FleetModule,\n} from './contract'\n\n/** Canonical ingest path — resolved against the site's Sonor API origin. */\nexport const FLEET_HEARTBEAT_PATH = '/api/public/fleet/heartbeat'\n\nexport interface SendFleetHeartbeatOptions {\n /** Sonor API origin (e.g. https://api.sonor.io). Trailing slash tolerated. */\n apiUrl: string\n /** Project API key — scopes the request; the project is resolved server-side. */\n apiKey: string\n /** Client-observable active modules (validated/deduped by the contract). */\n modules?: FleetModule[] | string[]\n /** Normalized host for the multi-site sub-dimension (server re-normalizes). */\n site?: string | null\n /** Installed Next.js version, if the caller can resolve it. */\n nextVersion?: string | null\n /** 'client' (runtime boot) or 'build' (CI). Default 'client'. */\n source?: FleetHeartbeatSource\n /** Override the reported kit version. Defaults to the runtime SITE_KIT_VERSION. */\n kitVersion?: string\n /** Log one info line with the result. Never logs at error level. */\n debug?: boolean\n}\n\n/**\n * Best-effort runtime detection of the host Next.js version. Unreliable by\n * design — Next does not expose its version consistently across app/pages\n * router — so it returns null unless a site injects `__SITE_KIT_NEXT_VERSION__`\n * or `window.next.version` is present. Build-time callers should pass\n * `nextVersion` explicitly (resolvable from `next/package.json`).\n */\nexport function detectNextVersion(): string | null {\n if (typeof window === 'undefined') return null\n const w = window as unknown as {\n __SITE_KIT_NEXT_VERSION__?: string\n next?: { version?: string }\n }\n return sanitizeFleetVersion(w.__SITE_KIT_NEXT_VERSION__ ?? w.next?.version ?? null)\n}\n\n/**\n * Fire one heartbeat. Fire-and-forget by contract: it never throws — a\n * telemetry POST must never break a visitor's page or a site's build. Returns\n * true only when the POST was accepted (2xx).\n */\nexport async function sendFleetHeartbeat(opts: SendFleetHeartbeatOptions): Promise<boolean> {\n const { apiUrl, apiKey } = opts\n if (!apiUrl || !apiKey) return false\n\n const payload: FleetHeartbeatPayload = buildFleetHeartbeatPayload({\n kitVersion: opts.kitVersion || SITE_KIT_VERSION,\n nextVersion: opts.nextVersion,\n modules: opts.modules,\n site: opts.site,\n source: opts.source,\n })\n\n try {\n const res = await sonorFetch(\n `${apiUrl.replace(/\\/$/, '')}${FLEET_HEARTBEAT_PATH}`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n },\n // retries: 0 — the once-per-session guard already bounds volume, and a\n // missed heartbeat is harmless (the next page load or passive\n // x-sitekit-version capture re-registers the site).\n { apiKey, retries: 0 },\n )\n if (opts.debug) {\n // info, never error: a visitor's console must stay clean (Lighthouse).\n console.info(\n `[Sonor fleet] heartbeat ${res.status} v${payload.kit_version} [${payload.modules.join(',') || 'none'}]`,\n )\n }\n return res.ok\n } catch (err) {\n if (opts.debug) console.info('[Sonor fleet] heartbeat skipped (network)', err)\n return false\n }\n}\n","/**\n * @sonordev/site-kit/fleet — FleetHeartbeat\n *\n * A childless, effect-only client component that reports this site's build\n * fingerprint (kit version, Next.js version, active modules, host) to Sonor\n * exactly once per browser session. It exists so the platform can answer\n * \"which version / modules / key state is each site running\" without shelling\n * into every client repo.\n *\n * It is mounted as a deferred, CHILDLESS SIBLING — never a wrapper — by\n * SiteKitClientProviders, behind the same idle/first-interaction gate as\n * Analytics and Engage, so it never enters the critical hydration path (see\n * CLAUDE.md \"the page must server-render\"). It renders null and carries no\n * visitor data.\n */\n\n'use client'\n\nimport { useEffect, useRef } from 'react'\nimport { useDeferredActivation } from '../shared/defer'\nimport { SITE_KIT_VERSION } from '../shared/version'\nimport { detectNextVersion, sendFleetHeartbeat } from './heartbeat'\nimport type { FleetModule } from './contract'\n\n/** Session guard key — one heartbeat per session, re-sent when version/host changes. */\nconst SESSION_GUARD_KEY = '_sk_fleet_hb'\n\nexport interface FleetHeartbeatProps {\n /** Sonor API origin. Falls back to `window.__SITE_KIT_API_URL__` then api.sonor.io. */\n apiUrl?: string\n /** Project API key. Falls back to `window.__SITE_KIT_API_KEY__`. */\n apiKey?: string\n /** Host for the multi-site sub-dimension. Falls back to `__SITE_KIT_SITE__` then location.host. */\n site?: string | null\n /** Client-observable active modules (SiteKitClientProviders passes the enabled set). */\n modules?: FleetModule[]\n /** Installed Next.js version if known; otherwise best-effort runtime detection. */\n nextVersion?: string | null\n /**\n * Self-defer to idle / first interaction. Default true so standalone use is\n * safe. SiteKitClientProviders already gates the mount and passes false.\n */\n defer?: boolean\n /** Send at most once per browser session per (version, host). Default true. */\n oncePerSession?: boolean\n debug?: boolean\n}\n\nexport function FleetHeartbeat({\n apiUrl,\n apiKey,\n site,\n modules,\n nextVersion,\n defer = true,\n oncePerSession = true,\n debug = false,\n}: FleetHeartbeatProps) {\n const ready = useDeferredActivation(defer)\n const sentRef = useRef(false)\n\n useEffect(() => {\n if (!ready || sentRef.current) return\n if (typeof window === 'undefined') return\n\n const w = window as unknown as {\n __SITE_KIT_API_URL__?: string\n __SITE_KIT_API_KEY__?: string\n __SITE_KIT_SITE__?: string\n }\n const url = apiUrl || w.__SITE_KIT_API_URL__ || 'https://api.sonor.io'\n const key = apiKey || w.__SITE_KIT_API_KEY__\n if (!key) return // no key resolvable — nothing to authenticate the heartbeat\n\n const resolvedSite = site ?? w.__SITE_KIT_SITE__ ?? window.location.host ?? ''\n\n // Once-per-session-per-(version,host): keeps heartbeat volume to one POST\n // per visitor session while still re-reporting after an upgrade or on a\n // different sub-site. Storage-disabled browsers simply send once per load.\n if (oncePerSession) {\n const stamp = `${SITE_KIT_VERSION}:${resolvedSite}`\n try {\n if (sessionStorage.getItem(SESSION_GUARD_KEY) === stamp) {\n sentRef.current = true\n return\n }\n sessionStorage.setItem(SESSION_GUARD_KEY, stamp)\n } catch {\n /* storage unavailable — fall through and send this load */\n }\n }\n\n sentRef.current = true\n void sendFleetHeartbeat({\n apiUrl: url,\n apiKey: key,\n site: resolvedSite,\n modules,\n nextVersion: nextVersion ?? detectNextVersion(),\n source: 'client',\n debug,\n })\n }, [ready, apiUrl, apiKey, site, modules, nextVersion, oncePerSession, debug])\n\n return null\n}\n"]}
1
+ {"version":3,"sources":["../src/fleet/heartbeat.ts","../src/fleet/FleetHeartbeat.tsx"],"names":["sanitizeFleetVersion","buildFleetHeartbeatPayload","SITE_KIT_VERSION","sonorFetch","useDeferredActivation","useRef","useEffect"],"mappings":"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","file":"chunk-MPACJDDG.js","sourcesContent":["/**\n * @sonordev/site-kit/fleet — heartbeat sender (single source of truth for the POST)\n *\n * One place that turns fleet inputs into a validated payload and ships it to\n * `/api/public/fleet/heartbeat`. Used by BOTH the client component\n * (FleetHeartbeat) and any build-time caller, so the send path never forks.\n *\n * It routes through the canonical `sonorFetch` transport, which already stamps\n * the x-sitekit-version header, attaches x-api-key, and honors the auth\n * circuit breaker — a site on a stale/canceled key will not spam the API.\n */\n\nimport { sonorFetch } from '../shared/sonorFetch'\nimport { SITE_KIT_VERSION } from '../shared/version'\nimport {\n buildFleetHeartbeatPayload,\n sanitizeFleetVersion,\n type FleetHeartbeatPayload,\n type FleetHeartbeatSource,\n type FleetModule,\n} from './contract'\n\n/** Canonical ingest path — resolved against the site's Sonor API origin. */\nexport const FLEET_HEARTBEAT_PATH = '/api/public/fleet/heartbeat'\n\nexport interface SendFleetHeartbeatOptions {\n /** Sonor API origin (e.g. https://api.sonor.io). Trailing slash tolerated. */\n apiUrl: string\n /** Project API key — scopes the request; the project is resolved server-side. */\n apiKey: string\n /** Client-observable active modules (validated/deduped by the contract). */\n modules?: FleetModule[] | string[]\n /** Normalized host for the multi-site sub-dimension (server re-normalizes). */\n site?: string | null\n /** Installed Next.js version, if the caller can resolve it. */\n nextVersion?: string | null\n /** 'client' (runtime boot) or 'build' (CI). Default 'client'. */\n source?: FleetHeartbeatSource\n /** Override the reported kit version. Defaults to the runtime SITE_KIT_VERSION. */\n kitVersion?: string\n /** Log one info line with the result. Never logs at error level. */\n debug?: boolean\n}\n\n/**\n * Best-effort runtime detection of the host Next.js version. Unreliable by\n * design — Next does not expose its version consistently across app/pages\n * router — so it returns null unless a site injects `__SITE_KIT_NEXT_VERSION__`\n * or `window.next.version` is present. Build-time callers should pass\n * `nextVersion` explicitly (resolvable from `next/package.json`).\n */\nexport function detectNextVersion(): string | null {\n if (typeof window === 'undefined') return null\n const w = window as unknown as {\n __SITE_KIT_NEXT_VERSION__?: string\n next?: { version?: string }\n }\n return sanitizeFleetVersion(w.__SITE_KIT_NEXT_VERSION__ ?? w.next?.version ?? null)\n}\n\n/**\n * Fire one heartbeat. Fire-and-forget by contract: it never throws — a\n * telemetry POST must never break a visitor's page or a site's build. Returns\n * true only when the POST was accepted (2xx).\n */\nexport async function sendFleetHeartbeat(opts: SendFleetHeartbeatOptions): Promise<boolean> {\n const { apiUrl, apiKey } = opts\n if (!apiUrl || !apiKey) return false\n\n const payload: FleetHeartbeatPayload = buildFleetHeartbeatPayload({\n kitVersion: opts.kitVersion || SITE_KIT_VERSION,\n nextVersion: opts.nextVersion,\n modules: opts.modules,\n site: opts.site,\n source: opts.source,\n })\n\n try {\n const res = await sonorFetch(\n `${apiUrl.replace(/\\/$/, '')}${FLEET_HEARTBEAT_PATH}`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n },\n // retries: 0 — the once-per-session guard already bounds volume, and a\n // missed heartbeat is harmless (the next page load or passive\n // x-sitekit-version capture re-registers the site).\n { apiKey, retries: 0 },\n )\n if (opts.debug) {\n // info, never error: a visitor's console must stay clean (Lighthouse).\n console.info(\n `[Sonor fleet] heartbeat ${res.status} v${payload.kit_version} [${payload.modules.join(',') || 'none'}]`,\n )\n }\n return res.ok\n } catch (err) {\n if (opts.debug) console.info('[Sonor fleet] heartbeat skipped (network)', err)\n return false\n }\n}\n","/**\n * @sonordev/site-kit/fleet — FleetHeartbeat\n *\n * A childless, effect-only client component that reports this site's build\n * fingerprint (kit version, Next.js version, active modules, host) to Sonor\n * exactly once per browser session. It exists so the platform can answer\n * \"which version / modules / key state is each site running\" without shelling\n * into every client repo.\n *\n * It is mounted as a deferred, CHILDLESS SIBLING — never a wrapper — by\n * SiteKitClientProviders, behind the same idle/first-interaction gate as\n * Analytics and Engage, so it never enters the critical hydration path (see\n * CLAUDE.md \"the page must server-render\"). It renders null and carries no\n * visitor data.\n */\n\n'use client'\n\nimport { useEffect, useRef } from 'react'\nimport { useDeferredActivation } from '../shared/defer'\nimport { SITE_KIT_VERSION } from '../shared/version'\nimport { detectNextVersion, sendFleetHeartbeat } from './heartbeat'\nimport type { FleetModule } from './contract'\n\n/** Session guard key — one heartbeat per session, re-sent when version/host changes. */\nconst SESSION_GUARD_KEY = '_sk_fleet_hb'\n\nexport interface FleetHeartbeatProps {\n /** Sonor API origin. Falls back to `window.__SITE_KIT_API_URL__` then api.sonor.io. */\n apiUrl?: string\n /** Project API key. Falls back to `window.__SITE_KIT_API_KEY__`. */\n apiKey?: string\n /** Host for the multi-site sub-dimension. Falls back to `__SITE_KIT_SITE__` then location.host. */\n site?: string | null\n /** Client-observable active modules (SiteKitClientProviders passes the enabled set). */\n modules?: FleetModule[]\n /** Installed Next.js version if known; otherwise best-effort runtime detection. */\n nextVersion?: string | null\n /**\n * Self-defer to idle / first interaction. Default true so standalone use is\n * safe. SiteKitClientProviders already gates the mount and passes false.\n */\n defer?: boolean\n /** Send at most once per browser session per (version, host). Default true. */\n oncePerSession?: boolean\n debug?: boolean\n}\n\nexport function FleetHeartbeat({\n apiUrl,\n apiKey,\n site,\n modules,\n nextVersion,\n defer = true,\n oncePerSession = true,\n debug = false,\n}: FleetHeartbeatProps) {\n const ready = useDeferredActivation(defer)\n const sentRef = useRef(false)\n\n useEffect(() => {\n if (!ready || sentRef.current) return\n if (typeof window === 'undefined') return\n\n const w = window as unknown as {\n __SITE_KIT_API_URL__?: string\n __SITE_KIT_API_KEY__?: string\n __SITE_KIT_SITE__?: string\n }\n const url = apiUrl || w.__SITE_KIT_API_URL__ || 'https://api.sonor.io'\n const key = apiKey || w.__SITE_KIT_API_KEY__\n if (!key) return // no key resolvable — nothing to authenticate the heartbeat\n\n const resolvedSite = site ?? w.__SITE_KIT_SITE__ ?? window.location.host ?? ''\n\n // Once-per-session-per-(version,host): keeps heartbeat volume to one POST\n // per visitor session while still re-reporting after an upgrade or on a\n // different sub-site. Storage-disabled browsers simply send once per load.\n if (oncePerSession) {\n const stamp = `${SITE_KIT_VERSION}:${resolvedSite}`\n try {\n if (sessionStorage.getItem(SESSION_GUARD_KEY) === stamp) {\n sentRef.current = true\n return\n }\n sessionStorage.setItem(SESSION_GUARD_KEY, stamp)\n } catch {\n /* storage unavailable — fall through and send this load */\n }\n }\n\n sentRef.current = true\n void sendFleetHeartbeat({\n apiUrl: url,\n apiKey: key,\n site: resolvedSite,\n modules,\n nextVersion: nextVersion ?? detectNextVersion(),\n source: 'client',\n debug,\n })\n }, [ready, apiUrl, apiKey, site, modules, nextVersion, oncePerSession, debug])\n\n return null\n}\n"]}
@@ -60,7 +60,7 @@ function useSignal() {
60
60
  return react.useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
61
61
  }
62
62
  var LazySignalCore = dynamic__default.default(
63
- () => import('./SignalCore-LJ3NUEU4.js').then((m) => ({ default: m.SignalCore })),
63
+ () => import('./SignalCore-PD75O7GB.js').then((m) => ({ default: m.SignalCore })),
64
64
  { ssr: false }
65
65
  );
66
66
  function SignalBridge({
@@ -172,5 +172,5 @@ exports.useSignalConfig = useSignalConfig;
172
172
  exports.useSignalEvent = useSignalEvent;
173
173
  exports.useSignalExperiment = useSignalExperiment;
174
174
  exports.useSignalOutcome = useSignalOutcome;
175
- //# sourceMappingURL=chunk-W4WGNWQG.js.map
176
- //# sourceMappingURL=chunk-W4WGNWQG.js.map
175
+ //# sourceMappingURL=chunk-MRRTAJWQ.js.map
176
+ //# sourceMappingURL=chunk-MRRTAJWQ.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/signal/store.ts","../src/signal/SignalBridge.tsx"],"names":["useSyncExternalStore","dynamic","useState","useRef","useCallback","useMemo","useEffect","jsxs","Fragment","jsx"],"mappings":"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","file":"chunk-W4WGNWQG.js","sourcesContent":["'use client'\n\n/**\n * Signal store — module singleton behind useSignal().\n *\n * 4.0: SignalBridge no longer wraps page children in a React context; it\n * mounts as a childless sibling and publishes its API here. useSignal()\n * subscribes via useSyncExternalStore, so consumers work anywhere in the\n * tree — and BEFORE the bridge mounts, they get a buffering stub whose\n * trackEvent/trackOutcome queue calls that the bridge drains on publish.\n * getExperiment returns null pre-publish, identical to the pre-config\n * `loading` window consumers already handle.\n *\n * Behavior change vs 3.x (major): useSignal() no longer throws outside a\n * SignalBridge. With signal disabled it stays a bounded no-op stub — a\n * one-time debug warning fires instead of a crash.\n */\n\nimport { useSyncExternalStore } from 'react'\nimport type {\n SignalContextValue,\n SignalEvent,\n SignalOutcome,\n} from './types'\n\nconst pendingEvents: Partial<SignalEvent>[] = []\nconst pendingOutcomes: SignalOutcome[] = []\nconst MAX_PENDING = 100\nlet warnedNoBridge = false\n\n/** Frozen pre-bridge stub — stable reference (useSyncExternalStore requirement). */\nconst STUB: SignalContextValue = Object.freeze({\n config: null,\n loading: true,\n error: null,\n trackEvent: (event: Partial<SignalEvent>) => {\n if (pendingEvents.length < MAX_PENDING) pendingEvents.push(event)\n else if (!warnedNoBridge) {\n warnedNoBridge = true\n console.warn('[site-kit] Signal events are queuing but no SignalBridge has mounted — is signal enabled on SiteKitLayout?')\n }\n },\n trackOutcome: (outcome: SignalOutcome) => {\n if (pendingOutcomes.length < MAX_PENDING) pendingOutcomes.push(outcome)\n },\n getExperiment: () => null,\n refreshConfig: async () => {},\n})\n\nlet current: SignalContextValue = STUB\nconst listeners = new Set<() => void>()\n\nfunction emit() {\n for (const l of listeners) l()\n}\n\n/** Bridge-side: publish the live API (called whenever its value changes). */\nexport function publishSignal(value: SignalContextValue): void {\n const firstPublish = current === STUB\n current = value\n if (firstPublish) {\n // Drain calls made before the bridge mounted. The bridge's own trackEvent\n // buffers again internally until SignalCore loads, so nothing is lost.\n pendingEvents.splice(0).forEach(e => value.trackEvent(e))\n pendingOutcomes.splice(0).forEach(o => value.trackOutcome(o))\n }\n emit()\n}\n\n/** Bridge-side: reset on unmount so a remount starts clean (tests, HMR). */\nexport function resetSignal(): void {\n current = STUB\n emit()\n}\n\nfunction subscribe(onChange: () => void) {\n listeners.add(onChange)\n return () => listeners.delete(onChange)\n}\n\nconst getSnapshot = () => current\nconst getServerSnapshot = () => STUB\n\n/**\n * Access Signal (experiments, behavioral events) from anywhere in the tree.\n * No provider required — see module docs for pre-mount buffering semantics.\n */\nexport function useSignal(): SignalContextValue {\n return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot)\n}\n","/**\n * @sonordev/site-kit/signal - SignalBridge\n *\n * Central coordination layer for Signal AI integration.\n *\n * Architecture (2026-07 perf release): SignalBridge is a tiny SSR-SAFE context shell. It\n * renders {children} immediately — children server-render and hydrate\n * normally, no ssr:false wrapper — and lazy-loads the heavy machinery\n * (SignalCore: config fetch, SSE streaming, experiment assignment, event\n * queue, behavior tracking) as a CHILDLESS sibling. The core registers its\n * imperative API into the shell on mount; `trackEvent`/`trackOutcome` calls\n * made before the core is ready are buffered and drained on registration.\n *\n * This removes the old failure mode where enabling `signal` de-opted the\n * whole route to client rendering (the same SSR bailout the analytics\n * sibling-mount fixed) — the #1 recommendation of the 2026-07 perf audit.\n *\n * The core is NOT idle-deferred: experiment variants must apply as early as\n * possible, so it starts loading on hydration.\n */\n\n'use client'\n\nimport React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport dynamic from 'next/dynamic'\nimport type { SignalCoreApi, SignalCoreState } from './SignalCore'\nimport type {\n SignalConfig,\n SignalContextValue,\n SignalBridgeProps,\n SignalEvent,\n SignalOutcome,\n ExperimentAssignment,\n} from './types'\n\n// Heavy machinery — loaded client-side only, never wraps content.\nimport { publishSignal, resetSignal, useSignal } from './store'\n\nconst LazySignalCore = dynamic(\n () => import('./SignalCore').then(m => ({ default: m.SignalCore })),\n { ssr: false },\n)\n\n// ============================================\n// Context\n// ============================================\n\n// 4.0: the context is gone. useSignal() reads the module store, so consumers\n// work anywhere in the tree and SignalBridge mounts as a childless sibling.\nexport { useSignal } from './store'\n\n// ============================================\n// SignalBridge Component (shell)\n// ============================================\n\nexport function SignalBridge({\n enabled = true,\n realtime = true,\n experiments = true,\n behaviorTracking = true,\n visitorId: externalVisitorId,\n sessionId: externalSessionId,\n pageMetadataRef,\n children,\n}: SignalBridgeProps) {\n const [config, setConfig] = useState<SignalConfig | null>(null)\n const [loading, setLoading] = useState(true)\n const [error, setError] = useState<Error | null>(null)\n\n const coreRef = useRef<SignalCoreApi | null>(null)\n // Calls made before the core chunk loads are buffered here and drained on\n // registration — consumers can call trackEvent/trackOutcome from first render.\n const pendingEventsRef = useRef<Partial<SignalEvent>[]>([])\n const pendingOutcomesRef = useRef<SignalOutcome[]>([])\n\n const handleState = useCallback((state: SignalCoreState) => {\n setConfig(state.config)\n setLoading(state.loading)\n setError(state.error)\n }, [])\n\n const handleRegister = useCallback((api: SignalCoreApi | null) => {\n coreRef.current = api\n if (api) {\n pendingEventsRef.current.splice(0).forEach(event => api.trackEvent(event))\n pendingOutcomesRef.current.splice(0).forEach(outcome => api.trackOutcome(outcome))\n }\n }, [])\n\n const trackEvent = useCallback((event: Partial<SignalEvent>) => {\n if (coreRef.current) {\n coreRef.current.trackEvent(event)\n } else {\n pendingEventsRef.current.push(event)\n }\n }, [])\n\n const trackOutcome = useCallback((outcome: SignalOutcome) => {\n if (coreRef.current) {\n coreRef.current.trackOutcome(outcome)\n } else {\n pendingOutcomesRef.current.push(outcome)\n }\n }, [])\n\n const getExperiment = useCallback((experimentId: string): ExperimentAssignment | null => {\n // null before the core loads — identical to the pre-config-fetch behavior\n // consumers already handle via `loading`.\n return coreRef.current?.getExperiment(experimentId) ?? null\n }, [])\n\n const refreshConfig = useCallback(async () => {\n await coreRef.current?.refreshConfig()\n }, [])\n\n const contextValue = useMemo<SignalContextValue>(() => ({\n config,\n loading,\n error,\n trackEvent,\n trackOutcome,\n getExperiment,\n refreshConfig,\n }), [config, loading, error, trackEvent, trackOutcome, getExperiment, refreshConfig])\n\n // Publish to the module store (drains pre-mount buffered calls on first\n // publish); reset on unmount so tests/HMR start clean.\n useEffect(() => {\n publishSignal(contextValue)\n }, [contextValue])\n useEffect(() => () => resetSignal(), [])\n\n return (\n <>\n {children}\n {enabled && (\n <LazySignalCore\n enabled={enabled}\n realtime={realtime}\n experiments={experiments}\n behaviorTracking={behaviorTracking}\n externalVisitorId={externalVisitorId}\n externalSessionId={externalSessionId}\n pageMetadataRef={pageMetadataRef}\n onRegister={handleRegister}\n onState={handleState}\n />\n )}\n </>\n )\n}\n\n// ============================================\n// Convenience Hooks\n// ============================================\n\n/**\n * Hook to access Signal config\n */\nexport function useSignalConfig(): SignalConfig | null {\n const { config } = useSignal()\n return config\n}\n\n/**\n * Hook for tracking events\n */\nexport function useSignalEvent() {\n const { trackEvent } = useSignal()\n return trackEvent\n}\n\n/**\n * Hook for tracking outcomes/conversions\n */\nexport function useSignalOutcome() {\n const { trackOutcome } = useSignal()\n return { trackOutcome }\n}\n\n/**\n * Hook for experiment assignment\n */\nexport function useSignalExperiment(experimentId: string): {\n assignment: ExperimentAssignment | null\n variant: string | null\n isControl: boolean\n} {\n const { getExperiment, config } = useSignal()\n const assignment = getExperiment(experimentId)\n\n // Check if experiment is running\n const experiment = config?.experiments?.find(e => e.id === experimentId)\n const isRunning = experiment?.status === 'running'\n\n return {\n assignment: isRunning ? assignment : null,\n variant: isRunning && assignment ? assignment.variant_key : null,\n isControl: !assignment || assignment.variant_key === 'control',\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/signal/store.ts","../src/signal/SignalBridge.tsx"],"names":["useSyncExternalStore","dynamic","useState","useRef","useCallback","useMemo","useEffect","jsxs","Fragment","jsx"],"mappings":"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","file":"chunk-MRRTAJWQ.js","sourcesContent":["'use client'\n\n/**\n * Signal store — module singleton behind useSignal().\n *\n * 4.0: SignalBridge no longer wraps page children in a React context; it\n * mounts as a childless sibling and publishes its API here. useSignal()\n * subscribes via useSyncExternalStore, so consumers work anywhere in the\n * tree — and BEFORE the bridge mounts, they get a buffering stub whose\n * trackEvent/trackOutcome queue calls that the bridge drains on publish.\n * getExperiment returns null pre-publish, identical to the pre-config\n * `loading` window consumers already handle.\n *\n * Behavior change vs 3.x (major): useSignal() no longer throws outside a\n * SignalBridge. With signal disabled it stays a bounded no-op stub — a\n * one-time debug warning fires instead of a crash.\n */\n\nimport { useSyncExternalStore } from 'react'\nimport type {\n SignalContextValue,\n SignalEvent,\n SignalOutcome,\n} from './types'\n\nconst pendingEvents: Partial<SignalEvent>[] = []\nconst pendingOutcomes: SignalOutcome[] = []\nconst MAX_PENDING = 100\nlet warnedNoBridge = false\n\n/** Frozen pre-bridge stub — stable reference (useSyncExternalStore requirement). */\nconst STUB: SignalContextValue = Object.freeze({\n config: null,\n loading: true,\n error: null,\n trackEvent: (event: Partial<SignalEvent>) => {\n if (pendingEvents.length < MAX_PENDING) pendingEvents.push(event)\n else if (!warnedNoBridge) {\n warnedNoBridge = true\n console.warn('[site-kit] Signal events are queuing but no SignalBridge has mounted — is signal enabled on SiteKitLayout?')\n }\n },\n trackOutcome: (outcome: SignalOutcome) => {\n if (pendingOutcomes.length < MAX_PENDING) pendingOutcomes.push(outcome)\n },\n getExperiment: () => null,\n refreshConfig: async () => {},\n})\n\nlet current: SignalContextValue = STUB\nconst listeners = new Set<() => void>()\n\nfunction emit() {\n for (const l of listeners) l()\n}\n\n/** Bridge-side: publish the live API (called whenever its value changes). */\nexport function publishSignal(value: SignalContextValue): void {\n const firstPublish = current === STUB\n current = value\n if (firstPublish) {\n // Drain calls made before the bridge mounted. The bridge's own trackEvent\n // buffers again internally until SignalCore loads, so nothing is lost.\n pendingEvents.splice(0).forEach(e => value.trackEvent(e))\n pendingOutcomes.splice(0).forEach(o => value.trackOutcome(o))\n }\n emit()\n}\n\n/** Bridge-side: reset on unmount so a remount starts clean (tests, HMR). */\nexport function resetSignal(): void {\n current = STUB\n emit()\n}\n\nfunction subscribe(onChange: () => void) {\n listeners.add(onChange)\n return () => listeners.delete(onChange)\n}\n\nconst getSnapshot = () => current\nconst getServerSnapshot = () => STUB\n\n/**\n * Access Signal (experiments, behavioral events) from anywhere in the tree.\n * No provider required — see module docs for pre-mount buffering semantics.\n */\nexport function useSignal(): SignalContextValue {\n return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot)\n}\n","/**\n * @sonordev/site-kit/signal - SignalBridge\n *\n * Central coordination layer for Signal AI integration.\n *\n * Architecture (2026-07 perf release): SignalBridge is a tiny SSR-SAFE context shell. It\n * renders {children} immediately — children server-render and hydrate\n * normally, no ssr:false wrapper — and lazy-loads the heavy machinery\n * (SignalCore: config fetch, SSE streaming, experiment assignment, event\n * queue, behavior tracking) as a CHILDLESS sibling. The core registers its\n * imperative API into the shell on mount; `trackEvent`/`trackOutcome` calls\n * made before the core is ready are buffered and drained on registration.\n *\n * This removes the old failure mode where enabling `signal` de-opted the\n * whole route to client rendering (the same SSR bailout the analytics\n * sibling-mount fixed) — the #1 recommendation of the 2026-07 perf audit.\n *\n * The core is NOT idle-deferred: experiment variants must apply as early as\n * possible, so it starts loading on hydration.\n */\n\n'use client'\n\nimport React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport dynamic from 'next/dynamic'\nimport type { SignalCoreApi, SignalCoreState } from './SignalCore'\nimport type {\n SignalConfig,\n SignalContextValue,\n SignalBridgeProps,\n SignalEvent,\n SignalOutcome,\n ExperimentAssignment,\n} from './types'\n\n// Heavy machinery — loaded client-side only, never wraps content.\nimport { publishSignal, resetSignal, useSignal } from './store'\n\nconst LazySignalCore = dynamic(\n () => import('./SignalCore').then(m => ({ default: m.SignalCore })),\n { ssr: false },\n)\n\n// ============================================\n// Context\n// ============================================\n\n// 4.0: the context is gone. useSignal() reads the module store, so consumers\n// work anywhere in the tree and SignalBridge mounts as a childless sibling.\nexport { useSignal } from './store'\n\n// ============================================\n// SignalBridge Component (shell)\n// ============================================\n\nexport function SignalBridge({\n enabled = true,\n realtime = true,\n experiments = true,\n behaviorTracking = true,\n visitorId: externalVisitorId,\n sessionId: externalSessionId,\n pageMetadataRef,\n children,\n}: SignalBridgeProps) {\n const [config, setConfig] = useState<SignalConfig | null>(null)\n const [loading, setLoading] = useState(true)\n const [error, setError] = useState<Error | null>(null)\n\n const coreRef = useRef<SignalCoreApi | null>(null)\n // Calls made before the core chunk loads are buffered here and drained on\n // registration — consumers can call trackEvent/trackOutcome from first render.\n const pendingEventsRef = useRef<Partial<SignalEvent>[]>([])\n const pendingOutcomesRef = useRef<SignalOutcome[]>([])\n\n const handleState = useCallback((state: SignalCoreState) => {\n setConfig(state.config)\n setLoading(state.loading)\n setError(state.error)\n }, [])\n\n const handleRegister = useCallback((api: SignalCoreApi | null) => {\n coreRef.current = api\n if (api) {\n pendingEventsRef.current.splice(0).forEach(event => api.trackEvent(event))\n pendingOutcomesRef.current.splice(0).forEach(outcome => api.trackOutcome(outcome))\n }\n }, [])\n\n const trackEvent = useCallback((event: Partial<SignalEvent>) => {\n if (coreRef.current) {\n coreRef.current.trackEvent(event)\n } else {\n pendingEventsRef.current.push(event)\n }\n }, [])\n\n const trackOutcome = useCallback((outcome: SignalOutcome) => {\n if (coreRef.current) {\n coreRef.current.trackOutcome(outcome)\n } else {\n pendingOutcomesRef.current.push(outcome)\n }\n }, [])\n\n const getExperiment = useCallback((experimentId: string): ExperimentAssignment | null => {\n // null before the core loads — identical to the pre-config-fetch behavior\n // consumers already handle via `loading`.\n return coreRef.current?.getExperiment(experimentId) ?? null\n }, [])\n\n const refreshConfig = useCallback(async () => {\n await coreRef.current?.refreshConfig()\n }, [])\n\n const contextValue = useMemo<SignalContextValue>(() => ({\n config,\n loading,\n error,\n trackEvent,\n trackOutcome,\n getExperiment,\n refreshConfig,\n }), [config, loading, error, trackEvent, trackOutcome, getExperiment, refreshConfig])\n\n // Publish to the module store (drains pre-mount buffered calls on first\n // publish); reset on unmount so tests/HMR start clean.\n useEffect(() => {\n publishSignal(contextValue)\n }, [contextValue])\n useEffect(() => () => resetSignal(), [])\n\n return (\n <>\n {children}\n {enabled && (\n <LazySignalCore\n enabled={enabled}\n realtime={realtime}\n experiments={experiments}\n behaviorTracking={behaviorTracking}\n externalVisitorId={externalVisitorId}\n externalSessionId={externalSessionId}\n pageMetadataRef={pageMetadataRef}\n onRegister={handleRegister}\n onState={handleState}\n />\n )}\n </>\n )\n}\n\n// ============================================\n// Convenience Hooks\n// ============================================\n\n/**\n * Hook to access Signal config\n */\nexport function useSignalConfig(): SignalConfig | null {\n const { config } = useSignal()\n return config\n}\n\n/**\n * Hook for tracking events\n */\nexport function useSignalEvent() {\n const { trackEvent } = useSignal()\n return trackEvent\n}\n\n/**\n * Hook for tracking outcomes/conversions\n */\nexport function useSignalOutcome() {\n const { trackOutcome } = useSignal()\n return { trackOutcome }\n}\n\n/**\n * Hook for experiment assignment\n */\nexport function useSignalExperiment(experimentId: string): {\n assignment: ExperimentAssignment | null\n variant: string | null\n isControl: boolean\n} {\n const { getExperiment, config } = useSignal()\n const assignment = getExperiment(experimentId)\n\n // Check if experiment is running\n const experiment = config?.experiments?.find(e => e.id === experimentId)\n const isRunning = experiment?.status === 'running'\n\n return {\n assignment: isRunning ? assignment : null,\n variant: isRunning && assignment ? assignment.variant_key : null,\n isControl: !assignment || assignment.variant_key === 'control',\n }\n}\n"]}
@@ -0,0 +1,283 @@
1
+ 'use client';
2
+ import { useState, useRef, useCallback, useEffect } from 'react';
3
+ import { jsxs, jsx } from 'react/jsx-runtime';
4
+
5
+ // src/forms/DatePicker.tsx
6
+ var DAYS = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
7
+ var MONTHS = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
8
+ function pad(n) {
9
+ return n < 10 ? `0${n}` : `${n}`;
10
+ }
11
+ function toDateStr(y, m, d) {
12
+ return `${y}-${pad(m + 1)}-${pad(d)}`;
13
+ }
14
+ function parseDate(str) {
15
+ if (!str) return null;
16
+ const [y, m, d] = str.split("-").map(Number);
17
+ if (!y || !m || !d) return null;
18
+ return { year: y, month: m - 1, day: d };
19
+ }
20
+ function getDaysInMonth(year, month) {
21
+ return new Date(year, month + 1, 0).getDate();
22
+ }
23
+ function getFirstDayOfMonth(year, month) {
24
+ return new Date(year, month, 1).getDay();
25
+ }
26
+ function formatDisplay(dateStr) {
27
+ const parsed = parseDate(dateStr);
28
+ if (!parsed) return "";
29
+ return `${MONTHS[parsed.month]} ${parsed.day}, ${parsed.year}`;
30
+ }
31
+ function DatePicker({
32
+ value,
33
+ onChange,
34
+ id,
35
+ name,
36
+ required,
37
+ minDate,
38
+ placeholder = "Select a date",
39
+ className = "",
40
+ style
41
+ }) {
42
+ const today = /* @__PURE__ */ new Date();
43
+ const todayStr = toDateStr(today.getFullYear(), today.getMonth(), today.getDate());
44
+ const effectiveMin = minDate || "";
45
+ const parsed = parseDate(value);
46
+ const [viewYear, setViewYear] = useState(parsed?.year || today.getFullYear());
47
+ const [viewMonth, setViewMonth] = useState(parsed?.month ?? today.getMonth());
48
+ const [open, setOpen] = useState(false);
49
+ const [closing, setClosing] = useState(false);
50
+ const closeTimer = useRef(null);
51
+ const containerRef = useRef(null);
52
+ const closeAnimated = useCallback(() => {
53
+ if (closeTimer.current !== null) return;
54
+ setClosing(true);
55
+ closeTimer.current = window.setTimeout(() => {
56
+ closeTimer.current = null;
57
+ setClosing(false);
58
+ setOpen(false);
59
+ }, 180);
60
+ }, []);
61
+ useEffect(
62
+ () => () => {
63
+ if (closeTimer.current !== null) window.clearTimeout(closeTimer.current);
64
+ },
65
+ []
66
+ );
67
+ useEffect(() => {
68
+ if (!open) return;
69
+ const handler = (e) => {
70
+ if (containerRef.current && !containerRef.current.contains(e.target)) {
71
+ closeAnimated();
72
+ }
73
+ };
74
+ document.addEventListener("mousedown", handler);
75
+ return () => document.removeEventListener("mousedown", handler);
76
+ }, [open, closeAnimated]);
77
+ useEffect(() => {
78
+ if (!open) return;
79
+ const handler = (e) => {
80
+ if (e.key === "Escape") closeAnimated();
81
+ };
82
+ window.addEventListener("keydown", handler);
83
+ return () => window.removeEventListener("keydown", handler);
84
+ }, [open, closeAnimated]);
85
+ const prevMonth = useCallback(() => {
86
+ if (viewMonth === 0) {
87
+ setViewMonth(11);
88
+ setViewYear((y) => y - 1);
89
+ } else setViewMonth((m) => m - 1);
90
+ }, [viewMonth]);
91
+ const nextMonth = useCallback(() => {
92
+ if (viewMonth === 11) {
93
+ setViewMonth(0);
94
+ setViewYear((y) => y + 1);
95
+ } else setViewMonth((m) => m + 1);
96
+ }, [viewMonth]);
97
+ const selectDay = useCallback((day) => {
98
+ const dateStr = toDateStr(viewYear, viewMonth, day);
99
+ onChange(dateStr);
100
+ closeAnimated();
101
+ }, [viewYear, viewMonth, onChange, closeAnimated]);
102
+ const isDisabled = useCallback((day) => {
103
+ const dateStr = toDateStr(viewYear, viewMonth, day);
104
+ return dateStr < effectiveMin;
105
+ }, [viewYear, viewMonth, effectiveMin]);
106
+ const isSelected = useCallback((day) => {
107
+ if (!value) return false;
108
+ return toDateStr(viewYear, viewMonth, day) === value;
109
+ }, [viewYear, viewMonth, value]);
110
+ const isToday = useCallback((day) => {
111
+ return toDateStr(viewYear, viewMonth, day) === todayStr;
112
+ }, [viewYear, viewMonth, todayStr]);
113
+ const daysInMonth = getDaysInMonth(viewYear, viewMonth);
114
+ const firstDay = getFirstDayOfMonth(viewYear, viewMonth);
115
+ const weeks = [];
116
+ let week = Array(firstDay).fill(null);
117
+ for (let d = 1; d <= daysInMonth; d++) {
118
+ week.push(d);
119
+ if (week.length === 7) {
120
+ weeks.push(week);
121
+ week = [];
122
+ }
123
+ }
124
+ if (week.length > 0) {
125
+ while (week.length < 7) week.push(null);
126
+ weeks.push(week);
127
+ }
128
+ return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: `sk-datepicker ${className}`, style: { position: "relative", ...style }, children: [
129
+ /* @__PURE__ */ jsx("input", { type: "hidden", name, value: value || "" }),
130
+ /* @__PURE__ */ jsxs(
131
+ "button",
132
+ {
133
+ type: "button",
134
+ id,
135
+ onClick: () => open ? closeAnimated() : setOpen(true),
136
+ "aria-required": required,
137
+ "aria-expanded": open,
138
+ style: {
139
+ display: "flex",
140
+ alignItems: "center",
141
+ justifyContent: "space-between",
142
+ width: "100%",
143
+ padding: "var(--sk-input-padding, 10px 12px)",
144
+ backgroundColor: "var(--sk-input-bg, #fff)",
145
+ border: `1px solid ${open ? "var(--sk-input-border-focus, #2563eb)" : "var(--sk-input-border, #d1d5db)"}`,
146
+ borderRadius: "var(--sk-input-radius, 0.375rem)",
147
+ fontSize: "var(--sk-font-size, 14px)",
148
+ color: value ? "var(--sk-form-success-text, #111)" : "var(--sk-text-tertiary, #9ca3af)",
149
+ cursor: "pointer",
150
+ outline: "none",
151
+ textAlign: "left"
152
+ },
153
+ children: [
154
+ /* @__PURE__ */ jsx("span", { className: value ? "sk-datepicker__value--stamp" : void 0, children: value ? formatDisplay(value) : placeholder }, value || "empty"),
155
+ /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", style: { opacity: 0.5, flexShrink: 0 }, children: [
156
+ /* @__PURE__ */ jsx("rect", { x: "3", y: "4", width: "18", height: "18", rx: "2", ry: "2" }),
157
+ /* @__PURE__ */ jsx("line", { x1: "16", y1: "2", x2: "16", y2: "6" }),
158
+ /* @__PURE__ */ jsx("line", { x1: "8", y1: "2", x2: "8", y2: "6" }),
159
+ /* @__PURE__ */ jsx("line", { x1: "3", y1: "10", x2: "21", y2: "10" })
160
+ ] })
161
+ ]
162
+ }
163
+ ),
164
+ open && /* @__PURE__ */ jsxs(
165
+ "div",
166
+ {
167
+ className: "sk-datepicker__pop",
168
+ "data-sk-closing": closing ? "true" : void 0,
169
+ style: {
170
+ position: "absolute",
171
+ top: "100%",
172
+ left: 0,
173
+ right: 0,
174
+ marginTop: "4px",
175
+ backgroundColor: "var(--sk-input-bg, #fff)",
176
+ border: "1px solid var(--sk-input-border, #d1d5db)",
177
+ borderRadius: "var(--sk-input-radius, 0.375rem)",
178
+ boxShadow: "0 10px 25px rgba(0,0,0,0.3)",
179
+ zIndex: 50,
180
+ padding: "12px",
181
+ minWidth: "280px"
182
+ },
183
+ children: [
184
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: "12px" }, children: [
185
+ /* @__PURE__ */ jsx(
186
+ "button",
187
+ {
188
+ type: "button",
189
+ onClick: prevMonth,
190
+ style: { background: "none", border: "none", cursor: "pointer", padding: "4px 8px", color: "var(--sk-label-color, #6b7280)", fontSize: "18px", lineHeight: 1 },
191
+ "aria-label": "Previous month",
192
+ children: "\u2039"
193
+ }
194
+ ),
195
+ /* @__PURE__ */ jsxs("span", { style: { fontWeight: 600, fontSize: "14px", color: "var(--sk-form-success-text, #111)" }, children: [
196
+ MONTHS[viewMonth],
197
+ " ",
198
+ viewYear
199
+ ] }),
200
+ /* @__PURE__ */ jsx(
201
+ "button",
202
+ {
203
+ type: "button",
204
+ onClick: nextMonth,
205
+ style: { background: "none", border: "none", cursor: "pointer", padding: "4px 8px", color: "var(--sk-label-color, #6b7280)", fontSize: "18px", lineHeight: 1 },
206
+ "aria-label": "Next month",
207
+ children: "\u203A"
208
+ }
209
+ )
210
+ ] }),
211
+ /* @__PURE__ */ jsx("div", { style: { display: "grid", gridTemplateColumns: "repeat(7, 1fr)", gap: "2px", marginBottom: "4px" }, children: DAYS.map((d) => /* @__PURE__ */ jsx("div", { style: { textAlign: "center", fontSize: "11px", fontWeight: 600, color: "var(--sk-text-tertiary, #9ca3af)", padding: "4px 0", textTransform: "uppercase", letterSpacing: "0.05em" }, children: d }, d)) }),
212
+ weeks.map((week2, wi) => /* @__PURE__ */ jsx("div", { style: { display: "grid", gridTemplateColumns: "repeat(7, 1fr)", gap: "2px" }, children: week2.map((day, di) => {
213
+ if (day === null) {
214
+ return /* @__PURE__ */ jsx("div", { style: { padding: "6px" } }, di);
215
+ }
216
+ const disabled = isDisabled(day);
217
+ const selected = isSelected(day);
218
+ const todayMark = isToday(day);
219
+ return /* @__PURE__ */ jsx(
220
+ "button",
221
+ {
222
+ type: "button",
223
+ disabled,
224
+ onClick: () => selectDay(day),
225
+ style: {
226
+ padding: "6px",
227
+ textAlign: "center",
228
+ fontSize: "13px",
229
+ fontWeight: selected ? 700 : todayMark ? 600 : 400,
230
+ borderRadius: "6px",
231
+ border: todayMark && !selected ? "1px solid var(--sk-primary, #2563eb)" : "1px solid transparent",
232
+ background: selected ? "var(--sk-primary, #2563eb)" : "transparent",
233
+ color: disabled ? "var(--sk-text-tertiary, #9ca3af)" : selected ? "#fff" : "var(--sk-form-success-text, #111)",
234
+ cursor: disabled ? "not-allowed" : "pointer",
235
+ opacity: disabled ? 0.4 : 1,
236
+ transition: "background 0.15s, color 0.15s"
237
+ },
238
+ onMouseEnter: (e) => {
239
+ if (!disabled && !selected) {
240
+ e.target.style.background = "var(--sk-input-border, #e5e7eb)";
241
+ }
242
+ },
243
+ onMouseLeave: (e) => {
244
+ if (!disabled && !selected) {
245
+ e.target.style.background = "transparent";
246
+ }
247
+ },
248
+ "aria-label": `${MONTHS[viewMonth]} ${day}, ${viewYear}`,
249
+ "aria-selected": selected,
250
+ children: day
251
+ },
252
+ di
253
+ );
254
+ }) }, wi)),
255
+ /* @__PURE__ */ jsx("div", { style: { marginTop: "8px", textAlign: "center" }, children: /* @__PURE__ */ jsx(
256
+ "button",
257
+ {
258
+ type: "button",
259
+ onClick: () => {
260
+ onChange(todayStr);
261
+ setOpen(false);
262
+ },
263
+ style: {
264
+ background: "none",
265
+ border: "none",
266
+ cursor: "pointer",
267
+ fontSize: "12px",
268
+ fontWeight: 600,
269
+ color: "var(--sk-primary, #2563eb)",
270
+ padding: "4px 8px"
271
+ },
272
+ children: "Today"
273
+ }
274
+ ) })
275
+ ]
276
+ }
277
+ )
278
+ ] });
279
+ }
280
+
281
+ export { DatePicker };
282
+ //# sourceMappingURL=chunk-MU3D5IO7.mjs.map
283
+ //# sourceMappingURL=chunk-MU3D5IO7.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/forms/DatePicker.tsx"],"names":["week"],"mappings":"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","file":"chunk-MU3D5IO7.mjs","sourcesContent":["'use client'\n\nimport React, { useState, useRef, useEffect, useCallback } from 'react'\n\ninterface DatePickerProps {\n value: string // YYYY-MM-DD or empty\n onChange: (value: string) => void\n id?: string\n name?: string\n required?: boolean\n minDate?: string // YYYY-MM-DD\n placeholder?: string\n className?: string\n style?: React.CSSProperties\n}\n\nconst DAYS = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa']\nconst MONTHS = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']\n\nfunction pad(n: number): string {\n return n < 10 ? `0${n}` : `${n}`\n}\n\nfunction toDateStr(y: number, m: number, d: number): string {\n return `${y}-${pad(m + 1)}-${pad(d)}`\n}\n\nfunction parseDate(str: string): { year: number; month: number; day: number } | null {\n if (!str) return null\n const [y, m, d] = str.split('-').map(Number)\n if (!y || !m || !d) return null\n return { year: y, month: m - 1, day: d }\n}\n\nfunction getDaysInMonth(year: number, month: number): number {\n return new Date(year, month + 1, 0).getDate()\n}\n\nfunction getFirstDayOfMonth(year: number, month: number): number {\n return new Date(year, month, 1).getDay()\n}\n\nfunction formatDisplay(dateStr: string): string {\n const parsed = parseDate(dateStr)\n if (!parsed) return ''\n return `${MONTHS[parsed.month]} ${parsed.day}, ${parsed.year}`\n}\n\nexport function DatePicker({\n value,\n onChange,\n id,\n name,\n required,\n minDate,\n placeholder = 'Select a date',\n className = '',\n style,\n}: DatePickerProps) {\n const today = new Date()\n const todayStr = toDateStr(today.getFullYear(), today.getMonth(), today.getDate())\n // Only restrict dates when a minDate is explicitly configured. Defaulting to\n // today silently disabled every valid day for past-date fields (birthdates).\n const effectiveMin = minDate || ''\n\n const parsed = parseDate(value)\n const [viewYear, setViewYear] = useState(parsed?.year || today.getFullYear())\n const [viewMonth, setViewMonth] = useState(parsed?.month ?? today.getMonth())\n const [open, setOpen] = useState(false)\n // Container-morph exit: the calendar plays its collapse animation before\n // unmounting, so picking a date morphs the panel back into the tile.\n const [closing, setClosing] = useState(false)\n const closeTimer = useRef<number | null>(null)\n const containerRef = useRef<HTMLDivElement>(null)\n\n const closeAnimated = useCallback(() => {\n if (closeTimer.current !== null) return\n setClosing(true)\n closeTimer.current = window.setTimeout(() => {\n closeTimer.current = null\n setClosing(false)\n setOpen(false)\n }, 180)\n }, [])\n useEffect(\n () => () => {\n if (closeTimer.current !== null) window.clearTimeout(closeTimer.current)\n },\n [],\n )\n\n // Close on click outside\n useEffect(() => {\n if (!open) return\n const handler = (e: MouseEvent) => {\n if (containerRef.current && !containerRef.current.contains(e.target as Node)) {\n closeAnimated()\n }\n }\n document.addEventListener('mousedown', handler)\n return () => document.removeEventListener('mousedown', handler)\n }, [open, closeAnimated])\n\n // Close on escape\n useEffect(() => {\n if (!open) return\n const handler = (e: KeyboardEvent) => {\n if (e.key === 'Escape') closeAnimated()\n }\n window.addEventListener('keydown', handler)\n return () => window.removeEventListener('keydown', handler)\n }, [open, closeAnimated])\n\n const prevMonth = useCallback(() => {\n if (viewMonth === 0) { setViewMonth(11); setViewYear(y => y - 1) }\n else setViewMonth(m => m - 1)\n }, [viewMonth])\n\n const nextMonth = useCallback(() => {\n if (viewMonth === 11) { setViewMonth(0); setViewYear(y => y + 1) }\n else setViewMonth(m => m + 1)\n }, [viewMonth])\n\n const selectDay = useCallback((day: number) => {\n const dateStr = toDateStr(viewYear, viewMonth, day)\n onChange(dateStr)\n closeAnimated()\n }, [viewYear, viewMonth, onChange, closeAnimated])\n\n const isDisabled = useCallback((day: number): boolean => {\n const dateStr = toDateStr(viewYear, viewMonth, day)\n return dateStr < effectiveMin\n }, [viewYear, viewMonth, effectiveMin])\n\n const isSelected = useCallback((day: number): boolean => {\n if (!value) return false\n return toDateStr(viewYear, viewMonth, day) === value\n }, [viewYear, viewMonth, value])\n\n const isToday = useCallback((day: number): boolean => {\n return toDateStr(viewYear, viewMonth, day) === todayStr\n }, [viewYear, viewMonth, todayStr])\n\n // Build calendar grid\n const daysInMonth = getDaysInMonth(viewYear, viewMonth)\n const firstDay = getFirstDayOfMonth(viewYear, viewMonth)\n const weeks: (number | null)[][] = []\n let week: (number | null)[] = Array(firstDay).fill(null)\n\n for (let d = 1; d <= daysInMonth; d++) {\n week.push(d)\n if (week.length === 7) { weeks.push(week); week = [] }\n }\n if (week.length > 0) {\n while (week.length < 7) week.push(null)\n weeks.push(week)\n }\n\n return (\n <div ref={containerRef} className={`sk-datepicker ${className}`} style={{ position: 'relative', ...style }}>\n {/* Hidden native input for form submission */}\n <input type=\"hidden\" name={name} value={value || ''} />\n\n {/* Trigger button styled as input */}\n <button\n type=\"button\"\n id={id}\n onClick={() => (open ? closeAnimated() : setOpen(true))}\n aria-required={required}\n aria-expanded={open}\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n width: '100%',\n padding: 'var(--sk-input-padding, 10px 12px)',\n backgroundColor: 'var(--sk-input-bg, #fff)',\n border: `1px solid ${open ? 'var(--sk-input-border-focus, #2563eb)' : 'var(--sk-input-border, #d1d5db)'}`,\n borderRadius: 'var(--sk-input-radius, 0.375rem)',\n fontSize: 'var(--sk-font-size, 14px)',\n color: value ? 'var(--sk-form-success-text, #111)' : 'var(--sk-text-tertiary, #9ca3af)',\n cursor: 'pointer',\n outline: 'none',\n textAlign: 'left',\n }}\n >\n {/* Keyed by value: the picked date stamps in with a pop as the\n calendar morphs back into the tile. */}\n <span key={value || 'empty'} className={value ? 'sk-datepicker__value--stamp' : undefined}>\n {value ? formatDisplay(value) : placeholder}\n </span>\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" style={{ opacity: 0.5, flexShrink: 0 }}>\n <rect x=\"3\" y=\"4\" width=\"18\" height=\"18\" rx=\"2\" ry=\"2\" />\n <line x1=\"16\" y1=\"2\" x2=\"16\" y2=\"6\" />\n <line x1=\"8\" y1=\"2\" x2=\"8\" y2=\"6\" />\n <line x1=\"3\" y1=\"10\" x2=\"21\" y2=\"10\" />\n </svg>\n </button>\n\n {/* Calendar dropdown — morphs out of the tile (sk-cal-out) and back in\n on selection (sk-cal-in via data-sk-closing before unmount). */}\n {open && (\n <div\n className=\"sk-datepicker__pop\"\n data-sk-closing={closing ? 'true' : undefined}\n style={{\n position: 'absolute',\n top: '100%',\n left: 0,\n right: 0,\n marginTop: '4px',\n backgroundColor: 'var(--sk-input-bg, #fff)',\n border: '1px solid var(--sk-input-border, #d1d5db)',\n borderRadius: 'var(--sk-input-radius, 0.375rem)',\n boxShadow: '0 10px 25px rgba(0,0,0,0.3)',\n zIndex: 50,\n padding: '12px',\n minWidth: '280px',\n }}\n >\n {/* Month/Year header */}\n <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '12px' }}>\n <button\n type=\"button\"\n onClick={prevMonth}\n style={{ background: 'none', border: 'none', cursor: 'pointer', padding: '4px 8px', color: 'var(--sk-label-color, #6b7280)', fontSize: '18px', lineHeight: 1 }}\n aria-label=\"Previous month\"\n >\n &lsaquo;\n </button>\n <span style={{ fontWeight: 600, fontSize: '14px', color: 'var(--sk-form-success-text, #111)' }}>\n {MONTHS[viewMonth]} {viewYear}\n </span>\n <button\n type=\"button\"\n onClick={nextMonth}\n style={{ background: 'none', border: 'none', cursor: 'pointer', padding: '4px 8px', color: 'var(--sk-label-color, #6b7280)', fontSize: '18px', lineHeight: 1 }}\n aria-label=\"Next month\"\n >\n &rsaquo;\n </button>\n </div>\n\n {/* Day headers */}\n <div style={{ display: 'grid', gridTemplateColumns: 'repeat(7, 1fr)', gap: '2px', marginBottom: '4px' }}>\n {DAYS.map(d => (\n <div key={d} style={{ textAlign: 'center', fontSize: '11px', fontWeight: 600, color: 'var(--sk-text-tertiary, #9ca3af)', padding: '4px 0', textTransform: 'uppercase', letterSpacing: '0.05em' }}>\n {d}\n </div>\n ))}\n </div>\n\n {/* Day grid */}\n {weeks.map((week, wi) => (\n <div key={wi} style={{ display: 'grid', gridTemplateColumns: 'repeat(7, 1fr)', gap: '2px' }}>\n {week.map((day, di) => {\n if (day === null) {\n return <div key={di} style={{ padding: '6px' }} />\n }\n\n const disabled = isDisabled(day)\n const selected = isSelected(day)\n const todayMark = isToday(day)\n\n return (\n <button\n key={di}\n type=\"button\"\n disabled={disabled}\n onClick={() => selectDay(day)}\n style={{\n padding: '6px',\n textAlign: 'center',\n fontSize: '13px',\n fontWeight: selected ? 700 : todayMark ? 600 : 400,\n borderRadius: '6px',\n border: todayMark && !selected ? '1px solid var(--sk-primary, #2563eb)' : '1px solid transparent',\n background: selected ? 'var(--sk-primary, #2563eb)' : 'transparent',\n color: disabled\n ? 'var(--sk-text-tertiary, #9ca3af)'\n : selected\n ? '#fff'\n : 'var(--sk-form-success-text, #111)',\n cursor: disabled ? 'not-allowed' : 'pointer',\n opacity: disabled ? 0.4 : 1,\n transition: 'background 0.15s, color 0.15s',\n }}\n onMouseEnter={(e) => {\n if (!disabled && !selected) {\n ;(e.target as HTMLElement).style.background = 'var(--sk-input-border, #e5e7eb)'\n }\n }}\n onMouseLeave={(e) => {\n if (!disabled && !selected) {\n ;(e.target as HTMLElement).style.background = 'transparent'\n }\n }}\n aria-label={`${MONTHS[viewMonth]} ${day}, ${viewYear}`}\n aria-selected={selected}\n >\n {day}\n </button>\n )\n })}\n </div>\n ))}\n\n {/* Today shortcut */}\n <div style={{ marginTop: '8px', textAlign: 'center' }}>\n <button\n type=\"button\"\n onClick={() => { onChange(todayStr); setOpen(false) }}\n style={{\n background: 'none',\n border: 'none',\n cursor: 'pointer',\n fontSize: '12px',\n fontWeight: 600,\n color: 'var(--sk-primary, #2563eb)',\n padding: '4px 8px',\n }}\n >\n Today\n </button>\n </div>\n </div>\n )}\n </div>\n )\n}\n"]}
@@ -62,6 +62,15 @@ var CSS = `
62
62
  transition: grid-template-rows .42s cubic-bezier(.22,1,.36,1);
63
63
  }
64
64
  .sk-stage__collapse > div { overflow: hidden; min-height: 0; }
65
+ /* The clip only needs to work VERTICALLY (that's the collapse axis). Full
66
+ overflow:hidden also chops the focus aura / lock-in glow on the field
67
+ inside \u2014 a hard vertical edge on both sides of the input. overflow:clip
68
+ allows per-axis mixing without creating a scroll container, so modern
69
+ browsers keep the collapse clip and let the glow breathe sideways; older
70
+ ones keep the (harmless) clipped-glow behavior. */
71
+ @supports (overflow: clip) {
72
+ .sk-stage__collapse > div { overflow-x: visible; overflow-y: clip; }
73
+ }
65
74
  .sk-stage__beat[data-sk-beat-state="active"] .sk-stage__collapse--body { grid-template-rows: 1fr; }
66
75
  .sk-stage__beat:not([data-sk-beat-state="active"]) .sk-stage__collapse--summary { grid-template-rows: 1fr; }
67
76
 
@@ -192,5 +201,5 @@ function SummaryTick() {
192
201
 
193
202
  exports.StageStyles = StageStyles;
194
203
  exports.SummaryTick = SummaryTick;
195
- //# sourceMappingURL=chunk-L5NLP3EW.js.map
196
- //# sourceMappingURL=chunk-L5NLP3EW.js.map
204
+ //# sourceMappingURL=chunk-MVHR77V4.js.map
205
+ //# sourceMappingURL=chunk-MVHR77V4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/forms/experience-chrome.tsx"],"names":["jsx"],"mappings":";;;;;AAgBA,IAAM,QAAA,GAAW,cAAA;AAEjB,IAAM,GAAA,GAAM;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA;AAoLL,SAAS,WAAA,GAAc;AAC5B,EAAA,uBAAOA,cAAA,CAAC,WAAM,EAAA,EAAI,QAAA,EAAU,yBAAyB,EAAE,MAAA,EAAQ,KAAI,EAAG,CAAA;AACxE;AAEO,SAAS,WAAA,GAAc;AAC5B,EAAA,sCACG,MAAA,EAAA,EAAK,SAAA,EAAU,wBAAA,EAAyB,cAAA,EAAa,sBAAqB,aAAA,EAAY,MAAA,EACrF,QAAA,kBAAAA,cAAA,CAAC,KAAA,EAAA,EAAI,OAAM,GAAA,EAAI,MAAA,EAAO,KAAI,OAAA,EAAQ,WAAA,EAAY,MAAK,MAAA,EACjD,QAAA,kBAAAA,cAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,CAAA,EAAE,oBAAA;AAAA,MACF,MAAA,EAAO,oCAAA;AAAA,MACP,WAAA,EAAY,KAAA;AAAA,MACZ,aAAA,EAAc,OAAA;AAAA,MACd,cAAA,EAAe;AAAA;AAAA,KAEnB,CAAA,EACF,CAAA;AAEJ","file":"chunk-MVHR77V4.js","sourcesContent":["'use client'\n\n/**\n * Chrome shared by the alternate form experiences (stage, spotlight): the\n * head row, the segmented progress meter, the collapse mechanics, and the\n * settled-answer check. Lives apart from either component so loading ONE\n * experience never drags the other one's markup and CSS along with it.\n *\n * Construction rules match delight.tsx: zero deps, one deduped style tag,\n * data-sk-part on everything, --sk-* theming, and every accent mixes\n * --sk-primary toward currentColor (the contrast law). Reduced motion\n * collapses animation to instant states.\n */\n\nimport React from 'react'\n\nconst STYLE_ID = 'sk-stage-css'\n\nconst CSS = `\n@keyframes sk-stage-shake { 0%, 100% { transform: translateX(0) } 18% { transform: translateX(-9px) } 40% { transform: translateX(7px) } 62% { transform: translateX(-5px) } 82% { transform: translateX(3px) } }\n@keyframes sk-stage-seg-pop { 0% { transform: scaleY(2.4) } 100% { transform: scaleY(1) } }\n@keyframes sk-stage-seg-breathe { 0%, 100% { opacity: .3 } 50% { opacity: .7 } }\n@keyframes sk-stage-ready-pop { 0% { transform: scale(.8) } 60% { transform: scale(1.15) } 100% { transform: scale(1) } }\n@keyframes sk-stage-tick-in { 0% { transform: scale(.3); opacity: 0 } 70% { transform: scale(1.2) } 100% { transform: scale(1); opacity: 1 } }\n\n.sk-stage { --sk-font-size: 1.125rem; }\n\n/* ---- Head: form name + honest time cost ---- */\n.sk-stage__head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; margin-bottom: 10px; }\n.sk-stage__head-name, .sk-stage__head-meta {\n font-size: 12px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;\n opacity: .75; margin: 0;\n}\n.sk-stage__head-meta { font-weight: 600; opacity: .6; white-space: nowrap; }\n\n/* ---- HUD: segmented progress + counter. Neutral game-style pips — this is\n the site's chrome, not platform branding. ---- */\n.sk-stage__hud { display: flex; align-items: center; gap: 12px; margin-bottom: 8px; }\n.sk-stage__meter { flex: 1; display: flex; align-items: center; gap: 4px; }\n.sk-stage__seg {\n flex: 1; height: 5px; border-radius: 999px;\n background: color-mix(in srgb, currentColor 16%, transparent);\n transition: background-color .3s ease;\n transform-origin: center;\n}\n.sk-stage__seg[data-sk-lit=\"true\"] {\n background: color-mix(in srgb, var(--sk-primary, #2563eb) 72%, currentColor);\n animation: sk-stage-seg-pop .3s cubic-bezier(.22,1.4,.36,1) both;\n}\n.sk-stage__seg[data-sk-live=\"true\"] {\n background: color-mix(in srgb, var(--sk-primary, #2563eb) 55%, currentColor);\n animation: sk-stage-seg-breathe 1.7s ease-in-out infinite;\n}\n.sk-stage__counter {\n font-size: 11px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;\n opacity: .75; font-variant-numeric: tabular-nums; white-space: nowrap;\n}\n.sk-stage__counter[data-sk-ready=\"true\"] {\n opacity: 1;\n color: color-mix(in srgb, var(--sk-primary, #2563eb) 60%, currentColor);\n animation: sk-stage-ready-pop .4s cubic-bezier(.22,1.4,.36,1);\n}\n\n/* ---- The accordion of beats ----\n Each beat holds two collapsible layers (summary row / expanded body) built\n on grid-template-rows 0fr<->1fr. Handing off between beats runs FOUR of\n these morphs at once (old body closes + old summary opens + new summary\n closes + new body opens) — the \"previous field settles, next field blooms\"\n transition, in pure CSS. */\n.sk-stage__beats { margin: 6px 0 0; }\n.sk-stage__beat { border-bottom: 1px solid color-mix(in srgb, currentColor 14%, transparent); }\n.sk-stage__collapse {\n display: grid; grid-template-rows: 0fr;\n transition: grid-template-rows .42s cubic-bezier(.22,1,.36,1);\n}\n.sk-stage__collapse > div { overflow: hidden; min-height: 0; }\n/* The clip only needs to work VERTICALLY (that's the collapse axis). Full\n overflow:hidden also chops the focus aura / lock-in glow on the field\n inside — a hard vertical edge on both sides of the input. overflow:clip\n allows per-axis mixing without creating a scroll container, so modern\n browsers keep the collapse clip and let the glow breathe sideways; older\n ones keep the (harmless) clipped-glow behavior. */\n@supports (overflow: clip) {\n .sk-stage__collapse > div { overflow-x: visible; overflow-y: clip; }\n}\n.sk-stage__beat[data-sk-beat-state=\"active\"] .sk-stage__collapse--body { grid-template-rows: 1fr; }\n.sk-stage__beat:not([data-sk-beat-state=\"active\"]) .sk-stage__collapse--summary { grid-template-rows: 1fr; }\n\n/* Inner content blooms slightly after the container starts opening. */\n.sk-stage__body-inner {\n opacity: 0; transform: translateY(10px);\n transition: opacity .26s ease, transform .3s cubic-bezier(.22,1,.36,1);\n padding: 4px 2px 22px;\n}\n.sk-stage__beat[data-sk-beat-state=\"active\"] .sk-stage__body-inner {\n opacity: 1; transform: translateY(0);\n transition-delay: .1s, .1s;\n}\n.sk-stage__summary-inner {\n opacity: 0; transition: opacity .26s ease .08s;\n}\n.sk-stage__beat:not([data-sk-beat-state=\"active\"]) .sk-stage__summary-inner { opacity: 1; }\n\n/* Summary rows: the settled (filled) and waiting (upcoming) states. */\n.sk-stage__summary {\n display: flex; align-items: baseline; gap: 12px; width: 100%;\n padding: 13px 2px; text-align: left; cursor: pointer;\n background: none; border: none; color: inherit; font: inherit;\n transition: background-color .15s ease;\n}\n.sk-stage__summary:hover,\n.sk-stage__summary:focus-visible { background: color-mix(in srgb, currentColor 7%, transparent); }\n.sk-stage__summary-idx {\n font-size: 11px; font-weight: 700; letter-spacing: .06em;\n font-variant-numeric: tabular-nums; opacity: .5; flex-shrink: 0;\n}\n.sk-stage__summary-label {\n font-size: 12px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase;\n opacity: .72; flex-shrink: 0;\n}\n.sk-stage__summary-value {\n margin-left: auto; font-weight: 600; text-align: right;\n display: flex; align-items: baseline; gap: 8px; min-width: 0;\n}\n/* Narrow screens: the answer takes its own line under the label — a squeezed\n flex column wraps long values one character per line (found live). */\n@media (max-width: 480px) {\n .sk-stage__summary { flex-wrap: wrap; row-gap: 2px; }\n .sk-stage__summary-value {\n margin-left: 0; flex-basis: 100%; justify-content: flex-start;\n text-align: left; padding-left: 26px;\n }\n}\n.sk-stage__summary-value--skipped { font-weight: 400; font-style: italic; opacity: .6; }\n.sk-stage__beat[data-sk-beat-filled=\"true\"] .sk-stage__summary-label { opacity: .55; }\n.sk-stage__summary-tick {\n width: 15px; height: 15px; border-radius: 50%; flex-shrink: 0; align-self: center;\n background: color-mix(in srgb, var(--sk-primary, #2563eb) 72%, currentColor);\n display: inline-flex; align-items: center; justify-content: center;\n animation: sk-stage-tick-in .28s cubic-bezier(.22,1.4,.36,1) both;\n}\n\n/* ---- Active beat body ---- */\n.sk-stage__fields { display: flex; flex-wrap: wrap; gap: 16px; }\n.sk-stage__fields > * { flex: 1 1 100%; }\n/* Solo beat: the question is the headline. */\n.sk-stage .sk-stage__label {\n font-size: clamp(1.3rem, 1rem + 1.4vw, 1.9rem);\n line-height: 1.14;\n margin-bottom: 14px !important; /* FormField inlines margin-bottom: 6 */\n}\n.sk-stage { --sk-label-weight: 700; }\n/* Grouped beat: fields share the row (like the classic grid), labels calm\n back down so the pair reads as one quick moment. */\n@media (min-width: 640px) {\n .sk-stage__question--group .sk-stage__fields > * { flex: 1 1 0; min-width: 180px; }\n}\n.sk-stage .sk-stage__question--group .sk-stage__label {\n font-size: 1.05rem;\n line-height: 1.3;\n margin-bottom: 8px !important;\n}\n.sk-stage__eyebrow {\n font-size: 12px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase;\n opacity: .7; margin: 0 0 10px;\n}\n.sk-stage__eyebrow--greet { opacity: .9; }\n.sk-stage__beat-title {\n margin: 0 0 14px; font-size: clamp(1.2rem, 1rem + 1vw, 1.6rem);\n font-weight: 800; line-height: 1.1;\n}\n.sk-stage__actions { display: flex; align-items: center; gap: 10px; margin-top: 18px; }\n.sk-stage__hint { font-size: 11px; opacity: .6; }\n.sk-stage__hint kbd {\n font: inherit; padding: 1px 5px; border-radius: 4px;\n border: 1px solid color-mix(in srgb, currentColor 35%, transparent);\n}\n@media (pointer: coarse) { .sk-stage__hint { display: none; } }\n\n/* ---- Finale: the submit row ---- */\n.sk-stage__finale { display: flex; align-items: center; gap: 10px; margin-top: 22px; }\n.sk-stage__shake { animation: sk-stage-shake .38s ease; }\n.sk-stage__submit-error { font-size: 14px; margin-top: 12px; }\n\n@media (prefers-reduced-motion: reduce) {\n .sk-stage__collapse { transition: none }\n .sk-stage__body-inner { transition: none; opacity: 1; transform: none }\n .sk-stage__summary-inner { transition: none; opacity: 1 }\n .sk-stage__seg { transition: none }\n .sk-stage__seg[data-sk-lit=\"true\"] { animation: none }\n .sk-stage__seg[data-sk-live=\"true\"] { animation: none; opacity: .5 }\n .sk-stage__counter[data-sk-ready=\"true\"] { animation: none }\n .sk-stage__summary { transition: none }\n .sk-stage__summary-tick { animation: none }\n .sk-stage__shake { animation: none }\n}\n`\n\nexport function StageStyles() {\n return <style id={STYLE_ID} dangerouslySetInnerHTML={{ __html: CSS }} />\n}\n\nexport function SummaryTick() {\n return (\n <span className=\"sk-stage__summary-tick\" data-sk-part=\"stage-summary-tick\" aria-hidden=\"true\">\n <svg width=\"9\" height=\"9\" viewBox=\"0 0 16 16\" fill=\"none\">\n <path\n d=\"M3.5 8.5l3 3 6-6.5\"\n stroke=\"var(--sk-form-success-check, #fff)\"\n strokeWidth=\"2.6\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n </span>\n )\n}\n"]}