@sonordev/agency-site-kit 0.3.1 → 0.4.1

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 (374) 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-WNR4KNG4.cjs → chunk-5MEZU3OG.cjs} +4 -4
  6. package/dist/{chunk-WNR4KNG4.cjs.map → chunk-5MEZU3OG.cjs.map} +1 -1
  7. package/dist/chunk-7D4H36UF.js +13 -0
  8. package/dist/chunk-7D4H36UF.js.map +1 -0
  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-K2BGTDSN.js +45 -0
  16. package/dist/chunk-K2BGTDSN.js.map +1 -0
  17. package/dist/chunk-KXLROAO5.js +291 -0
  18. package/dist/chunk-KXLROAO5.js.map +1 -0
  19. package/dist/chunk-L4EOQVRJ.js +6 -0
  20. package/dist/chunk-L4EOQVRJ.js.map +1 -0
  21. package/dist/chunk-NI4FVCLS.cjs +32 -0
  22. package/dist/chunk-NI4FVCLS.cjs.map +1 -0
  23. package/dist/chunk-NLGUPJZA.js +507 -0
  24. package/dist/chunk-NLGUPJZA.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-QGSW43TM.cjs +538 -0
  28. package/dist/chunk-QGSW43TM.cjs.map +1 -0
  29. package/dist/{chunk-TVFXN6W6.js → chunk-QL6KVIFR.js} +8 -4
  30. package/dist/chunk-QL6KVIFR.js.map +1 -0
  31. package/dist/chunk-RWQ675N6.js +165 -0
  32. package/dist/chunk-RWQ675N6.js.map +1 -0
  33. package/dist/chunk-UXUU7FVT.cjs +171 -0
  34. package/dist/chunk-UXUU7FVT.cjs.map +1 -0
  35. package/dist/chunk-XJ434N7I.js +30 -0
  36. package/dist/chunk-XJ434N7I.js.map +1 -0
  37. package/dist/{chunk-EE75WRCM.cjs → chunk-XOWSXZYW.cjs} +8 -4
  38. package/dist/chunk-XOWSXZYW.cjs.map +1 -0
  39. package/dist/chunk-ZBPF7TLZ.cjs +16 -0
  40. package/dist/chunk-ZBPF7TLZ.cjs.map +1 -0
  41. package/dist/chunk-ZINKVJQU.js +67 -0
  42. package/dist/chunk-ZINKVJQU.js.map +1 -0
  43. package/dist/index.cjs +53 -30
  44. package/dist/index.cjs.map +1 -1
  45. package/dist/index.d.cts +17 -9
  46. package/dist/index.d.ts +17 -9
  47. package/dist/index.js +8 -9
  48. package/dist/index.js.map +1 -1
  49. package/dist/layout/index.cjs +3 -3
  50. package/dist/layout/index.d.cts +1 -1
  51. package/dist/layout/index.d.ts +1 -1
  52. package/dist/layout/index.js +2 -2
  53. package/dist/portfolio/client.cjs +376 -12
  54. package/dist/portfolio/client.cjs.map +1 -1
  55. package/dist/portfolio/client.d.cts +27 -85
  56. package/dist/portfolio/client.d.ts +27 -85
  57. package/dist/portfolio/client.js +182 -4
  58. package/dist/portfolio/client.js.map +1 -1
  59. package/dist/portfolio/components/PortfolioAnalytics.cjs +87 -0
  60. package/dist/portfolio/components/PortfolioAnalytics.cjs.map +1 -0
  61. package/dist/portfolio/components/PortfolioAnalytics.d.cts +5 -0
  62. package/dist/portfolio/components/PortfolioAnalytics.d.ts +5 -0
  63. package/dist/portfolio/components/PortfolioAnalytics.js +9 -0
  64. package/dist/portfolio/components/PortfolioAnalytics.js.map +1 -0
  65. package/dist/portfolio/components/PortfolioErrorFallback.cjs +65 -0
  66. package/dist/portfolio/components/PortfolioErrorFallback.cjs.map +1 -0
  67. package/dist/portfolio/components/PortfolioErrorFallback.d.cts +22 -0
  68. package/dist/portfolio/components/PortfolioErrorFallback.d.ts +22 -0
  69. package/dist/portfolio/components/PortfolioErrorFallback.js +9 -0
  70. package/dist/portfolio/components/PortfolioErrorFallback.js.map +1 -0
  71. package/dist/portfolio/components/PortfolioLeadCTA.cjs +91 -0
  72. package/dist/portfolio/components/PortfolioLeadCTA.cjs.map +1 -0
  73. package/dist/portfolio/components/PortfolioLeadCTA.d.cts +34 -0
  74. package/dist/portfolio/components/PortfolioLeadCTA.d.ts +34 -0
  75. package/dist/portfolio/components/PortfolioLeadCTA.js +9 -0
  76. package/dist/portfolio/components/PortfolioLeadCTA.js.map +1 -0
  77. package/dist/portfolio/index.cjs +43 -19
  78. package/dist/portfolio/index.d.cts +210 -7
  79. package/dist/portfolio/index.d.ts +210 -7
  80. package/dist/portfolio/index.js +7 -7
  81. package/dist/portfolio/primitives/AnimatedCounter.cjs +88 -0
  82. package/dist/portfolio/primitives/AnimatedCounter.cjs.map +1 -0
  83. package/dist/portfolio/primitives/AnimatedCounter.d.cts +22 -0
  84. package/dist/portfolio/primitives/AnimatedCounter.d.ts +22 -0
  85. package/dist/portfolio/primitives/AnimatedCounter.js +68 -0
  86. package/dist/portfolio/primitives/AnimatedCounter.js.map +1 -0
  87. package/dist/portfolio/primitives/AnnotatedImage.cjs +320 -0
  88. package/dist/portfolio/primitives/AnnotatedImage.cjs.map +1 -0
  89. package/dist/portfolio/primitives/AnnotatedImage.d.cts +18 -0
  90. package/dist/portfolio/primitives/AnnotatedImage.d.ts +18 -0
  91. package/dist/portfolio/primitives/AnnotatedImage.js +264 -0
  92. package/dist/portfolio/primitives/AnnotatedImage.js.map +1 -0
  93. package/dist/portfolio/primitives/BeforeAfterSlider.cjs +348 -0
  94. package/dist/portfolio/primitives/BeforeAfterSlider.cjs.map +1 -0
  95. package/dist/portfolio/primitives/BeforeAfterSlider.d.cts +13 -0
  96. package/dist/portfolio/primitives/BeforeAfterSlider.d.ts +13 -0
  97. package/dist/portfolio/primitives/BeforeAfterSlider.js +292 -0
  98. package/dist/portfolio/primitives/BeforeAfterSlider.js.map +1 -0
  99. package/dist/portfolio/primitives/DeviceTrifolio.cjs +298 -0
  100. package/dist/portfolio/primitives/DeviceTrifolio.cjs.map +1 -0
  101. package/dist/portfolio/primitives/DeviceTrifolio.d.cts +14 -0
  102. package/dist/portfolio/primitives/DeviceTrifolio.d.ts +14 -0
  103. package/dist/portfolio/primitives/DeviceTrifolio.js +242 -0
  104. package/dist/portfolio/primitives/DeviceTrifolio.js.map +1 -0
  105. package/dist/portfolio/primitives/FrequencyBarSkeleton.cjs +88 -0
  106. package/dist/portfolio/primitives/FrequencyBarSkeleton.cjs.map +1 -0
  107. package/dist/portfolio/primitives/FrequencyBarSkeleton.d.cts +10 -0
  108. package/dist/portfolio/primitives/FrequencyBarSkeleton.d.ts +10 -0
  109. package/dist/portfolio/primitives/FrequencyBarSkeleton.js +68 -0
  110. package/dist/portfolio/primitives/FrequencyBarSkeleton.js.map +1 -0
  111. package/dist/portfolio/primitives/GlassCard.cjs +45 -0
  112. package/dist/portfolio/primitives/GlassCard.cjs.map +1 -0
  113. package/dist/portfolio/primitives/GlassCard.d.cts +11 -0
  114. package/dist/portfolio/primitives/GlassCard.d.ts +11 -0
  115. package/dist/portfolio/primitives/GlassCard.js +43 -0
  116. package/dist/portfolio/primitives/GlassCard.js.map +1 -0
  117. package/dist/portfolio/primitives/MetricBadge.cjs +74 -0
  118. package/dist/portfolio/primitives/MetricBadge.cjs.map +1 -0
  119. package/dist/portfolio/primitives/MetricBadge.d.cts +9 -0
  120. package/dist/portfolio/primitives/MetricBadge.d.ts +9 -0
  121. package/dist/portfolio/primitives/MetricBadge.js +54 -0
  122. package/dist/portfolio/primitives/MetricBadge.js.map +1 -0
  123. package/dist/portfolio/primitives/SVGChart.cjs +316 -0
  124. package/dist/portfolio/primitives/SVGChart.cjs.map +1 -0
  125. package/dist/portfolio/primitives/SVGChart.d.cts +18 -0
  126. package/dist/portfolio/primitives/SVGChart.d.ts +18 -0
  127. package/dist/portfolio/primitives/SVGChart.js +296 -0
  128. package/dist/portfolio/primitives/SVGChart.js.map +1 -0
  129. package/dist/portfolio/primitives/ScrollReveal.cjs +97 -0
  130. package/dist/portfolio/primitives/ScrollReveal.cjs.map +1 -0
  131. package/dist/portfolio/primitives/ScrollReveal.d.cts +27 -0
  132. package/dist/portfolio/primitives/ScrollReveal.d.ts +27 -0
  133. package/dist/portfolio/primitives/ScrollReveal.js +77 -0
  134. package/dist/portfolio/primitives/ScrollReveal.js.map +1 -0
  135. package/dist/portfolio/primitives/SplitHeadline.cjs +107 -0
  136. package/dist/portfolio/primitives/SplitHeadline.cjs.map +1 -0
  137. package/dist/portfolio/primitives/SplitHeadline.d.cts +24 -0
  138. package/dist/portfolio/primitives/SplitHeadline.d.ts +24 -0
  139. package/dist/portfolio/primitives/SplitHeadline.js +87 -0
  140. package/dist/portfolio/primitives/SplitHeadline.js.map +1 -0
  141. package/dist/portfolio/sections/BeforeAfterSection.cjs +271 -0
  142. package/dist/portfolio/sections/BeforeAfterSection.cjs.map +1 -0
  143. package/dist/portfolio/sections/BeforeAfterSection.d.cts +9 -0
  144. package/dist/portfolio/sections/BeforeAfterSection.d.ts +9 -0
  145. package/dist/{BeforeAfterSection-6BHFLY4Y.js → portfolio/sections/BeforeAfterSection.js} +66 -27
  146. package/dist/portfolio/sections/BeforeAfterSection.js.map +1 -0
  147. package/dist/portfolio/sections/CTASection.cjs +123 -0
  148. package/dist/portfolio/sections/CTASection.cjs.map +1 -0
  149. package/dist/portfolio/sections/CTASection.d.cts +9 -0
  150. package/dist/portfolio/sections/CTASection.d.ts +9 -0
  151. package/dist/portfolio/sections/CTASection.js +93 -0
  152. package/dist/portfolio/sections/CTASection.js.map +1 -0
  153. package/dist/{ChallengesSection-IZ3DHECS.cjs → portfolio/sections/ChallengesSection.cjs} +13 -7
  154. package/dist/portfolio/sections/ChallengesSection.cjs.map +1 -0
  155. package/dist/portfolio/sections/ChallengesSection.d.cts +9 -0
  156. package/dist/portfolio/sections/ChallengesSection.d.ts +9 -0
  157. package/dist/{ChallengesSection-GEQGVSJN.js → portfolio/sections/ChallengesSection.js} +6 -5
  158. package/dist/portfolio/sections/ChallengesSection.js.map +1 -0
  159. package/dist/portfolio/sections/ConversionFunnelSection.cjs +263 -0
  160. package/dist/portfolio/sections/ConversionFunnelSection.cjs.map +1 -0
  161. package/dist/portfolio/sections/ConversionFunnelSection.d.cts +9 -0
  162. package/dist/portfolio/sections/ConversionFunnelSection.d.ts +9 -0
  163. package/dist/{ConversionFunnelSection-D3GE4NKE.js → portfolio/sections/ConversionFunnelSection.js} +60 -56
  164. package/dist/portfolio/sections/ConversionFunnelSection.js.map +1 -0
  165. package/dist/{DesignSystemSection-KXIQXITF.cjs → portfolio/sections/DesignSystemSection.cjs} +13 -7
  166. package/dist/portfolio/sections/DesignSystemSection.cjs.map +1 -0
  167. package/dist/portfolio/sections/DesignSystemSection.d.cts +32 -0
  168. package/dist/portfolio/sections/DesignSystemSection.d.ts +32 -0
  169. package/dist/{DesignSystemSection-2R5BRBGO.js → portfolio/sections/DesignSystemSection.js} +5 -4
  170. package/dist/portfolio/sections/DesignSystemSection.js.map +1 -0
  171. package/dist/{DetailsSection-TTUZAPZZ.cjs → portfolio/sections/DetailsSection.cjs} +12 -6
  172. package/dist/portfolio/sections/DetailsSection.cjs.map +1 -0
  173. package/dist/portfolio/sections/DetailsSection.d.cts +9 -0
  174. package/dist/portfolio/sections/DetailsSection.d.ts +9 -0
  175. package/dist/{DetailsSection-A6PZQUQL.js → portfolio/sections/DetailsSection.js} +5 -4
  176. package/dist/portfolio/sections/DetailsSection.js.map +1 -0
  177. package/dist/portfolio/sections/FeatureSpotlightSection.cjs +267 -0
  178. package/dist/portfolio/sections/FeatureSpotlightSection.cjs.map +1 -0
  179. package/dist/portfolio/sections/FeatureSpotlightSection.d.cts +9 -0
  180. package/dist/portfolio/sections/FeatureSpotlightSection.d.ts +9 -0
  181. package/dist/{FeatureSpotlightSection-B7P3JGNL.js → portfolio/sections/FeatureSpotlightSection.js} +39 -33
  182. package/dist/portfolio/sections/FeatureSpotlightSection.js.map +1 -0
  183. package/dist/portfolio/sections/GallerySection.cjs +321 -0
  184. package/dist/portfolio/sections/GallerySection.cjs.map +1 -0
  185. package/dist/portfolio/sections/GallerySection.d.cts +9 -0
  186. package/dist/portfolio/sections/GallerySection.d.ts +9 -0
  187. package/dist/{GallerySection-VMKORC47.js → portfolio/sections/GallerySection.js} +65 -18
  188. package/dist/portfolio/sections/GallerySection.js.map +1 -0
  189. package/dist/portfolio/sections/HeroSection.cjs +180 -0
  190. package/dist/portfolio/sections/HeroSection.cjs.map +1 -0
  191. package/dist/portfolio/sections/HeroSection.d.cts +15 -0
  192. package/dist/portfolio/sections/HeroSection.d.ts +15 -0
  193. package/dist/portfolio/sections/HeroSection.js +171 -0
  194. package/dist/portfolio/sections/HeroSection.js.map +1 -0
  195. package/dist/{MetricsTimelineSection-4L6DUHJ5.cjs → portfolio/sections/MetricsTimelineSection.cjs} +102 -61
  196. package/dist/portfolio/sections/MetricsTimelineSection.cjs.map +1 -0
  197. package/dist/portfolio/sections/MetricsTimelineSection.d.cts +9 -0
  198. package/dist/portfolio/sections/MetricsTimelineSection.d.ts +9 -0
  199. package/dist/{MetricsTimelineSection-6BT5GNFV.js → portfolio/sections/MetricsTimelineSection.js} +45 -29
  200. package/dist/portfolio/sections/MetricsTimelineSection.js.map +1 -0
  201. package/dist/portfolio/sections/PerformanceSection.cjs +336 -0
  202. package/dist/portfolio/sections/PerformanceSection.cjs.map +1 -0
  203. package/dist/portfolio/sections/PerformanceSection.d.cts +22 -0
  204. package/dist/portfolio/sections/PerformanceSection.d.ts +22 -0
  205. package/dist/{PerformanceSection-MGCEIXDX.js → portfolio/sections/PerformanceSection.js} +59 -104
  206. package/dist/portfolio/sections/PerformanceSection.js.map +1 -0
  207. package/dist/{ResultsSection-XLGMMQKY.cjs → portfolio/sections/ResultsSection.cjs} +23 -10
  208. package/dist/portfolio/sections/ResultsSection.cjs.map +1 -0
  209. package/dist/portfolio/sections/ResultsSection.d.cts +13 -0
  210. package/dist/portfolio/sections/ResultsSection.d.ts +13 -0
  211. package/dist/{ResultsSection-DFUJ5U6M.js → portfolio/sections/ResultsSection.js} +14 -7
  212. package/dist/portfolio/sections/ResultsSection.js.map +1 -0
  213. package/dist/portfolio/sections/ServicesSection.cjs +129 -0
  214. package/dist/portfolio/sections/ServicesSection.cjs.map +1 -0
  215. package/dist/portfolio/sections/ServicesSection.d.cts +9 -0
  216. package/dist/portfolio/sections/ServicesSection.d.ts +9 -0
  217. package/dist/portfolio/sections/ServicesSection.js +122 -0
  218. package/dist/portfolio/sections/ServicesSection.js.map +1 -0
  219. package/dist/{SiteArchitectureSection-J7BPBERH.cjs → portfolio/sections/SiteArchitectureSection.cjs} +117 -93
  220. package/dist/portfolio/sections/SiteArchitectureSection.cjs.map +1 -0
  221. package/dist/portfolio/sections/SiteArchitectureSection.d.cts +21 -0
  222. package/dist/portfolio/sections/SiteArchitectureSection.d.ts +21 -0
  223. package/dist/{SiteArchitectureSection-TE22OAWV.js → portfolio/sections/SiteArchitectureSection.js} +47 -48
  224. package/dist/portfolio/sections/SiteArchitectureSection.js.map +1 -0
  225. package/dist/{SpeedComparisonSection-EZKFQVGW.cjs → portfolio/sections/SpeedComparisonSection.cjs} +13 -7
  226. package/dist/portfolio/sections/SpeedComparisonSection.cjs.map +1 -0
  227. package/dist/portfolio/sections/SpeedComparisonSection.d.cts +20 -0
  228. package/dist/portfolio/sections/SpeedComparisonSection.d.ts +20 -0
  229. package/dist/{SpeedComparisonSection-Y3K7OFZQ.js → portfolio/sections/SpeedComparisonSection.js} +6 -5
  230. package/dist/portfolio/sections/SpeedComparisonSection.js.map +1 -0
  231. package/dist/portfolio/sections/StrategySection.cjs +233 -0
  232. package/dist/portfolio/sections/StrategySection.cjs.map +1 -0
  233. package/dist/portfolio/sections/StrategySection.d.cts +9 -0
  234. package/dist/portfolio/sections/StrategySection.d.ts +9 -0
  235. package/dist/{StrategySection-DI5RSCJU.js → portfolio/sections/StrategySection.js} +60 -26
  236. package/dist/portfolio/sections/StrategySection.js.map +1 -0
  237. package/dist/portfolio/sections/TeamSection.cjs +162 -0
  238. package/dist/portfolio/sections/TeamSection.cjs.map +1 -0
  239. package/dist/portfolio/sections/TeamSection.d.cts +9 -0
  240. package/dist/portfolio/sections/TeamSection.d.ts +9 -0
  241. package/dist/portfolio/sections/TeamSection.js +106 -0
  242. package/dist/portfolio/sections/TeamSection.js.map +1 -0
  243. package/dist/portfolio/sections/TechStackSection.cjs +106 -0
  244. package/dist/portfolio/sections/TechStackSection.cjs.map +1 -0
  245. package/dist/portfolio/sections/TechStackSection.d.cts +9 -0
  246. package/dist/portfolio/sections/TechStackSection.d.ts +9 -0
  247. package/dist/portfolio/sections/TechStackSection.js +99 -0
  248. package/dist/portfolio/sections/TechStackSection.js.map +1 -0
  249. package/dist/{TestimonialSection-XPTFUQIN.cjs → portfolio/sections/TestimonialSection.cjs} +47 -28
  250. package/dist/portfolio/sections/TestimonialSection.cjs.map +1 -0
  251. package/dist/portfolio/sections/TestimonialSection.d.cts +9 -0
  252. package/dist/portfolio/sections/TestimonialSection.d.ts +9 -0
  253. package/dist/{TestimonialSection-6RGSMXQB.js → portfolio/sections/TestimonialSection.js} +40 -27
  254. package/dist/portfolio/sections/TestimonialSection.js.map +1 -0
  255. package/dist/portfolio/sections/VideoSection.cjs +181 -0
  256. package/dist/portfolio/sections/VideoSection.cjs.map +1 -0
  257. package/dist/portfolio/sections/VideoSection.d.cts +9 -0
  258. package/dist/portfolio/sections/VideoSection.d.ts +9 -0
  259. package/dist/{VideoSection-4A2HC6K6.js → portfolio/sections/VideoSection.js} +21 -13
  260. package/dist/portfolio/sections/VideoSection.js.map +1 -0
  261. package/dist/portfolio/server.cjs +56 -144
  262. package/dist/portfolio/server.cjs.map +1 -1
  263. package/dist/portfolio/server.d.cts +157 -7
  264. package/dist/portfolio/server.d.ts +157 -7
  265. package/dist/portfolio/server.js +3 -142
  266. package/dist/portfolio/server.js.map +1 -1
  267. package/dist/preview.cjs +56 -0
  268. package/dist/preview.cjs.map +1 -0
  269. package/dist/preview.d.cts +31 -0
  270. package/dist/preview.d.ts +31 -0
  271. package/dist/preview.js +54 -0
  272. package/dist/preview.js.map +1 -0
  273. package/dist/{types-DL4t_Cfa.d.ts → types-0tVPy4pU.d.cts} +7 -1
  274. package/dist/{types-DL4t_Cfa.d.cts → types-0tVPy4pU.d.ts} +7 -1
  275. package/dist/types-D0nb6KAx.d.cts +299 -0
  276. package/dist/types-D0nb6KAx.d.ts +299 -0
  277. package/package.json +38 -14
  278. package/templates/api-portfolio-preview.ts +6 -0
  279. package/templates/api-revalidate.ts +5 -0
  280. package/templates/work-category-page.tsx +40 -0
  281. package/templates/work-error.tsx +3 -0
  282. package/templates/work-loading.tsx +1 -0
  283. package/templates/work-page.tsx +37 -0
  284. package/templates/work-slug-loading.tsx +1 -0
  285. package/templates/work-slug-page.tsx +41 -0
  286. package/dist/BeforeAfterSection-6BHFLY4Y.js.map +0 -1
  287. package/dist/BeforeAfterSection-JTORBR3A.cjs +0 -181
  288. package/dist/BeforeAfterSection-JTORBR3A.cjs.map +0 -1
  289. package/dist/CTASection-4JKLXEUF.cjs +0 -111
  290. package/dist/CTASection-4JKLXEUF.cjs.map +0 -1
  291. package/dist/CTASection-BJA72XIL.js +0 -106
  292. package/dist/CTASection-BJA72XIL.js.map +0 -1
  293. package/dist/ChallengesSection-GEQGVSJN.js.map +0 -1
  294. package/dist/ChallengesSection-IZ3DHECS.cjs.map +0 -1
  295. package/dist/ConversionFunnelSection-AUUSJ5HQ.cjs +0 -209
  296. package/dist/ConversionFunnelSection-AUUSJ5HQ.cjs.map +0 -1
  297. package/dist/ConversionFunnelSection-D3GE4NKE.js.map +0 -1
  298. package/dist/DesignSystemSection-2R5BRBGO.js.map +0 -1
  299. package/dist/DesignSystemSection-KXIQXITF.cjs.map +0 -1
  300. package/dist/DetailsSection-A6PZQUQL.js.map +0 -1
  301. package/dist/DetailsSection-TTUZAPZZ.cjs.map +0 -1
  302. package/dist/FeatureSpotlightSection-B7P3JGNL.js.map +0 -1
  303. package/dist/FeatureSpotlightSection-WRHXS7TU.cjs +0 -210
  304. package/dist/FeatureSpotlightSection-WRHXS7TU.cjs.map +0 -1
  305. package/dist/GallerySection-VMKORC47.js.map +0 -1
  306. package/dist/GallerySection-WJ4PQDBI.cjs +0 -219
  307. package/dist/GallerySection-WJ4PQDBI.cjs.map +0 -1
  308. package/dist/MetricsTimelineSection-4L6DUHJ5.cjs.map +0 -1
  309. package/dist/MetricsTimelineSection-6BT5GNFV.js.map +0 -1
  310. package/dist/PerformanceSection-24TVVFZA.cjs +0 -356
  311. package/dist/PerformanceSection-24TVVFZA.cjs.map +0 -1
  312. package/dist/PerformanceSection-MGCEIXDX.js.map +0 -1
  313. package/dist/ResultsSection-DFUJ5U6M.js.map +0 -1
  314. package/dist/ResultsSection-XLGMMQKY.cjs.map +0 -1
  315. package/dist/ServicesSection-D5V3Q4GR.js +0 -118
  316. package/dist/ServicesSection-D5V3Q4GR.js.map +0 -1
  317. package/dist/ServicesSection-WJMGK2MF.cjs +0 -120
  318. package/dist/ServicesSection-WJMGK2MF.cjs.map +0 -1
  319. package/dist/SiteArchitectureSection-J7BPBERH.cjs.map +0 -1
  320. package/dist/SiteArchitectureSection-TE22OAWV.js.map +0 -1
  321. package/dist/SpeedComparisonSection-EZKFQVGW.cjs.map +0 -1
  322. package/dist/SpeedComparisonSection-Y3K7OFZQ.js.map +0 -1
  323. package/dist/StrategySection-CJ7Y6OFQ.cjs +0 -174
  324. package/dist/StrategySection-CJ7Y6OFQ.cjs.map +0 -1
  325. package/dist/StrategySection-DI5RSCJU.js.map +0 -1
  326. package/dist/TeamSection-DZVSNZE6.cjs +0 -112
  327. package/dist/TeamSection-DZVSNZE6.cjs.map +0 -1
  328. package/dist/TeamSection-HGKFW6PQ.js +0 -107
  329. package/dist/TeamSection-HGKFW6PQ.js.map +0 -1
  330. package/dist/TechStackSection-2AQ7RGY3.js +0 -93
  331. package/dist/TechStackSection-2AQ7RGY3.js.map +0 -1
  332. package/dist/TechStackSection-VTNNZR5V.cjs +0 -95
  333. package/dist/TechStackSection-VTNNZR5V.cjs.map +0 -1
  334. package/dist/TestimonialSection-6RGSMXQB.js.map +0 -1
  335. package/dist/TestimonialSection-XPTFUQIN.cjs.map +0 -1
  336. package/dist/VideoSection-4A2HC6K6.js.map +0 -1
  337. package/dist/VideoSection-G3DFS7UH.cjs +0 -118
  338. package/dist/VideoSection-G3DFS7UH.cjs.map +0 -1
  339. package/dist/chunk-2Y4O3LWM.js +0 -53
  340. package/dist/chunk-2Y4O3LWM.js.map +0 -1
  341. package/dist/chunk-633UMQQH.cjs +0 -31
  342. package/dist/chunk-633UMQQH.cjs.map +0 -1
  343. package/dist/chunk-7CFFAKDM.js +0 -74
  344. package/dist/chunk-7CFFAKDM.js.map +0 -1
  345. package/dist/chunk-APG2QSMB.js +0 -131
  346. package/dist/chunk-APG2QSMB.js.map +0 -1
  347. package/dist/chunk-AVCH4LMG.cjs +0 -620
  348. package/dist/chunk-AVCH4LMG.cjs.map +0 -1
  349. package/dist/chunk-AYBUHRFS.js +0 -613
  350. package/dist/chunk-AYBUHRFS.js.map +0 -1
  351. package/dist/chunk-EE75WRCM.cjs.map +0 -1
  352. package/dist/chunk-IKBK7HYX.cjs +0 -79
  353. package/dist/chunk-IKBK7HYX.cjs.map +0 -1
  354. package/dist/chunk-KEOHORIH.cjs +0 -79
  355. package/dist/chunk-KEOHORIH.cjs.map +0 -1
  356. package/dist/chunk-OA5ZM4OA.cjs +0 -139
  357. package/dist/chunk-OA5ZM4OA.cjs.map +0 -1
  358. package/dist/chunk-TVFXN6W6.js.map +0 -1
  359. package/dist/chunk-VVTK4OL6.js +0 -28
  360. package/dist/chunk-VVTK4OL6.js.map +0 -1
  361. package/dist/chunk-XCKXHK44.js +0 -15
  362. package/dist/chunk-XCKXHK44.js.map +0 -1
  363. package/dist/chunk-XONXEFJY.cjs +0 -58
  364. package/dist/chunk-XONXEFJY.cjs.map +0 -1
  365. package/dist/chunk-XQNJED46.cjs +0 -19
  366. package/dist/chunk-XQNJED46.cjs.map +0 -1
  367. package/dist/chunk-YB4B3OMC.js +0 -74
  368. package/dist/chunk-YB4B3OMC.js.map +0 -1
  369. package/dist/portfolio/sections.cjs +0 -20
  370. package/dist/portfolio/sections.cjs.map +0 -1
  371. package/dist/portfolio/sections.d.cts +0 -42
  372. package/dist/portfolio/sections.d.ts +0 -42
  373. package/dist/portfolio/sections.js +0 -4
  374. package/dist/portfolio/sections.js.map +0 -1
@@ -1,145 +1,6 @@
1
- import { sanitizePortfolioSections } from '../chunk-VVTK4OL6.js';
2
- import { apiFetch } from '../chunk-TVFXN6W6.js';
3
- import { PORTFOLIO_CACHE_TAG } from '../chunk-N7TVKUYQ.js';
1
+ export { PortfolioApiError, PortfolioContractError, assertPortfolioItemShape, generatePortfolioCategoryStaticParams, generatePortfolioIndexMetadata, generatePortfolioMetadata, generatePortfolioSitemapEntries, generatePortfolioStaticParams, getPortfolioBrandConfig, getPortfolioCategories, getPortfolioItem, getPortfolioItems, slugifyCategory } from '../chunk-KXLROAO5.js';
4
2
  export { PORTFOLIO_CACHE_TAG } from '../chunk-N7TVKUYQ.js';
5
-
6
- // src/portfolio/server.ts
7
- var LOG_PREFIX = "[@sonordev/agency-site-kit/portfolio]";
8
- function portfolioGet(path) {
9
- return apiFetch(path, { method: "GET", tags: [PORTFOLIO_CACHE_TAG] });
10
- }
11
- function buildQuery(params) {
12
- const entries = Object.entries(params).filter(
13
- (entry) => entry[1] != null
14
- );
15
- if (entries.length === 0) return "";
16
- const qs = new URLSearchParams(
17
- entries.map(([k, v]) => [k, String(v)])
18
- ).toString();
19
- return `?${qs}`;
20
- }
21
- async function getPortfolioItems(options) {
22
- const fallback = { items: [], total: 0, limit: 0, offset: 0 };
23
- const query = buildQuery({
24
- category: options?.category,
25
- featured: options?.featured,
26
- limit: options?.limit,
27
- offset: options?.offset
28
- });
29
- const data = await portfolioGet(
30
- `/api/public/portfolio/items${query}`
31
- );
32
- if (!data) {
33
- console.warn(`${LOG_PREFIX} Failed to fetch portfolio items.`);
34
- return fallback;
35
- }
36
- return data;
37
- }
38
- async function getPortfolioItem(slug) {
39
- if (!slug) {
40
- console.warn(`${LOG_PREFIX} getPortfolioItem called with empty slug.`);
41
- return null;
42
- }
43
- const data = await portfolioGet(
44
- `/api/public/portfolio/items/${encodeURIComponent(slug)}`
45
- );
46
- if (!data || !data.found || !data.item) {
47
- console.warn(`${LOG_PREFIX} Portfolio item not found for slug: "${slug}".`);
48
- return null;
49
- }
50
- const { item } = data;
51
- return {
52
- id: item.id,
53
- slug: item.slug,
54
- title: item.title,
55
- subtitle: item.subtitle,
56
- category: item.category,
57
- services: item.services,
58
- description: item.description,
59
- hero_image: item.hero_image,
60
- hero_image_alt: item.hero_image_alt,
61
- live_url: item.live_url,
62
- project_id: item.project_id ?? null,
63
- kpis: item.kpis,
64
- details: item.details,
65
- seo: item.seo,
66
- featured: item.featured,
67
- order: item.order,
68
- published_at: item.published_at,
69
- hero_screenshots: item.hero_screenshots,
70
- baseline_metrics: item.baseline_metrics,
71
- current_metrics: item.current_metrics,
72
- metrics_last_refreshed_at: item.metrics_last_refreshed_at,
73
- // Sanitized HERE because this is the last genuinely-server-side hop:
74
- // PortfolioPage compiles to a client component (tsup 'use client'
75
- // banner), so raw client-site paths in sections would serialize into
76
- // the RSC payload and get crawled against the agency site (GSC 404s).
77
- sections: sanitizePortfolioSections(item.sections ?? []),
78
- metricsDelta: item.metricsDelta ?? []
79
- };
80
- }
81
- async function getPortfolioCategories() {
82
- const data = await portfolioGet("/api/public/portfolio/categories");
83
- if (!data) {
84
- console.warn(`${LOG_PREFIX} Failed to fetch portfolio categories.`);
85
- return [];
86
- }
87
- return data;
88
- }
89
- async function getPortfolioBrandConfig() {
90
- const fallback = {
91
- primary: "#6366f1",
92
- secondary: "#8b5cf6",
93
- background: "#0a0a0f",
94
- backgroundElevated: "#12121a",
95
- surface: "#1a1a2e",
96
- surfaceHover: "#22223a",
97
- surfaceBorder: "#2a2a3e",
98
- textPrimary: "#f0f0f5",
99
- textSecondary: "#a0a0b5",
100
- textTertiary: "#6b6b80",
101
- fontHeading: "Inter, system-ui, sans-serif",
102
- fontBody: "Inter, system-ui, sans-serif"
103
- };
104
- const data = await portfolioGet(
105
- "/api/public/portfolio/config"
106
- );
107
- if (!data || !data.brand) {
108
- console.warn(`${LOG_PREFIX} Failed to fetch brand config, using defaults.`);
109
- return fallback;
110
- }
111
- return data.brand;
112
- }
113
- async function generatePortfolioMetadata(slug) {
114
- const item = await getPortfolioItem(slug);
115
- if (!item) {
116
- return {};
117
- }
118
- const seo = item.seo;
119
- const title = seo?.metaTitle || item.title;
120
- const description = seo?.metaDescription || item.description;
121
- const keywords = seo?.keywords ?? [];
122
- const ogImages = [];
123
- if (item.hero_image) {
124
- ogImages.push(item.hero_image);
125
- }
126
- return {
127
- title,
128
- description,
129
- keywords: keywords.length > 0 ? keywords : void 0,
130
- openGraph: {
131
- title,
132
- description,
133
- images: ogImages.length > 0 ? ogImages : void 0,
134
- type: "article"
135
- }
136
- };
137
- }
138
- async function generatePortfolioStaticParams() {
139
- const data = await getPortfolioItems({ limit: 500, offset: 0 });
140
- return data.items.map((item) => ({ slug: item.slug }));
141
- }
142
-
143
- export { generatePortfolioMetadata, generatePortfolioStaticParams, getPortfolioBrandConfig, getPortfolioCategories, getPortfolioItem, getPortfolioItems };
3
+ import '../chunk-L4EOQVRJ.js';
4
+ import '../chunk-QL6KVIFR.js';
144
5
  //# sourceMappingURL=server.js.map
145
6
  //# sourceMappingURL=server.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/portfolio/server.ts"],"names":[],"mappings":"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","file":"server.js","sourcesContent":["/**\n * @sonordev/agency-site-kit — Portfolio server-side data fetching\n *\n * RSC (React Server Component) data layer for portfolio pages.\n * All functions are async and designed for use in Next.js App Router\n * server components, generateMetadata(), and generateStaticParams().\n *\n * This file is server-only — no 'use client' directive.\n */\n\nimport { apiFetch } from '../shared/api';\nimport { PORTFOLIO_CACHE_TAG } from '../shared/cache';\nimport { sanitizePortfolioSections } from './sanitize';\nimport type {\n BrandConfig,\n MetricsDelta,\n PortfolioConfigResponse,\n PortfolioItemFull,\n PortfolioListResponse,\n PortfolioSeoData,\n PortfolioSection,\n} from '../types';\n\nconst LOG_PREFIX = '[@sonordev/agency-site-kit/portfolio]';\n\n// Re-export so consumers and route handlers can reference the same tag.\nexport { PORTFOLIO_CACHE_TAG } from '../shared/cache';\n\n/**\n * GET against the public portfolio API with the shared portfolio cache tag,\n * so the Portal's revalidation webhook can invalidate every portfolio fetch\n * (list, detail, categories, config, sitemap data) via one revalidateTag.\n */\nfunction portfolioGet<T>(path: string): Promise<T | null> {\n return apiFetch<T>(path, { method: 'GET', tags: [PORTFOLIO_CACHE_TAG] });\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Builds a query string from an object, omitting undefined/null values.\n */\nfunction buildQuery(params: Record<string, string | number | boolean | undefined | null>): string {\n const entries = Object.entries(params).filter(\n (entry): entry is [string, string | number | boolean] => entry[1] != null,\n );\n if (entries.length === 0) return '';\n const qs = new URLSearchParams(\n entries.map(([k, v]) => [k, String(v)]),\n ).toString();\n return `?${qs}`;\n}\n\n// ---------------------------------------------------------------------------\n// Public API — List items\n// ---------------------------------------------------------------------------\n\n/**\n * Fetches published portfolio items from the public API.\n * Cached via Next.js ISR (revalidate: 3600).\n */\nexport async function getPortfolioItems(options?: {\n category?: string;\n featured?: boolean;\n limit?: number;\n offset?: number;\n}): Promise<PortfolioListResponse> {\n const fallback: PortfolioListResponse = { items: [], total: 0, limit: 0, offset: 0 };\n\n const query = buildQuery({\n category: options?.category,\n featured: options?.featured,\n limit: options?.limit,\n offset: options?.offset,\n });\n\n const data = await portfolioGet<PortfolioListResponse>(\n `/api/public/portfolio/items${query}`,\n );\n\n if (!data) {\n console.warn(`${LOG_PREFIX} Failed to fetch portfolio items.`);\n return fallback;\n }\n\n return data;\n}\n\n// ---------------------------------------------------------------------------\n// Public API — Single item\n// ---------------------------------------------------------------------------\n\n/** Shape returned by the single-item API endpoint. */\ninterface PortfolioItemApiResponse {\n found: boolean;\n item: {\n id: string;\n slug: string;\n title: string;\n subtitle: string;\n category: string;\n services: string[];\n description: string;\n hero_image: string;\n hero_image_alt: string;\n live_url: string | null;\n project_id: string | null;\n kpis: PortfolioItemFull['kpis'];\n details: PortfolioItemFull['details'];\n seo: PortfolioItemFull['seo'];\n featured: boolean;\n order?: number;\n published_at: string | null;\n hero_screenshots: PortfolioItemFull['hero_screenshots'];\n baseline_metrics: PortfolioItemFull['baseline_metrics'];\n current_metrics: PortfolioItemFull['current_metrics'];\n metrics_last_refreshed_at: string | null;\n sections: PortfolioSection[];\n metricsDelta: MetricsDelta[];\n };\n}\n\n/**\n * Fetches a single portfolio item by slug, including full Sanity sections\n * and live metrics delta. This is the main data function for portfolio detail pages.\n *\n * Returns null when the item is not found or the API call fails.\n */\nexport async function getPortfolioItem(slug: string): Promise<PortfolioItemFull | null> {\n if (!slug) {\n console.warn(`${LOG_PREFIX} getPortfolioItem called with empty slug.`);\n return null;\n }\n\n const data = await portfolioGet<PortfolioItemApiResponse>(\n `/api/public/portfolio/items/${encodeURIComponent(slug)}`,\n );\n\n if (!data || !data.found || !data.item) {\n console.warn(`${LOG_PREFIX} Portfolio item not found for slug: \"${slug}\".`);\n return null;\n }\n\n // Map the API response to the PortfolioItemFull type\n const { item } = data;\n return {\n id: item.id,\n slug: item.slug,\n title: item.title,\n subtitle: item.subtitle,\n category: item.category,\n services: item.services,\n description: item.description,\n hero_image: item.hero_image,\n hero_image_alt: item.hero_image_alt,\n live_url: item.live_url,\n project_id: item.project_id ?? null,\n kpis: item.kpis,\n details: item.details,\n seo: item.seo,\n featured: item.featured,\n order: item.order,\n published_at: item.published_at,\n hero_screenshots: item.hero_screenshots,\n baseline_metrics: item.baseline_metrics,\n current_metrics: item.current_metrics,\n metrics_last_refreshed_at: item.metrics_last_refreshed_at,\n // Sanitized HERE because this is the last genuinely-server-side hop:\n // PortfolioPage compiles to a client component (tsup 'use client'\n // banner), so raw client-site paths in sections would serialize into\n // the RSC payload and get crawled against the agency site (GSC 404s).\n sections: sanitizePortfolioSections(item.sections ?? []),\n metricsDelta: item.metricsDelta ?? [],\n };\n}\n\n// ---------------------------------------------------------------------------\n// Public API — Categories\n// ---------------------------------------------------------------------------\n\n/**\n * Fetches available portfolio categories.\n * Returns an empty array on failure.\n */\nexport async function getPortfolioCategories(): Promise<string[]> {\n const data = await portfolioGet<string[]>('/api/public/portfolio/categories');\n\n if (!data) {\n console.warn(`${LOG_PREFIX} Failed to fetch portfolio categories.`);\n return [];\n }\n\n return data;\n}\n\n// ---------------------------------------------------------------------------\n// Public API — Brand config\n// ---------------------------------------------------------------------------\n\n/**\n * Fetches brand configuration for the agency (colors, fonts, logo).\n * Returns sensible defaults when the API call fails.\n */\nexport async function getPortfolioBrandConfig(): Promise<BrandConfig> {\n const fallback: BrandConfig = {\n primary: '#6366f1',\n secondary: '#8b5cf6',\n background: '#0a0a0f',\n backgroundElevated: '#12121a',\n surface: '#1a1a2e',\n surfaceHover: '#22223a',\n surfaceBorder: '#2a2a3e',\n textPrimary: '#f0f0f5',\n textSecondary: '#a0a0b5',\n textTertiary: '#6b6b80',\n fontHeading: 'Inter, system-ui, sans-serif',\n fontBody: 'Inter, system-ui, sans-serif',\n };\n\n const data = await portfolioGet<PortfolioConfigResponse>(\n '/api/public/portfolio/config',\n );\n\n if (!data || !data.brand) {\n console.warn(`${LOG_PREFIX} Failed to fetch brand config, using defaults.`);\n return fallback;\n }\n\n return data.brand;\n}\n\n// ---------------------------------------------------------------------------\n// Next.js Metadata helper\n// ---------------------------------------------------------------------------\n\n/**\n * Generates Next.js Metadata for a portfolio item page.\n * For use in generateMetadata() in the App Router.\n *\n * Returns an empty object when the item cannot be found, which is safe\n * to spread into the metadata return value.\n */\nexport async function generatePortfolioMetadata(slug: string): Promise<{\n title?: string;\n description?: string;\n keywords?: string[];\n openGraph?: {\n title?: string;\n description?: string;\n images?: string[];\n type?: string;\n };\n}> {\n const item = await getPortfolioItem(slug);\n\n if (!item) {\n return {};\n }\n\n // Prefer dedicated SEO data if available, fall back to item fields\n const seo: PortfolioSeoData | null = item.seo;\n const title = seo?.metaTitle || item.title;\n const description = seo?.metaDescription || item.description;\n const keywords = seo?.keywords ?? [];\n\n // Collect OG images: prefer SEO og image, then hero image\n const ogImages: string[] = [];\n if (item.hero_image) {\n ogImages.push(item.hero_image);\n }\n\n return {\n title,\n description,\n keywords: keywords.length > 0 ? keywords : undefined,\n openGraph: {\n title,\n description,\n images: ogImages.length > 0 ? ogImages : undefined,\n type: 'article',\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Next.js Static Params helper\n// ---------------------------------------------------------------------------\n\n/**\n * Generates static params for portfolio pages.\n * For use in generateStaticParams() in the App Router.\n *\n * Fetches all published portfolio items and returns their slugs.\n */\nexport async function generatePortfolioStaticParams(): Promise<Array<{ slug: string }>> {\n // Fetch a large batch to cover all published items\n const data = await getPortfolioItems({ limit: 500, offset: 0 });\n\n return data.items.map((item) => ({ slug: item.slug }));\n}\n"]}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"server.js"}
@@ -0,0 +1,56 @@
1
+ 'use strict';
2
+
3
+ var chunkHQGAX2LI_cjs = require('./chunk-HQGAX2LI.cjs');
4
+ var chunkXOWSXZYW_cjs = require('./chunk-XOWSXZYW.cjs');
5
+ var headers = require('next/headers');
6
+ var server = require('next/server');
7
+
8
+ function createPortfolioPreviewHandler(options = {}) {
9
+ const basePath = options.basePath ?? "/work";
10
+ return {
11
+ async GET(request) {
12
+ const url = new URL(request.url);
13
+ const slug = url.searchParams.get("slug") ?? "";
14
+ const token = url.searchParams.get("token") ?? "";
15
+ const exit = url.searchParams.get("exit");
16
+ const dm = await headers.draftMode();
17
+ if (exit) {
18
+ dm.disable();
19
+ const jar2 = await headers.cookies();
20
+ jar2.delete(chunkHQGAX2LI_cjs.PORTFOLIO_PREVIEW_COOKIE);
21
+ return server.NextResponse.redirect(new URL(basePath, url.origin));
22
+ }
23
+ if (!slug || !token) {
24
+ return server.NextResponse.json(
25
+ { error: "Missing slug or token" },
26
+ { status: 400 }
27
+ );
28
+ }
29
+ const data = await chunkXOWSXZYW_cjs.apiFetch(
30
+ `/api/public/portfolio/items/${encodeURIComponent(slug)}?preview=${encodeURIComponent(token)}`,
31
+ { method: "GET", revalidate: 0 }
32
+ );
33
+ if (!data?.found) {
34
+ return server.NextResponse.json(
35
+ { error: "Invalid or expired preview token" },
36
+ { status: 401 }
37
+ );
38
+ }
39
+ dm.enable();
40
+ const jar = await headers.cookies();
41
+ jar.set(chunkHQGAX2LI_cjs.PORTFOLIO_PREVIEW_COOKIE, token, {
42
+ httpOnly: true,
43
+ sameSite: "lax",
44
+ secure: url.protocol === "https:",
45
+ maxAge: 60 * 60,
46
+ // matches the token TTL
47
+ path: "/"
48
+ });
49
+ return server.NextResponse.redirect(new URL(`${basePath}/${slug}`, url.origin));
50
+ }
51
+ };
52
+ }
53
+
54
+ exports.createPortfolioPreviewHandler = createPortfolioPreviewHandler;
55
+ //# sourceMappingURL=preview.cjs.map
56
+ //# sourceMappingURL=preview.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/preview.ts"],"names":["draftMode","jar","cookies","PORTFOLIO_PREVIEW_COOKIE","NextResponse","apiFetch"],"mappings":";;;;;;;AAiCO,SAAS,6BAAA,CACd,OAAA,GAA0C,EAAC,EACO;AAClD,EAAA,MAAM,QAAA,GAAW,QAAQ,QAAA,IAAY,OAAA;AAErC,EAAA,OAAO;AAAA,IACL,MAAM,IAAI,OAAA,EAAqC;AAC7C,MAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,OAAA,CAAQ,GAAG,CAAA;AAC/B,MAAA,MAAM,IAAA,GAAO,GAAA,CAAI,YAAA,CAAa,GAAA,CAAI,MAAM,CAAA,IAAK,EAAA;AAC7C,MAAA,MAAM,KAAA,GAAQ,GAAA,CAAI,YAAA,CAAa,GAAA,CAAI,OAAO,CAAA,IAAK,EAAA;AAC/C,MAAA,MAAM,IAAA,GAAO,GAAA,CAAI,YAAA,CAAa,GAAA,CAAI,MAAM,CAAA;AAExC,MAAA,MAAM,EAAA,GAAK,MAAMA,iBAAA,EAAU;AAE3B,MAAA,IAAI,IAAA,EAAM;AACR,QAAA,EAAA,CAAG,OAAA,EAAQ;AACX,QAAA,MAAMC,IAAAA,GAAM,MAAMC,eAAA,EAAQ;AAC1B,QAAAD,IAAAA,CAAI,OAAOE,0CAAwB,CAAA;AACnC,QAAA,OAAOC,oBAAa,QAAA,CAAS,IAAI,IAAI,QAAA,EAAU,GAAA,CAAI,MAAM,CAAC,CAAA;AAAA,MAC5D;AAEA,MAAA,IAAI,CAAC,IAAA,IAAQ,CAAC,KAAA,EAAO;AACnB,QAAA,OAAOA,mBAAA,CAAa,IAAA;AAAA,UAClB,EAAE,OAAO,uBAAA,EAAwB;AAAA,UACjC,EAAE,QAAQ,GAAA;AAAI,SAChB;AAAA,MACF;AAGA,MAAA,MAAM,OAAO,MAAMC,0BAAA;AAAA,QACjB,+BAA+B,kBAAA,CAAmB,IAAI,CAAC,CAAA,SAAA,EAAY,kBAAA,CAAmB,KAAK,CAAC,CAAA,CAAA;AAAA,QAC5F,EAAE,MAAA,EAAQ,KAAA,EAAO,UAAA,EAAY,CAAA;AAAE,OACjC;AAEA,MAAA,IAAI,CAAC,MAAM,KAAA,EAAO;AAChB,QAAA,OAAOD,mBAAA,CAAa,IAAA;AAAA,UAClB,EAAE,OAAO,kCAAA,EAAmC;AAAA,UAC5C,EAAE,QAAQ,GAAA;AAAI,SAChB;AAAA,MACF;AAEA,MAAA,EAAA,CAAG,MAAA,EAAO;AACV,MAAA,MAAM,GAAA,GAAM,MAAMF,eAAA,EAAQ;AAC1B,MAAA,GAAA,CAAI,GAAA,CAAIC,4CAA0B,KAAA,EAAO;AAAA,QACvC,QAAA,EAAU,IAAA;AAAA,QACV,QAAA,EAAU,KAAA;AAAA,QACV,MAAA,EAAQ,IAAI,QAAA,KAAa,QAAA;AAAA,QACzB,QAAQ,EAAA,GAAK,EAAA;AAAA;AAAA,QACb,IAAA,EAAM;AAAA,OACP,CAAA;AAED,MAAA,OAAOC,mBAAA,CAAa,QAAA,CAAS,IAAI,GAAA,CAAI,CAAA,EAAG,QAAQ,CAAA,CAAA,EAAI,IAAI,CAAA,CAAA,EAAI,GAAA,CAAI,MAAM,CAAC,CAAA;AAAA,IACzE;AAAA,GACF;AACF","file":"preview.cjs","sourcesContent":["/**\n * @sonordev/agency-site-kit/preview — draft preview route factory\n *\n * Lets the Sonor dashboard's Preview button open an UNPUBLISHED case study\n * on the live agency site. Mount it as a route handler:\n *\n * ```ts\n * // app/api/portfolio-preview/route.ts\n * import { createPortfolioPreviewHandler } from '@sonordev/agency-site-kit/preview';\n * export const { GET } = createPortfolioPreviewHandler();\n * ```\n *\n * Flow: the dashboard requests a short-lived token from the Sonor API\n * (hash-stored, ~1h TTL) and opens\n * `{site}/api/portfolio-preview?slug=…&token=…`. This handler validates the\n * token against the public API (a draft that resolves with the token IS the\n * validation), enables Next draft mode, stores the token in an httpOnly\n * cookie, and redirects to the case study. `getPortfolioItem` sees draft\n * mode + cookie and fetches with `?preview=` and no caching.\n *\n * `?exit=1` disables draft mode and clears the cookie.\n */\n\nimport { draftMode, cookies } from 'next/headers';\nimport { NextResponse } from 'next/server';\nimport { apiFetch } from './shared/api';\nimport { PORTFOLIO_PREVIEW_COOKIE } from './shared/preview';\n\nexport interface PortfolioPreviewHandlerOptions {\n /** Route prefix for case studies. Defaults to \"/work\". */\n basePath?: string;\n}\n\nexport function createPortfolioPreviewHandler(\n options: PortfolioPreviewHandlerOptions = {},\n): { GET: (request: Request) => Promise<Response> } {\n const basePath = options.basePath ?? '/work';\n\n return {\n async GET(request: Request): Promise<Response> {\n const url = new URL(request.url);\n const slug = url.searchParams.get('slug') ?? '';\n const token = url.searchParams.get('token') ?? '';\n const exit = url.searchParams.get('exit');\n\n const dm = await draftMode();\n\n if (exit) {\n dm.disable();\n const jar = await cookies();\n jar.delete(PORTFOLIO_PREVIEW_COOKIE);\n return NextResponse.redirect(new URL(basePath, url.origin));\n }\n\n if (!slug || !token) {\n return NextResponse.json(\n { error: 'Missing slug or token' },\n { status: 400 },\n );\n }\n\n // Validation = the draft resolves with this token. Uncached on purpose.\n const data = await apiFetch<{ found: boolean }>(\n `/api/public/portfolio/items/${encodeURIComponent(slug)}?preview=${encodeURIComponent(token)}`,\n { method: 'GET', revalidate: 0 },\n );\n\n if (!data?.found) {\n return NextResponse.json(\n { error: 'Invalid or expired preview token' },\n { status: 401 },\n );\n }\n\n dm.enable();\n const jar = await cookies();\n jar.set(PORTFOLIO_PREVIEW_COOKIE, token, {\n httpOnly: true,\n sameSite: 'lax',\n secure: url.protocol === 'https:',\n maxAge: 60 * 60, // matches the token TTL\n path: '/',\n });\n\n return NextResponse.redirect(new URL(`${basePath}/${slug}`, url.origin));\n },\n };\n}\n"]}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * @sonordev/agency-site-kit/preview — draft preview route factory
3
+ *
4
+ * Lets the Sonor dashboard's Preview button open an UNPUBLISHED case study
5
+ * on the live agency site. Mount it as a route handler:
6
+ *
7
+ * ```ts
8
+ * // app/api/portfolio-preview/route.ts
9
+ * import { createPortfolioPreviewHandler } from '@sonordev/agency-site-kit/preview';
10
+ * export const { GET } = createPortfolioPreviewHandler();
11
+ * ```
12
+ *
13
+ * Flow: the dashboard requests a short-lived token from the Sonor API
14
+ * (hash-stored, ~1h TTL) and opens
15
+ * `{site}/api/portfolio-preview?slug=…&token=…`. This handler validates the
16
+ * token against the public API (a draft that resolves with the token IS the
17
+ * validation), enables Next draft mode, stores the token in an httpOnly
18
+ * cookie, and redirects to the case study. `getPortfolioItem` sees draft
19
+ * mode + cookie and fetches with `?preview=` and no caching.
20
+ *
21
+ * `?exit=1` disables draft mode and clears the cookie.
22
+ */
23
+ interface PortfolioPreviewHandlerOptions {
24
+ /** Route prefix for case studies. Defaults to "/work". */
25
+ basePath?: string;
26
+ }
27
+ declare function createPortfolioPreviewHandler(options?: PortfolioPreviewHandlerOptions): {
28
+ GET: (request: Request) => Promise<Response>;
29
+ };
30
+
31
+ export { type PortfolioPreviewHandlerOptions, createPortfolioPreviewHandler };
@@ -0,0 +1,31 @@
1
+ /**
2
+ * @sonordev/agency-site-kit/preview — draft preview route factory
3
+ *
4
+ * Lets the Sonor dashboard's Preview button open an UNPUBLISHED case study
5
+ * on the live agency site. Mount it as a route handler:
6
+ *
7
+ * ```ts
8
+ * // app/api/portfolio-preview/route.ts
9
+ * import { createPortfolioPreviewHandler } from '@sonordev/agency-site-kit/preview';
10
+ * export const { GET } = createPortfolioPreviewHandler();
11
+ * ```
12
+ *
13
+ * Flow: the dashboard requests a short-lived token from the Sonor API
14
+ * (hash-stored, ~1h TTL) and opens
15
+ * `{site}/api/portfolio-preview?slug=…&token=…`. This handler validates the
16
+ * token against the public API (a draft that resolves with the token IS the
17
+ * validation), enables Next draft mode, stores the token in an httpOnly
18
+ * cookie, and redirects to the case study. `getPortfolioItem` sees draft
19
+ * mode + cookie and fetches with `?preview=` and no caching.
20
+ *
21
+ * `?exit=1` disables draft mode and clears the cookie.
22
+ */
23
+ interface PortfolioPreviewHandlerOptions {
24
+ /** Route prefix for case studies. Defaults to "/work". */
25
+ basePath?: string;
26
+ }
27
+ declare function createPortfolioPreviewHandler(options?: PortfolioPreviewHandlerOptions): {
28
+ GET: (request: Request) => Promise<Response>;
29
+ };
30
+
31
+ export { type PortfolioPreviewHandlerOptions, createPortfolioPreviewHandler };
@@ -0,0 +1,54 @@
1
+ import { PORTFOLIO_PREVIEW_COOKIE } from './chunk-L4EOQVRJ.js';
2
+ import { apiFetch } from './chunk-QL6KVIFR.js';
3
+ import { draftMode, cookies } from 'next/headers';
4
+ import { NextResponse } from 'next/server';
5
+
6
+ function createPortfolioPreviewHandler(options = {}) {
7
+ const basePath = options.basePath ?? "/work";
8
+ return {
9
+ async GET(request) {
10
+ const url = new URL(request.url);
11
+ const slug = url.searchParams.get("slug") ?? "";
12
+ const token = url.searchParams.get("token") ?? "";
13
+ const exit = url.searchParams.get("exit");
14
+ const dm = await draftMode();
15
+ if (exit) {
16
+ dm.disable();
17
+ const jar2 = await cookies();
18
+ jar2.delete(PORTFOLIO_PREVIEW_COOKIE);
19
+ return NextResponse.redirect(new URL(basePath, url.origin));
20
+ }
21
+ if (!slug || !token) {
22
+ return NextResponse.json(
23
+ { error: "Missing slug or token" },
24
+ { status: 400 }
25
+ );
26
+ }
27
+ const data = await apiFetch(
28
+ `/api/public/portfolio/items/${encodeURIComponent(slug)}?preview=${encodeURIComponent(token)}`,
29
+ { method: "GET", revalidate: 0 }
30
+ );
31
+ if (!data?.found) {
32
+ return NextResponse.json(
33
+ { error: "Invalid or expired preview token" },
34
+ { status: 401 }
35
+ );
36
+ }
37
+ dm.enable();
38
+ const jar = await cookies();
39
+ jar.set(PORTFOLIO_PREVIEW_COOKIE, token, {
40
+ httpOnly: true,
41
+ sameSite: "lax",
42
+ secure: url.protocol === "https:",
43
+ maxAge: 60 * 60,
44
+ // matches the token TTL
45
+ path: "/"
46
+ });
47
+ return NextResponse.redirect(new URL(`${basePath}/${slug}`, url.origin));
48
+ }
49
+ };
50
+ }
51
+
52
+ export { createPortfolioPreviewHandler };
53
+ //# sourceMappingURL=preview.js.map
54
+ //# sourceMappingURL=preview.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/preview.ts"],"names":["jar"],"mappings":";;;;;AAiCO,SAAS,6BAAA,CACd,OAAA,GAA0C,EAAC,EACO;AAClD,EAAA,MAAM,QAAA,GAAW,QAAQ,QAAA,IAAY,OAAA;AAErC,EAAA,OAAO;AAAA,IACL,MAAM,IAAI,OAAA,EAAqC;AAC7C,MAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,OAAA,CAAQ,GAAG,CAAA;AAC/B,MAAA,MAAM,IAAA,GAAO,GAAA,CAAI,YAAA,CAAa,GAAA,CAAI,MAAM,CAAA,IAAK,EAAA;AAC7C,MAAA,MAAM,KAAA,GAAQ,GAAA,CAAI,YAAA,CAAa,GAAA,CAAI,OAAO,CAAA,IAAK,EAAA;AAC/C,MAAA,MAAM,IAAA,GAAO,GAAA,CAAI,YAAA,CAAa,GAAA,CAAI,MAAM,CAAA;AAExC,MAAA,MAAM,EAAA,GAAK,MAAM,SAAA,EAAU;AAE3B,MAAA,IAAI,IAAA,EAAM;AACR,QAAA,EAAA,CAAG,OAAA,EAAQ;AACX,QAAA,MAAMA,IAAAA,GAAM,MAAM,OAAA,EAAQ;AAC1B,QAAAA,IAAAA,CAAI,OAAO,wBAAwB,CAAA;AACnC,QAAA,OAAO,aAAa,QAAA,CAAS,IAAI,IAAI,QAAA,EAAU,GAAA,CAAI,MAAM,CAAC,CAAA;AAAA,MAC5D;AAEA,MAAA,IAAI,CAAC,IAAA,IAAQ,CAAC,KAAA,EAAO;AACnB,QAAA,OAAO,YAAA,CAAa,IAAA;AAAA,UAClB,EAAE,OAAO,uBAAA,EAAwB;AAAA,UACjC,EAAE,QAAQ,GAAA;AAAI,SAChB;AAAA,MACF;AAGA,MAAA,MAAM,OAAO,MAAM,QAAA;AAAA,QACjB,+BAA+B,kBAAA,CAAmB,IAAI,CAAC,CAAA,SAAA,EAAY,kBAAA,CAAmB,KAAK,CAAC,CAAA,CAAA;AAAA,QAC5F,EAAE,MAAA,EAAQ,KAAA,EAAO,UAAA,EAAY,CAAA;AAAE,OACjC;AAEA,MAAA,IAAI,CAAC,MAAM,KAAA,EAAO;AAChB,QAAA,OAAO,YAAA,CAAa,IAAA;AAAA,UAClB,EAAE,OAAO,kCAAA,EAAmC;AAAA,UAC5C,EAAE,QAAQ,GAAA;AAAI,SAChB;AAAA,MACF;AAEA,MAAA,EAAA,CAAG,MAAA,EAAO;AACV,MAAA,MAAM,GAAA,GAAM,MAAM,OAAA,EAAQ;AAC1B,MAAA,GAAA,CAAI,GAAA,CAAI,0BAA0B,KAAA,EAAO;AAAA,QACvC,QAAA,EAAU,IAAA;AAAA,QACV,QAAA,EAAU,KAAA;AAAA,QACV,MAAA,EAAQ,IAAI,QAAA,KAAa,QAAA;AAAA,QACzB,QAAQ,EAAA,GAAK,EAAA;AAAA;AAAA,QACb,IAAA,EAAM;AAAA,OACP,CAAA;AAED,MAAA,OAAO,YAAA,CAAa,QAAA,CAAS,IAAI,GAAA,CAAI,CAAA,EAAG,QAAQ,CAAA,CAAA,EAAI,IAAI,CAAA,CAAA,EAAI,GAAA,CAAI,MAAM,CAAC,CAAA;AAAA,IACzE;AAAA,GACF;AACF","file":"preview.js","sourcesContent":["/**\n * @sonordev/agency-site-kit/preview — draft preview route factory\n *\n * Lets the Sonor dashboard's Preview button open an UNPUBLISHED case study\n * on the live agency site. Mount it as a route handler:\n *\n * ```ts\n * // app/api/portfolio-preview/route.ts\n * import { createPortfolioPreviewHandler } from '@sonordev/agency-site-kit/preview';\n * export const { GET } = createPortfolioPreviewHandler();\n * ```\n *\n * Flow: the dashboard requests a short-lived token from the Sonor API\n * (hash-stored, ~1h TTL) and opens\n * `{site}/api/portfolio-preview?slug=…&token=…`. This handler validates the\n * token against the public API (a draft that resolves with the token IS the\n * validation), enables Next draft mode, stores the token in an httpOnly\n * cookie, and redirects to the case study. `getPortfolioItem` sees draft\n * mode + cookie and fetches with `?preview=` and no caching.\n *\n * `?exit=1` disables draft mode and clears the cookie.\n */\n\nimport { draftMode, cookies } from 'next/headers';\nimport { NextResponse } from 'next/server';\nimport { apiFetch } from './shared/api';\nimport { PORTFOLIO_PREVIEW_COOKIE } from './shared/preview';\n\nexport interface PortfolioPreviewHandlerOptions {\n /** Route prefix for case studies. Defaults to \"/work\". */\n basePath?: string;\n}\n\nexport function createPortfolioPreviewHandler(\n options: PortfolioPreviewHandlerOptions = {},\n): { GET: (request: Request) => Promise<Response> } {\n const basePath = options.basePath ?? '/work';\n\n return {\n async GET(request: Request): Promise<Response> {\n const url = new URL(request.url);\n const slug = url.searchParams.get('slug') ?? '';\n const token = url.searchParams.get('token') ?? '';\n const exit = url.searchParams.get('exit');\n\n const dm = await draftMode();\n\n if (exit) {\n dm.disable();\n const jar = await cookies();\n jar.delete(PORTFOLIO_PREVIEW_COOKIE);\n return NextResponse.redirect(new URL(basePath, url.origin));\n }\n\n if (!slug || !token) {\n return NextResponse.json(\n { error: 'Missing slug or token' },\n { status: 400 },\n );\n }\n\n // Validation = the draft resolves with this token. Uncached on purpose.\n const data = await apiFetch<{ found: boolean }>(\n `/api/public/portfolio/items/${encodeURIComponent(slug)}?preview=${encodeURIComponent(token)}`,\n { method: 'GET', revalidate: 0 },\n );\n\n if (!data?.found) {\n return NextResponse.json(\n { error: 'Invalid or expired preview token' },\n { status: 401 },\n );\n }\n\n dm.enable();\n const jar = await cookies();\n jar.set(PORTFOLIO_PREVIEW_COOKIE, token, {\n httpOnly: true,\n sameSite: 'lax',\n secure: url.protocol === 'https:',\n maxAge: 60 * 60, // matches the token TTL\n path: '/',\n });\n\n return NextResponse.redirect(new URL(`${basePath}/${slug}`, url.origin));\n },\n };\n}\n"]}
@@ -332,6 +332,12 @@ interface PortfolioItem {
332
332
  interface PortfolioItemFull extends PortfolioItem {
333
333
  sections: PortfolioSection[];
334
334
  metricsDelta: MetricsDelta[];
335
+ /**
336
+ * True once the agency deliberately reordered sections in the dashboard.
337
+ * PortfolioPage respects the sections array order when set (hero pinned
338
+ * first); otherwise it applies the canonical order.
339
+ */
340
+ sectionsOrdered?: boolean;
335
341
  }
336
342
  interface PortfolioListResponse {
337
343
  items: PortfolioItem[];
@@ -345,4 +351,4 @@ interface PortfolioConfigResponse {
345
351
  orgSlug: string;
346
352
  }
347
353
 
348
- export { type PortfolioSection as A, type BrandConfig as B, type PortfolioSeoData as C, type PortfolioServiceItem as D, type PortfolioServicesData as E, type PortfolioStrategyData as F, type PortfolioStrategyPhase as G, type PortfolioTeamData as H, type PortfolioTeamMember as I, type PortfolioTechItem as J, type PortfolioTechStackData as K, type PortfolioTestimonialData as L, type MetricSource as M, type PortfolioVideoData as N, type PortfolioItemFull as P, type SanityImageRef as S, type PortfolioListResponse as a, type PortfolioSectionType as b, type PortfolioSectionDataMap as c, type PortfolioSectionData as d, type MetricsDelta as e, type MetricsSnapshot as f, PORTFOLIO_SECTION_TYPES as g, type PortfolioAnnotation as h, type PortfolioBeforeAfterData as i, type PortfolioCTAData as j, type PortfolioChallengeItem as k, type PortfolioChallengesData as l, type PortfolioConfigResponse as m, type PortfolioConversionFunnelData as n, type PortfolioDetailsData as o, type PortfolioFeatureSpotlightData as p, type PortfolioFunnelStage as q, type PortfolioGalleryData as r, type PortfolioGalleryImage as s, type PortfolioHeroData as t, type PortfolioItem as u, type PortfolioKPI as v, type PortfolioMetricsDataPoint as w, type PortfolioMetricsTimelineData as x, type PortfolioResultItem as y, type PortfolioResultsData as z };
354
+ export { type PortfolioResultItem as A, type BrandConfig as B, type PortfolioSectionData as C, type PortfolioSectionDataMap as D, type PortfolioSectionType as E, type PortfolioSeoData as F, type PortfolioServiceItem as G, type PortfolioStrategyData as H, type PortfolioStrategyPhase as I, type PortfolioTeamData as J, type PortfolioTeamMember as K, type PortfolioTechItem as L, type MetricsDelta as M, type PortfolioVideoData as N, type PortfolioItemFull as P, type SanityImageRef as S, type PortfolioSection as a, type PortfolioListResponse as b, type PortfolioChallengesData as c, type PortfolioDetailsData as d, type PortfolioHeroData as e, type PortfolioResultsData as f, type PortfolioServicesData as g, type PortfolioTechStackData as h, type PortfolioTestimonialData as i, type MetricSource as j, type MetricsSnapshot as k, PORTFOLIO_SECTION_TYPES as l, type PortfolioAnnotation as m, type PortfolioBeforeAfterData as n, type PortfolioCTAData as o, type PortfolioChallengeItem as p, type PortfolioConfigResponse as q, type PortfolioConversionFunnelData as r, type PortfolioFeatureSpotlightData as s, type PortfolioFunnelStage as t, type PortfolioGalleryData as u, type PortfolioGalleryImage as v, type PortfolioItem as w, type PortfolioKPI as x, type PortfolioMetricsDataPoint as y, type PortfolioMetricsTimelineData as z };
@@ -332,6 +332,12 @@ interface PortfolioItem {
332
332
  interface PortfolioItemFull extends PortfolioItem {
333
333
  sections: PortfolioSection[];
334
334
  metricsDelta: MetricsDelta[];
335
+ /**
336
+ * True once the agency deliberately reordered sections in the dashboard.
337
+ * PortfolioPage respects the sections array order when set (hero pinned
338
+ * first); otherwise it applies the canonical order.
339
+ */
340
+ sectionsOrdered?: boolean;
335
341
  }
336
342
  interface PortfolioListResponse {
337
343
  items: PortfolioItem[];
@@ -345,4 +351,4 @@ interface PortfolioConfigResponse {
345
351
  orgSlug: string;
346
352
  }
347
353
 
348
- export { type PortfolioSection as A, type BrandConfig as B, type PortfolioSeoData as C, type PortfolioServiceItem as D, type PortfolioServicesData as E, type PortfolioStrategyData as F, type PortfolioStrategyPhase as G, type PortfolioTeamData as H, type PortfolioTeamMember as I, type PortfolioTechItem as J, type PortfolioTechStackData as K, type PortfolioTestimonialData as L, type MetricSource as M, type PortfolioVideoData as N, type PortfolioItemFull as P, type SanityImageRef as S, type PortfolioListResponse as a, type PortfolioSectionType as b, type PortfolioSectionDataMap as c, type PortfolioSectionData as d, type MetricsDelta as e, type MetricsSnapshot as f, PORTFOLIO_SECTION_TYPES as g, type PortfolioAnnotation as h, type PortfolioBeforeAfterData as i, type PortfolioCTAData as j, type PortfolioChallengeItem as k, type PortfolioChallengesData as l, type PortfolioConfigResponse as m, type PortfolioConversionFunnelData as n, type PortfolioDetailsData as o, type PortfolioFeatureSpotlightData as p, type PortfolioFunnelStage as q, type PortfolioGalleryData as r, type PortfolioGalleryImage as s, type PortfolioHeroData as t, type PortfolioItem as u, type PortfolioKPI as v, type PortfolioMetricsDataPoint as w, type PortfolioMetricsTimelineData as x, type PortfolioResultItem as y, type PortfolioResultsData as z };
354
+ export { type PortfolioResultItem as A, type BrandConfig as B, type PortfolioSectionData as C, type PortfolioSectionDataMap as D, type PortfolioSectionType as E, type PortfolioSeoData as F, type PortfolioServiceItem as G, type PortfolioStrategyData as H, type PortfolioStrategyPhase as I, type PortfolioTeamData as J, type PortfolioTeamMember as K, type PortfolioTechItem as L, type MetricsDelta as M, type PortfolioVideoData as N, type PortfolioItemFull as P, type SanityImageRef as S, type PortfolioSection as a, type PortfolioListResponse as b, type PortfolioChallengesData as c, type PortfolioDetailsData as d, type PortfolioHeroData as e, type PortfolioResultsData as f, type PortfolioServicesData as g, type PortfolioTechStackData as h, type PortfolioTestimonialData as i, type MetricSource as j, type MetricsSnapshot as k, PORTFOLIO_SECTION_TYPES as l, type PortfolioAnnotation as m, type PortfolioBeforeAfterData as n, type PortfolioCTAData as o, type PortfolioChallengeItem as p, type PortfolioConfigResponse as q, type PortfolioConversionFunnelData as r, type PortfolioFeatureSpotlightData as s, type PortfolioFunnelStage as t, type PortfolioGalleryData as u, type PortfolioGalleryImage as v, type PortfolioItem as w, type PortfolioKPI as x, type PortfolioMetricsDataPoint as y, type PortfolioMetricsTimelineData as z };