@sonordev/agency-site-kit 0.3.1 → 0.4.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 (369) hide show
  1. package/README.md +302 -0
  2. package/bin/setup.mjs +79 -0
  3. package/dist/{chunk-RXBK2EOE.js → chunk-4IES23GV.js} +3 -3
  4. package/dist/{chunk-RXBK2EOE.js.map → chunk-4IES23GV.js.map} +1 -1
  5. package/dist/chunk-4N7ENJ7L.cjs +108 -0
  6. package/dist/chunk-4N7ENJ7L.cjs.map +1 -0
  7. package/dist/{chunk-WNR4KNG4.cjs → chunk-5MEZU3OG.cjs} +4 -4
  8. package/dist/{chunk-WNR4KNG4.cjs.map → chunk-5MEZU3OG.cjs.map} +1 -1
  9. package/dist/chunk-7ZHRMWJU.cjs +306 -0
  10. package/dist/chunk-7ZHRMWJU.cjs.map +1 -0
  11. package/dist/chunk-CVT7I5BQ.js +71 -0
  12. package/dist/chunk-CVT7I5BQ.js.map +1 -0
  13. package/dist/chunk-HQGAX2LI.cjs +8 -0
  14. package/dist/chunk-HQGAX2LI.cjs.map +1 -0
  15. package/dist/chunk-KXLROAO5.js +291 -0
  16. package/dist/chunk-KXLROAO5.js.map +1 -0
  17. package/dist/chunk-L4EOQVRJ.js +6 -0
  18. package/dist/chunk-L4EOQVRJ.js.map +1 -0
  19. package/dist/chunk-L7E3UHQC.cjs +535 -0
  20. package/dist/chunk-L7E3UHQC.cjs.map +1 -0
  21. package/dist/chunk-NI4FVCLS.cjs +32 -0
  22. package/dist/chunk-NI4FVCLS.cjs.map +1 -0
  23. package/dist/chunk-NIZJLONK.js +106 -0
  24. package/dist/chunk-NIZJLONK.js.map +1 -0
  25. package/dist/chunk-QDXZ27R7.js +33 -0
  26. package/dist/chunk-QDXZ27R7.js.map +1 -0
  27. package/dist/{chunk-TVFXN6W6.js → chunk-QL6KVIFR.js} +8 -4
  28. package/dist/chunk-QL6KVIFR.js.map +1 -0
  29. package/dist/chunk-UNTOEWQL.js +504 -0
  30. package/dist/chunk-UNTOEWQL.js.map +1 -0
  31. package/dist/chunk-XJ434N7I.js +30 -0
  32. package/dist/chunk-XJ434N7I.js.map +1 -0
  33. package/dist/chunk-XLK77USA.js +45 -0
  34. package/dist/chunk-XLK77USA.js.map +1 -0
  35. package/dist/{chunk-EE75WRCM.cjs → chunk-XOWSXZYW.cjs} +8 -4
  36. package/dist/chunk-XOWSXZYW.cjs.map +1 -0
  37. package/dist/chunk-ZINKVJQU.js +67 -0
  38. package/dist/chunk-ZINKVJQU.js.map +1 -0
  39. package/dist/index.cjs +29 -31
  40. package/dist/index.cjs.map +1 -1
  41. package/dist/index.d.cts +17 -9
  42. package/dist/index.d.ts +17 -9
  43. package/dist/index.js +7 -9
  44. package/dist/index.js.map +1 -1
  45. package/dist/layout/index.cjs +3 -3
  46. package/dist/layout/index.d.cts +1 -1
  47. package/dist/layout/index.d.ts +1 -1
  48. package/dist/layout/index.js +2 -2
  49. package/dist/portfolio/client.cjs +342 -12
  50. package/dist/portfolio/client.cjs.map +1 -1
  51. package/dist/portfolio/client.d.cts +11 -88
  52. package/dist/portfolio/client.d.ts +11 -88
  53. package/dist/portfolio/client.js +150 -4
  54. package/dist/portfolio/client.js.map +1 -1
  55. package/dist/portfolio/components/PortfolioAnalytics.cjs +87 -0
  56. package/dist/portfolio/components/PortfolioAnalytics.cjs.map +1 -0
  57. package/dist/portfolio/components/PortfolioAnalytics.d.cts +5 -0
  58. package/dist/portfolio/components/PortfolioAnalytics.d.ts +5 -0
  59. package/dist/portfolio/components/PortfolioAnalytics.js +9 -0
  60. package/dist/portfolio/components/PortfolioAnalytics.js.map +1 -0
  61. package/dist/portfolio/components/PortfolioErrorFallback.cjs +65 -0
  62. package/dist/portfolio/components/PortfolioErrorFallback.cjs.map +1 -0
  63. package/dist/portfolio/components/PortfolioErrorFallback.d.cts +22 -0
  64. package/dist/portfolio/components/PortfolioErrorFallback.d.ts +22 -0
  65. package/dist/portfolio/components/PortfolioErrorFallback.js +9 -0
  66. package/dist/portfolio/components/PortfolioErrorFallback.js.map +1 -0
  67. package/dist/portfolio/components/PortfolioLeadCTA.cjs +91 -0
  68. package/dist/portfolio/components/PortfolioLeadCTA.cjs.map +1 -0
  69. package/dist/portfolio/components/PortfolioLeadCTA.d.cts +34 -0
  70. package/dist/portfolio/components/PortfolioLeadCTA.d.ts +34 -0
  71. package/dist/portfolio/components/PortfolioLeadCTA.js +9 -0
  72. package/dist/portfolio/components/PortfolioLeadCTA.js.map +1 -0
  73. package/dist/portfolio/index.cjs +19 -20
  74. package/dist/portfolio/index.d.cts +133 -7
  75. package/dist/portfolio/index.d.ts +133 -7
  76. package/dist/portfolio/index.js +6 -7
  77. package/dist/portfolio/primitives/AnimatedCounter.cjs +88 -0
  78. package/dist/portfolio/primitives/AnimatedCounter.cjs.map +1 -0
  79. package/dist/portfolio/primitives/AnimatedCounter.d.cts +22 -0
  80. package/dist/portfolio/primitives/AnimatedCounter.d.ts +22 -0
  81. package/dist/portfolio/primitives/AnimatedCounter.js +68 -0
  82. package/dist/portfolio/primitives/AnimatedCounter.js.map +1 -0
  83. package/dist/portfolio/primitives/AnnotatedImage.cjs +320 -0
  84. package/dist/portfolio/primitives/AnnotatedImage.cjs.map +1 -0
  85. package/dist/portfolio/primitives/AnnotatedImage.d.cts +18 -0
  86. package/dist/portfolio/primitives/AnnotatedImage.d.ts +18 -0
  87. package/dist/portfolio/primitives/AnnotatedImage.js +264 -0
  88. package/dist/portfolio/primitives/AnnotatedImage.js.map +1 -0
  89. package/dist/portfolio/primitives/BeforeAfterSlider.cjs +348 -0
  90. package/dist/portfolio/primitives/BeforeAfterSlider.cjs.map +1 -0
  91. package/dist/portfolio/primitives/BeforeAfterSlider.d.cts +13 -0
  92. package/dist/portfolio/primitives/BeforeAfterSlider.d.ts +13 -0
  93. package/dist/portfolio/primitives/BeforeAfterSlider.js +292 -0
  94. package/dist/portfolio/primitives/BeforeAfterSlider.js.map +1 -0
  95. package/dist/portfolio/primitives/DeviceTrifolio.cjs +298 -0
  96. package/dist/portfolio/primitives/DeviceTrifolio.cjs.map +1 -0
  97. package/dist/portfolio/primitives/DeviceTrifolio.d.cts +14 -0
  98. package/dist/portfolio/primitives/DeviceTrifolio.d.ts +14 -0
  99. package/dist/portfolio/primitives/DeviceTrifolio.js +242 -0
  100. package/dist/portfolio/primitives/DeviceTrifolio.js.map +1 -0
  101. package/dist/portfolio/primitives/FrequencyBarSkeleton.cjs +88 -0
  102. package/dist/portfolio/primitives/FrequencyBarSkeleton.cjs.map +1 -0
  103. package/dist/portfolio/primitives/FrequencyBarSkeleton.d.cts +10 -0
  104. package/dist/portfolio/primitives/FrequencyBarSkeleton.d.ts +10 -0
  105. package/dist/portfolio/primitives/FrequencyBarSkeleton.js +68 -0
  106. package/dist/portfolio/primitives/FrequencyBarSkeleton.js.map +1 -0
  107. package/dist/portfolio/primitives/GlassCard.cjs +45 -0
  108. package/dist/portfolio/primitives/GlassCard.cjs.map +1 -0
  109. package/dist/portfolio/primitives/GlassCard.d.cts +11 -0
  110. package/dist/portfolio/primitives/GlassCard.d.ts +11 -0
  111. package/dist/portfolio/primitives/GlassCard.js +43 -0
  112. package/dist/portfolio/primitives/GlassCard.js.map +1 -0
  113. package/dist/portfolio/primitives/MetricBadge.cjs +74 -0
  114. package/dist/portfolio/primitives/MetricBadge.cjs.map +1 -0
  115. package/dist/portfolio/primitives/MetricBadge.d.cts +9 -0
  116. package/dist/portfolio/primitives/MetricBadge.d.ts +9 -0
  117. package/dist/portfolio/primitives/MetricBadge.js +54 -0
  118. package/dist/portfolio/primitives/MetricBadge.js.map +1 -0
  119. package/dist/portfolio/primitives/SVGChart.cjs +316 -0
  120. package/dist/portfolio/primitives/SVGChart.cjs.map +1 -0
  121. package/dist/portfolio/primitives/SVGChart.d.cts +18 -0
  122. package/dist/portfolio/primitives/SVGChart.d.ts +18 -0
  123. package/dist/portfolio/primitives/SVGChart.js +296 -0
  124. package/dist/portfolio/primitives/SVGChart.js.map +1 -0
  125. package/dist/portfolio/primitives/ScrollReveal.cjs +97 -0
  126. package/dist/portfolio/primitives/ScrollReveal.cjs.map +1 -0
  127. package/dist/portfolio/primitives/ScrollReveal.d.cts +27 -0
  128. package/dist/portfolio/primitives/ScrollReveal.d.ts +27 -0
  129. package/dist/portfolio/primitives/ScrollReveal.js +77 -0
  130. package/dist/portfolio/primitives/ScrollReveal.js.map +1 -0
  131. package/dist/portfolio/primitives/SplitHeadline.cjs +107 -0
  132. package/dist/portfolio/primitives/SplitHeadline.cjs.map +1 -0
  133. package/dist/portfolio/primitives/SplitHeadline.d.cts +24 -0
  134. package/dist/portfolio/primitives/SplitHeadline.d.ts +24 -0
  135. package/dist/portfolio/primitives/SplitHeadline.js +87 -0
  136. package/dist/portfolio/primitives/SplitHeadline.js.map +1 -0
  137. package/dist/portfolio/sections/BeforeAfterSection.cjs +271 -0
  138. package/dist/portfolio/sections/BeforeAfterSection.cjs.map +1 -0
  139. package/dist/portfolio/sections/BeforeAfterSection.d.cts +9 -0
  140. package/dist/portfolio/sections/BeforeAfterSection.d.ts +9 -0
  141. package/dist/{BeforeAfterSection-6BHFLY4Y.js → portfolio/sections/BeforeAfterSection.js} +66 -27
  142. package/dist/portfolio/sections/BeforeAfterSection.js.map +1 -0
  143. package/dist/portfolio/sections/CTASection.cjs +123 -0
  144. package/dist/portfolio/sections/CTASection.cjs.map +1 -0
  145. package/dist/portfolio/sections/CTASection.d.cts +9 -0
  146. package/dist/portfolio/sections/CTASection.d.ts +9 -0
  147. package/dist/portfolio/sections/CTASection.js +93 -0
  148. package/dist/portfolio/sections/CTASection.js.map +1 -0
  149. package/dist/{ChallengesSection-IZ3DHECS.cjs → portfolio/sections/ChallengesSection.cjs} +12 -6
  150. package/dist/portfolio/sections/ChallengesSection.cjs.map +1 -0
  151. package/dist/portfolio/sections/ChallengesSection.d.cts +9 -0
  152. package/dist/portfolio/sections/ChallengesSection.d.ts +9 -0
  153. package/dist/{ChallengesSection-GEQGVSJN.js → portfolio/sections/ChallengesSection.js} +5 -4
  154. package/dist/portfolio/sections/ChallengesSection.js.map +1 -0
  155. package/dist/portfolio/sections/ConversionFunnelSection.cjs +263 -0
  156. package/dist/portfolio/sections/ConversionFunnelSection.cjs.map +1 -0
  157. package/dist/portfolio/sections/ConversionFunnelSection.d.cts +9 -0
  158. package/dist/portfolio/sections/ConversionFunnelSection.d.ts +9 -0
  159. package/dist/{ConversionFunnelSection-D3GE4NKE.js → portfolio/sections/ConversionFunnelSection.js} +60 -56
  160. package/dist/portfolio/sections/ConversionFunnelSection.js.map +1 -0
  161. package/dist/{DesignSystemSection-KXIQXITF.cjs → portfolio/sections/DesignSystemSection.cjs} +13 -7
  162. package/dist/portfolio/sections/DesignSystemSection.cjs.map +1 -0
  163. package/dist/portfolio/sections/DesignSystemSection.d.cts +32 -0
  164. package/dist/portfolio/sections/DesignSystemSection.d.ts +32 -0
  165. package/dist/{DesignSystemSection-2R5BRBGO.js → portfolio/sections/DesignSystemSection.js} +5 -4
  166. package/dist/portfolio/sections/DesignSystemSection.js.map +1 -0
  167. package/dist/{DetailsSection-TTUZAPZZ.cjs → portfolio/sections/DetailsSection.cjs} +12 -6
  168. package/dist/portfolio/sections/DetailsSection.cjs.map +1 -0
  169. package/dist/portfolio/sections/DetailsSection.d.cts +9 -0
  170. package/dist/portfolio/sections/DetailsSection.d.ts +9 -0
  171. package/dist/{DetailsSection-A6PZQUQL.js → portfolio/sections/DetailsSection.js} +5 -4
  172. package/dist/portfolio/sections/DetailsSection.js.map +1 -0
  173. package/dist/portfolio/sections/FeatureSpotlightSection.cjs +267 -0
  174. package/dist/portfolio/sections/FeatureSpotlightSection.cjs.map +1 -0
  175. package/dist/portfolio/sections/FeatureSpotlightSection.d.cts +9 -0
  176. package/dist/portfolio/sections/FeatureSpotlightSection.d.ts +9 -0
  177. package/dist/{FeatureSpotlightSection-B7P3JGNL.js → portfolio/sections/FeatureSpotlightSection.js} +39 -33
  178. package/dist/portfolio/sections/FeatureSpotlightSection.js.map +1 -0
  179. package/dist/portfolio/sections/GallerySection.cjs +321 -0
  180. package/dist/portfolio/sections/GallerySection.cjs.map +1 -0
  181. package/dist/portfolio/sections/GallerySection.d.cts +9 -0
  182. package/dist/portfolio/sections/GallerySection.d.ts +9 -0
  183. package/dist/{GallerySection-VMKORC47.js → portfolio/sections/GallerySection.js} +65 -18
  184. package/dist/portfolio/sections/GallerySection.js.map +1 -0
  185. package/dist/portfolio/sections/HeroSection.cjs +180 -0
  186. package/dist/portfolio/sections/HeroSection.cjs.map +1 -0
  187. package/dist/portfolio/sections/HeroSection.d.cts +15 -0
  188. package/dist/portfolio/sections/HeroSection.d.ts +15 -0
  189. package/dist/portfolio/sections/HeroSection.js +171 -0
  190. package/dist/portfolio/sections/HeroSection.js.map +1 -0
  191. package/dist/{MetricsTimelineSection-4L6DUHJ5.cjs → portfolio/sections/MetricsTimelineSection.cjs} +102 -61
  192. package/dist/portfolio/sections/MetricsTimelineSection.cjs.map +1 -0
  193. package/dist/portfolio/sections/MetricsTimelineSection.d.cts +9 -0
  194. package/dist/portfolio/sections/MetricsTimelineSection.d.ts +9 -0
  195. package/dist/{MetricsTimelineSection-6BT5GNFV.js → portfolio/sections/MetricsTimelineSection.js} +45 -29
  196. package/dist/portfolio/sections/MetricsTimelineSection.js.map +1 -0
  197. package/dist/{PerformanceSection-24TVVFZA.cjs → portfolio/sections/PerformanceSection.cjs} +136 -93
  198. package/dist/portfolio/sections/PerformanceSection.cjs.map +1 -0
  199. package/dist/portfolio/sections/PerformanceSection.d.cts +22 -0
  200. package/dist/portfolio/sections/PerformanceSection.d.ts +22 -0
  201. package/dist/{PerformanceSection-MGCEIXDX.js → portfolio/sections/PerformanceSection.js} +58 -40
  202. package/dist/portfolio/sections/PerformanceSection.js.map +1 -0
  203. package/dist/{ResultsSection-XLGMMQKY.cjs → portfolio/sections/ResultsSection.cjs} +22 -10
  204. package/dist/portfolio/sections/ResultsSection.cjs.map +1 -0
  205. package/dist/portfolio/sections/ResultsSection.d.cts +13 -0
  206. package/dist/portfolio/sections/ResultsSection.d.ts +13 -0
  207. package/dist/{ResultsSection-DFUJ5U6M.js → portfolio/sections/ResultsSection.js} +13 -7
  208. package/dist/portfolio/sections/ResultsSection.js.map +1 -0
  209. package/dist/portfolio/sections/ServicesSection.cjs +129 -0
  210. package/dist/portfolio/sections/ServicesSection.cjs.map +1 -0
  211. package/dist/portfolio/sections/ServicesSection.d.cts +9 -0
  212. package/dist/portfolio/sections/ServicesSection.d.ts +9 -0
  213. package/dist/portfolio/sections/ServicesSection.js +122 -0
  214. package/dist/portfolio/sections/ServicesSection.js.map +1 -0
  215. package/dist/{SiteArchitectureSection-J7BPBERH.cjs → portfolio/sections/SiteArchitectureSection.cjs} +116 -92
  216. package/dist/portfolio/sections/SiteArchitectureSection.cjs.map +1 -0
  217. package/dist/portfolio/sections/SiteArchitectureSection.d.cts +21 -0
  218. package/dist/portfolio/sections/SiteArchitectureSection.d.ts +21 -0
  219. package/dist/{SiteArchitectureSection-TE22OAWV.js → portfolio/sections/SiteArchitectureSection.js} +46 -47
  220. package/dist/portfolio/sections/SiteArchitectureSection.js.map +1 -0
  221. package/dist/{SpeedComparisonSection-EZKFQVGW.cjs → portfolio/sections/SpeedComparisonSection.cjs} +12 -6
  222. package/dist/portfolio/sections/SpeedComparisonSection.cjs.map +1 -0
  223. package/dist/portfolio/sections/SpeedComparisonSection.d.cts +20 -0
  224. package/dist/portfolio/sections/SpeedComparisonSection.d.ts +20 -0
  225. package/dist/{SpeedComparisonSection-Y3K7OFZQ.js → portfolio/sections/SpeedComparisonSection.js} +5 -4
  226. package/dist/portfolio/sections/SpeedComparisonSection.js.map +1 -0
  227. package/dist/portfolio/sections/StrategySection.cjs +233 -0
  228. package/dist/portfolio/sections/StrategySection.cjs.map +1 -0
  229. package/dist/portfolio/sections/StrategySection.d.cts +9 -0
  230. package/dist/portfolio/sections/StrategySection.d.ts +9 -0
  231. package/dist/{StrategySection-DI5RSCJU.js → portfolio/sections/StrategySection.js} +60 -26
  232. package/dist/portfolio/sections/StrategySection.js.map +1 -0
  233. package/dist/portfolio/sections/TeamSection.cjs +162 -0
  234. package/dist/portfolio/sections/TeamSection.cjs.map +1 -0
  235. package/dist/portfolio/sections/TeamSection.d.cts +9 -0
  236. package/dist/portfolio/sections/TeamSection.d.ts +9 -0
  237. package/dist/portfolio/sections/TeamSection.js +106 -0
  238. package/dist/portfolio/sections/TeamSection.js.map +1 -0
  239. package/dist/portfolio/sections/TechStackSection.cjs +106 -0
  240. package/dist/portfolio/sections/TechStackSection.cjs.map +1 -0
  241. package/dist/portfolio/sections/TechStackSection.d.cts +9 -0
  242. package/dist/portfolio/sections/TechStackSection.d.ts +9 -0
  243. package/dist/portfolio/sections/TechStackSection.js +99 -0
  244. package/dist/portfolio/sections/TechStackSection.js.map +1 -0
  245. package/dist/{TestimonialSection-XPTFUQIN.cjs → portfolio/sections/TestimonialSection.cjs} +47 -28
  246. package/dist/portfolio/sections/TestimonialSection.cjs.map +1 -0
  247. package/dist/portfolio/sections/TestimonialSection.d.cts +9 -0
  248. package/dist/portfolio/sections/TestimonialSection.d.ts +9 -0
  249. package/dist/{TestimonialSection-6RGSMXQB.js → portfolio/sections/TestimonialSection.js} +40 -27
  250. package/dist/portfolio/sections/TestimonialSection.js.map +1 -0
  251. package/dist/portfolio/sections/VideoSection.cjs +181 -0
  252. package/dist/portfolio/sections/VideoSection.cjs.map +1 -0
  253. package/dist/portfolio/sections/VideoSection.d.cts +9 -0
  254. package/dist/portfolio/sections/VideoSection.d.ts +9 -0
  255. package/dist/{VideoSection-4A2HC6K6.js → portfolio/sections/VideoSection.js} +21 -13
  256. package/dist/portfolio/sections/VideoSection.js.map +1 -0
  257. package/dist/portfolio/server.cjs +56 -144
  258. package/dist/portfolio/server.cjs.map +1 -1
  259. package/dist/portfolio/server.d.cts +157 -7
  260. package/dist/portfolio/server.d.ts +157 -7
  261. package/dist/portfolio/server.js +3 -142
  262. package/dist/portfolio/server.js.map +1 -1
  263. package/dist/preview.cjs +56 -0
  264. package/dist/preview.cjs.map +1 -0
  265. package/dist/preview.d.cts +31 -0
  266. package/dist/preview.d.ts +31 -0
  267. package/dist/preview.js +54 -0
  268. package/dist/preview.js.map +1 -0
  269. package/dist/{types-DL4t_Cfa.d.ts → types-0tVPy4pU.d.cts} +7 -1
  270. package/dist/{types-DL4t_Cfa.d.cts → types-0tVPy4pU.d.ts} +7 -1
  271. package/dist/types-D0nb6KAx.d.cts +299 -0
  272. package/dist/types-D0nb6KAx.d.ts +299 -0
  273. package/package.json +38 -14
  274. package/templates/api-portfolio-preview.ts +6 -0
  275. package/templates/api-revalidate.ts +5 -0
  276. package/templates/work-category-page.tsx +40 -0
  277. package/templates/work-error.tsx +3 -0
  278. package/templates/work-loading.tsx +1 -0
  279. package/templates/work-page.tsx +37 -0
  280. package/templates/work-slug-loading.tsx +1 -0
  281. package/templates/work-slug-page.tsx +41 -0
  282. package/dist/BeforeAfterSection-6BHFLY4Y.js.map +0 -1
  283. package/dist/BeforeAfterSection-JTORBR3A.cjs +0 -181
  284. package/dist/BeforeAfterSection-JTORBR3A.cjs.map +0 -1
  285. package/dist/CTASection-4JKLXEUF.cjs +0 -111
  286. package/dist/CTASection-4JKLXEUF.cjs.map +0 -1
  287. package/dist/CTASection-BJA72XIL.js +0 -106
  288. package/dist/CTASection-BJA72XIL.js.map +0 -1
  289. package/dist/ChallengesSection-GEQGVSJN.js.map +0 -1
  290. package/dist/ChallengesSection-IZ3DHECS.cjs.map +0 -1
  291. package/dist/ConversionFunnelSection-AUUSJ5HQ.cjs +0 -209
  292. package/dist/ConversionFunnelSection-AUUSJ5HQ.cjs.map +0 -1
  293. package/dist/ConversionFunnelSection-D3GE4NKE.js.map +0 -1
  294. package/dist/DesignSystemSection-2R5BRBGO.js.map +0 -1
  295. package/dist/DesignSystemSection-KXIQXITF.cjs.map +0 -1
  296. package/dist/DetailsSection-A6PZQUQL.js.map +0 -1
  297. package/dist/DetailsSection-TTUZAPZZ.cjs.map +0 -1
  298. package/dist/FeatureSpotlightSection-B7P3JGNL.js.map +0 -1
  299. package/dist/FeatureSpotlightSection-WRHXS7TU.cjs +0 -210
  300. package/dist/FeatureSpotlightSection-WRHXS7TU.cjs.map +0 -1
  301. package/dist/GallerySection-VMKORC47.js.map +0 -1
  302. package/dist/GallerySection-WJ4PQDBI.cjs +0 -219
  303. package/dist/GallerySection-WJ4PQDBI.cjs.map +0 -1
  304. package/dist/MetricsTimelineSection-4L6DUHJ5.cjs.map +0 -1
  305. package/dist/MetricsTimelineSection-6BT5GNFV.js.map +0 -1
  306. package/dist/PerformanceSection-24TVVFZA.cjs.map +0 -1
  307. package/dist/PerformanceSection-MGCEIXDX.js.map +0 -1
  308. package/dist/ResultsSection-DFUJ5U6M.js.map +0 -1
  309. package/dist/ResultsSection-XLGMMQKY.cjs.map +0 -1
  310. package/dist/ServicesSection-D5V3Q4GR.js +0 -118
  311. package/dist/ServicesSection-D5V3Q4GR.js.map +0 -1
  312. package/dist/ServicesSection-WJMGK2MF.cjs +0 -120
  313. package/dist/ServicesSection-WJMGK2MF.cjs.map +0 -1
  314. package/dist/SiteArchitectureSection-J7BPBERH.cjs.map +0 -1
  315. package/dist/SiteArchitectureSection-TE22OAWV.js.map +0 -1
  316. package/dist/SpeedComparisonSection-EZKFQVGW.cjs.map +0 -1
  317. package/dist/SpeedComparisonSection-Y3K7OFZQ.js.map +0 -1
  318. package/dist/StrategySection-CJ7Y6OFQ.cjs +0 -174
  319. package/dist/StrategySection-CJ7Y6OFQ.cjs.map +0 -1
  320. package/dist/StrategySection-DI5RSCJU.js.map +0 -1
  321. package/dist/TeamSection-DZVSNZE6.cjs +0 -112
  322. package/dist/TeamSection-DZVSNZE6.cjs.map +0 -1
  323. package/dist/TeamSection-HGKFW6PQ.js +0 -107
  324. package/dist/TeamSection-HGKFW6PQ.js.map +0 -1
  325. package/dist/TechStackSection-2AQ7RGY3.js +0 -93
  326. package/dist/TechStackSection-2AQ7RGY3.js.map +0 -1
  327. package/dist/TechStackSection-VTNNZR5V.cjs +0 -95
  328. package/dist/TechStackSection-VTNNZR5V.cjs.map +0 -1
  329. package/dist/TestimonialSection-6RGSMXQB.js.map +0 -1
  330. package/dist/TestimonialSection-XPTFUQIN.cjs.map +0 -1
  331. package/dist/VideoSection-4A2HC6K6.js.map +0 -1
  332. package/dist/VideoSection-G3DFS7UH.cjs +0 -118
  333. package/dist/VideoSection-G3DFS7UH.cjs.map +0 -1
  334. package/dist/chunk-2Y4O3LWM.js +0 -53
  335. package/dist/chunk-2Y4O3LWM.js.map +0 -1
  336. package/dist/chunk-633UMQQH.cjs +0 -31
  337. package/dist/chunk-633UMQQH.cjs.map +0 -1
  338. package/dist/chunk-7CFFAKDM.js +0 -74
  339. package/dist/chunk-7CFFAKDM.js.map +0 -1
  340. package/dist/chunk-APG2QSMB.js +0 -131
  341. package/dist/chunk-APG2QSMB.js.map +0 -1
  342. package/dist/chunk-AVCH4LMG.cjs +0 -620
  343. package/dist/chunk-AVCH4LMG.cjs.map +0 -1
  344. package/dist/chunk-AYBUHRFS.js +0 -613
  345. package/dist/chunk-AYBUHRFS.js.map +0 -1
  346. package/dist/chunk-EE75WRCM.cjs.map +0 -1
  347. package/dist/chunk-IKBK7HYX.cjs +0 -79
  348. package/dist/chunk-IKBK7HYX.cjs.map +0 -1
  349. package/dist/chunk-KEOHORIH.cjs +0 -79
  350. package/dist/chunk-KEOHORIH.cjs.map +0 -1
  351. package/dist/chunk-OA5ZM4OA.cjs +0 -139
  352. package/dist/chunk-OA5ZM4OA.cjs.map +0 -1
  353. package/dist/chunk-TVFXN6W6.js.map +0 -1
  354. package/dist/chunk-VVTK4OL6.js +0 -28
  355. package/dist/chunk-VVTK4OL6.js.map +0 -1
  356. package/dist/chunk-XCKXHK44.js +0 -15
  357. package/dist/chunk-XCKXHK44.js.map +0 -1
  358. package/dist/chunk-XONXEFJY.cjs +0 -58
  359. package/dist/chunk-XONXEFJY.cjs.map +0 -1
  360. package/dist/chunk-XQNJED46.cjs +0 -19
  361. package/dist/chunk-XQNJED46.cjs.map +0 -1
  362. package/dist/chunk-YB4B3OMC.js +0 -74
  363. package/dist/chunk-YB4B3OMC.js.map +0 -1
  364. package/dist/portfolio/sections.cjs +0 -20
  365. package/dist/portfolio/sections.cjs.map +0 -1
  366. package/dist/portfolio/sections.d.cts +0 -42
  367. package/dist/portfolio/sections.d.ts +0 -42
  368. package/dist/portfolio/sections.js +0 -4
  369. package/dist/portfolio/sections.js.map +0 -1
@@ -0,0 +1,107 @@
1
+ 'use client';
2
+ "use strict";
3
+ "use client";
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
21
+
22
+ // src/portfolio/components/primitives/SplitHeadline.tsx
23
+ var SplitHeadline_exports = {};
24
+ __export(SplitHeadline_exports, {
25
+ default: () => SplitHeadline
26
+ });
27
+ module.exports = __toCommonJS(SplitHeadline_exports);
28
+ var import_react = require("react");
29
+ var import_jsx_runtime = require("react/jsx-runtime");
30
+ var EASE = "cubic-bezier(0.25, 0.46, 0.45, 0.94)";
31
+ function SplitHeadline({
32
+ children,
33
+ tag: Tag = "h2",
34
+ className,
35
+ style,
36
+ delay = 0,
37
+ duration = 0.8,
38
+ staggerAmount = 0.02
39
+ }) {
40
+ const ref = (0, import_react.useRef)(null);
41
+ const splitContent = (0, import_react.useMemo)(() => {
42
+ const words = children.split(" ");
43
+ return words.map((word, wi) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { style: { display: "inline-block", whiteSpace: "nowrap" }, children: [
44
+ word.split("").map((char, ci) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
45
+ "span",
46
+ {
47
+ className: "split-char",
48
+ style: { display: "inline-block" },
49
+ children: char
50
+ },
51
+ ci
52
+ )),
53
+ wi < words.length - 1 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { display: "inline-block" }, children: "\xA0" })
54
+ ] }, wi));
55
+ }, [children]);
56
+ (0, import_react.useEffect)(() => {
57
+ const el = ref.current;
58
+ if (!el) return;
59
+ if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
60
+ const rect = el.getBoundingClientRect();
61
+ if (rect.top < window.innerHeight && rect.bottom > 0) return;
62
+ const chars = Array.from(el.querySelectorAll(".split-char"));
63
+ for (const char of chars) {
64
+ char.style.opacity = "0";
65
+ char.style.transform = "translateY(20px)";
66
+ }
67
+ const io = new IntersectionObserver(
68
+ ([entry]) => {
69
+ if (!entry.isIntersecting) return;
70
+ chars.forEach((char, i) => {
71
+ const charDelay = delay + i * staggerAmount;
72
+ char.style.transition = `opacity ${duration}s ${EASE} ${charDelay}s, transform ${duration}s ${EASE} ${charDelay}s`;
73
+ char.style.opacity = "1";
74
+ char.style.transform = "translateY(0px)";
75
+ });
76
+ io.disconnect();
77
+ },
78
+ { rootMargin: "0px 0px -15% 0px" }
79
+ // ≈ 'top 85%' trigger start
80
+ );
81
+ io.observe(el);
82
+ return () => {
83
+ io.disconnect();
84
+ for (const char of chars) {
85
+ char.style.transition = "";
86
+ char.style.opacity = "";
87
+ char.style.transform = "";
88
+ }
89
+ };
90
+ }, [delay, duration, staggerAmount]);
91
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
92
+ Tag,
93
+ {
94
+ ref,
95
+ className,
96
+ style: {
97
+ overflowX: "clip",
98
+ overflowY: "hidden",
99
+ overflowWrap: "break-word",
100
+ wordBreak: "break-word",
101
+ ...style
102
+ },
103
+ children: splitContent
104
+ }
105
+ );
106
+ }
107
+ //# sourceMappingURL=SplitHeadline.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/portfolio/components/primitives/SplitHeadline.tsx"],"sourcesContent":["'use client';\n\nimport React, { useRef, useEffect, useMemo } from 'react';\n\ninterface SplitHeadlineProps {\n children: string;\n tag?: 'h1' | 'h2' | 'h3' | 'h4' | 'p';\n className?: string;\n style?: React.CSSProperties;\n delay?: number;\n duration?: number;\n staggerAmount?: number;\n}\n\nconst EASE = 'cubic-bezier(0.25, 0.46, 0.45, 0.94)'; // ≈ power2.out\n\n/**\n * Per-character staggered headline reveal — IntersectionObserver + CSS\n * transitions.\n *\n * SSR renders the full headline visible (crawlers and no-JS users always see\n * the text). Characters are hidden at hydration ONLY when the headline is\n * still below the viewport, so above-the-fold copy is never re-hidden (house\n * LCP rule). Each character transitions in with an incremental delay,\n * matching the old staggered tween.\n */\nexport default function SplitHeadline({\n children,\n tag: Tag = 'h2',\n className,\n style,\n delay = 0,\n duration = 0.8,\n staggerAmount = 0.02,\n}: SplitHeadlineProps) {\n const ref = useRef<HTMLElement>(null);\n\n // Split text into words and characters, preserving spaces\n const splitContent = useMemo(() => {\n const words = children.split(' ');\n return words.map((word, wi) => (\n <span key={wi} style={{ display: 'inline-block', whiteSpace: 'nowrap' }}>\n {word.split('').map((char, ci) => (\n <span\n key={ci}\n className=\"split-char\"\n style={{ display: 'inline-block' }}\n >\n {char}\n </span>\n ))}\n {wi < words.length - 1 && (\n <span style={{ display: 'inline-block' }}>&nbsp;</span>\n )}\n </span>\n ));\n }, [children]);\n\n useEffect(() => {\n const el = ref.current;\n if (!el) return;\n\n // Respect prefers-reduced-motion — the SSR markup is already visible.\n if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;\n\n // Never re-hide a headline already in the viewport at hydration.\n const rect = el.getBoundingClientRect();\n if (rect.top < window.innerHeight && rect.bottom > 0) return;\n\n const chars = Array.from(el.querySelectorAll<HTMLElement>('.split-char'));\n\n for (const char of chars) {\n char.style.opacity = '0';\n char.style.transform = 'translateY(20px)';\n }\n\n const io = new IntersectionObserver(\n ([entry]) => {\n if (!entry.isIntersecting) return;\n chars.forEach((char, i) => {\n const charDelay = delay + i * staggerAmount;\n char.style.transition = `opacity ${duration}s ${EASE} ${charDelay}s, transform ${duration}s ${EASE} ${charDelay}s`;\n char.style.opacity = '1';\n char.style.transform = 'translateY(0px)';\n });\n io.disconnect();\n },\n { rootMargin: '0px 0px -15% 0px' }, // ≈ 'top 85%' trigger start\n );\n io.observe(el);\n\n return () => {\n io.disconnect();\n for (const char of chars) {\n char.style.transition = '';\n char.style.opacity = '';\n char.style.transform = '';\n }\n };\n }, [delay, duration, staggerAmount]);\n\n return (\n <Tag\n ref={ref as React.RefObject<any>}\n className={className}\n style={{\n overflowX: 'clip',\n overflowY: 'hidden',\n overflowWrap: 'break-word',\n wordBreak: 'break-word',\n ...style,\n }}\n >\n {splitContent}\n </Tag>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA,mBAAkD;AAuC5C;AA3BN,IAAM,OAAO;AAYE,SAAR,cAA+B;AAAA,EACpC;AAAA,EACA,KAAK,MAAM;AAAA,EACX;AAAA,EACA;AAAA,EACA,QAAQ;AAAA,EACR,WAAW;AAAA,EACX,gBAAgB;AAClB,GAAuB;AACrB,QAAM,UAAM,qBAAoB,IAAI;AAGpC,QAAM,mBAAe,sBAAQ,MAAM;AACjC,UAAM,QAAQ,SAAS,MAAM,GAAG;AAChC,WAAO,MAAM,IAAI,CAAC,MAAM,OACtB,6CAAC,UAAc,OAAO,EAAE,SAAS,gBAAgB,YAAY,SAAS,GACnE;AAAA,WAAK,MAAM,EAAE,EAAE,IAAI,CAAC,MAAM,OACzB;AAAA,QAAC;AAAA;AAAA,UAEC,WAAU;AAAA,UACV,OAAO,EAAE,SAAS,eAAe;AAAA,UAEhC;AAAA;AAAA,QAJI;AAAA,MAKP,CACD;AAAA,MACA,KAAK,MAAM,SAAS,KACnB,4CAAC,UAAK,OAAO,EAAE,SAAS,eAAe,GAAG,kBAAM;AAAA,SAXzC,EAaX,CACD;AAAA,EACH,GAAG,CAAC,QAAQ,CAAC;AAEb,8BAAU,MAAM;AACd,UAAM,KAAK,IAAI;AACf,QAAI,CAAC,GAAI;AAGT,QAAI,OAAO,WAAW,kCAAkC,EAAE,QAAS;AAGnE,UAAM,OAAO,GAAG,sBAAsB;AACtC,QAAI,KAAK,MAAM,OAAO,eAAe,KAAK,SAAS,EAAG;AAEtD,UAAM,QAAQ,MAAM,KAAK,GAAG,iBAA8B,aAAa,CAAC;AAExE,eAAW,QAAQ,OAAO;AACxB,WAAK,MAAM,UAAU;AACrB,WAAK,MAAM,YAAY;AAAA,IACzB;AAEA,UAAM,KAAK,IAAI;AAAA,MACb,CAAC,CAAC,KAAK,MAAM;AACX,YAAI,CAAC,MAAM,eAAgB;AAC3B,cAAM,QAAQ,CAAC,MAAM,MAAM;AACzB,gBAAM,YAAY,QAAQ,IAAI;AAC9B,eAAK,MAAM,aAAa,WAAW,QAAQ,KAAK,IAAI,IAAI,SAAS,gBAAgB,QAAQ,KAAK,IAAI,IAAI,SAAS;AAC/G,eAAK,MAAM,UAAU;AACrB,eAAK,MAAM,YAAY;AAAA,QACzB,CAAC;AACD,WAAG,WAAW;AAAA,MAChB;AAAA,MACA,EAAE,YAAY,mBAAmB;AAAA;AAAA,IACnC;AACA,OAAG,QAAQ,EAAE;AAEb,WAAO,MAAM;AACX,SAAG,WAAW;AACd,iBAAW,QAAQ,OAAO;AACxB,aAAK,MAAM,aAAa;AACxB,aAAK,MAAM,UAAU;AACrB,aAAK,MAAM,YAAY;AAAA,MACzB;AAAA,IACF;AAAA,EACF,GAAG,CAAC,OAAO,UAAU,aAAa,CAAC;AAEnC,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA,OAAO;AAAA,QACL,WAAW;AAAA,QACX,WAAW;AAAA,QACX,cAAc;AAAA,QACd,WAAW;AAAA,QACX,GAAG;AAAA,MACL;AAAA,MAEC;AAAA;AAAA,EACH;AAEJ;","names":[]}
@@ -0,0 +1,24 @@
1
+ import React from 'react';
2
+
3
+ interface SplitHeadlineProps {
4
+ children: string;
5
+ tag?: 'h1' | 'h2' | 'h3' | 'h4' | 'p';
6
+ className?: string;
7
+ style?: React.CSSProperties;
8
+ delay?: number;
9
+ duration?: number;
10
+ staggerAmount?: number;
11
+ }
12
+ /**
13
+ * Per-character staggered headline reveal — IntersectionObserver + CSS
14
+ * transitions.
15
+ *
16
+ * SSR renders the full headline visible (crawlers and no-JS users always see
17
+ * the text). Characters are hidden at hydration ONLY when the headline is
18
+ * still below the viewport, so above-the-fold copy is never re-hidden (house
19
+ * LCP rule). Each character transitions in with an incremental delay,
20
+ * matching the old staggered tween.
21
+ */
22
+ declare function SplitHeadline({ children, tag: Tag, className, style, delay, duration, staggerAmount, }: SplitHeadlineProps): React.JSX.Element;
23
+
24
+ export { SplitHeadline as default };
@@ -0,0 +1,24 @@
1
+ import React from 'react';
2
+
3
+ interface SplitHeadlineProps {
4
+ children: string;
5
+ tag?: 'h1' | 'h2' | 'h3' | 'h4' | 'p';
6
+ className?: string;
7
+ style?: React.CSSProperties;
8
+ delay?: number;
9
+ duration?: number;
10
+ staggerAmount?: number;
11
+ }
12
+ /**
13
+ * Per-character staggered headline reveal — IntersectionObserver + CSS
14
+ * transitions.
15
+ *
16
+ * SSR renders the full headline visible (crawlers and no-JS users always see
17
+ * the text). Characters are hidden at hydration ONLY when the headline is
18
+ * still below the viewport, so above-the-fold copy is never re-hidden (house
19
+ * LCP rule). Each character transitions in with an incremental delay,
20
+ * matching the old staggered tween.
21
+ */
22
+ declare function SplitHeadline({ children, tag: Tag, className, style, delay, duration, staggerAmount, }: SplitHeadlineProps): React.JSX.Element;
23
+
24
+ export { SplitHeadline as default };
@@ -0,0 +1,87 @@
1
+ 'use client';
2
+ "use client";
3
+
4
+ // src/portfolio/components/primitives/SplitHeadline.tsx
5
+ import { useRef, useEffect, useMemo } from "react";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+ var EASE = "cubic-bezier(0.25, 0.46, 0.45, 0.94)";
8
+ function SplitHeadline({
9
+ children,
10
+ tag: Tag = "h2",
11
+ className,
12
+ style,
13
+ delay = 0,
14
+ duration = 0.8,
15
+ staggerAmount = 0.02
16
+ }) {
17
+ const ref = useRef(null);
18
+ const splitContent = useMemo(() => {
19
+ const words = children.split(" ");
20
+ return words.map((word, wi) => /* @__PURE__ */ jsxs("span", { style: { display: "inline-block", whiteSpace: "nowrap" }, children: [
21
+ word.split("").map((char, ci) => /* @__PURE__ */ jsx(
22
+ "span",
23
+ {
24
+ className: "split-char",
25
+ style: { display: "inline-block" },
26
+ children: char
27
+ },
28
+ ci
29
+ )),
30
+ wi < words.length - 1 && /* @__PURE__ */ jsx("span", { style: { display: "inline-block" }, children: "\xA0" })
31
+ ] }, wi));
32
+ }, [children]);
33
+ useEffect(() => {
34
+ const el = ref.current;
35
+ if (!el) return;
36
+ if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
37
+ const rect = el.getBoundingClientRect();
38
+ if (rect.top < window.innerHeight && rect.bottom > 0) return;
39
+ const chars = Array.from(el.querySelectorAll(".split-char"));
40
+ for (const char of chars) {
41
+ char.style.opacity = "0";
42
+ char.style.transform = "translateY(20px)";
43
+ }
44
+ const io = new IntersectionObserver(
45
+ ([entry]) => {
46
+ if (!entry.isIntersecting) return;
47
+ chars.forEach((char, i) => {
48
+ const charDelay = delay + i * staggerAmount;
49
+ char.style.transition = `opacity ${duration}s ${EASE} ${charDelay}s, transform ${duration}s ${EASE} ${charDelay}s`;
50
+ char.style.opacity = "1";
51
+ char.style.transform = "translateY(0px)";
52
+ });
53
+ io.disconnect();
54
+ },
55
+ { rootMargin: "0px 0px -15% 0px" }
56
+ // ≈ 'top 85%' trigger start
57
+ );
58
+ io.observe(el);
59
+ return () => {
60
+ io.disconnect();
61
+ for (const char of chars) {
62
+ char.style.transition = "";
63
+ char.style.opacity = "";
64
+ char.style.transform = "";
65
+ }
66
+ };
67
+ }, [delay, duration, staggerAmount]);
68
+ return /* @__PURE__ */ jsx(
69
+ Tag,
70
+ {
71
+ ref,
72
+ className,
73
+ style: {
74
+ overflowX: "clip",
75
+ overflowY: "hidden",
76
+ overflowWrap: "break-word",
77
+ wordBreak: "break-word",
78
+ ...style
79
+ },
80
+ children: splitContent
81
+ }
82
+ );
83
+ }
84
+ export {
85
+ SplitHeadline as default
86
+ };
87
+ //# sourceMappingURL=SplitHeadline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/portfolio/components/primitives/SplitHeadline.tsx"],"sourcesContent":["'use client';\n\nimport React, { useRef, useEffect, useMemo } from 'react';\n\ninterface SplitHeadlineProps {\n children: string;\n tag?: 'h1' | 'h2' | 'h3' | 'h4' | 'p';\n className?: string;\n style?: React.CSSProperties;\n delay?: number;\n duration?: number;\n staggerAmount?: number;\n}\n\nconst EASE = 'cubic-bezier(0.25, 0.46, 0.45, 0.94)'; // ≈ power2.out\n\n/**\n * Per-character staggered headline reveal — IntersectionObserver + CSS\n * transitions.\n *\n * SSR renders the full headline visible (crawlers and no-JS users always see\n * the text). Characters are hidden at hydration ONLY when the headline is\n * still below the viewport, so above-the-fold copy is never re-hidden (house\n * LCP rule). Each character transitions in with an incremental delay,\n * matching the old staggered tween.\n */\nexport default function SplitHeadline({\n children,\n tag: Tag = 'h2',\n className,\n style,\n delay = 0,\n duration = 0.8,\n staggerAmount = 0.02,\n}: SplitHeadlineProps) {\n const ref = useRef<HTMLElement>(null);\n\n // Split text into words and characters, preserving spaces\n const splitContent = useMemo(() => {\n const words = children.split(' ');\n return words.map((word, wi) => (\n <span key={wi} style={{ display: 'inline-block', whiteSpace: 'nowrap' }}>\n {word.split('').map((char, ci) => (\n <span\n key={ci}\n className=\"split-char\"\n style={{ display: 'inline-block' }}\n >\n {char}\n </span>\n ))}\n {wi < words.length - 1 && (\n <span style={{ display: 'inline-block' }}>&nbsp;</span>\n )}\n </span>\n ));\n }, [children]);\n\n useEffect(() => {\n const el = ref.current;\n if (!el) return;\n\n // Respect prefers-reduced-motion — the SSR markup is already visible.\n if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;\n\n // Never re-hide a headline already in the viewport at hydration.\n const rect = el.getBoundingClientRect();\n if (rect.top < window.innerHeight && rect.bottom > 0) return;\n\n const chars = Array.from(el.querySelectorAll<HTMLElement>('.split-char'));\n\n for (const char of chars) {\n char.style.opacity = '0';\n char.style.transform = 'translateY(20px)';\n }\n\n const io = new IntersectionObserver(\n ([entry]) => {\n if (!entry.isIntersecting) return;\n chars.forEach((char, i) => {\n const charDelay = delay + i * staggerAmount;\n char.style.transition = `opacity ${duration}s ${EASE} ${charDelay}s, transform ${duration}s ${EASE} ${charDelay}s`;\n char.style.opacity = '1';\n char.style.transform = 'translateY(0px)';\n });\n io.disconnect();\n },\n { rootMargin: '0px 0px -15% 0px' }, // ≈ 'top 85%' trigger start\n );\n io.observe(el);\n\n return () => {\n io.disconnect();\n for (const char of chars) {\n char.style.transition = '';\n char.style.opacity = '';\n char.style.transform = '';\n }\n };\n }, [delay, duration, staggerAmount]);\n\n return (\n <Tag\n ref={ref as React.RefObject<any>}\n className={className}\n style={{\n overflowX: 'clip',\n overflowY: 'hidden',\n overflowWrap: 'break-word',\n wordBreak: 'break-word',\n ...style,\n }}\n >\n {splitContent}\n </Tag>\n );\n}\n"],"mappings":";;;;AAEA,SAAgB,QAAQ,WAAW,eAAe;AAuC5C,SAEI,KAFJ;AA3BN,IAAM,OAAO;AAYE,SAAR,cAA+B;AAAA,EACpC;AAAA,EACA,KAAK,MAAM;AAAA,EACX;AAAA,EACA;AAAA,EACA,QAAQ;AAAA,EACR,WAAW;AAAA,EACX,gBAAgB;AAClB,GAAuB;AACrB,QAAM,MAAM,OAAoB,IAAI;AAGpC,QAAM,eAAe,QAAQ,MAAM;AACjC,UAAM,QAAQ,SAAS,MAAM,GAAG;AAChC,WAAO,MAAM,IAAI,CAAC,MAAM,OACtB,qBAAC,UAAc,OAAO,EAAE,SAAS,gBAAgB,YAAY,SAAS,GACnE;AAAA,WAAK,MAAM,EAAE,EAAE,IAAI,CAAC,MAAM,OACzB;AAAA,QAAC;AAAA;AAAA,UAEC,WAAU;AAAA,UACV,OAAO,EAAE,SAAS,eAAe;AAAA,UAEhC;AAAA;AAAA,QAJI;AAAA,MAKP,CACD;AAAA,MACA,KAAK,MAAM,SAAS,KACnB,oBAAC,UAAK,OAAO,EAAE,SAAS,eAAe,GAAG,kBAAM;AAAA,SAXzC,EAaX,CACD;AAAA,EACH,GAAG,CAAC,QAAQ,CAAC;AAEb,YAAU,MAAM;AACd,UAAM,KAAK,IAAI;AACf,QAAI,CAAC,GAAI;AAGT,QAAI,OAAO,WAAW,kCAAkC,EAAE,QAAS;AAGnE,UAAM,OAAO,GAAG,sBAAsB;AACtC,QAAI,KAAK,MAAM,OAAO,eAAe,KAAK,SAAS,EAAG;AAEtD,UAAM,QAAQ,MAAM,KAAK,GAAG,iBAA8B,aAAa,CAAC;AAExE,eAAW,QAAQ,OAAO;AACxB,WAAK,MAAM,UAAU;AACrB,WAAK,MAAM,YAAY;AAAA,IACzB;AAEA,UAAM,KAAK,IAAI;AAAA,MACb,CAAC,CAAC,KAAK,MAAM;AACX,YAAI,CAAC,MAAM,eAAgB;AAC3B,cAAM,QAAQ,CAAC,MAAM,MAAM;AACzB,gBAAM,YAAY,QAAQ,IAAI;AAC9B,eAAK,MAAM,aAAa,WAAW,QAAQ,KAAK,IAAI,IAAI,SAAS,gBAAgB,QAAQ,KAAK,IAAI,IAAI,SAAS;AAC/G,eAAK,MAAM,UAAU;AACrB,eAAK,MAAM,YAAY;AAAA,QACzB,CAAC;AACD,WAAG,WAAW;AAAA,MAChB;AAAA,MACA,EAAE,YAAY,mBAAmB;AAAA;AAAA,IACnC;AACA,OAAG,QAAQ,EAAE;AAEb,WAAO,MAAM;AACX,SAAG,WAAW;AACd,iBAAW,QAAQ,OAAO;AACxB,aAAK,MAAM,aAAa;AACxB,aAAK,MAAM,UAAU;AACrB,aAAK,MAAM,YAAY;AAAA,MACzB;AAAA,IACF;AAAA,EACF,GAAG,CAAC,OAAO,UAAU,aAAa,CAAC;AAEnC,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA,OAAO;AAAA,QACL,WAAW;AAAA,QACX,WAAW;AAAA,QACX,cAAc;AAAA,QACd,WAAW;AAAA,QACX,GAAG;AAAA,MACL;AAAA,MAEC;AAAA;AAAA,EACH;AAEJ;","names":[]}
@@ -0,0 +1,271 @@
1
+ 'use client';
2
+ "use strict";
3
+ "use client";
4
+ var __create = Object.create;
5
+ var __defProp = Object.defineProperty;
6
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
7
+ var __getOwnPropNames = Object.getOwnPropertyNames;
8
+ var __getProtoOf = Object.getPrototypeOf;
9
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
10
+ var __export = (target, all) => {
11
+ for (var name in all)
12
+ __defProp(target, name, { get: all[name], enumerable: true });
13
+ };
14
+ var __copyProps = (to, from, except, desc) => {
15
+ if (from && typeof from === "object" || typeof from === "function") {
16
+ for (let key of __getOwnPropNames(from))
17
+ if (!__hasOwnProp.call(to, key) && key !== except)
18
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
19
+ }
20
+ return to;
21
+ };
22
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
23
+ // If the importer is in node compatibility mode or this is not an ESM
24
+ // file that has been converted to a CommonJS file using a Babel-
25
+ // compatible transform (i.e. "__esModule" has not been set), then set
26
+ // "default" to the CommonJS "module.exports" for node compatibility.
27
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
28
+ mod
29
+ ));
30
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
31
+
32
+ // src/portfolio/components/sections/BeforeAfterSection.tsx
33
+ var BeforeAfterSection_exports = {};
34
+ __export(BeforeAfterSection_exports, {
35
+ default: () => BeforeAfterSection
36
+ });
37
+ module.exports = __toCommonJS(BeforeAfterSection_exports);
38
+ var import_react = require("react");
39
+ var import_image = __toESM(require("next/image"), 1);
40
+
41
+ // src/shared/images.ts
42
+ var SANITY_ASSET_REF = /^image-[A-Za-z0-9]+-\d+x\d+-[a-z0-9]+$/;
43
+ var FILENAME_DIMS = /-(\d{2,5})x(\d{2,5})\.[a-z0-9]+(?:\?.*)?$/i;
44
+ var warnedAboutRefs = false;
45
+ function resolvePortfolioImage(input) {
46
+ const raw = typeof input === "string" ? input : input?.asset?._ref ?? "";
47
+ const value = (raw || "").trim();
48
+ if (!value) return null;
49
+ if (SANITY_ASSET_REF.test(value)) {
50
+ if (!warnedAboutRefs && typeof console !== "undefined") {
51
+ warnedAboutRefs = true;
52
+ console.warn(
53
+ "[@sonordev/agency-site-kit] Received a raw Sanity asset ref instead of an image URL \u2014 skipping render. The Sonor API should resolve image refs to cdn.sanity.io URLs before returning portfolio sections."
54
+ );
55
+ }
56
+ return null;
57
+ }
58
+ if (!/^https?:\/\//i.test(value) && !value.startsWith("/")) return null;
59
+ const dims = value.match(FILENAME_DIMS);
60
+ if (dims) {
61
+ const width = parseInt(dims[1], 10);
62
+ const height = parseInt(dims[2], 10);
63
+ if (width > 0 && height > 0) return { url: value, width, height };
64
+ }
65
+ return { url: value };
66
+ }
67
+
68
+ // src/portfolio/components/sections/BeforeAfterSection.tsx
69
+ var import_ScrollReveal = __toESM(require("@sonordev/agency-site-kit/portfolio/primitives/ScrollReveal"), 1);
70
+ var import_GlassCard = __toESM(require("@sonordev/agency-site-kit/portfolio/primitives/GlassCard"), 1);
71
+ var import_jsx_runtime = require("react/jsx-runtime");
72
+ function BeforeAfterSection({ data }) {
73
+ const containerRef = (0, import_react.useRef)(null);
74
+ const [position, setPosition] = (0, import_react.useState)(data.defaultPosition ?? 50);
75
+ const [handleHidden, setHandleHidden] = (0, import_react.useState)(false);
76
+ const isDragging = (0, import_react.useRef)(false);
77
+ const before = resolvePortfolioImage(data.beforeUrl || data.before);
78
+ const after = resolvePortfolioImage(data.afterUrl || data.after);
79
+ (0, import_react.useEffect)(() => {
80
+ const el = containerRef.current;
81
+ if (!el) return;
82
+ if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
83
+ const rect = el.getBoundingClientRect();
84
+ if (rect.top < window.innerHeight && rect.bottom > 0) return;
85
+ setHandleHidden(true);
86
+ const io = new IntersectionObserver(([entry]) => {
87
+ if (entry.isIntersecting) {
88
+ setHandleHidden(false);
89
+ io.disconnect();
90
+ }
91
+ });
92
+ io.observe(el);
93
+ return () => io.disconnect();
94
+ }, []);
95
+ const updatePosition = (0, import_react.useCallback)((clientX) => {
96
+ const container = containerRef.current;
97
+ if (!container) return;
98
+ const rect = container.getBoundingClientRect();
99
+ const x = clientX - rect.left;
100
+ const percent = Math.max(0, Math.min(100, x / rect.width * 100));
101
+ setPosition(percent);
102
+ }, []);
103
+ const handlePointerDown = (0, import_react.useCallback)(
104
+ (e) => {
105
+ isDragging.current = true;
106
+ e.target.setPointerCapture(e.pointerId);
107
+ updatePosition(e.clientX);
108
+ },
109
+ [updatePosition]
110
+ );
111
+ const handlePointerMove = (0, import_react.useCallback)(
112
+ (e) => {
113
+ if (!isDragging.current) return;
114
+ updatePosition(e.clientX);
115
+ },
116
+ [updatePosition]
117
+ );
118
+ const handlePointerUp = (0, import_react.useCallback)(() => {
119
+ isDragging.current = false;
120
+ }, []);
121
+ const handleKeyDown = (0, import_react.useCallback)((e) => {
122
+ const step = e.shiftKey ? 10 : 5;
123
+ const nudge = (delta) => {
124
+ e.preventDefault();
125
+ setPosition((prev) => Math.max(0, Math.min(100, prev + delta)));
126
+ };
127
+ if (e.key === "ArrowLeft" || e.key === "ArrowDown") nudge(-step);
128
+ else if (e.key === "ArrowRight" || e.key === "ArrowUp") nudge(step);
129
+ else if (e.key === "Home") nudge(-100);
130
+ else if (e.key === "End") nudge(100);
131
+ }, []);
132
+ if (!before || !after) return null;
133
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
134
+ "section",
135
+ {
136
+ className: "w-full py-20 md:py-28",
137
+ style: { background: "var(--sk-bg, #0a0a0a)" },
138
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "max-w-5xl mx-auto px-6", children: [
139
+ (data.title || data.description) && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ScrollReveal.default, { y: 30, children: [
140
+ data.title && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
141
+ "h2",
142
+ {
143
+ className: "text-3xl md:text-4xl font-bold mb-4",
144
+ style: {
145
+ color: "var(--sk-text-primary, #ffffff)",
146
+ fontFamily: "var(--sk-font-heading, inherit)"
147
+ },
148
+ children: data.title
149
+ }
150
+ ),
151
+ data.description && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
152
+ "p",
153
+ {
154
+ className: "text-lg mb-12 max-w-2xl",
155
+ style: { color: "var(--sk-text-secondary, #a1a1aa)" },
156
+ children: data.description
157
+ }
158
+ )
159
+ ] }),
160
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ScrollReveal.default, { y: 40, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_GlassCard.default, { padding: "sm", hover: false, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
161
+ "div",
162
+ {
163
+ ref: containerRef,
164
+ role: "slider",
165
+ tabIndex: 0,
166
+ "aria-label": "Before and after comparison",
167
+ "aria-valuemin": 0,
168
+ "aria-valuemax": 100,
169
+ "aria-valuenow": Math.round(position),
170
+ "aria-valuetext": `${Math.round(position)}% before, ${100 - Math.round(position)}% after`,
171
+ className: "relative w-full overflow-hidden rounded-xl cursor-col-resize select-none",
172
+ style: { aspectRatio: "16/10" },
173
+ onPointerDown: handlePointerDown,
174
+ onPointerMove: handlePointerMove,
175
+ onPointerUp: handlePointerUp,
176
+ onPointerCancel: handlePointerUp,
177
+ onKeyDown: handleKeyDown,
178
+ children: [
179
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
180
+ import_image.default,
181
+ {
182
+ src: after.url,
183
+ alt: "After",
184
+ fill: true,
185
+ sizes: "(max-width: 1280px) 100vw, 1200px",
186
+ className: "object-cover",
187
+ draggable: false
188
+ }
189
+ ),
190
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
191
+ "div",
192
+ {
193
+ className: "absolute inset-0 overflow-hidden",
194
+ style: { width: `${position}%` },
195
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
196
+ import_image.default,
197
+ {
198
+ src: before.url,
199
+ alt: "Before",
200
+ fill: true,
201
+ sizes: "(max-width: 1280px) 100vw, 1200px",
202
+ className: "object-cover",
203
+ style: { minWidth: containerRef.current?.offsetWidth || "100%" },
204
+ draggable: false
205
+ }
206
+ )
207
+ }
208
+ ),
209
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
210
+ "div",
211
+ {
212
+ className: "absolute top-0 bottom-0 w-0.5",
213
+ style: {
214
+ left: `${position}%`,
215
+ transform: "translateX(-50%)",
216
+ background: "#ffffff",
217
+ boxShadow: "0 0 8px rgba(0,0,0,0.5)"
218
+ }
219
+ }
220
+ ),
221
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
222
+ "div",
223
+ {
224
+ className: "absolute top-1/2 flex items-center justify-center w-10 h-10 rounded-full",
225
+ style: {
226
+ left: `${position}%`,
227
+ transform: handleHidden ? "translate(-50%, -50%) scale(0.8)" : "translate(-50%, -50%) scale(1)",
228
+ opacity: handleHidden ? 0 : 1,
229
+ // Transition only on reveal so the hydration hide is instant;
230
+ // .6s back-out overshoot after a .5s beat, like the old entrance.
231
+ transition: handleHidden ? "none" : "opacity 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) 0.5s, transform 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) 0.5s",
232
+ background: "var(--sk-primary, #6366f1)",
233
+ boxShadow: "0 4px 16px rgba(0,0,0,0.3)",
234
+ border: "3px solid #ffffff",
235
+ zIndex: 5
236
+ },
237
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M5 3l-4 5 4 5M11 3l4 5-4 5", stroke: "#ffffff", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) })
238
+ }
239
+ ),
240
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
241
+ "span",
242
+ {
243
+ className: "absolute top-4 left-4 px-3 py-1 rounded-lg text-xs font-semibold uppercase tracking-wider",
244
+ style: {
245
+ background: "rgba(0,0,0,0.6)",
246
+ color: "#ffffff",
247
+ backdropFilter: "blur(4px)"
248
+ },
249
+ children: data.beforeLabel || "Before"
250
+ }
251
+ ),
252
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
253
+ "span",
254
+ {
255
+ className: "absolute top-4 right-4 px-3 py-1 rounded-lg text-xs font-semibold uppercase tracking-wider",
256
+ style: {
257
+ background: "rgba(0,0,0,0.6)",
258
+ color: "#ffffff",
259
+ backdropFilter: "blur(4px)"
260
+ },
261
+ children: data.afterLabel || "After"
262
+ }
263
+ )
264
+ ]
265
+ }
266
+ ) }) })
267
+ ] })
268
+ }
269
+ );
270
+ }
271
+ //# sourceMappingURL=BeforeAfterSection.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/portfolio/components/sections/BeforeAfterSection.tsx","../../../src/shared/images.ts"],"sourcesContent":["'use client';\n\nimport React, { useRef, useState, useCallback, useEffect } from 'react';\nimport Image from 'next/image';\nimport type { PortfolioBeforeAfterData } from '../../../types';\nimport { resolvePortfolioImage } from '../../../shared/images';\nimport ScrollReveal from '@sonordev/agency-site-kit/portfolio/primitives/ScrollReveal';\nimport GlassCard from '@sonordev/agency-site-kit/portfolio/primitives/GlassCard';\n\ninterface BeforeAfterSectionProps {\n data: PortfolioBeforeAfterData;\n}\n\nexport default function BeforeAfterSection({ data }: BeforeAfterSectionProps) {\n const containerRef = useRef<HTMLDivElement>(null);\n const [position, setPosition] = useState(data.defaultPosition ?? 50);\n const [handleHidden, setHandleHidden] = useState(false);\n const isDragging = useRef(false);\n\n // Support both direct URL strings and (API-resolved) Sanity image objects\n const before = resolvePortfolioImage((data as any).beforeUrl || data.before);\n const after = resolvePortfolioImage((data as any).afterUrl || data.after);\n\n // Slider-handle entrance (ScrollReveal pattern): SSR renders it visible,\n // hydration hides it only while the comparison is still below the viewport,\n // then an IntersectionObserver pops it back in via CSS transition.\n useEffect(() => {\n const el = containerRef.current;\n if (!el) return;\n if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;\n\n // Never re-hide a handle already in the viewport at hydration.\n const rect = el.getBoundingClientRect();\n if (rect.top < window.innerHeight && rect.bottom > 0) return;\n\n setHandleHidden(true);\n const io = new IntersectionObserver(([entry]) => {\n if (entry.isIntersecting) {\n setHandleHidden(false);\n io.disconnect();\n }\n });\n io.observe(el);\n return () => io.disconnect();\n }, []);\n\n const updatePosition = useCallback((clientX: number) => {\n const container = containerRef.current;\n if (!container) return;\n\n const rect = container.getBoundingClientRect();\n const x = clientX - rect.left;\n const percent = Math.max(0, Math.min(100, (x / rect.width) * 100));\n setPosition(percent);\n }, []);\n\n const handlePointerDown = useCallback(\n (e: React.PointerEvent) => {\n isDragging.current = true;\n (e.target as HTMLElement).setPointerCapture(e.pointerId);\n updatePosition(e.clientX);\n },\n [updatePosition],\n );\n\n const handlePointerMove = useCallback(\n (e: React.PointerEvent) => {\n if (!isDragging.current) return;\n updatePosition(e.clientX);\n },\n [updatePosition],\n );\n\n const handlePointerUp = useCallback(() => {\n isDragging.current = false;\n }, []);\n\n // Keyboard operation — arrows nudge, Home/End jump (WAI-ARIA slider)\n const handleKeyDown = useCallback((e: React.KeyboardEvent) => {\n const step = e.shiftKey ? 10 : 5;\n const nudge = (delta: number) => {\n e.preventDefault();\n setPosition((prev) => Math.max(0, Math.min(100, prev + delta)));\n };\n if (e.key === 'ArrowLeft' || e.key === 'ArrowDown') nudge(-step);\n else if (e.key === 'ArrowRight' || e.key === 'ArrowUp') nudge(step);\n else if (e.key === 'Home') nudge(-100);\n else if (e.key === 'End') nudge(100);\n }, []);\n\n // Without both images the comparison is meaningless — render nothing\n // rather than a frame with broken images.\n if (!before || !after) return null;\n\n return (\n <section\n className=\"w-full py-20 md:py-28\"\n style={{ background: 'var(--sk-bg, #0a0a0a)' }}\n >\n <div className=\"max-w-5xl mx-auto px-6\">\n {(data.title || data.description) && (\n <ScrollReveal y={30}>\n {data.title && (\n <h2\n className=\"text-3xl md:text-4xl font-bold mb-4\"\n style={{\n color: 'var(--sk-text-primary, #ffffff)',\n fontFamily: 'var(--sk-font-heading, inherit)',\n }}\n >\n {data.title}\n </h2>\n )}\n {data.description && (\n <p\n className=\"text-lg mb-12 max-w-2xl\"\n style={{ color: 'var(--sk-text-secondary, #a1a1aa)' }}\n >\n {data.description}\n </p>\n )}\n </ScrollReveal>\n )}\n\n <ScrollReveal y={40}>\n <GlassCard padding=\"sm\" hover={false}>\n <div\n ref={containerRef}\n role=\"slider\"\n tabIndex={0}\n aria-label=\"Before and after comparison\"\n aria-valuemin={0}\n aria-valuemax={100}\n aria-valuenow={Math.round(position)}\n aria-valuetext={`${Math.round(position)}% before, ${100 - Math.round(position)}% after`}\n className=\"relative w-full overflow-hidden rounded-xl cursor-col-resize select-none\"\n style={{ aspectRatio: '16/10' }}\n onPointerDown={handlePointerDown}\n onPointerMove={handlePointerMove}\n onPointerUp={handlePointerUp}\n onPointerCancel={handlePointerUp}\n onKeyDown={handleKeyDown}\n >\n {/* After image (full background) */}\n <Image\n src={after.url}\n alt=\"After\"\n fill\n sizes=\"(max-width: 1280px) 100vw, 1200px\"\n className=\"object-cover\"\n draggable={false}\n />\n\n {/* Before image (clipped) */}\n <div\n className=\"absolute inset-0 overflow-hidden\"\n style={{ width: `${position}%` }}\n >\n <Image\n src={before.url}\n alt=\"Before\"\n fill\n sizes=\"(max-width: 1280px) 100vw, 1200px\"\n className=\"object-cover\"\n style={{ minWidth: containerRef.current?.offsetWidth || '100%' }}\n draggable={false}\n />\n </div>\n\n {/* Slider line */}\n <div\n className=\"absolute top-0 bottom-0 w-0.5\"\n style={{\n left: `${position}%`,\n transform: 'translateX(-50%)',\n background: '#ffffff',\n boxShadow: '0 0 8px rgba(0,0,0,0.5)',\n }}\n />\n\n {/* Slider handle */}\n <div\n className=\"absolute top-1/2 flex items-center justify-center w-10 h-10 rounded-full\"\n style={{\n left: `${position}%`,\n transform: handleHidden\n ? 'translate(-50%, -50%) scale(0.8)'\n : 'translate(-50%, -50%) scale(1)',\n opacity: handleHidden ? 0 : 1,\n // Transition only on reveal so the hydration hide is instant;\n // .6s back-out overshoot after a .5s beat, like the old entrance.\n transition: handleHidden\n ? 'none'\n : 'opacity 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) 0.5s, transform 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) 0.5s',\n background: 'var(--sk-primary, #6366f1)',\n boxShadow: '0 4px 16px rgba(0,0,0,0.3)',\n border: '3px solid #ffffff',\n zIndex: 5,\n }}\n >\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\">\n <path d=\"M5 3l-4 5 4 5M11 3l4 5-4 5\" stroke=\"#ffffff\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n </div>\n\n {/* Before / After labels */}\n <span\n className=\"absolute top-4 left-4 px-3 py-1 rounded-lg text-xs font-semibold uppercase tracking-wider\"\n style={{\n background: 'rgba(0,0,0,0.6)',\n color: '#ffffff',\n backdropFilter: 'blur(4px)',\n }}\n >\n {(data as any).beforeLabel || 'Before'}\n </span>\n <span\n className=\"absolute top-4 right-4 px-3 py-1 rounded-lg text-xs font-semibold uppercase tracking-wider\"\n style={{\n background: 'rgba(0,0,0,0.6)',\n color: '#ffffff',\n backdropFilter: 'blur(4px)',\n }}\n >\n {(data as any).afterLabel || 'After'}\n </span>\n </div>\n </GlassCard>\n </ScrollReveal>\n </div>\n </section>\n );\n}\n","/**\n * Portfolio image resolution — single source of truth for turning the mixed\n * image shapes the API sends (plain URL strings, Supabase Storage URLs,\n * Sanity image objects) into something safe to render.\n *\n * IMPORTANT: a raw Sanity asset _ref (`image-{id}-{W}x{H}-{fmt}`) is an asset\n * ID, NOT a URL. Rendering it as `src` issues a garbage relative request\n * against the agency site's origin. Resolving refs to cdn.sanity.io URLs is\n * the API's job (it owns the Sanity project config); this helper refuses refs\n * so a broken image can never render, and warns once so the gap is visible.\n *\n * When a URL carries `-{W}x{H}.{ext}` dimensions (Sanity CDN URLs always do),\n * they're parsed out so callers can give next/image exact intrinsic sizes.\n */\n\nexport interface ResolvedPortfolioImage {\n url: string;\n width?: number;\n height?: number;\n}\n\n/** Raw Sanity asset reference — an ID, not a URL. */\nconst SANITY_ASSET_REF = /^image-[A-Za-z0-9]+-\\d+x\\d+-[a-z0-9]+$/;\n\n/** `…-1200x800.png` (optionally with a query string) → intrinsic dimensions. */\nconst FILENAME_DIMS = /-(\\d{2,5})x(\\d{2,5})\\.[a-z0-9]+(?:\\?.*)?$/i;\n\ninterface SanityImageLike {\n asset?: { _ref?: string };\n}\n\nlet warnedAboutRefs = false;\n\n/**\n * Resolve a portfolio image input into a renderable URL (+ intrinsic\n * dimensions when they can be derived). Returns null for empty values and\n * for unresolved Sanity refs — callers skip rendering in that case.\n */\nexport function resolvePortfolioImage(\n input: string | SanityImageLike | null | undefined,\n): ResolvedPortfolioImage | null {\n const raw =\n typeof input === 'string' ? input : input?.asset?._ref ?? '';\n const value = (raw || '').trim();\n if (!value) return null;\n\n if (SANITY_ASSET_REF.test(value)) {\n if (!warnedAboutRefs && typeof console !== 'undefined') {\n warnedAboutRefs = true;\n console.warn(\n '[@sonordev/agency-site-kit] Received a raw Sanity asset ref instead of an ' +\n 'image URL — skipping render. The Sonor API should resolve image refs to ' +\n 'cdn.sanity.io URLs before returning portfolio sections.',\n );\n }\n return null;\n }\n\n // Only absolute http(s) URLs or site-rooted paths are renderable.\n if (!/^https?:\\/\\//i.test(value) && !value.startsWith('/')) return null;\n\n const dims = value.match(FILENAME_DIMS);\n if (dims) {\n const width = parseInt(dims[1], 10);\n const height = parseInt(dims[2], 10);\n if (width > 0 && height > 0) return { url: value, width, height };\n }\n\n return { url: value };\n}\n"],"mappings":"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","names":["ScrollReveal","GlassCard","Image"]}
@@ -0,0 +1,9 @@
1
+ import React from 'react';
2
+ import { P as PortfolioBeforeAfterData } from '../../types-D0nb6KAx.cjs';
3
+
4
+ interface BeforeAfterSectionProps {
5
+ data: PortfolioBeforeAfterData;
6
+ }
7
+ declare function BeforeAfterSection({ data }: BeforeAfterSectionProps): React.JSX.Element | null;
8
+
9
+ export { BeforeAfterSection as default };
@@ -0,0 +1,9 @@
1
+ import React from 'react';
2
+ import { P as PortfolioBeforeAfterData } from '../../types-D0nb6KAx.js';
3
+
4
+ interface BeforeAfterSectionProps {
5
+ data: PortfolioBeforeAfterData;
6
+ }
7
+ declare function BeforeAfterSection({ data }: BeforeAfterSectionProps): React.JSX.Element | null;
8
+
9
+ export { BeforeAfterSection as default };