@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.
- package/README.md +302 -0
- package/bin/setup.mjs +79 -0
- package/dist/{chunk-RXBK2EOE.js → chunk-4IES23GV.js} +3 -3
- package/dist/{chunk-RXBK2EOE.js.map → chunk-4IES23GV.js.map} +1 -1
- package/dist/chunk-4N7ENJ7L.cjs +108 -0
- package/dist/chunk-4N7ENJ7L.cjs.map +1 -0
- package/dist/{chunk-WNR4KNG4.cjs → chunk-5MEZU3OG.cjs} +4 -4
- package/dist/{chunk-WNR4KNG4.cjs.map → chunk-5MEZU3OG.cjs.map} +1 -1
- package/dist/chunk-7ZHRMWJU.cjs +306 -0
- package/dist/chunk-7ZHRMWJU.cjs.map +1 -0
- package/dist/chunk-CVT7I5BQ.js +71 -0
- package/dist/chunk-CVT7I5BQ.js.map +1 -0
- package/dist/chunk-HQGAX2LI.cjs +8 -0
- package/dist/chunk-HQGAX2LI.cjs.map +1 -0
- package/dist/chunk-KXLROAO5.js +291 -0
- package/dist/chunk-KXLROAO5.js.map +1 -0
- package/dist/chunk-L4EOQVRJ.js +6 -0
- package/dist/chunk-L4EOQVRJ.js.map +1 -0
- package/dist/chunk-L7E3UHQC.cjs +535 -0
- package/dist/chunk-L7E3UHQC.cjs.map +1 -0
- package/dist/chunk-NI4FVCLS.cjs +32 -0
- package/dist/chunk-NI4FVCLS.cjs.map +1 -0
- package/dist/chunk-NIZJLONK.js +106 -0
- package/dist/chunk-NIZJLONK.js.map +1 -0
- package/dist/chunk-QDXZ27R7.js +33 -0
- package/dist/chunk-QDXZ27R7.js.map +1 -0
- package/dist/{chunk-TVFXN6W6.js → chunk-QL6KVIFR.js} +8 -4
- package/dist/chunk-QL6KVIFR.js.map +1 -0
- package/dist/chunk-UNTOEWQL.js +504 -0
- package/dist/chunk-UNTOEWQL.js.map +1 -0
- package/dist/chunk-XJ434N7I.js +30 -0
- package/dist/chunk-XJ434N7I.js.map +1 -0
- package/dist/chunk-XLK77USA.js +45 -0
- package/dist/chunk-XLK77USA.js.map +1 -0
- package/dist/{chunk-EE75WRCM.cjs → chunk-XOWSXZYW.cjs} +8 -4
- package/dist/chunk-XOWSXZYW.cjs.map +1 -0
- package/dist/chunk-ZINKVJQU.js +67 -0
- package/dist/chunk-ZINKVJQU.js.map +1 -0
- package/dist/index.cjs +29 -31
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -9
- package/dist/index.d.ts +17 -9
- package/dist/index.js +7 -9
- package/dist/index.js.map +1 -1
- package/dist/layout/index.cjs +3 -3
- package/dist/layout/index.d.cts +1 -1
- package/dist/layout/index.d.ts +1 -1
- package/dist/layout/index.js +2 -2
- package/dist/portfolio/client.cjs +342 -12
- package/dist/portfolio/client.cjs.map +1 -1
- package/dist/portfolio/client.d.cts +11 -88
- package/dist/portfolio/client.d.ts +11 -88
- package/dist/portfolio/client.js +150 -4
- package/dist/portfolio/client.js.map +1 -1
- package/dist/portfolio/components/PortfolioAnalytics.cjs +87 -0
- package/dist/portfolio/components/PortfolioAnalytics.cjs.map +1 -0
- package/dist/portfolio/components/PortfolioAnalytics.d.cts +5 -0
- package/dist/portfolio/components/PortfolioAnalytics.d.ts +5 -0
- package/dist/portfolio/components/PortfolioAnalytics.js +9 -0
- package/dist/portfolio/components/PortfolioAnalytics.js.map +1 -0
- package/dist/portfolio/components/PortfolioErrorFallback.cjs +65 -0
- package/dist/portfolio/components/PortfolioErrorFallback.cjs.map +1 -0
- package/dist/portfolio/components/PortfolioErrorFallback.d.cts +22 -0
- package/dist/portfolio/components/PortfolioErrorFallback.d.ts +22 -0
- package/dist/portfolio/components/PortfolioErrorFallback.js +9 -0
- package/dist/portfolio/components/PortfolioErrorFallback.js.map +1 -0
- package/dist/portfolio/components/PortfolioLeadCTA.cjs +91 -0
- package/dist/portfolio/components/PortfolioLeadCTA.cjs.map +1 -0
- package/dist/portfolio/components/PortfolioLeadCTA.d.cts +34 -0
- package/dist/portfolio/components/PortfolioLeadCTA.d.ts +34 -0
- package/dist/portfolio/components/PortfolioLeadCTA.js +9 -0
- package/dist/portfolio/components/PortfolioLeadCTA.js.map +1 -0
- package/dist/portfolio/index.cjs +19 -20
- package/dist/portfolio/index.d.cts +133 -7
- package/dist/portfolio/index.d.ts +133 -7
- package/dist/portfolio/index.js +6 -7
- package/dist/portfolio/primitives/AnimatedCounter.cjs +88 -0
- package/dist/portfolio/primitives/AnimatedCounter.cjs.map +1 -0
- package/dist/portfolio/primitives/AnimatedCounter.d.cts +22 -0
- package/dist/portfolio/primitives/AnimatedCounter.d.ts +22 -0
- package/dist/portfolio/primitives/AnimatedCounter.js +68 -0
- package/dist/portfolio/primitives/AnimatedCounter.js.map +1 -0
- package/dist/portfolio/primitives/AnnotatedImage.cjs +320 -0
- package/dist/portfolio/primitives/AnnotatedImage.cjs.map +1 -0
- package/dist/portfolio/primitives/AnnotatedImage.d.cts +18 -0
- package/dist/portfolio/primitives/AnnotatedImage.d.ts +18 -0
- package/dist/portfolio/primitives/AnnotatedImage.js +264 -0
- package/dist/portfolio/primitives/AnnotatedImage.js.map +1 -0
- package/dist/portfolio/primitives/BeforeAfterSlider.cjs +348 -0
- package/dist/portfolio/primitives/BeforeAfterSlider.cjs.map +1 -0
- package/dist/portfolio/primitives/BeforeAfterSlider.d.cts +13 -0
- package/dist/portfolio/primitives/BeforeAfterSlider.d.ts +13 -0
- package/dist/portfolio/primitives/BeforeAfterSlider.js +292 -0
- package/dist/portfolio/primitives/BeforeAfterSlider.js.map +1 -0
- package/dist/portfolio/primitives/DeviceTrifolio.cjs +298 -0
- package/dist/portfolio/primitives/DeviceTrifolio.cjs.map +1 -0
- package/dist/portfolio/primitives/DeviceTrifolio.d.cts +14 -0
- package/dist/portfolio/primitives/DeviceTrifolio.d.ts +14 -0
- package/dist/portfolio/primitives/DeviceTrifolio.js +242 -0
- package/dist/portfolio/primitives/DeviceTrifolio.js.map +1 -0
- package/dist/portfolio/primitives/FrequencyBarSkeleton.cjs +88 -0
- package/dist/portfolio/primitives/FrequencyBarSkeleton.cjs.map +1 -0
- package/dist/portfolio/primitives/FrequencyBarSkeleton.d.cts +10 -0
- package/dist/portfolio/primitives/FrequencyBarSkeleton.d.ts +10 -0
- package/dist/portfolio/primitives/FrequencyBarSkeleton.js +68 -0
- package/dist/portfolio/primitives/FrequencyBarSkeleton.js.map +1 -0
- package/dist/portfolio/primitives/GlassCard.cjs +45 -0
- package/dist/portfolio/primitives/GlassCard.cjs.map +1 -0
- package/dist/portfolio/primitives/GlassCard.d.cts +11 -0
- package/dist/portfolio/primitives/GlassCard.d.ts +11 -0
- package/dist/portfolio/primitives/GlassCard.js +43 -0
- package/dist/portfolio/primitives/GlassCard.js.map +1 -0
- package/dist/portfolio/primitives/MetricBadge.cjs +74 -0
- package/dist/portfolio/primitives/MetricBadge.cjs.map +1 -0
- package/dist/portfolio/primitives/MetricBadge.d.cts +9 -0
- package/dist/portfolio/primitives/MetricBadge.d.ts +9 -0
- package/dist/portfolio/primitives/MetricBadge.js +54 -0
- package/dist/portfolio/primitives/MetricBadge.js.map +1 -0
- package/dist/portfolio/primitives/SVGChart.cjs +316 -0
- package/dist/portfolio/primitives/SVGChart.cjs.map +1 -0
- package/dist/portfolio/primitives/SVGChart.d.cts +18 -0
- package/dist/portfolio/primitives/SVGChart.d.ts +18 -0
- package/dist/portfolio/primitives/SVGChart.js +296 -0
- package/dist/portfolio/primitives/SVGChart.js.map +1 -0
- package/dist/portfolio/primitives/ScrollReveal.cjs +97 -0
- package/dist/portfolio/primitives/ScrollReveal.cjs.map +1 -0
- package/dist/portfolio/primitives/ScrollReveal.d.cts +27 -0
- package/dist/portfolio/primitives/ScrollReveal.d.ts +27 -0
- package/dist/portfolio/primitives/ScrollReveal.js +77 -0
- package/dist/portfolio/primitives/ScrollReveal.js.map +1 -0
- package/dist/portfolio/primitives/SplitHeadline.cjs +107 -0
- package/dist/portfolio/primitives/SplitHeadline.cjs.map +1 -0
- package/dist/portfolio/primitives/SplitHeadline.d.cts +24 -0
- package/dist/portfolio/primitives/SplitHeadline.d.ts +24 -0
- package/dist/portfolio/primitives/SplitHeadline.js +87 -0
- package/dist/portfolio/primitives/SplitHeadline.js.map +1 -0
- package/dist/portfolio/sections/BeforeAfterSection.cjs +271 -0
- package/dist/portfolio/sections/BeforeAfterSection.cjs.map +1 -0
- package/dist/portfolio/sections/BeforeAfterSection.d.cts +9 -0
- package/dist/portfolio/sections/BeforeAfterSection.d.ts +9 -0
- package/dist/{BeforeAfterSection-6BHFLY4Y.js → portfolio/sections/BeforeAfterSection.js} +66 -27
- package/dist/portfolio/sections/BeforeAfterSection.js.map +1 -0
- package/dist/portfolio/sections/CTASection.cjs +123 -0
- package/dist/portfolio/sections/CTASection.cjs.map +1 -0
- package/dist/portfolio/sections/CTASection.d.cts +9 -0
- package/dist/portfolio/sections/CTASection.d.ts +9 -0
- package/dist/portfolio/sections/CTASection.js +93 -0
- package/dist/portfolio/sections/CTASection.js.map +1 -0
- package/dist/{ChallengesSection-IZ3DHECS.cjs → portfolio/sections/ChallengesSection.cjs} +12 -6
- package/dist/portfolio/sections/ChallengesSection.cjs.map +1 -0
- package/dist/portfolio/sections/ChallengesSection.d.cts +9 -0
- package/dist/portfolio/sections/ChallengesSection.d.ts +9 -0
- package/dist/{ChallengesSection-GEQGVSJN.js → portfolio/sections/ChallengesSection.js} +5 -4
- package/dist/portfolio/sections/ChallengesSection.js.map +1 -0
- package/dist/portfolio/sections/ConversionFunnelSection.cjs +263 -0
- package/dist/portfolio/sections/ConversionFunnelSection.cjs.map +1 -0
- package/dist/portfolio/sections/ConversionFunnelSection.d.cts +9 -0
- package/dist/portfolio/sections/ConversionFunnelSection.d.ts +9 -0
- package/dist/{ConversionFunnelSection-D3GE4NKE.js → portfolio/sections/ConversionFunnelSection.js} +60 -56
- package/dist/portfolio/sections/ConversionFunnelSection.js.map +1 -0
- package/dist/{DesignSystemSection-KXIQXITF.cjs → portfolio/sections/DesignSystemSection.cjs} +13 -7
- package/dist/portfolio/sections/DesignSystemSection.cjs.map +1 -0
- package/dist/portfolio/sections/DesignSystemSection.d.cts +32 -0
- package/dist/portfolio/sections/DesignSystemSection.d.ts +32 -0
- package/dist/{DesignSystemSection-2R5BRBGO.js → portfolio/sections/DesignSystemSection.js} +5 -4
- package/dist/portfolio/sections/DesignSystemSection.js.map +1 -0
- package/dist/{DetailsSection-TTUZAPZZ.cjs → portfolio/sections/DetailsSection.cjs} +12 -6
- package/dist/portfolio/sections/DetailsSection.cjs.map +1 -0
- package/dist/portfolio/sections/DetailsSection.d.cts +9 -0
- package/dist/portfolio/sections/DetailsSection.d.ts +9 -0
- package/dist/{DetailsSection-A6PZQUQL.js → portfolio/sections/DetailsSection.js} +5 -4
- package/dist/portfolio/sections/DetailsSection.js.map +1 -0
- package/dist/portfolio/sections/FeatureSpotlightSection.cjs +267 -0
- package/dist/portfolio/sections/FeatureSpotlightSection.cjs.map +1 -0
- package/dist/portfolio/sections/FeatureSpotlightSection.d.cts +9 -0
- package/dist/portfolio/sections/FeatureSpotlightSection.d.ts +9 -0
- package/dist/{FeatureSpotlightSection-B7P3JGNL.js → portfolio/sections/FeatureSpotlightSection.js} +39 -33
- package/dist/portfolio/sections/FeatureSpotlightSection.js.map +1 -0
- package/dist/portfolio/sections/GallerySection.cjs +321 -0
- package/dist/portfolio/sections/GallerySection.cjs.map +1 -0
- package/dist/portfolio/sections/GallerySection.d.cts +9 -0
- package/dist/portfolio/sections/GallerySection.d.ts +9 -0
- package/dist/{GallerySection-VMKORC47.js → portfolio/sections/GallerySection.js} +65 -18
- package/dist/portfolio/sections/GallerySection.js.map +1 -0
- package/dist/portfolio/sections/HeroSection.cjs +180 -0
- package/dist/portfolio/sections/HeroSection.cjs.map +1 -0
- package/dist/portfolio/sections/HeroSection.d.cts +15 -0
- package/dist/portfolio/sections/HeroSection.d.ts +15 -0
- package/dist/portfolio/sections/HeroSection.js +171 -0
- package/dist/portfolio/sections/HeroSection.js.map +1 -0
- package/dist/{MetricsTimelineSection-4L6DUHJ5.cjs → portfolio/sections/MetricsTimelineSection.cjs} +102 -61
- package/dist/portfolio/sections/MetricsTimelineSection.cjs.map +1 -0
- package/dist/portfolio/sections/MetricsTimelineSection.d.cts +9 -0
- package/dist/portfolio/sections/MetricsTimelineSection.d.ts +9 -0
- package/dist/{MetricsTimelineSection-6BT5GNFV.js → portfolio/sections/MetricsTimelineSection.js} +45 -29
- package/dist/portfolio/sections/MetricsTimelineSection.js.map +1 -0
- package/dist/{PerformanceSection-24TVVFZA.cjs → portfolio/sections/PerformanceSection.cjs} +136 -93
- package/dist/portfolio/sections/PerformanceSection.cjs.map +1 -0
- package/dist/portfolio/sections/PerformanceSection.d.cts +22 -0
- package/dist/portfolio/sections/PerformanceSection.d.ts +22 -0
- package/dist/{PerformanceSection-MGCEIXDX.js → portfolio/sections/PerformanceSection.js} +58 -40
- package/dist/portfolio/sections/PerformanceSection.js.map +1 -0
- package/dist/{ResultsSection-XLGMMQKY.cjs → portfolio/sections/ResultsSection.cjs} +22 -10
- package/dist/portfolio/sections/ResultsSection.cjs.map +1 -0
- package/dist/portfolio/sections/ResultsSection.d.cts +13 -0
- package/dist/portfolio/sections/ResultsSection.d.ts +13 -0
- package/dist/{ResultsSection-DFUJ5U6M.js → portfolio/sections/ResultsSection.js} +13 -7
- package/dist/portfolio/sections/ResultsSection.js.map +1 -0
- package/dist/portfolio/sections/ServicesSection.cjs +129 -0
- package/dist/portfolio/sections/ServicesSection.cjs.map +1 -0
- package/dist/portfolio/sections/ServicesSection.d.cts +9 -0
- package/dist/portfolio/sections/ServicesSection.d.ts +9 -0
- package/dist/portfolio/sections/ServicesSection.js +122 -0
- package/dist/portfolio/sections/ServicesSection.js.map +1 -0
- package/dist/{SiteArchitectureSection-J7BPBERH.cjs → portfolio/sections/SiteArchitectureSection.cjs} +116 -92
- package/dist/portfolio/sections/SiteArchitectureSection.cjs.map +1 -0
- package/dist/portfolio/sections/SiteArchitectureSection.d.cts +21 -0
- package/dist/portfolio/sections/SiteArchitectureSection.d.ts +21 -0
- package/dist/{SiteArchitectureSection-TE22OAWV.js → portfolio/sections/SiteArchitectureSection.js} +46 -47
- package/dist/portfolio/sections/SiteArchitectureSection.js.map +1 -0
- package/dist/{SpeedComparisonSection-EZKFQVGW.cjs → portfolio/sections/SpeedComparisonSection.cjs} +12 -6
- package/dist/portfolio/sections/SpeedComparisonSection.cjs.map +1 -0
- package/dist/portfolio/sections/SpeedComparisonSection.d.cts +20 -0
- package/dist/portfolio/sections/SpeedComparisonSection.d.ts +20 -0
- package/dist/{SpeedComparisonSection-Y3K7OFZQ.js → portfolio/sections/SpeedComparisonSection.js} +5 -4
- package/dist/portfolio/sections/SpeedComparisonSection.js.map +1 -0
- package/dist/portfolio/sections/StrategySection.cjs +233 -0
- package/dist/portfolio/sections/StrategySection.cjs.map +1 -0
- package/dist/portfolio/sections/StrategySection.d.cts +9 -0
- package/dist/portfolio/sections/StrategySection.d.ts +9 -0
- package/dist/{StrategySection-DI5RSCJU.js → portfolio/sections/StrategySection.js} +60 -26
- package/dist/portfolio/sections/StrategySection.js.map +1 -0
- package/dist/portfolio/sections/TeamSection.cjs +162 -0
- package/dist/portfolio/sections/TeamSection.cjs.map +1 -0
- package/dist/portfolio/sections/TeamSection.d.cts +9 -0
- package/dist/portfolio/sections/TeamSection.d.ts +9 -0
- package/dist/portfolio/sections/TeamSection.js +106 -0
- package/dist/portfolio/sections/TeamSection.js.map +1 -0
- package/dist/portfolio/sections/TechStackSection.cjs +106 -0
- package/dist/portfolio/sections/TechStackSection.cjs.map +1 -0
- package/dist/portfolio/sections/TechStackSection.d.cts +9 -0
- package/dist/portfolio/sections/TechStackSection.d.ts +9 -0
- package/dist/portfolio/sections/TechStackSection.js +99 -0
- package/dist/portfolio/sections/TechStackSection.js.map +1 -0
- package/dist/{TestimonialSection-XPTFUQIN.cjs → portfolio/sections/TestimonialSection.cjs} +47 -28
- package/dist/portfolio/sections/TestimonialSection.cjs.map +1 -0
- package/dist/portfolio/sections/TestimonialSection.d.cts +9 -0
- package/dist/portfolio/sections/TestimonialSection.d.ts +9 -0
- package/dist/{TestimonialSection-6RGSMXQB.js → portfolio/sections/TestimonialSection.js} +40 -27
- package/dist/portfolio/sections/TestimonialSection.js.map +1 -0
- package/dist/portfolio/sections/VideoSection.cjs +181 -0
- package/dist/portfolio/sections/VideoSection.cjs.map +1 -0
- package/dist/portfolio/sections/VideoSection.d.cts +9 -0
- package/dist/portfolio/sections/VideoSection.d.ts +9 -0
- package/dist/{VideoSection-4A2HC6K6.js → portfolio/sections/VideoSection.js} +21 -13
- package/dist/portfolio/sections/VideoSection.js.map +1 -0
- package/dist/portfolio/server.cjs +56 -144
- package/dist/portfolio/server.cjs.map +1 -1
- package/dist/portfolio/server.d.cts +157 -7
- package/dist/portfolio/server.d.ts +157 -7
- package/dist/portfolio/server.js +3 -142
- package/dist/portfolio/server.js.map +1 -1
- package/dist/preview.cjs +56 -0
- package/dist/preview.cjs.map +1 -0
- package/dist/preview.d.cts +31 -0
- package/dist/preview.d.ts +31 -0
- package/dist/preview.js +54 -0
- package/dist/preview.js.map +1 -0
- package/dist/{types-DL4t_Cfa.d.ts → types-0tVPy4pU.d.cts} +7 -1
- package/dist/{types-DL4t_Cfa.d.cts → types-0tVPy4pU.d.ts} +7 -1
- package/dist/types-D0nb6KAx.d.cts +299 -0
- package/dist/types-D0nb6KAx.d.ts +299 -0
- package/package.json +38 -14
- package/templates/api-portfolio-preview.ts +6 -0
- package/templates/api-revalidate.ts +5 -0
- package/templates/work-category-page.tsx +40 -0
- package/templates/work-error.tsx +3 -0
- package/templates/work-loading.tsx +1 -0
- package/templates/work-page.tsx +37 -0
- package/templates/work-slug-loading.tsx +1 -0
- package/templates/work-slug-page.tsx +41 -0
- package/dist/BeforeAfterSection-6BHFLY4Y.js.map +0 -1
- package/dist/BeforeAfterSection-JTORBR3A.cjs +0 -181
- package/dist/BeforeAfterSection-JTORBR3A.cjs.map +0 -1
- package/dist/CTASection-4JKLXEUF.cjs +0 -111
- package/dist/CTASection-4JKLXEUF.cjs.map +0 -1
- package/dist/CTASection-BJA72XIL.js +0 -106
- package/dist/CTASection-BJA72XIL.js.map +0 -1
- package/dist/ChallengesSection-GEQGVSJN.js.map +0 -1
- package/dist/ChallengesSection-IZ3DHECS.cjs.map +0 -1
- package/dist/ConversionFunnelSection-AUUSJ5HQ.cjs +0 -209
- package/dist/ConversionFunnelSection-AUUSJ5HQ.cjs.map +0 -1
- package/dist/ConversionFunnelSection-D3GE4NKE.js.map +0 -1
- package/dist/DesignSystemSection-2R5BRBGO.js.map +0 -1
- package/dist/DesignSystemSection-KXIQXITF.cjs.map +0 -1
- package/dist/DetailsSection-A6PZQUQL.js.map +0 -1
- package/dist/DetailsSection-TTUZAPZZ.cjs.map +0 -1
- package/dist/FeatureSpotlightSection-B7P3JGNL.js.map +0 -1
- package/dist/FeatureSpotlightSection-WRHXS7TU.cjs +0 -210
- package/dist/FeatureSpotlightSection-WRHXS7TU.cjs.map +0 -1
- package/dist/GallerySection-VMKORC47.js.map +0 -1
- package/dist/GallerySection-WJ4PQDBI.cjs +0 -219
- package/dist/GallerySection-WJ4PQDBI.cjs.map +0 -1
- package/dist/MetricsTimelineSection-4L6DUHJ5.cjs.map +0 -1
- package/dist/MetricsTimelineSection-6BT5GNFV.js.map +0 -1
- package/dist/PerformanceSection-24TVVFZA.cjs.map +0 -1
- package/dist/PerformanceSection-MGCEIXDX.js.map +0 -1
- package/dist/ResultsSection-DFUJ5U6M.js.map +0 -1
- package/dist/ResultsSection-XLGMMQKY.cjs.map +0 -1
- package/dist/ServicesSection-D5V3Q4GR.js +0 -118
- package/dist/ServicesSection-D5V3Q4GR.js.map +0 -1
- package/dist/ServicesSection-WJMGK2MF.cjs +0 -120
- package/dist/ServicesSection-WJMGK2MF.cjs.map +0 -1
- package/dist/SiteArchitectureSection-J7BPBERH.cjs.map +0 -1
- package/dist/SiteArchitectureSection-TE22OAWV.js.map +0 -1
- package/dist/SpeedComparisonSection-EZKFQVGW.cjs.map +0 -1
- package/dist/SpeedComparisonSection-Y3K7OFZQ.js.map +0 -1
- package/dist/StrategySection-CJ7Y6OFQ.cjs +0 -174
- package/dist/StrategySection-CJ7Y6OFQ.cjs.map +0 -1
- package/dist/StrategySection-DI5RSCJU.js.map +0 -1
- package/dist/TeamSection-DZVSNZE6.cjs +0 -112
- package/dist/TeamSection-DZVSNZE6.cjs.map +0 -1
- package/dist/TeamSection-HGKFW6PQ.js +0 -107
- package/dist/TeamSection-HGKFW6PQ.js.map +0 -1
- package/dist/TechStackSection-2AQ7RGY3.js +0 -93
- package/dist/TechStackSection-2AQ7RGY3.js.map +0 -1
- package/dist/TechStackSection-VTNNZR5V.cjs +0 -95
- package/dist/TechStackSection-VTNNZR5V.cjs.map +0 -1
- package/dist/TestimonialSection-6RGSMXQB.js.map +0 -1
- package/dist/TestimonialSection-XPTFUQIN.cjs.map +0 -1
- package/dist/VideoSection-4A2HC6K6.js.map +0 -1
- package/dist/VideoSection-G3DFS7UH.cjs +0 -118
- package/dist/VideoSection-G3DFS7UH.cjs.map +0 -1
- package/dist/chunk-2Y4O3LWM.js +0 -53
- package/dist/chunk-2Y4O3LWM.js.map +0 -1
- package/dist/chunk-633UMQQH.cjs +0 -31
- package/dist/chunk-633UMQQH.cjs.map +0 -1
- package/dist/chunk-7CFFAKDM.js +0 -74
- package/dist/chunk-7CFFAKDM.js.map +0 -1
- package/dist/chunk-APG2QSMB.js +0 -131
- package/dist/chunk-APG2QSMB.js.map +0 -1
- package/dist/chunk-AVCH4LMG.cjs +0 -620
- package/dist/chunk-AVCH4LMG.cjs.map +0 -1
- package/dist/chunk-AYBUHRFS.js +0 -613
- package/dist/chunk-AYBUHRFS.js.map +0 -1
- package/dist/chunk-EE75WRCM.cjs.map +0 -1
- package/dist/chunk-IKBK7HYX.cjs +0 -79
- package/dist/chunk-IKBK7HYX.cjs.map +0 -1
- package/dist/chunk-KEOHORIH.cjs +0 -79
- package/dist/chunk-KEOHORIH.cjs.map +0 -1
- package/dist/chunk-OA5ZM4OA.cjs +0 -139
- package/dist/chunk-OA5ZM4OA.cjs.map +0 -1
- package/dist/chunk-TVFXN6W6.js.map +0 -1
- package/dist/chunk-VVTK4OL6.js +0 -28
- package/dist/chunk-VVTK4OL6.js.map +0 -1
- package/dist/chunk-XCKXHK44.js +0 -15
- package/dist/chunk-XCKXHK44.js.map +0 -1
- package/dist/chunk-XONXEFJY.cjs +0 -58
- package/dist/chunk-XONXEFJY.cjs.map +0 -1
- package/dist/chunk-XQNJED46.cjs +0 -19
- package/dist/chunk-XQNJED46.cjs.map +0 -1
- package/dist/chunk-YB4B3OMC.js +0 -74
- package/dist/chunk-YB4B3OMC.js.map +0 -1
- package/dist/portfolio/sections.cjs +0 -20
- package/dist/portfolio/sections.cjs.map +0 -1
- package/dist/portfolio/sections.d.cts +0 -42
- package/dist/portfolio/sections.d.ts +0 -42
- package/dist/portfolio/sections.js +0 -4
- package/dist/portfolio/sections.js.map +0 -1
|
@@ -0,0 +1,263 @@
|
|
|
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/ConversionFunnelSection.tsx
|
|
33
|
+
var ConversionFunnelSection_exports = {};
|
|
34
|
+
__export(ConversionFunnelSection_exports, {
|
|
35
|
+
default: () => ConversionFunnelSection
|
|
36
|
+
});
|
|
37
|
+
module.exports = __toCommonJS(ConversionFunnelSection_exports);
|
|
38
|
+
var import_react = __toESM(require("react"), 1);
|
|
39
|
+
|
|
40
|
+
// src/shared/images.ts
|
|
41
|
+
var SANITY_ASSET_REF = /^image-[A-Za-z0-9]+-\d+x\d+-[a-z0-9]+$/;
|
|
42
|
+
var FILENAME_DIMS = /-(\d{2,5})x(\d{2,5})\.[a-z0-9]+(?:\?.*)?$/i;
|
|
43
|
+
var warnedAboutRefs = false;
|
|
44
|
+
function resolvePortfolioImage(input) {
|
|
45
|
+
const raw = typeof input === "string" ? input : input?.asset?._ref ?? "";
|
|
46
|
+
const value = (raw || "").trim();
|
|
47
|
+
if (!value) return null;
|
|
48
|
+
if (SANITY_ASSET_REF.test(value)) {
|
|
49
|
+
if (!warnedAboutRefs && typeof console !== "undefined") {
|
|
50
|
+
warnedAboutRefs = true;
|
|
51
|
+
console.warn(
|
|
52
|
+
"[@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."
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
if (!/^https?:\/\//i.test(value) && !value.startsWith("/")) return null;
|
|
58
|
+
const dims = value.match(FILENAME_DIMS);
|
|
59
|
+
if (dims) {
|
|
60
|
+
const width = parseInt(dims[1], 10);
|
|
61
|
+
const height = parseInt(dims[2], 10);
|
|
62
|
+
if (width > 0 && height > 0) return { url: value, width, height };
|
|
63
|
+
}
|
|
64
|
+
return { url: value };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// src/portfolio/components/sections/ConversionFunnelSection.tsx
|
|
68
|
+
var import_ScrollReveal = __toESM(require("@sonordev/agency-site-kit/portfolio/primitives/ScrollReveal"), 1);
|
|
69
|
+
var import_AnimatedCounter = __toESM(require("@sonordev/agency-site-kit/portfolio/primitives/AnimatedCounter"), 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
|
+
var EASE = "cubic-bezier(0.25, 0.46, 0.45, 0.94)";
|
|
73
|
+
function ConversionFunnelSection({ data }) {
|
|
74
|
+
const funnelRef = (0, import_react.useRef)(null);
|
|
75
|
+
const flowLinesRef = (0, import_react.useRef)(null);
|
|
76
|
+
(0, import_react.useEffect)(() => {
|
|
77
|
+
const funnel = funnelRef.current;
|
|
78
|
+
if (!funnel) return;
|
|
79
|
+
const prefersReducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
80
|
+
if (prefersReducedMotion) return;
|
|
81
|
+
const rect = funnel.getBoundingClientRect();
|
|
82
|
+
if (rect.top < window.innerHeight && rect.bottom > 0) return;
|
|
83
|
+
const fills = Array.from(funnel.querySelectorAll(".funnel-stage-fill"));
|
|
84
|
+
const connectors = Array.from(
|
|
85
|
+
flowLinesRef.current?.querySelectorAll(".flow-connector") ?? []
|
|
86
|
+
);
|
|
87
|
+
if (fills.length === 0 && connectors.length === 0) return;
|
|
88
|
+
for (const el of [...fills, ...connectors]) {
|
|
89
|
+
el.style.transformOrigin = "left center";
|
|
90
|
+
el.style.transform = "scaleX(0)";
|
|
91
|
+
}
|
|
92
|
+
for (const el of connectors) el.style.opacity = "0";
|
|
93
|
+
const io = new IntersectionObserver(
|
|
94
|
+
([entry]) => {
|
|
95
|
+
if (!entry.isIntersecting) return;
|
|
96
|
+
io.disconnect();
|
|
97
|
+
fills.forEach((el, i) => {
|
|
98
|
+
el.style.transition = `transform 0.8s ${EASE} ${i * 0.2}s`;
|
|
99
|
+
el.style.transform = "scaleX(1)";
|
|
100
|
+
});
|
|
101
|
+
connectors.forEach((el, i) => {
|
|
102
|
+
const d = 0.3 + i * 0.15;
|
|
103
|
+
el.style.transition = `transform 0.5s ${EASE} ${d}s, opacity 0.5s ${EASE} ${d}s`;
|
|
104
|
+
el.style.transform = "scaleX(1)";
|
|
105
|
+
el.style.opacity = "1";
|
|
106
|
+
});
|
|
107
|
+
},
|
|
108
|
+
{ rootMargin: "0px 0px -30% 0px" }
|
|
109
|
+
// ≈ the old 'top 70%' trigger
|
|
110
|
+
);
|
|
111
|
+
io.observe(funnel);
|
|
112
|
+
return () => {
|
|
113
|
+
io.disconnect();
|
|
114
|
+
for (const el of [...fills, ...connectors]) {
|
|
115
|
+
el.style.transformOrigin = "";
|
|
116
|
+
el.style.transform = "";
|
|
117
|
+
el.style.transition = "";
|
|
118
|
+
el.style.opacity = "";
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
}, [data.stages.length]);
|
|
122
|
+
const maxValue = Math.max(...data.stages.map((s) => s.value), 1);
|
|
123
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
124
|
+
"section",
|
|
125
|
+
{
|
|
126
|
+
className: "w-full py-20 md:py-28",
|
|
127
|
+
style: { background: "var(--sk-bg, #0a0a0a)" },
|
|
128
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "max-w-6xl mx-auto px-6", children: [
|
|
129
|
+
(data.title || data.description) && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ScrollReveal.default, { y: 30, children: [
|
|
130
|
+
data.title && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
131
|
+
"h2",
|
|
132
|
+
{
|
|
133
|
+
className: "text-3xl md:text-4xl font-bold mb-4",
|
|
134
|
+
style: {
|
|
135
|
+
color: "var(--sk-text-primary, #ffffff)",
|
|
136
|
+
fontFamily: "var(--sk-font-heading, inherit)"
|
|
137
|
+
},
|
|
138
|
+
children: data.title
|
|
139
|
+
}
|
|
140
|
+
),
|
|
141
|
+
data.description && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
142
|
+
"p",
|
|
143
|
+
{
|
|
144
|
+
className: "text-lg mb-12 max-w-2xl",
|
|
145
|
+
style: { color: "var(--sk-text-secondary, #a1a1aa)" },
|
|
146
|
+
children: data.description
|
|
147
|
+
}
|
|
148
|
+
)
|
|
149
|
+
] }),
|
|
150
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ref: funnelRef, className: "hidden md:block", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex items-end gap-0 relative", ref: flowLinesRef, children: data.stages.map((stage, index) => {
|
|
151
|
+
const widthPercent = 60 + stage.value / maxValue * 40;
|
|
152
|
+
const stageColor = stage.color || "var(--sk-primary, #6366f1)";
|
|
153
|
+
const icon = resolvePortfolioImage(stage.icon);
|
|
154
|
+
const dropOff = index > 0 ? Math.round(
|
|
155
|
+
(data.stages[index - 1].value - stage.value) / data.stages[index - 1].value * 100
|
|
156
|
+
) : null;
|
|
157
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react.default.Fragment, { children: [
|
|
158
|
+
dropOff !== null && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col items-center justify-center px-2 shrink-0", style: { minWidth: 48 }, children: [
|
|
159
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
160
|
+
"div",
|
|
161
|
+
{
|
|
162
|
+
className: "flow-connector h-0.5 w-full mb-1",
|
|
163
|
+
style: {
|
|
164
|
+
background: `linear-gradient(to right, ${data.stages[index - 1].color || "var(--sk-primary, #6366f1)"}, ${stageColor})`,
|
|
165
|
+
willChange: "transform"
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
),
|
|
169
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
170
|
+
"span",
|
|
171
|
+
{
|
|
172
|
+
className: "text-xs font-medium whitespace-nowrap",
|
|
173
|
+
style: { color: "var(--sk-text-tertiary, #71717a)" },
|
|
174
|
+
children: [
|
|
175
|
+
"-",
|
|
176
|
+
dropOff,
|
|
177
|
+
"%"
|
|
178
|
+
]
|
|
179
|
+
}
|
|
180
|
+
)
|
|
181
|
+
] }),
|
|
182
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex-1", style: { maxWidth: `${widthPercent}%` }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_GlassCard.default, { padding: "md", hover: false, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "relative overflow-hidden rounded-lg", children: [
|
|
183
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
184
|
+
"div",
|
|
185
|
+
{
|
|
186
|
+
className: "funnel-stage-fill absolute inset-0 rounded-lg opacity-15",
|
|
187
|
+
style: {
|
|
188
|
+
background: stageColor,
|
|
189
|
+
willChange: "transform"
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
),
|
|
193
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "relative flex flex-col items-center gap-2 py-4", children: [
|
|
194
|
+
icon && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", { src: icon.url, alt: "", width: 24, height: 24, loading: "lazy", decoding: "async", className: "w-6 h-6 object-contain" }),
|
|
195
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
196
|
+
"span",
|
|
197
|
+
{
|
|
198
|
+
className: "text-xs font-semibold uppercase tracking-wider text-center",
|
|
199
|
+
style: { color: "var(--sk-text-tertiary, #71717a)" },
|
|
200
|
+
children: stage.label
|
|
201
|
+
}
|
|
202
|
+
),
|
|
203
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
204
|
+
"div",
|
|
205
|
+
{
|
|
206
|
+
className: "text-2xl font-bold",
|
|
207
|
+
style: { color: stageColor },
|
|
208
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_AnimatedCounter.default, { value: stage.value, duration: 2 })
|
|
209
|
+
}
|
|
210
|
+
)
|
|
211
|
+
] })
|
|
212
|
+
] }) }) })
|
|
213
|
+
] }, index);
|
|
214
|
+
}) }) }),
|
|
215
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "md:hidden flex flex-col gap-3", children: data.stages.map((stage, index) => {
|
|
216
|
+
const widthPercent = 60 + stage.value / maxValue * 40;
|
|
217
|
+
const stageColor = stage.color || "var(--sk-primary, #6366f1)";
|
|
218
|
+
const icon = resolvePortfolioImage(stage.icon);
|
|
219
|
+
const dropOff = index > 0 ? Math.round(
|
|
220
|
+
(data.stages[index - 1].value - stage.value) / data.stages[index - 1].value * 100
|
|
221
|
+
) : null;
|
|
222
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react.default.Fragment, { children: [
|
|
223
|
+
dropOff !== null && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center justify-center py-1", children: [
|
|
224
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M10 4v12M6 12l4 4 4-4", stroke: "var(--sk-text-tertiary, #71717a)", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }),
|
|
225
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
226
|
+
"span",
|
|
227
|
+
{
|
|
228
|
+
className: "text-xs font-medium ml-2",
|
|
229
|
+
style: { color: "var(--sk-text-tertiary, #71717a)" },
|
|
230
|
+
children: [
|
|
231
|
+
"-",
|
|
232
|
+
dropOff,
|
|
233
|
+
"% drop-off"
|
|
234
|
+
]
|
|
235
|
+
}
|
|
236
|
+
)
|
|
237
|
+
] }),
|
|
238
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ScrollReveal.default, { y: 20, delay: index * 0.1, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { width: `${widthPercent}%`, marginLeft: "auto", marginRight: "auto" }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_GlassCard.default, { padding: "md", hover: false, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center gap-4", children: [
|
|
239
|
+
icon && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", { src: icon.url, alt: "", width: 24, height: 24, loading: "lazy", decoding: "async", className: "w-6 h-6 object-contain shrink-0" }),
|
|
240
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex-1", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
241
|
+
"span",
|
|
242
|
+
{
|
|
243
|
+
className: "text-xs font-semibold uppercase tracking-wider",
|
|
244
|
+
style: { color: "var(--sk-text-tertiary, #71717a)" },
|
|
245
|
+
children: stage.label
|
|
246
|
+
}
|
|
247
|
+
) }),
|
|
248
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
249
|
+
"div",
|
|
250
|
+
{
|
|
251
|
+
className: "text-xl font-bold",
|
|
252
|
+
style: { color: stageColor },
|
|
253
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_AnimatedCounter.default, { value: stage.value, duration: 2 })
|
|
254
|
+
}
|
|
255
|
+
)
|
|
256
|
+
] }) }) }) })
|
|
257
|
+
] }, index);
|
|
258
|
+
}) })
|
|
259
|
+
] })
|
|
260
|
+
}
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
//# sourceMappingURL=ConversionFunnelSection.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/portfolio/components/sections/ConversionFunnelSection.tsx","../../../src/shared/images.ts"],"sourcesContent":["'use client';\n\nimport React, { useRef, useEffect } from 'react';\nimport type { PortfolioConversionFunnelData } from '../../../types';\nimport { resolvePortfolioImage } from '../../../shared/images';\nimport ScrollReveal from '@sonordev/agency-site-kit/portfolio/primitives/ScrollReveal';\nimport AnimatedCounter from '@sonordev/agency-site-kit/portfolio/primitives/AnimatedCounter';\nimport GlassCard from '@sonordev/agency-site-kit/portfolio/primitives/GlassCard';\n\ninterface ConversionFunnelSectionProps {\n data: PortfolioConversionFunnelData;\n}\n\nconst EASE = 'cubic-bezier(0.25, 0.46, 0.45, 0.94)'; // ≈ power2.out\n\nexport default function ConversionFunnelSection({ data }: ConversionFunnelSectionProps) {\n const funnelRef = useRef<HTMLDivElement>(null);\n const flowLinesRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const funnel = funnelRef.current;\n if (!funnel) return;\n\n const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n if (prefersReducedMotion) return;\n\n // Never re-hide a funnel already in the viewport at hydration.\n const rect = funnel.getBoundingClientRect();\n if (rect.top < window.innerHeight && rect.bottom > 0) return;\n\n // Stage fills sweep left-to-right; flow connectors follow after.\n const fills = Array.from(funnel.querySelectorAll<HTMLElement>('.funnel-stage-fill'));\n const connectors = Array.from(\n flowLinesRef.current?.querySelectorAll<HTMLElement>('.flow-connector') ?? [],\n );\n if (fills.length === 0 && connectors.length === 0) return;\n\n // Hidden state\n for (const el of [...fills, ...connectors]) {\n el.style.transformOrigin = 'left center';\n el.style.transform = 'scaleX(0)';\n }\n for (const el of connectors) el.style.opacity = '0';\n\n const io = new IntersectionObserver(\n ([entry]) => {\n if (!entry.isIntersecting) return;\n io.disconnect();\n fills.forEach((el, i) => {\n el.style.transition = `transform 0.8s ${EASE} ${i * 0.2}s`;\n el.style.transform = 'scaleX(1)';\n });\n connectors.forEach((el, i) => {\n const d = 0.3 + i * 0.15;\n el.style.transition = `transform 0.5s ${EASE} ${d}s, opacity 0.5s ${EASE} ${d}s`;\n el.style.transform = 'scaleX(1)';\n el.style.opacity = '1';\n });\n },\n { rootMargin: '0px 0px -30% 0px' }, // ≈ the old 'top 70%' trigger\n );\n io.observe(funnel);\n\n return () => {\n io.disconnect();\n for (const el of [...fills, ...connectors]) {\n el.style.transformOrigin = '';\n el.style.transform = '';\n el.style.transition = '';\n el.style.opacity = '';\n }\n };\n }, [data.stages.length]);\n\n const maxValue = Math.max(...data.stages.map((s) => s.value), 1);\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-6xl 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 {/* Desktop: horizontal funnel */}\n <div ref={funnelRef} className=\"hidden md:block\">\n <div className=\"flex items-end gap-0 relative\" ref={flowLinesRef}>\n {data.stages.map((stage, index) => {\n const widthPercent = 60 + (stage.value / maxValue) * 40;\n const stageColor = stage.color || 'var(--sk-primary, #6366f1)';\n const icon = resolvePortfolioImage(stage.icon);\n const dropOff =\n index > 0\n ? Math.round(\n ((data.stages[index - 1].value - stage.value) /\n data.stages[index - 1].value) *\n 100,\n )\n : null;\n\n return (\n <React.Fragment key={index}>\n {/* Drop-off connector */}\n {dropOff !== null && (\n <div className=\"flex flex-col items-center justify-center px-2 shrink-0\" style={{ minWidth: 48 }}>\n <div\n className=\"flow-connector h-0.5 w-full mb-1\"\n style={{\n background: `linear-gradient(to right, ${data.stages[index - 1].color || 'var(--sk-primary, #6366f1)'}, ${stageColor})`,\n willChange: 'transform',\n }}\n />\n <span\n className=\"text-xs font-medium whitespace-nowrap\"\n style={{ color: 'var(--sk-text-tertiary, #71717a)' }}\n >\n -{dropOff}%\n </span>\n </div>\n )}\n\n {/* Stage card */}\n <div className=\"flex-1\" style={{ maxWidth: `${widthPercent}%` }}>\n <GlassCard padding=\"md\" hover={false}>\n <div className=\"relative overflow-hidden rounded-lg\">\n {/* Animated fill bar */}\n <div\n className=\"funnel-stage-fill absolute inset-0 rounded-lg opacity-15\"\n style={{\n background: stageColor,\n willChange: 'transform',\n }}\n />\n <div className=\"relative flex flex-col items-center gap-2 py-4\">\n {/* Icon */}\n {icon && (\n <img src={icon.url} alt=\"\" width={24} height={24} loading=\"lazy\" decoding=\"async\" className=\"w-6 h-6 object-contain\" />\n )}\n <span\n className=\"text-xs font-semibold uppercase tracking-wider text-center\"\n style={{ color: 'var(--sk-text-tertiary, #71717a)' }}\n >\n {stage.label}\n </span>\n <div\n className=\"text-2xl font-bold\"\n style={{ color: stageColor }}\n >\n <AnimatedCounter value={stage.value} duration={2} />\n </div>\n </div>\n </div>\n </GlassCard>\n </div>\n </React.Fragment>\n );\n })}\n </div>\n </div>\n\n {/* Mobile: vertical funnel */}\n <div className=\"md:hidden flex flex-col gap-3\">\n {data.stages.map((stage, index) => {\n const widthPercent = 60 + (stage.value / maxValue) * 40;\n const stageColor = stage.color || 'var(--sk-primary, #6366f1)';\n const icon = resolvePortfolioImage(stage.icon);\n const dropOff =\n index > 0\n ? Math.round(\n ((data.stages[index - 1].value - stage.value) /\n data.stages[index - 1].value) *\n 100,\n )\n : null;\n\n return (\n <React.Fragment key={index}>\n {dropOff !== null && (\n <div className=\"flex items-center justify-center py-1\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" fill=\"none\">\n <path d=\"M10 4v12M6 12l4 4 4-4\" stroke=\"var(--sk-text-tertiary, #71717a)\" strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n <span\n className=\"text-xs font-medium ml-2\"\n style={{ color: 'var(--sk-text-tertiary, #71717a)' }}\n >\n -{dropOff}% drop-off\n </span>\n </div>\n )}\n <ScrollReveal y={20} delay={index * 0.1}>\n <div style={{ width: `${widthPercent}%`, marginLeft: 'auto', marginRight: 'auto' }}>\n <GlassCard padding=\"md\" hover={false}>\n <div className=\"flex items-center gap-4\">\n {icon && (\n <img src={icon.url} alt=\"\" width={24} height={24} loading=\"lazy\" decoding=\"async\" className=\"w-6 h-6 object-contain shrink-0\" />\n )}\n <div className=\"flex-1\">\n <span\n className=\"text-xs font-semibold uppercase tracking-wider\"\n style={{ color: 'var(--sk-text-tertiary, #71717a)' }}\n >\n {stage.label}\n </span>\n </div>\n <div\n className=\"text-xl font-bold\"\n style={{ color: stageColor }}\n >\n <AnimatedCounter value={stage.value} duration={2} />\n </div>\n </div>\n </GlassCard>\n </div>\n </ScrollReveal>\n </React.Fragment>\n );\n })}\n </div>\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","React","GlassCard","AnimatedCounter"]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { b as PortfolioConversionFunnelData } from '../../types-D0nb6KAx.cjs';
|
|
3
|
+
|
|
4
|
+
interface ConversionFunnelSectionProps {
|
|
5
|
+
data: PortfolioConversionFunnelData;
|
|
6
|
+
}
|
|
7
|
+
declare function ConversionFunnelSection({ data }: ConversionFunnelSectionProps): React.JSX.Element;
|
|
8
|
+
|
|
9
|
+
export { ConversionFunnelSection as default };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { b as PortfolioConversionFunnelData } from '../../types-D0nb6KAx.js';
|
|
3
|
+
|
|
4
|
+
interface ConversionFunnelSectionProps {
|
|
5
|
+
data: PortfolioConversionFunnelData;
|
|
6
|
+
}
|
|
7
|
+
declare function ConversionFunnelSection({ data }: ConversionFunnelSectionProps): React.JSX.Element;
|
|
8
|
+
|
|
9
|
+
export { ConversionFunnelSection as default };
|
package/dist/{ConversionFunnelSection-D3GE4NKE.js → portfolio/sections/ConversionFunnelSection.js}
RENAMED
|
@@ -1,62 +1,64 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import gsap from 'gsap';
|
|
7
|
-
import { ScrollTrigger } from 'gsap/ScrollTrigger';
|
|
8
|
-
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
2
|
+
"use client";
|
|
3
|
+
import {
|
|
4
|
+
resolvePortfolioImage
|
|
5
|
+
} from "../../chunk-QDXZ27R7.js";
|
|
9
6
|
|
|
7
|
+
// src/portfolio/components/sections/ConversionFunnelSection.tsx
|
|
8
|
+
import React, { useRef, useEffect } from "react";
|
|
9
|
+
import ScrollReveal from "@sonordev/agency-site-kit/portfolio/primitives/ScrollReveal";
|
|
10
|
+
import AnimatedCounter from "@sonordev/agency-site-kit/portfolio/primitives/AnimatedCounter";
|
|
11
|
+
import GlassCard from "@sonordev/agency-site-kit/portfolio/primitives/GlassCard";
|
|
12
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
13
|
+
var EASE = "cubic-bezier(0.25, 0.46, 0.45, 0.94)";
|
|
10
14
|
function ConversionFunnelSection({ data }) {
|
|
11
15
|
const funnelRef = useRef(null);
|
|
12
16
|
const flowLinesRef = useRef(null);
|
|
13
17
|
useEffect(() => {
|
|
14
|
-
|
|
18
|
+
const funnel = funnelRef.current;
|
|
19
|
+
if (!funnel) return;
|
|
15
20
|
const prefersReducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
16
21
|
if (prefersReducedMotion) return;
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
);
|
|
22
|
+
const rect = funnel.getBoundingClientRect();
|
|
23
|
+
if (rect.top < window.innerHeight && rect.bottom > 0) return;
|
|
24
|
+
const fills = Array.from(funnel.querySelectorAll(".funnel-stage-fill"));
|
|
25
|
+
const connectors = Array.from(
|
|
26
|
+
flowLinesRef.current?.querySelectorAll(".flow-connector") ?? []
|
|
27
|
+
);
|
|
28
|
+
if (fills.length === 0 && connectors.length === 0) return;
|
|
29
|
+
for (const el of [...fills, ...connectors]) {
|
|
30
|
+
el.style.transformOrigin = "left center";
|
|
31
|
+
el.style.transform = "scaleX(0)";
|
|
32
|
+
}
|
|
33
|
+
for (const el of connectors) el.style.opacity = "0";
|
|
34
|
+
const io = new IntersectionObserver(
|
|
35
|
+
([entry]) => {
|
|
36
|
+
if (!entry.isIntersecting) return;
|
|
37
|
+
io.disconnect();
|
|
38
|
+
fills.forEach((el, i) => {
|
|
39
|
+
el.style.transition = `transform 0.8s ${EASE} ${i * 0.2}s`;
|
|
40
|
+
el.style.transform = "scaleX(1)";
|
|
41
|
+
});
|
|
42
|
+
connectors.forEach((el, i) => {
|
|
43
|
+
const d = 0.3 + i * 0.15;
|
|
44
|
+
el.style.transition = `transform 0.5s ${EASE} ${d}s, opacity 0.5s ${EASE} ${d}s`;
|
|
45
|
+
el.style.transform = "scaleX(1)";
|
|
46
|
+
el.style.opacity = "1";
|
|
47
|
+
});
|
|
48
|
+
},
|
|
49
|
+
{ rootMargin: "0px 0px -30% 0px" }
|
|
50
|
+
// ≈ the old 'top 70%' trigger
|
|
51
|
+
);
|
|
52
|
+
io.observe(funnel);
|
|
53
|
+
return () => {
|
|
54
|
+
io.disconnect();
|
|
55
|
+
for (const el of [...fills, ...connectors]) {
|
|
56
|
+
el.style.transformOrigin = "";
|
|
57
|
+
el.style.transform = "";
|
|
58
|
+
el.style.transition = "";
|
|
59
|
+
el.style.opacity = "";
|
|
57
60
|
}
|
|
58
|
-
}
|
|
59
|
-
return () => ctx.revert();
|
|
61
|
+
};
|
|
60
62
|
}, [data.stages.length]);
|
|
61
63
|
const maxValue = Math.max(...data.stages.map((s) => s.value), 1);
|
|
62
64
|
return /* @__PURE__ */ jsx(
|
|
@@ -89,6 +91,7 @@ function ConversionFunnelSection({ data }) {
|
|
|
89
91
|
/* @__PURE__ */ jsx("div", { ref: funnelRef, className: "hidden md:block", children: /* @__PURE__ */ jsx("div", { className: "flex items-end gap-0 relative", ref: flowLinesRef, children: data.stages.map((stage, index) => {
|
|
90
92
|
const widthPercent = 60 + stage.value / maxValue * 40;
|
|
91
93
|
const stageColor = stage.color || "var(--sk-primary, #6366f1)";
|
|
94
|
+
const icon = resolvePortfolioImage(stage.icon);
|
|
92
95
|
const dropOff = index > 0 ? Math.round(
|
|
93
96
|
(data.stages[index - 1].value - stage.value) / data.stages[index - 1].value * 100
|
|
94
97
|
) : null;
|
|
@@ -129,7 +132,7 @@ function ConversionFunnelSection({ data }) {
|
|
|
129
132
|
}
|
|
130
133
|
),
|
|
131
134
|
/* @__PURE__ */ jsxs("div", { className: "relative flex flex-col items-center gap-2 py-4", children: [
|
|
132
|
-
|
|
135
|
+
icon && /* @__PURE__ */ jsx("img", { src: icon.url, alt: "", width: 24, height: 24, loading: "lazy", decoding: "async", className: "w-6 h-6 object-contain" }),
|
|
133
136
|
/* @__PURE__ */ jsx(
|
|
134
137
|
"span",
|
|
135
138
|
{
|
|
@@ -153,6 +156,7 @@ function ConversionFunnelSection({ data }) {
|
|
|
153
156
|
/* @__PURE__ */ jsx("div", { className: "md:hidden flex flex-col gap-3", children: data.stages.map((stage, index) => {
|
|
154
157
|
const widthPercent = 60 + stage.value / maxValue * 40;
|
|
155
158
|
const stageColor = stage.color || "var(--sk-primary, #6366f1)";
|
|
159
|
+
const icon = resolvePortfolioImage(stage.icon);
|
|
156
160
|
const dropOff = index > 0 ? Math.round(
|
|
157
161
|
(data.stages[index - 1].value - stage.value) / data.stages[index - 1].value * 100
|
|
158
162
|
) : null;
|
|
@@ -173,7 +177,7 @@ function ConversionFunnelSection({ data }) {
|
|
|
173
177
|
)
|
|
174
178
|
] }),
|
|
175
179
|
/* @__PURE__ */ jsx(ScrollReveal, { y: 20, delay: index * 0.1, children: /* @__PURE__ */ jsx("div", { style: { width: `${widthPercent}%`, marginLeft: "auto", marginRight: "auto" }, children: /* @__PURE__ */ jsx(GlassCard, { padding: "md", hover: false, children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4", children: [
|
|
176
|
-
|
|
180
|
+
icon && /* @__PURE__ */ jsx("img", { src: icon.url, alt: "", width: 24, height: 24, loading: "lazy", decoding: "async", className: "w-6 h-6 object-contain shrink-0" }),
|
|
177
181
|
/* @__PURE__ */ jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsx(
|
|
178
182
|
"span",
|
|
179
183
|
{
|
|
@@ -197,7 +201,7 @@ function ConversionFunnelSection({ data }) {
|
|
|
197
201
|
}
|
|
198
202
|
);
|
|
199
203
|
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
//# sourceMappingURL=ConversionFunnelSection
|
|
204
|
+
export {
|
|
205
|
+
ConversionFunnelSection as default
|
|
206
|
+
};
|
|
207
|
+
//# sourceMappingURL=ConversionFunnelSection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/portfolio/components/sections/ConversionFunnelSection.tsx"],"sourcesContent":["'use client';\n\nimport React, { useRef, useEffect } from 'react';\nimport type { PortfolioConversionFunnelData } from '../../../types';\nimport { resolvePortfolioImage } from '../../../shared/images';\nimport ScrollReveal from '@sonordev/agency-site-kit/portfolio/primitives/ScrollReveal';\nimport AnimatedCounter from '@sonordev/agency-site-kit/portfolio/primitives/AnimatedCounter';\nimport GlassCard from '@sonordev/agency-site-kit/portfolio/primitives/GlassCard';\n\ninterface ConversionFunnelSectionProps {\n data: PortfolioConversionFunnelData;\n}\n\nconst EASE = 'cubic-bezier(0.25, 0.46, 0.45, 0.94)'; // ≈ power2.out\n\nexport default function ConversionFunnelSection({ data }: ConversionFunnelSectionProps) {\n const funnelRef = useRef<HTMLDivElement>(null);\n const flowLinesRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const funnel = funnelRef.current;\n if (!funnel) return;\n\n const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n if (prefersReducedMotion) return;\n\n // Never re-hide a funnel already in the viewport at hydration.\n const rect = funnel.getBoundingClientRect();\n if (rect.top < window.innerHeight && rect.bottom > 0) return;\n\n // Stage fills sweep left-to-right; flow connectors follow after.\n const fills = Array.from(funnel.querySelectorAll<HTMLElement>('.funnel-stage-fill'));\n const connectors = Array.from(\n flowLinesRef.current?.querySelectorAll<HTMLElement>('.flow-connector') ?? [],\n );\n if (fills.length === 0 && connectors.length === 0) return;\n\n // Hidden state\n for (const el of [...fills, ...connectors]) {\n el.style.transformOrigin = 'left center';\n el.style.transform = 'scaleX(0)';\n }\n for (const el of connectors) el.style.opacity = '0';\n\n const io = new IntersectionObserver(\n ([entry]) => {\n if (!entry.isIntersecting) return;\n io.disconnect();\n fills.forEach((el, i) => {\n el.style.transition = `transform 0.8s ${EASE} ${i * 0.2}s`;\n el.style.transform = 'scaleX(1)';\n });\n connectors.forEach((el, i) => {\n const d = 0.3 + i * 0.15;\n el.style.transition = `transform 0.5s ${EASE} ${d}s, opacity 0.5s ${EASE} ${d}s`;\n el.style.transform = 'scaleX(1)';\n el.style.opacity = '1';\n });\n },\n { rootMargin: '0px 0px -30% 0px' }, // ≈ the old 'top 70%' trigger\n );\n io.observe(funnel);\n\n return () => {\n io.disconnect();\n for (const el of [...fills, ...connectors]) {\n el.style.transformOrigin = '';\n el.style.transform = '';\n el.style.transition = '';\n el.style.opacity = '';\n }\n };\n }, [data.stages.length]);\n\n const maxValue = Math.max(...data.stages.map((s) => s.value), 1);\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-6xl 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 {/* Desktop: horizontal funnel */}\n <div ref={funnelRef} className=\"hidden md:block\">\n <div className=\"flex items-end gap-0 relative\" ref={flowLinesRef}>\n {data.stages.map((stage, index) => {\n const widthPercent = 60 + (stage.value / maxValue) * 40;\n const stageColor = stage.color || 'var(--sk-primary, #6366f1)';\n const icon = resolvePortfolioImage(stage.icon);\n const dropOff =\n index > 0\n ? Math.round(\n ((data.stages[index - 1].value - stage.value) /\n data.stages[index - 1].value) *\n 100,\n )\n : null;\n\n return (\n <React.Fragment key={index}>\n {/* Drop-off connector */}\n {dropOff !== null && (\n <div className=\"flex flex-col items-center justify-center px-2 shrink-0\" style={{ minWidth: 48 }}>\n <div\n className=\"flow-connector h-0.5 w-full mb-1\"\n style={{\n background: `linear-gradient(to right, ${data.stages[index - 1].color || 'var(--sk-primary, #6366f1)'}, ${stageColor})`,\n willChange: 'transform',\n }}\n />\n <span\n className=\"text-xs font-medium whitespace-nowrap\"\n style={{ color: 'var(--sk-text-tertiary, #71717a)' }}\n >\n -{dropOff}%\n </span>\n </div>\n )}\n\n {/* Stage card */}\n <div className=\"flex-1\" style={{ maxWidth: `${widthPercent}%` }}>\n <GlassCard padding=\"md\" hover={false}>\n <div className=\"relative overflow-hidden rounded-lg\">\n {/* Animated fill bar */}\n <div\n className=\"funnel-stage-fill absolute inset-0 rounded-lg opacity-15\"\n style={{\n background: stageColor,\n willChange: 'transform',\n }}\n />\n <div className=\"relative flex flex-col items-center gap-2 py-4\">\n {/* Icon */}\n {icon && (\n <img src={icon.url} alt=\"\" width={24} height={24} loading=\"lazy\" decoding=\"async\" className=\"w-6 h-6 object-contain\" />\n )}\n <span\n className=\"text-xs font-semibold uppercase tracking-wider text-center\"\n style={{ color: 'var(--sk-text-tertiary, #71717a)' }}\n >\n {stage.label}\n </span>\n <div\n className=\"text-2xl font-bold\"\n style={{ color: stageColor }}\n >\n <AnimatedCounter value={stage.value} duration={2} />\n </div>\n </div>\n </div>\n </GlassCard>\n </div>\n </React.Fragment>\n );\n })}\n </div>\n </div>\n\n {/* Mobile: vertical funnel */}\n <div className=\"md:hidden flex flex-col gap-3\">\n {data.stages.map((stage, index) => {\n const widthPercent = 60 + (stage.value / maxValue) * 40;\n const stageColor = stage.color || 'var(--sk-primary, #6366f1)';\n const icon = resolvePortfolioImage(stage.icon);\n const dropOff =\n index > 0\n ? Math.round(\n ((data.stages[index - 1].value - stage.value) /\n data.stages[index - 1].value) *\n 100,\n )\n : null;\n\n return (\n <React.Fragment key={index}>\n {dropOff !== null && (\n <div className=\"flex items-center justify-center py-1\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" fill=\"none\">\n <path d=\"M10 4v12M6 12l4 4 4-4\" stroke=\"var(--sk-text-tertiary, #71717a)\" strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n <span\n className=\"text-xs font-medium ml-2\"\n style={{ color: 'var(--sk-text-tertiary, #71717a)' }}\n >\n -{dropOff}% drop-off\n </span>\n </div>\n )}\n <ScrollReveal y={20} delay={index * 0.1}>\n <div style={{ width: `${widthPercent}%`, marginLeft: 'auto', marginRight: 'auto' }}>\n <GlassCard padding=\"md\" hover={false}>\n <div className=\"flex items-center gap-4\">\n {icon && (\n <img src={icon.url} alt=\"\" width={24} height={24} loading=\"lazy\" decoding=\"async\" className=\"w-6 h-6 object-contain shrink-0\" />\n )}\n <div className=\"flex-1\">\n <span\n className=\"text-xs font-semibold uppercase tracking-wider\"\n style={{ color: 'var(--sk-text-tertiary, #71717a)' }}\n >\n {stage.label}\n </span>\n </div>\n <div\n className=\"text-xl font-bold\"\n style={{ color: stageColor }}\n >\n <AnimatedCounter value={stage.value} duration={2} />\n </div>\n </div>\n </GlassCard>\n </div>\n </ScrollReveal>\n </React.Fragment>\n );\n })}\n </div>\n </div>\n </section>\n );\n}\n"],"mappings":"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","names":[]}
|
package/dist/{DesignSystemSection-KXIQXITF.cjs → portfolio/sections/DesignSystemSection.cjs}
RENAMED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
var
|
|
3
|
+
var ScrollReveal = require('@sonordev/agency-site-kit/portfolio/primitives/ScrollReveal');
|
|
4
|
+
var GlassCard = require('@sonordev/agency-site-kit/portfolio/primitives/GlassCard');
|
|
5
5
|
var jsxRuntime = require('react/jsx-runtime');
|
|
6
6
|
|
|
7
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
8
|
+
|
|
9
|
+
var ScrollReveal__default = /*#__PURE__*/_interopDefault(ScrollReveal);
|
|
10
|
+
var GlassCard__default = /*#__PURE__*/_interopDefault(GlassCard);
|
|
11
|
+
|
|
12
|
+
// src/portfolio/components/sections/DesignSystemSection.tsx
|
|
7
13
|
function isLightColor(hex) {
|
|
8
14
|
const c = hex.replace("#", "");
|
|
9
15
|
const r = parseInt(c.substring(0, 2), 16);
|
|
@@ -22,7 +28,7 @@ function DesignSystemSection({ data }) {
|
|
|
22
28
|
className: "w-full py-20 md:py-28",
|
|
23
29
|
style: { background: "var(--sk-bg, #0a0a0a)" },
|
|
24
30
|
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-5xl mx-auto px-6", children: [
|
|
25
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
31
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ScrollReveal__default.default, { y: 30, children: [
|
|
26
32
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
27
33
|
"h2",
|
|
28
34
|
{
|
|
@@ -44,7 +50,7 @@ function DesignSystemSection({ data }) {
|
|
|
44
50
|
)
|
|
45
51
|
] }),
|
|
46
52
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-8", children: [
|
|
47
|
-
colors.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
53
|
+
colors.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(ScrollReveal__default.default, { y: 20, children: /* @__PURE__ */ jsxRuntime.jsxs(GlassCard__default.default, { padding: "lg", hover: false, children: [
|
|
48
54
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
49
55
|
"h3",
|
|
50
56
|
{
|
|
@@ -85,7 +91,7 @@ function DesignSystemSection({ data }) {
|
|
|
85
91
|
)
|
|
86
92
|
] }, ci)) })
|
|
87
93
|
] }) }),
|
|
88
|
-
fonts.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
94
|
+
fonts.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(ScrollReveal__default.default, { y: 20, delay: 0.1, children: /* @__PURE__ */ jsxRuntime.jsxs(GlassCard__default.default, { padding: "lg", hover: false, children: [
|
|
89
95
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
90
96
|
"h3",
|
|
91
97
|
{
|
|
@@ -170,5 +176,5 @@ function DesignSystemSection({ data }) {
|
|
|
170
176
|
}
|
|
171
177
|
|
|
172
178
|
module.exports = DesignSystemSection;
|
|
173
|
-
//# sourceMappingURL=DesignSystemSection
|
|
174
|
-
//# sourceMappingURL=DesignSystemSection
|
|
179
|
+
//# sourceMappingURL=DesignSystemSection.cjs.map
|
|
180
|
+
//# sourceMappingURL=DesignSystemSection.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/portfolio/components/sections/DesignSystemSection.tsx"],"names":["jsx","jsxs","ScrollReveal","GlassCard"],"mappings":";;;;;;;;;;;;AA8BA,SAAS,aAAa,GAAA,EAAsB;AAC1C,EAAA,MAAM,CAAA,GAAI,GAAA,CAAI,OAAA,CAAQ,GAAA,EAAK,EAAE,CAAA;AAC7B,EAAA,MAAM,IAAI,QAAA,CAAS,CAAA,CAAE,UAAU,CAAA,EAAG,CAAC,GAAG,EAAE,CAAA;AACxC,EAAA,MAAM,IAAI,QAAA,CAAS,CAAA,CAAE,UAAU,CAAA,EAAG,CAAC,GAAG,EAAE,CAAA;AACxC,EAAA,MAAM,IAAI,QAAA,CAAS,CAAA,CAAE,UAAU,CAAA,EAAG,CAAC,GAAG,EAAE,CAAA;AACxC,EAAA,OAAA,CAAQ,IAAI,GAAA,GAAM,CAAA,GAAI,GAAA,GAAM,CAAA,GAAI,OAAO,GAAA,GAAO,GAAA;AAChD;AAEe,SAAR,mBAAA,CAAqC,EAAE,IAAA,EAAK,EAA6B;AAC9E,EAAA,MAAM,EAAE,MAAA,EAAQ,KAAA,EAAM,GAAI,IAAA;AAC1B,EAAA,MAAM,WAAA,GAAc,KAAA,CAAM,IAAA,CAAK,CAAC,CAAA,KAAM,EAAE,KAAA,KAAU,SAAS,CAAA,IAAK,KAAA,CAAM,CAAC,CAAA;AACvE,EAAA,MAAM,QAAA,GAAW,KAAA,CAAM,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,KAAA,KAAU,MAAM,CAAA,IAAK,KAAA,CAAM,CAAC,CAAA,IAAK,MAAM,CAAC,CAAA;AAE7E,EAAA,IAAI,CAAC,MAAA,CAAO,MAAA,IAAU,CAAC,KAAA,CAAM,QAAQ,OAAO,IAAA;AAE5C,EAAA,uBACEA,cAAA;AAAA,IAAC,SAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAU,uBAAA;AAAA,MACV,KAAA,EAAO,EAAE,UAAA,EAAY,uBAAA,EAAwB;AAAA,MAE7C,QAAA,kBAAAC,eAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,wBAAA,EACb,QAAA,EAAA;AAAA,wBAAAA,eAAA,CAACC,6BAAA,EAAA,EAAa,GAAG,EAAA,EACf,QAAA,EAAA;AAAA,0BAAAF,cAAA;AAAA,YAAC,IAAA;AAAA,YAAA;AAAA,cACC,SAAA,EAAU,qCAAA;AAAA,cACV,KAAA,EAAO;AAAA,gBACL,KAAA,EAAO,iCAAA;AAAA,gBACP,UAAA,EAAY;AAAA,eACd;AAAA,cACD,QAAA,EAAA;AAAA;AAAA,WAED;AAAA,0BACAA,cAAA;AAAA,YAAC,GAAA;AAAA,YAAA;AAAA,cACC,SAAA,EAAU,yBAAA;AAAA,cACV,KAAA,EAAO,EAAE,KAAA,EAAO,mCAAA,EAAoC;AAAA,cACrD,QAAA,EAAA;AAAA;AAAA;AAED,SAAA,EACF,CAAA;AAAA,wBAEAC,eAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,uCAAA,EAEZ,QAAA,EAAA;AAAA,UAAA,MAAA,CAAO,MAAA,GAAS,CAAA,oBACfD,cAAA,CAACE,6BAAA,EAAA,EAAa,CAAA,EAAG,EAAA,EACf,QAAA,kBAAAD,eAAA,CAACE,0BAAA,EAAA,EAAU,OAAA,EAAQ,IAAA,EAAK,KAAA,EAAO,KAAA,EAC7B,QAAA,EAAA;AAAA,4BAAAH,cAAA;AAAA,cAAC,IAAA;AAAA,cAAA;AAAA,gBACC,SAAA,EAAU,8BAAA;AAAA,gBACV,KAAA,EAAO,EAAE,KAAA,EAAO,iCAAA,EAAkC;AAAA,gBACnD,QAAA,EAAA;AAAA;AAAA,aAED;AAAA,4BACAA,cAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,wBAAA,EACZ,QAAA,EAAA,MAAA,CAAO,GAAA,CAAI,CAAC,KAAA,EAAO,EAAA,qBAClBC,eAAA,CAAC,KAAA,EAAA,EAAa,SAAA,EAAU,kCAAA,EACtB,QAAA,EAAA;AAAA,8BAAAD,cAAA;AAAA,gBAAC,KAAA;AAAA,gBAAA;AAAA,kBACC,SAAA,EAAU,2EAAA;AAAA,kBACV,KAAA,EAAO;AAAA,oBACL,YAAY,KAAA,CAAM,GAAA;AAAA,oBAClB,WAAA,EAAa;AAAA,mBACf;AAAA,kBAEA,QAAA,kBAAAA,cAAA;AAAA,oBAAC,MAAA;AAAA,oBAAA;AAAA,sBACC,SAAA,EAAU,yDAAA;AAAA,sBACV,KAAA,EAAO;AAAA,wBACL,KAAA,EAAO,YAAA,CAAa,KAAA,CAAM,GAAG,IAAI,MAAA,GAAS,MAAA;AAAA,wBAC1C,UAAA,EAAY,YAAA,CAAa,KAAA,CAAM,GAAG,IAAI,kBAAA,GAAqB;AAAA,uBAC7D;AAAA,sBAEC,QAAA,EAAA,KAAA,CAAM;AAAA;AAAA;AACT;AAAA,eACF;AAAA,cACC,MAAM,OAAA,oBACLA,cAAA;AAAA,gBAAC,MAAA;AAAA,gBAAA;AAAA,kBACC,SAAA,EAAU,wBAAA;AAAA,kBACV,KAAA,EAAO,EAAE,KAAA,EAAO,kCAAA,EAAmC;AAAA,kBAElD,QAAA,EAAA,KAAA,CAAM;AAAA;AAAA;AACT,aAAA,EAAA,EAxBM,EA0BV,CACD,CAAA,EACH;AAAA,WAAA,EACF,CAAA,EACF,CAAA;AAAA,UAID,KAAA,CAAM,MAAA,GAAS,CAAA,oBACdA,cAAA,CAACE,iCAAa,CAAA,EAAG,EAAA,EAAI,KAAA,EAAO,GAAA,EAC1B,QAAA,kBAAAD,eAAA,CAACE,0BAAA,EAAA,EAAU,OAAA,EAAQ,IAAA,EAAK,OAAO,KAAA,EAC7B,QAAA,EAAA;AAAA,4BAAAH,cAAA;AAAA,cAAC,IAAA;AAAA,cAAA;AAAA,gBACC,SAAA,EAAU,8BAAA;AAAA,gBACV,KAAA,EAAO,EAAE,KAAA,EAAO,iCAAA,EAAkC;AAAA,gBACnD,QAAA,EAAA;AAAA;AAAA,aAED;AAAA,4BACAC,eAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,qBAAA,EACZ,QAAA,EAAA;AAAA,cAAA,WAAA,oCACE,KAAA,EAAA,EACC,QAAA,EAAA;AAAA,gCAAAD,cAAA;AAAA,kBAAC,MAAA;AAAA,kBAAA;AAAA,oBACC,SAAA,EAAU,6DAAA;AAAA,oBACV,KAAA,EAAO,EAAE,KAAA,EAAO,kCAAA,EAAmC;AAAA,oBACpD,QAAA,EAAA;AAAA;AAAA,iBAED;AAAA,gCACAA,cAAA;AAAA,kBAAC,GAAA;AAAA,kBAAA;AAAA,oBACC,SAAA,EAAU,oBAAA;AAAA,oBACV,KAAA,EAAO;AAAA,sBACL,KAAA,EAAO,iCAAA;AAAA,sBACP,YAAY,WAAA,CAAY;AAAA,qBAC1B;AAAA,oBAEC,QAAA,EAAA,WAAA,CAAY;AAAA;AAAA,iBACf;AAAA,gBACC,WAAA,CAAY,OAAA,IAAW,WAAA,CAAY,OAAA,CAAQ,SAAS,CAAA,oBACnDA,cAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,iBAAA,EACZ,QAAA,EAAA,WAAA,CAAY,OAAA,CAAQ,GAAA,CAAI,CAAC,CAAA,qBACxBA,cAAA;AAAA,kBAAC,MAAA;AAAA,kBAAA;AAAA,oBAEC,SAAA,EAAU,6BAAA;AAAA,oBACV,KAAA,EAAO;AAAA,sBACL,UAAA,EAAY,uEAAA;AAAA,sBACZ,KAAA,EAAO,kCAAA;AAAA,sBACP,UAAA,EAAY;AAAA,qBACd;AAAA,oBAEC,QAAA,EAAA;AAAA,mBAAA;AAAA,kBARI;AAAA,iBAUR,CAAA,EACH;AAAA,eAAA,EAEJ,CAAA;AAAA,cAED,YAAY,QAAA,CAAS,MAAA,KAAW,WAAA,EAAa,MAAA,oCAC3C,KAAA,EAAA,EACC,QAAA,EAAA;AAAA,gCAAAA,cAAA;AAAA,kBAAC,MAAA;AAAA,kBAAA;AAAA,oBACC,SAAA,EAAU,6DAAA;AAAA,oBACV,KAAA,EAAO,EAAE,KAAA,EAAO,kCAAA,EAAmC;AAAA,oBACpD,QAAA,EAAA;AAAA;AAAA,iBAED;AAAA,gCACAA,cAAA;AAAA,kBAAC,GAAA;AAAA,kBAAA;AAAA,oBACC,SAAA,EAAU,SAAA;AAAA,oBACV,KAAA,EAAO;AAAA,sBACL,KAAA,EAAO,iCAAA;AAAA,sBACP,YAAY,QAAA,CAAS;AAAA,qBACvB;AAAA,oBAEC,QAAA,EAAA,QAAA,CAAS;AAAA;AAAA,iBACZ;AAAA,gCACAA,cAAA;AAAA,kBAAC,GAAA;AAAA,kBAAA;AAAA,oBACC,SAAA,EAAU,8BAAA;AAAA,oBACV,KAAA,EAAO;AAAA,sBACL,KAAA,EAAO,kCAAA;AAAA,sBACP,YAAY,QAAA,CAAS;AAAA,qBACvB;AAAA,oBACD,QAAA,EAAA;AAAA;AAAA;AAED,eAAA,EACF;AAAA,aAAA,EAEJ;AAAA,WAAA,EACF,CAAA,EACF;AAAA,SAAA,EAEJ;AAAA,OAAA,EACF;AAAA;AAAA,GACF;AAEJ","file":"DesignSystemSection.cjs","sourcesContent":["// Server component — renders on the server; the client primitives below cross the\n// RSC boundary via package subpaths (never import them relatively here).\nimport React from 'react';\nimport ScrollReveal from '@sonordev/agency-site-kit/portfolio/primitives/ScrollReveal';\nimport GlassCard from '@sonordev/agency-site-kit/portfolio/primitives/GlassCard';\n\ninterface ColorEntry {\n hex: string;\n usageCount?: number;\n context?: string;\n}\n\ninterface FontEntry {\n family: string;\n weights?: number[];\n usage?: string; // 'heading' | 'body' | 'accent'\n}\n\nexport interface DesignSystemData {\n colors: ColorEntry[];\n fonts: FontEntry[];\n headingStyles?: { font?: string; size?: string; weight?: string | number };\n bodyStyles?: { font?: string; size?: string; lineHeight?: string };\n}\n\ninterface DesignSystemSectionProps {\n data: DesignSystemData;\n}\n\n/** Determine if a hex color is light (needs dark text label) */\nfunction isLightColor(hex: string): boolean {\n const c = hex.replace('#', '');\n const r = parseInt(c.substring(0, 2), 16);\n const g = parseInt(c.substring(2, 4), 16);\n const b = parseInt(c.substring(4, 6), 16);\n return (r * 299 + g * 587 + b * 114) / 1000 > 140;\n}\n\nexport default function DesignSystemSection({ data }: DesignSystemSectionProps) {\n const { colors, fonts } = data;\n const headingFont = fonts.find((f) => f.usage === 'heading') || fonts[0];\n const bodyFont = fonts.find((f) => f.usage === 'body') || fonts[1] || fonts[0];\n\n if (!colors.length && !fonts.length) 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 <ScrollReveal y={30}>\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 Design System\n </h2>\n <p\n className=\"text-lg mb-12 max-w-2xl\"\n style={{ color: 'var(--sk-text-secondary, #a1a1aa)' }}\n >\n The visual language we crafted for this project.\n </p>\n </ScrollReveal>\n\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-8\">\n {/* Color Palette */}\n {colors.length > 0 && (\n <ScrollReveal y={20}>\n <GlassCard padding=\"lg\" hover={false}>\n <h3\n className=\"text-base font-semibold mb-4\"\n style={{ color: 'var(--sk-text-primary, #ffffff)' }}\n >\n Color Palette\n </h3>\n <div className=\"grid grid-cols-3 gap-3\">\n {colors.map((color, ci) => (\n <div key={ci} className=\"flex flex-col items-center gap-2\">\n <div\n className=\"w-full aspect-square rounded-lg border flex items-end justify-center pb-2\"\n style={{\n background: color.hex,\n borderColor: 'color-mix(in srgb, var(--sk-text-tertiary, #71717a) 20%, transparent)',\n }}\n >\n <span\n className=\"text-[10px] font-mono font-medium px-1.5 py-0.5 rounded\"\n style={{\n color: isLightColor(color.hex) ? '#000' : '#fff',\n background: isLightColor(color.hex) ? 'rgba(0,0,0,0.15)' : 'rgba(255,255,255,0.15)',\n }}\n >\n {color.hex}\n </span>\n </div>\n {color.context && (\n <span\n className=\"text-[10px] capitalize\"\n style={{ color: 'var(--sk-text-tertiary, #71717a)' }}\n >\n {color.context}\n </span>\n )}\n </div>\n ))}\n </div>\n </GlassCard>\n </ScrollReveal>\n )}\n\n {/* Typography */}\n {fonts.length > 0 && (\n <ScrollReveal y={20} delay={0.1}>\n <GlassCard padding=\"lg\" hover={false}>\n <h3\n className=\"text-base font-semibold mb-4\"\n style={{ color: 'var(--sk-text-primary, #ffffff)' }}\n >\n Typography\n </h3>\n <div className=\"flex flex-col gap-6\">\n {headingFont && (\n <div>\n <span\n className=\"text-[10px] font-medium uppercase tracking-wider block mb-2\"\n style={{ color: 'var(--sk-text-tertiary, #71717a)' }}\n >\n Headings\n </span>\n <p\n className=\"text-2xl font-bold\"\n style={{\n color: 'var(--sk-text-primary, #ffffff)',\n fontFamily: headingFont.family,\n }}\n >\n {headingFont.family}\n </p>\n {headingFont.weights && headingFont.weights.length > 0 && (\n <div className=\"flex gap-2 mt-2\">\n {headingFont.weights.map((w) => (\n <span\n key={w}\n className=\"text-xs px-2 py-0.5 rounded\"\n style={{\n background: 'color-mix(in srgb, var(--sk-text-tertiary, #71717a) 15%, transparent)',\n color: 'var(--sk-text-tertiary, #71717a)',\n fontWeight: w,\n }}\n >\n {w}\n </span>\n ))}\n </div>\n )}\n </div>\n )}\n {bodyFont && bodyFont.family !== headingFont?.family && (\n <div>\n <span\n className=\"text-[10px] font-medium uppercase tracking-wider block mb-2\"\n style={{ color: 'var(--sk-text-tertiary, #71717a)' }}\n >\n Body\n </span>\n <p\n className=\"text-lg\"\n style={{\n color: 'var(--sk-text-primary, #ffffff)',\n fontFamily: bodyFont.family,\n }}\n >\n {bodyFont.family}\n </p>\n <p\n className=\"text-sm mt-2 leading-relaxed\"\n style={{\n color: 'var(--sk-text-tertiary, #71717a)',\n fontFamily: bodyFont.family,\n }}\n >\n The quick brown fox jumps over the lazy dog. Clean, readable type that puts content first.\n </p>\n </div>\n )}\n </div>\n </GlassCard>\n </ScrollReveal>\n )}\n </div>\n </div>\n </section>\n );\n}\n"]}
|