@sonordev/agency-site-kit 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/dist/{BeforeAfterSection-6QUJOBO2.js → BeforeAfterSection-6BHFLY4Y.js} +6 -6
  2. package/dist/BeforeAfterSection-6BHFLY4Y.js.map +1 -0
  3. package/dist/{BeforeAfterSection-DVAWWE4K.cjs → BeforeAfterSection-JTORBR3A.cjs} +6 -6
  4. package/dist/BeforeAfterSection-JTORBR3A.cjs.map +1 -0
  5. package/dist/DesignSystemSection-2R5BRBGO.js +172 -0
  6. package/dist/DesignSystemSection-2R5BRBGO.js.map +1 -0
  7. package/dist/DesignSystemSection-KXIQXITF.cjs +174 -0
  8. package/dist/DesignSystemSection-KXIQXITF.cjs.map +1 -0
  9. package/dist/{DetailsSection-FB763FS7.js → DetailsSection-A6PZQUQL.js} +14 -5
  10. package/dist/DetailsSection-A6PZQUQL.js.map +1 -0
  11. package/dist/{DetailsSection-OACJFGH7.cjs → DetailsSection-TTUZAPZZ.cjs} +14 -5
  12. package/dist/DetailsSection-TTUZAPZZ.cjs.map +1 -0
  13. package/dist/PerformanceSection-24TVVFZA.cjs +356 -0
  14. package/dist/PerformanceSection-24TVVFZA.cjs.map +1 -0
  15. package/dist/PerformanceSection-MGCEIXDX.js +351 -0
  16. package/dist/PerformanceSection-MGCEIXDX.js.map +1 -0
  17. package/dist/SiteArchitectureSection-EE6VQSXM.cjs +349 -0
  18. package/dist/SiteArchitectureSection-EE6VQSXM.cjs.map +1 -0
  19. package/dist/SiteArchitectureSection-PBBRTARV.js +344 -0
  20. package/dist/SiteArchitectureSection-PBBRTARV.js.map +1 -0
  21. package/dist/SpeedComparisonSection-EZKFQVGW.cjs +174 -0
  22. package/dist/SpeedComparisonSection-EZKFQVGW.cjs.map +1 -0
  23. package/dist/SpeedComparisonSection-Y3K7OFZQ.js +172 -0
  24. package/dist/SpeedComparisonSection-Y3K7OFZQ.js.map +1 -0
  25. package/dist/{StrategySection-3ED3QW4R.cjs → StrategySection-CJ7Y6OFQ.cjs} +18 -24
  26. package/dist/StrategySection-CJ7Y6OFQ.cjs.map +1 -0
  27. package/dist/{StrategySection-VUWMIYYP.js → StrategySection-DI5RSCJU.js} +18 -24
  28. package/dist/StrategySection-DI5RSCJU.js.map +1 -0
  29. package/dist/TechStackSection-2AQ7RGY3.js +93 -0
  30. package/dist/TechStackSection-2AQ7RGY3.js.map +1 -0
  31. package/dist/TechStackSection-VTNNZR5V.cjs +95 -0
  32. package/dist/TechStackSection-VTNNZR5V.cjs.map +1 -0
  33. package/dist/{chunk-XMC4DN6G.js → chunk-APG2QSMB.js} +8 -8
  34. package/dist/chunk-APG2QSMB.js.map +1 -0
  35. package/dist/chunk-JTI3F3QY.cjs +619 -0
  36. package/dist/chunk-JTI3F3QY.cjs.map +1 -0
  37. package/dist/{chunk-NAS4K5UR.cjs → chunk-OA5ZM4OA.cjs} +8 -8
  38. package/dist/chunk-OA5ZM4OA.cjs.map +1 -0
  39. package/dist/{chunk-QIC6JFFD.js → chunk-OMOF4VR5.js} +14 -14
  40. package/dist/chunk-OMOF4VR5.js.map +1 -0
  41. package/dist/chunk-RLVW7WEK.js +612 -0
  42. package/dist/chunk-RLVW7WEK.js.map +1 -0
  43. package/dist/{chunk-5FKOLIV6.cjs → chunk-XM2QD3AK.cjs} +14 -14
  44. package/dist/chunk-XM2QD3AK.cjs.map +1 -0
  45. package/dist/index.cjs +13 -13
  46. package/dist/index.d.cts +2 -2
  47. package/dist/index.d.ts +2 -2
  48. package/dist/index.js +3 -3
  49. package/dist/layout/index.cjs +2 -2
  50. package/dist/layout/index.d.cts +1 -1
  51. package/dist/layout/index.d.ts +1 -1
  52. package/dist/layout/index.js +1 -1
  53. package/dist/portfolio/client.cjs +3 -3
  54. package/dist/portfolio/client.d.cts +7 -3
  55. package/dist/portfolio/client.d.ts +7 -3
  56. package/dist/portfolio/client.js +1 -1
  57. package/dist/portfolio/index.cjs +6 -6
  58. package/dist/portfolio/index.d.cts +2 -2
  59. package/dist/portfolio/index.d.ts +2 -2
  60. package/dist/portfolio/index.js +2 -2
  61. package/dist/portfolio/sections.d.cts +1 -1
  62. package/dist/portfolio/sections.d.ts +1 -1
  63. package/dist/portfolio/server.cjs +1 -0
  64. package/dist/portfolio/server.cjs.map +1 -1
  65. package/dist/portfolio/server.d.cts +1 -1
  66. package/dist/portfolio/server.d.ts +1 -1
  67. package/dist/portfolio/server.js +1 -0
  68. package/dist/portfolio/server.js.map +1 -1
  69. package/dist/{types-BMUhBhWx.d.cts → types-DL4t_Cfa.d.cts} +3 -1
  70. package/dist/{types-BMUhBhWx.d.ts → types-DL4t_Cfa.d.ts} +3 -1
  71. package/package.json +1 -1
  72. package/dist/BeforeAfterSection-6QUJOBO2.js.map +0 -1
  73. package/dist/BeforeAfterSection-DVAWWE4K.cjs.map +0 -1
  74. package/dist/DetailsSection-FB763FS7.js.map +0 -1
  75. package/dist/DetailsSection-OACJFGH7.cjs.map +0 -1
  76. package/dist/StrategySection-3ED3QW4R.cjs.map +0 -1
  77. package/dist/StrategySection-VUWMIYYP.js.map +0 -1
  78. package/dist/TechStackSection-OCUYG4XT.js +0 -90
  79. package/dist/TechStackSection-OCUYG4XT.js.map +0 -1
  80. package/dist/TechStackSection-VKJK4KQB.cjs +0 -91
  81. package/dist/TechStackSection-VKJK4KQB.cjs.map +0 -1
  82. package/dist/chunk-2VNNFAG6.js +0 -415
  83. package/dist/chunk-2VNNFAG6.js.map +0 -1
  84. package/dist/chunk-5FKOLIV6.cjs.map +0 -1
  85. package/dist/chunk-NAS4K5UR.cjs.map +0 -1
  86. package/dist/chunk-QIC6JFFD.js.map +0 -1
  87. package/dist/chunk-TAPNXT7X.cjs +0 -422
  88. package/dist/chunk-TAPNXT7X.cjs.map +0 -1
  89. package/dist/chunk-XMC4DN6G.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/portfolio/components/primitives/SplitHeadline.tsx","../src/portfolio/components/primitives/DeviceTrifolio.tsx","../src/portfolio/components/sections/HeroSection.tsx","../src/portfolio/components/PortfolioPage.tsx","../src/types.ts"],"names":["useRef","useMemo","jsxs","jsx","useEffect","gsap","ScrollTrigger","useState","useCallback","ScrollReveal","GlassCard","AnimatedCounter","dynamic"],"mappings":"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","file":"chunk-JTI3F3QY.cjs","sourcesContent":["'use client';\n\nimport React, { useRef, useEffect, useMemo } from 'react';\nimport gsap from 'gsap';\nimport { ScrollTrigger } from 'gsap/ScrollTrigger';\n\ninterface SplitHeadlineProps {\n children: string;\n tag?: 'h1' | 'h2' | 'h3' | 'h4' | 'p';\n className?: string;\n style?: React.CSSProperties;\n delay?: number;\n duration?: number;\n staggerAmount?: number;\n}\n\nexport default function SplitHeadline({\n children,\n tag: Tag = 'h2',\n className,\n style,\n delay = 0,\n duration = 0.8,\n staggerAmount = 0.02,\n}: SplitHeadlineProps) {\n const ref = useRef<HTMLElement>(null);\n\n // Split text into words and characters, preserving spaces\n const splitContent = useMemo(() => {\n const words = children.split(' ');\n return words.map((word, wi) => (\n <span key={wi} style={{ display: 'inline-block', whiteSpace: 'nowrap' }}>\n {word.split('').map((char, ci) => (\n <span\n key={ci}\n className=\"split-char\"\n style={{ display: 'inline-block' }}\n >\n {char}\n </span>\n ))}\n {wi < words.length - 1 && (\n <span style={{ display: 'inline-block' }}>&nbsp;</span>\n )}\n </span>\n ));\n }, [children]);\n\n useEffect(() => {\n gsap.registerPlugin(ScrollTrigger);\n\n const el = ref.current;\n if (!el) return;\n\n // Respect prefers-reduced-motion\n const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n if (prefersReducedMotion) {\n gsap.set(el.querySelectorAll('.split-char'), { opacity: 1, y: 0 });\n return;\n }\n\n const chars = el.querySelectorAll('.split-char');\n\n const ctx = gsap.context(() => {\n gsap.fromTo(\n chars,\n {\n opacity: 0,\n y: 20,\n },\n {\n opacity: 1,\n y: 0,\n duration,\n delay,\n stagger: staggerAmount,\n ease: 'power2.out',\n scrollTrigger: {\n trigger: el,\n start: 'top 85%',\n toggleActions: 'play none none none',\n },\n },\n );\n }, el);\n\n return () => ctx.revert();\n }, [delay, duration, staggerAmount]);\n\n return (\n <Tag\n ref={ref as React.RefObject<any>}\n className={className}\n style={{\n overflowX: 'clip',\n overflowY: 'hidden',\n overflowWrap: 'break-word',\n wordBreak: 'break-word',\n ...style,\n }}\n >\n {splitContent}\n </Tag>\n );\n}\n","'use client';\n\nimport React, { useRef, useEffect, useState, useCallback } from 'react';\nimport gsap from 'gsap';\nimport { ScrollTrigger } from 'gsap/ScrollTrigger';\n\ninterface DeviceTrifolioProps {\n url?: string;\n screenshots?: {\n desktop?: string;\n tablet?: string;\n mobile?: string;\n };\n className?: string;\n disableAnimation?: boolean;\n}\n\n// ─── Hooks ───────────────────────────────────────────────────────────────────\n\nfunction useScaledIframe(viewportWidth: number) {\n const containerRef = useRef<HTMLDivElement>(null);\n const [scale, setScale] = useState(1);\n\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n\n const updateScale = () => {\n const containerWidth = container.offsetWidth;\n setScale(containerWidth / viewportWidth);\n };\n\n updateScale();\n const resizeObserver = new ResizeObserver(updateScale);\n resizeObserver.observe(container);\n return () => resizeObserver.disconnect();\n }, [viewportWidth]);\n\n return { containerRef, scale };\n}\n\nfunction useScrollSafeIframe() {\n const [isLoaded, setIsLoaded] = useState(false);\n const [iframeFailed, setIframeFailed] = useState(false);\n const iframeRef = useRef<HTMLIFrameElement>(null);\n const loadTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const handleLoad = useCallback(() => {\n const iframe = iframeRef.current;\n if (!iframe) return;\n\n // Wait for iframe to paint, then verify it rendered real content\n loadTimerRef.current = setTimeout(() => {\n let isCrossOrigin = false;\n\n try {\n // Try accessing contentDocument — cross-origin sites throw here\n const doc = iframe.contentDocument;\n if (!doc || !doc.body) {\n setIframeFailed(true);\n return;\n }\n\n // Same-origin access succeeded. This means either:\n // 1. A same-origin site loaded (e.g., our own site in dev)\n // 2. Browser rendered its own error page (X-Frame-Options block, connection refused, etc.)\n //\n // Browser error pages are same-origin and have short/generic content.\n // Real sites have <title>, many DOM nodes, scripts, styles, etc.\n const title = doc.title || '';\n const bodyText = doc.body?.innerText?.trim() || '';\n const numElements = doc.querySelectorAll('*').length;\n\n // Heuristic: real sites have a meaningful title AND many DOM elements\n // Browser error pages have very few elements and generic/empty titles\n if (numElements > 20 && title.length > 0 && bodyText.length > 50) {\n setIsLoaded(true);\n } else {\n setIframeFailed(true);\n }\n } catch {\n isCrossOrigin = true;\n }\n\n if (isCrossOrigin) {\n // Cross-origin: the real remote site loaded (can't be a browser error page\n // since those are always same-origin). This is the success case.\n setIsLoaded(true);\n }\n }, 2000);\n }, []);\n\n const handleError = useCallback(() => {\n setIframeFailed(true);\n if (loadTimerRef.current) clearTimeout(loadTimerRef.current);\n }, []);\n\n // Cleanup timer on unmount\n useEffect(() => {\n return () => {\n if (loadTimerRef.current) clearTimeout(loadTimerRef.current);\n };\n }, []);\n\n const wrapperStyle: React.CSSProperties = {\n position: 'absolute',\n left: 0,\n top: 0,\n zIndex: 1,\n overflow: 'hidden',\n width: '100%',\n height: '100%',\n };\n\n const iframeStyle: React.CSSProperties = {};\n\n return { iframeRef, handleLoad, handleError, wrapperStyle, iframeStyle, isLoaded, iframeFailed };\n}\n\n// ─── Device Frames ───────────────────────────────────────────────────────────\n\n/**\n * Hook that detects if an iframe successfully loaded real content.\n * Cross-origin = success (real site loaded). Same-origin with minimal DOM = blocked/error.\n */\nfunction useIframeLoadDetection() {\n const [loaded, setLoaded] = useState(false);\n const iframeRef = useRef<HTMLIFrameElement>(null);\n\n const onLoad = useCallback(() => {\n const iframe = iframeRef.current;\n if (!iframe) return;\n\n // Delay check to let the iframe paint\n setTimeout(() => {\n try {\n // If we can access contentDocument, it's same-origin.\n // Real sites are cross-origin and throw here → catch block → success.\n const doc = iframe.contentDocument;\n if (!doc?.body) { return; } // blocked\n const numElements = doc.querySelectorAll('*').length;\n // Browser error pages have very few elements. Real same-origin sites have many.\n if (numElements > 20) setLoaded(true);\n } catch {\n // Cross-origin error = the real remote site loaded successfully\n setLoaded(true);\n }\n }, 1500);\n }, []);\n\n return { iframeRef, loaded, onLoad };\n}\n\nfunction MacBookFrame({ url, screenshot }: { url: string; screenshot?: string }) {\n const viewportWidth = 1440;\n const viewportHeight = 900;\n const { containerRef, scale } = useScaledIframe(viewportWidth);\n const { iframeRef, loaded: iframeLoaded, onLoad: onIframeLoad } = useIframeLoadDetection();\n\n return (\n <div className=\"w-full\">\n <div\n className=\"relative rounded-t-[10px] p-[4px] pb-0\"\n style={{\n background: 'linear-gradient(135deg, #e8e8ed 0%, #d1d1d6 100%)',\n boxShadow: '0 -4px 30px rgba(0,0,0,0.3), inset 0 1px 0 rgba(255,255,255,0.9)',\n }}\n >\n <div\n className=\"relative rounded-t-[7px] px-[8px] pt-[8px] pb-0\"\n style={{ background: '#0a0a0a' }}\n >\n <div\n className=\"absolute top-0 left-1/2 -translate-x-1/2 w-[70px] h-[18px] flex items-center justify-center z-10\"\n style={{ background: '#0a0a0a', borderRadius: '0 0 10px 10px' }}\n >\n <div className=\"w-[5px] h-[5px] rounded-full\" style={{ background: '#2a3a2a', boxShadow: '0 0 0 1px #1a1a1a' }} />\n </div>\n\n <div ref={containerRef} className=\"relative overflow-hidden rounded-t-[3px]\" style={{ aspectRatio: `${viewportWidth}/${viewportHeight}`, background: '#0a0a0a' }}>\n {screenshot && <img src={screenshot} alt=\"Desktop preview\" style={{ width: '100%', height: '100%', objectFit: 'cover', objectPosition: 'top center', display: 'block', position: 'absolute' as const, inset: 0, zIndex: 2, opacity: iframeLoaded ? 0 : 1, transition: 'opacity 0.8s ease', pointerEvents: 'none' as const }} />}\n {url && <iframe src={url} title=\"Desktop\" loading=\"eager\" scrolling=\"no\" tabIndex={-1} sandbox=\"allow-scripts allow-same-origin\" ref={iframeRef} onLoad={onIframeLoad} className=\"border-0 pointer-events-none\" style={{ position: 'absolute', top: 0, left: 0, width: `${viewportWidth}px`, height: `${viewportHeight}px`, transformOrigin: 'top left', transform: `scale(${scale})` }} />}\n </div>\n </div>\n </div>\n\n {/* Black chin */}\n <div className=\"h-[8px]\" style={{ background: '#0a0a0a', boxShadow: '0 1px 4px rgba(0,0,0,0.3)' }} />\n\n {/* Silver base */}\n <div\n className=\"relative h-[12px] rounded-b-[10px]\"\n style={{\n background: 'linear-gradient(to bottom, #e0e0e5, #c8c8cd 20%, #b0b0b5 80%, #a0a0a5)',\n boxShadow: '0 4px 15px rgba(0,0,0,0.3)',\n }}\n >\n <div\n className=\"absolute top-0 left-1/2 -translate-x-1/2 w-[25%] h-[4px] rounded-b-[4px]\"\n style={{ background: 'linear-gradient(to bottom, #909095, #a8a8ad)' }}\n />\n </div>\n </div>\n );\n}\n\nfunction IPadFrame({ url, screenshot }: { url: string; screenshot?: string }) {\n const viewportWidth = 768;\n const viewportHeight = 1024;\n const { containerRef, scale } = useScaledIframe(viewportWidth);\n const { iframeRef, loaded: iframeLoaded, onLoad: onIframeLoad } = useIframeLoadDetection();\n\n return (\n <div className=\"relative p-[1.5%]\" style={{ background: 'linear-gradient(135deg, #e8e8ed 0%, #d1d1d6 100%)', borderRadius: '8% / 6%', boxShadow: '0 30px 60px -15px rgba(0,0,0,0.4), 0 0 0 1px rgba(255,255,255,0.9) inset, inset 0 1px 0 rgba(255,255,255,0.8)' }}>\n <div className=\"relative p-[2%]\" style={{ background: '#0a0a0a', borderRadius: '6% / 4.5%' }}>\n <div className=\"absolute top-[1.2%] left-1/2 -translate-x-1/2 w-[1.8%] aspect-square rounded-full z-10\" style={{ background: '#1a1a1f' }} />\n <div ref={containerRef} className=\"relative overflow-hidden\" style={{ borderRadius: '2%', aspectRatio: `${viewportWidth}/${viewportHeight}`, background: '#0a0a0a' }}>\n {screenshot && <img src={screenshot} alt=\"Tablet preview\" style={{ width: '100%', height: '100%', objectFit: 'cover', objectPosition: 'top center', display: 'block', position: 'absolute' as const, inset: 0, zIndex: 2, opacity: iframeLoaded ? 0 : 1, transition: 'opacity 0.8s ease', pointerEvents: 'none' as const }} />}\n {url && <iframe src={url} title=\"Tablet\" loading=\"eager\" scrolling=\"no\" tabIndex={-1} sandbox=\"allow-scripts allow-same-origin\" ref={iframeRef} onLoad={onIframeLoad} className=\"border-0 pointer-events-none\" style={{ position: 'absolute', top: 0, left: 0, width: `${viewportWidth}px`, height: `${viewportHeight}px`, transformOrigin: 'top left', transform: `scale(${scale})` }} />}\n </div>\n </div>\n </div>\n );\n}\n\nfunction IPhoneFrame({ url, screenshot }: { url: string; screenshot?: string }) {\n const viewportWidth = 393;\n const viewportHeight = 852;\n const { containerRef, scale } = useScaledIframe(viewportWidth);\n const { iframeRef, loaded: iframeLoaded, onLoad: onIframeLoad } = useIframeLoadDetection();\n\n return (\n <div\n className=\"relative p-[2.5%]\"\n style={{\n background: 'linear-gradient(135deg, #e8e8ed 0%, #d1d1d6 100%)',\n borderRadius: '18% / 8%',\n boxShadow:\n '0 30px 60px -15px rgba(0,0,0,0.4), 0 0 0 1px rgba(255,255,255,0.9) inset, inset 0 1px 0 rgba(255,255,255,0.8)',\n }}\n >\n {/* Side buttons */}\n <div className=\"absolute left-0 top-[18%] w-[1%] h-[6%] rounded-l-sm\" style={{ background: '#c8c8cd' }} />\n <div className=\"absolute left-0 top-[26%] w-[1%] h-[10%] rounded-l-sm\" style={{ background: '#c8c8cd' }} />\n <div className=\"absolute left-0 top-[38%] w-[1%] h-[10%] rounded-l-sm\" style={{ background: '#c8c8cd' }} />\n <div className=\"absolute right-0 top-[22%] w-[1%] h-[12%] rounded-r-sm\" style={{ background: '#c8c8cd' }} />\n\n {/* Black bezel */}\n <div\n className=\"relative p-[3%]\"\n style={{ background: '#0a0a0a', borderRadius: '14% / 6%' }}\n >\n {/* Dynamic Island */}\n <div\n className=\"absolute top-[2.5%] left-1/2 -translate-x-1/2 w-[28%] h-[3.5%] rounded-full z-10\"\n style={{ background: '#1a1a1a' }}\n />\n\n <div ref={containerRef} className=\"relative overflow-hidden\" style={{ borderRadius: '8% / 4%', aspectRatio: `${viewportWidth}/${viewportHeight}`, background: '#0a0a0a' }}>\n {screenshot && <img src={screenshot} alt=\"Mobile preview\" style={{ width: '100%', height: '100%', objectFit: 'cover', objectPosition: 'top center', display: 'block', position: 'absolute' as const, inset: 0, zIndex: 2, opacity: iframeLoaded ? 0 : 1, transition: 'opacity 0.8s ease', pointerEvents: 'none' as const }} />}\n {url && <iframe src={url} title=\"Mobile\" loading=\"eager\" scrolling=\"no\" tabIndex={-1} sandbox=\"allow-scripts allow-same-origin\" ref={iframeRef} onLoad={onIframeLoad} className=\"border-0 pointer-events-none\" style={{ position: 'absolute', top: 0, left: 0, width: `${viewportWidth}px`, height: `${viewportHeight}px`, transformOrigin: 'top left', transform: `scale(${scale})` }} />}\n </div>\n\n {/* Home indicator */}\n <div\n className=\"absolute bottom-[2%] left-1/2 -translate-x-1/2 w-[34%] h-[1%] rounded-full\"\n style={{ background: 'rgba(255,255,255,0.25)' }}\n />\n </div>\n </div>\n );\n}\n\n// ─── Main Component ──────────────────────────────────────────────────────────\n\nexport default function DeviceTrifolio({\n url,\n screenshots,\n className = '',\n disableAnimation = false,\n}: DeviceTrifolioProps) {\n const trifolioRef = useRef<HTMLDivElement>(null);\n\n // Pass URL directly — iframes render immediately\n const frameUrl = url || '';\n\n // GSAP ScrollTrigger animation for device entrance\n useEffect(() => {\n gsap.registerPlugin(ScrollTrigger);\n\n const container = trifolioRef.current;\n if (!container || disableAnimation) return;\n\n const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n if (prefersReducedMotion) return;\n\n const macbook = container.querySelector<HTMLElement>('[data-device=\"macbook\"]');\n const ipad = container.querySelector<HTMLElement>('[data-device=\"ipad\"]');\n const iphone = container.querySelector<HTMLElement>('[data-device=\"iphone\"]');\n\n if (!macbook || !ipad || !iphone) return;\n\n const ctx = gsap.context(() => {\n gsap.set(container, { opacity: 0 });\n gsap.set(macbook, { opacity: 0, y: 40 });\n gsap.set(ipad, { opacity: 0, x: -60, y: 30 });\n gsap.set(iphone, { opacity: 0, x: 60, y: 30 });\n\n const tl = gsap.timeline({\n scrollTrigger: {\n trigger: container,\n start: 'top bottom',\n toggleActions: 'play none none none',\n },\n });\n\n tl.to(container, { opacity: 1, duration: 0.3 })\n .to(macbook, { opacity: 1, y: 0, duration: 0.7, ease: 'power2.out' }, 0.1)\n .to(ipad, { opacity: 1, x: 0, y: 0, duration: 0.6, ease: 'power2.out' }, 0.25)\n .to(iphone, { opacity: 1, x: 0, y: 0, duration: 0.6, ease: 'power2.out' }, 0.4);\n }, container);\n\n return () => ctx.revert();\n }, [disableAnimation]);\n\n return (\n <div\n ref={trifolioRef}\n data-screenshot-target=\"device-trifolio\"\n className={`relative w-full min-w-0 max-w-full overflow-x-clip ${className}`}\n style={{ aspectRatio: '11/5', minHeight: '200px' }}\n >\n {/* MacBook Pro - Center Back */}\n <div\n data-device=\"macbook\"\n className=\"absolute left-1/2 -translate-x-1/2 bottom-0 w-[65%] z-10\"\n >\n <MacBookFrame url={frameUrl} screenshot={screenshots?.desktop} />\n </div>\n\n {/* iPad - Front Left */}\n <div\n data-device=\"ipad\"\n className=\"absolute left-[8%] bottom-0 w-[28%] z-20\"\n >\n <IPadFrame url={frameUrl} screenshot={screenshots?.tablet} />\n </div>\n\n {/* iPhone - Front Right */}\n <div\n data-device=\"iphone\"\n className=\"absolute right-[10%] bottom-0 w-[14%] z-30\"\n >\n <IPhoneFrame url={frameUrl} screenshot={screenshots?.mobile} />\n </div>\n </div>\n );\n}\n","'use client';\n\nimport React, { useRef, useEffect } from 'react';\nimport gsap from 'gsap';\nimport { ScrollTrigger } from 'gsap/ScrollTrigger';\nimport type { PortfolioHeroData } from '../../../types';\nimport ScrollReveal from '../primitives/ScrollReveal';\nimport AnimatedCounter from '../primitives/AnimatedCounter';\nimport SplitHeadline from '../primitives/SplitHeadline';\nimport GlassCard from '../primitives/GlassCard';\nimport DeviceTrifolio from '../primitives/DeviceTrifolio';\n\ninterface HeroSectionProps {\n data: PortfolioHeroData;\n screenshots?: { desktop?: string; tablet?: string; mobile?: string };\n liveUrl?: string;\n}\n\nexport default function HeroSection({ data, screenshots, liveUrl }: HeroSectionProps) {\n const bgRef = useRef<HTMLDivElement>(null);\n const deviceRef = useRef<HTMLDivElement>(null);\n\n const shots = screenshots || data.screenshots;\n const url = liveUrl || data.liveUrl;\n\n\n useEffect(() => {\n gsap.registerPlugin(ScrollTrigger);\n\n const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n if (prefersReducedMotion) return;\n\n const ctx = gsap.context(() => {\n // Background parallax — bg moves at 0.3x scroll speed\n if (bgRef.current) {\n gsap.to(bgRef.current, {\n yPercent: 30,\n ease: 'none',\n scrollTrigger: {\n trigger: bgRef.current.parentElement,\n start: 'top top',\n end: 'bottom top',\n scrub: true,\n },\n });\n }\n\n // Device trifolio handles its own entrance animation via DeviceTrifolio component\n });\n\n return () => ctx.revert();\n }, []);\n\n return (\n <section\n className=\"relative overflow-hidden w-full\"\n style={{\n minHeight: '90vh',\n display: 'flex',\n flexDirection: 'column',\n justifyContent: 'center',\n background: 'var(--sk-bg, #0a0a0a)',\n }}\n >\n {/* Parallax background layer */}\n <div\n ref={bgRef}\n className=\"absolute inset-0 pointer-events-none\"\n style={{\n background: `radial-gradient(ellipse at 50% 20%, color-mix(in srgb, var(--sk-primary, #6366f1) 15%, transparent) 0%, transparent 70%)`,\n willChange: 'transform',\n }}\n />\n\n <div className=\"relative z-10 max-w-7xl mx-auto w-full min-w-0 px-4 sm:px-6 py-24 lg:py-32\">\n <div className=\"grid grid-cols-1 min-w-0 lg:grid-cols-2 gap-12 lg:gap-16 items-start\">\n {/* Text column — min-w-0 so grid track can shrink below content intrinsic width (mobile) */}\n <div className=\"flex min-w-0 max-w-full flex-col gap-6\">\n {/* Category badge */}\n <ScrollReveal y={20} duration={0.6}>\n <span\n className=\"inline-flex items-center self-start px-4 py-1.5 rounded-full text-sm font-medium\"\n style={{\n background: 'color-mix(in srgb, var(--sk-primary, #6366f1) 15%, transparent)',\n color: 'var(--sk-primary, #6366f1)',\n border: '1px solid color-mix(in srgb, var(--sk-primary, #6366f1) 25%, transparent)',\n }}\n >\n {data.category?.replace(/[-_]/g, ' ').replace(/\\b\\w/g, (c: string) => c.toUpperCase())}\n </span>\n </ScrollReveal>\n\n {/* Headline */}\n <SplitHeadline\n tag=\"h1\"\n className=\"text-4xl md:text-5xl lg:text-6xl font-bold leading-tight\"\n style={{\n color: 'var(--sk-text-primary, #ffffff)',\n fontFamily: 'var(--sk-font-heading, inherit)',\n }}\n >\n {data.headline}\n </SplitHeadline>\n\n {/* Subheadline */}\n <ScrollReveal y={20} delay={0.2}>\n <p\n className=\"text-xl md:text-2xl font-medium\"\n style={{ color: 'var(--sk-text-secondary, #a1a1aa)' }}\n >\n {data.subheadline}\n </p>\n </ScrollReveal>\n\n {/* Description */}\n <ScrollReveal y={20} delay={0.3}>\n <p\n className=\"text-base md:text-lg leading-relaxed\"\n style={{ color: 'var(--sk-text-tertiary, #71717a)' }}\n >\n {data.description}\n </p>\n </ScrollReveal>\n\n {/* Services tags */}\n {data.services.length > 0 && (\n <ScrollReveal y={20} delay={0.4} stagger={0.05}>\n <div className=\"flex flex-wrap gap-2\">\n {data.services.map((service) => (\n <span\n key={service}\n className=\"px-3 py-1 rounded-full text-xs font-medium\"\n style={{\n background: 'var(--sk-surface, rgba(255,255,255,0.05))',\n color: 'var(--sk-text-secondary, #a1a1aa)',\n border: '1px solid var(--sk-border, rgba(255,255,255,0.1))',\n }}\n >\n {service}\n </span>\n ))}\n </div>\n </ScrollReveal>\n )}\n\n {/* CTA button */}\n {url && (\n <ScrollReveal y={20} delay={0.5}>\n <a\n href={url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"inline-flex items-center gap-2 self-start px-6 py-3 rounded-xl text-sm font-semibold transition-opacity hover:opacity-90\"\n style={{\n background: 'var(--sk-primary, #6366f1)',\n color: '#ffffff',\n }}\n >\n View Live Site\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M6 3h7v7M13 3L3 13\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n </a>\n </ScrollReveal>\n )}\n\n </div>\n\n {/* Right column — trifolio + KPIs */}\n <div className=\"flex min-w-0 max-w-full flex-col gap-10 lg:pt-8\">\n {/* Device trifolio — realistic device frames */}\n {(url || shots?.desktop || shots?.tablet || shots?.mobile) && (\n <div ref={deviceRef} className=\"min-w-0 max-w-full\">\n <DeviceTrifolio\n screenshots={{\n desktop: shots?.desktop,\n tablet: shots?.tablet,\n mobile: shots?.mobile,\n }}\n url={url}\n />\n </div>\n )}\n\n {/* KPI cards — below trifolio in right column */}\n {data.kpis.length > 0 && (\n <ScrollReveal y={30} delay={0.6} stagger={0.1}>\n <div className=\"grid grid-cols-2 gap-3\">\n {data.kpis.map((kpi, i) => (\n <GlassCard key={i} padding=\"sm\" hover={false} className=\"text-center\">\n <div\n className=\"text-2xl md:text-3xl font-bold\"\n style={{ color: 'var(--sk-primary, #6366f1)' }}\n >\n <AnimatedCounter\n value={kpi.value}\n suffix={kpi.suffix}\n prefix={kpi.prefix}\n duration={2.5}\n />\n </div>\n <div\n className=\"text-xs mt-1 font-medium\"\n style={{ color: 'var(--sk-text-secondary, #a1a1aa)' }}\n >\n {kpi.label}\n </div>\n </GlassCard>\n ))}\n </div>\n </ScrollReveal>\n )}\n </div>\n </div>\n </div>\n </section>\n );\n}\n","/**\n * PortfolioPage — Server component that renders a full portfolio case study.\n *\n * Maps each section in item.sections to the correct renderer component.\n * All sections below the hero are loaded lazily via next/dynamic.\n *\n * NOTE: This file is intentionally NOT a client component — it is designed\n * to be used as an RSC in a Next.js app directory page.\n */\n\nimport React from 'react';\nimport dynamic from 'next/dynamic';\nimport type {\n PortfolioItemFull,\n PortfolioSection,\n PortfolioHeroData,\n PortfolioChallengesData,\n PortfolioStrategyData,\n PortfolioResultsData,\n PortfolioTechStackData,\n PortfolioServicesData,\n PortfolioTestimonialData,\n PortfolioGalleryData,\n PortfolioVideoData,\n PortfolioTeamData,\n PortfolioFeatureSpotlightData,\n PortfolioBeforeAfterData,\n PortfolioMetricsTimelineData,\n PortfolioConversionFunnelData,\n PortfolioDetailsData,\n PortfolioCTAData,\n} from '../../types';\n\n// Hero is loaded eagerly (above the fold)\nimport HeroSection from './sections/HeroSection';\n\n// All other sections are loaded lazily\nconst ChallengesSection = dynamic(() => import('./sections/ChallengesSection'), { ssr: true });\nconst StrategySection = dynamic(() => import('./sections/StrategySection'), { ssr: true });\nconst ResultsSection = dynamic(() => import('./sections/ResultsSection'), { ssr: true });\nconst TechStackSection = dynamic(() => import('./sections/TechStackSection'), { ssr: true });\nconst ServicesSection = dynamic(() => import('./sections/ServicesSection'), { ssr: true });\nconst TestimonialSection = dynamic(() => import('./sections/TestimonialSection'), { ssr: true });\nconst GallerySection = dynamic(() => import('./sections/GallerySection'), { ssr: true });\nconst VideoSection = dynamic(() => import('./sections/VideoSection'), { ssr: true });\nconst TeamSection = dynamic(() => import('./sections/TeamSection'), { ssr: true });\nconst FeatureSpotlightSection = dynamic(() => import('./sections/FeatureSpotlightSection'), { ssr: true });\nconst BeforeAfterSection = dynamic(() => import('./sections/BeforeAfterSection'), { ssr: true });\nconst MetricsTimelineSection = dynamic(() => import('./sections/MetricsTimelineSection'), { ssr: true });\nconst ConversionFunnelSection = dynamic(() => import('./sections/ConversionFunnelSection'), { ssr: true });\nconst PerformanceSection = dynamic(() => import('./sections/PerformanceSection'), { ssr: true });\nconst SiteArchitectureSection = dynamic(() => import('./sections/SiteArchitectureSection'), { ssr: true });\nconst SpeedComparisonSection = dynamic(() => import('./sections/SpeedComparisonSection'), { ssr: true });\nconst DesignSystemSection = dynamic(() => import('./sections/DesignSystemSection'), { ssr: true });\nconst DetailsSection = dynamic(() => import('./sections/DetailsSection'), { ssr: true });\nconst CTASection = dynamic(() => import('./sections/CTASection'), { ssr: true });\n\ninterface PortfolioPageProps {\n item: PortfolioItemFull;\n}\n\nfunction renderSection(section: PortfolioSection, item: PortfolioItemFull, index: number) {\n const key = `${section.sectionType}-${index}`;\n\n switch (section.sectionType) {\n case 'portfolioHero':\n return (\n <HeroSection\n key={key}\n data={section.data as PortfolioHeroData}\n screenshots={item.hero_screenshots ?? undefined}\n liveUrl={item.live_url ?? undefined}\n />\n );\n case 'portfolioChallenges':\n return <ChallengesSection key={key} data={section.data as PortfolioChallengesData} />;\n case 'portfolioStrategy':\n return <StrategySection key={key} data={section.data as PortfolioStrategyData} />;\n case 'portfolioResults':\n return <ResultsSection key={key} data={section.data as PortfolioResultsData} />;\n case 'portfolioTechStack':\n return <TechStackSection key={key} data={section.data as PortfolioTechStackData} />;\n case 'portfolioServices':\n return <ServicesSection key={key} data={section.data as PortfolioServicesData} />;\n case 'portfolioTestimonial':\n return <TestimonialSection key={key} data={section.data as PortfolioTestimonialData} />;\n case 'portfolioGallery':\n return <GallerySection key={key} data={section.data as PortfolioGalleryData} />;\n case 'portfolioVideo':\n return <VideoSection key={key} data={section.data as PortfolioVideoData} />;\n case 'portfolioTeam':\n return <TeamSection key={key} data={section.data as PortfolioTeamData} />;\n case 'portfolioFeatureSpotlight':\n return <FeatureSpotlightSection key={key} data={section.data as PortfolioFeatureSpotlightData} />;\n case 'portfolioBeforeAfter':\n return <BeforeAfterSection key={key} data={section.data as PortfolioBeforeAfterData} />;\n case 'portfolioMetricsTimeline':\n return <MetricsTimelineSection key={key} data={section.data as PortfolioMetricsTimelineData} />;\n case 'portfolioConversionFunnel':\n return <ConversionFunnelSection key={key} data={section.data as PortfolioConversionFunnelData} />;\n case 'portfolioPerformance':\n return <PerformanceSection key={key} data={section.data as any} />;\n case 'portfolioSpeedComparison':\n return <SpeedComparisonSection key={key} data={section.data as any} />;\n case 'portfolioSiteArchitecture':\n return <SiteArchitectureSection key={key} data={section.data as any} />;\n case 'portfolioDesignSystem':\n return <DesignSystemSection key={key} data={section.data as any} />;\n case 'portfolioDetails':\n return <DetailsSection key={key} data={section.data as PortfolioDetailsData} />;\n case 'portfolioCTA':\n return <CTASection key={key} data={section.data as PortfolioCTAData} />;\n case 'portfolioSeo':\n // SEO section is metadata-only; nothing to render visually.\n return null;\n default:\n return null;\n }\n}\n\n// Canonical display order — ensures consistent rendering regardless of API order\nconst SECTION_ORDER: Record<string, number> = {\n portfolioHero: 0,\n portfolioChallenges: 1,\n portfolioStrategy: 2,\n portfolioResults: 3,\n portfolioPerformance: 4,\n portfolioSpeedComparison: 5,\n portfolioSiteArchitecture: 6,\n portfolioTechStack: 7,\n portfolioDesignSystem: 8,\n portfolioServices: 9,\n portfolioBeforeAfter: 10,\n portfolioTestimonial: 11,\n portfolioMetricsTimeline: 12,\n portfolioConversionFunnel: 13,\n portfolioDetails: 11,\n portfolioSeo: 12,\n portfolioCta: 13,\n};\n\nexport default function PortfolioPage({ item }: PortfolioPageProps) {\n const sortedSections = [...item.sections].sort(\n (a, b) => (SECTION_ORDER[a.sectionType] ?? 99) - (SECTION_ORDER[b.sectionType] ?? 99)\n );\n\n return (\n <article className=\"flex w-full min-w-0 max-w-full flex-col overflow-x-clip\">\n {sortedSections.map((section, index) => {\n const rendered = renderSection(section, item, index);\n if (!rendered) return null;\n return (\n <div key={`wrapper-${section.sectionType}-${index}`} className=\"w-full\">\n {rendered}\n </div>\n );\n })}\n </article>\n );\n}\n","/**\n * @sonordev/agency-site-kit — Type definitions\n *\n * These types mirror the Portal API's cms/types.ts definitions for portfolio\n * content. Keep them in sync when modifying the API response shapes.\n */\n\n// ---------------------------------------------------------------------------\n// Brand Configuration\n// ---------------------------------------------------------------------------\n\nexport interface BrandConfig {\n /** Primary brand color (hex) */\n primary: string;\n /** Secondary/accent color (hex) */\n secondary: string;\n /** Background color (hex) */\n background: string;\n /** Elevated background — cards, modals (hex) */\n backgroundElevated: string;\n /** Surface color — interactive containers (hex) */\n surface: string;\n /** Surface hover state (hex) */\n surfaceHover: string;\n /** Surface border color (hex) */\n surfaceBorder: string;\n /** Primary text color (hex) */\n textPrimary: string;\n /** Secondary text color (hex) */\n textSecondary: string;\n /** Tertiary/muted text color (hex) */\n textTertiary: string;\n /** Heading font family */\n fontHeading: string;\n /** Body font family */\n fontBody: string;\n /** Logo URL (absolute or relative) */\n logoUrl?: string;\n /** Logo alt text */\n logoAlt?: string;\n /** Dark mode overrides — partial, merged with base values */\n darkMode?: Partial<Omit<BrandConfig, 'fontHeading' | 'fontBody' | 'logoUrl' | 'logoAlt' | 'darkMode'>>;\n /** Border radius tokens */\n radius?: {\n sm: string;\n md: string;\n lg: string;\n };\n}\n\n// ---------------------------------------------------------------------------\n// Sanity Image Reference\n// ---------------------------------------------------------------------------\n\nexport interface SanityImageRef {\n _type: 'image';\n asset: { _type: 'reference'; _ref: string };\n hotspot?: { x: number; y: number; height: number; width: number };\n crop?: { top: number; bottom: number; left: number; right: number };\n alt?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Metrics\n// ---------------------------------------------------------------------------\n\nexport type MetricSource = 'measured' | 'estimated';\n\nexport interface PortfolioKPI {\n label: string;\n value: number;\n suffix: string;\n prefix?: string;\n description: string;\n source: MetricSource;\n}\n\nexport interface MetricsDelta {\n metric: string;\n baseline: number;\n current: number;\n delta: number;\n deltaPercent: number;\n direction: 'up' | 'down' | 'flat';\n timespan: string;\n}\n\nexport interface MetricsSnapshot {\n seo: {\n clicks_28d?: number;\n impressions_28d?: number;\n avg_position?: number;\n ctr?: number;\n };\n cwv: {\n lcp_ms?: number;\n inp_ms?: number;\n cls?: number;\n performance_score?: number;\n };\n analytics: {\n sessions_28d?: number;\n page_views_28d?: number;\n conversion_rate?: number;\n bounce_rate?: number;\n };\n pages: {\n total_indexed?: number;\n total_crawled?: number;\n total_pages?: number;\n };\n}\n\n// ---------------------------------------------------------------------------\n// Portfolio Section Data Types\n// ---------------------------------------------------------------------------\n\nexport interface PortfolioHeroData {\n headline: string;\n subheadline: string;\n description: string;\n category: string;\n services: string[];\n liveUrl?: string;\n screenshots: {\n desktop?: string;\n tablet?: string;\n mobile?: string;\n };\n kpis: PortfolioKPI[];\n}\n\nexport interface PortfolioChallengeItem {\n title: string;\n description: string;\n solution: string;\n result?: string;\n icon?: string;\n}\n\nexport interface PortfolioChallengesData {\n items: PortfolioChallengeItem[];\n}\n\nexport interface PortfolioStrategyPhase {\n number: number;\n title: string;\n description: string;\n deliverables?: string[];\n icon?: string;\n timeline?: string;\n}\n\nexport interface PortfolioStrategyData {\n phases: PortfolioStrategyPhase[];\n}\n\nexport interface PortfolioResultItem {\n title: string;\n description: string;\n metric?: {\n value: number;\n suffix: string;\n prefix?: string;\n };\n source: MetricSource;\n icon?: string;\n}\n\nexport interface PortfolioResultsData {\n items: PortfolioResultItem[];\n}\n\nexport interface PortfolioTechItem {\n name: string;\n category: string;\n icon?: string;\n description?: string;\n}\n\nexport interface PortfolioTechStackData {\n technologies: PortfolioTechItem[];\n}\n\nexport interface PortfolioServiceItem {\n title: string;\n description: string;\n features: string[];\n icon?: string;\n}\n\nexport interface PortfolioServicesData {\n items: PortfolioServiceItem[];\n}\n\nexport interface PortfolioTestimonialData {\n quote: string;\n author: string;\n title: string;\n company: string;\n avatar?: SanityImageRef;\n rating?: number;\n}\n\nexport interface PortfolioGalleryImage {\n image: SanityImageRef;\n caption?: string;\n type?: 'screenshot' | 'before' | 'after' | 'design';\n}\n\nexport interface PortfolioGalleryData {\n images: PortfolioGalleryImage[];\n layout?: 'grid' | 'masonry';\n}\n\nexport interface PortfolioVideoData {\n url?: string;\n embedUrl?: string;\n thumbnail: SanityImageRef;\n title: string;\n duration?: string;\n platform?: 'youtube' | 'vimeo' | 'custom';\n}\n\nexport interface PortfolioTeamMember {\n name: string;\n role: string;\n avatar?: SanityImageRef;\n}\n\nexport interface PortfolioTeamData {\n members: PortfolioTeamMember[];\n}\n\nexport interface PortfolioAnnotation {\n x: number;\n y: number;\n label: string;\n description: string;\n icon?: string;\n}\n\nexport interface PortfolioFeatureSpotlightData {\n image: SanityImageRef;\n title: string;\n description: string;\n layout: 'left' | 'right' | 'full';\n annotations: PortfolioAnnotation[];\n}\n\nexport interface PortfolioBeforeAfterData {\n before: SanityImageRef;\n after: SanityImageRef;\n title?: string;\n description?: string;\n defaultPosition?: number;\n}\n\nexport interface PortfolioMetricsDataPoint {\n month: string;\n label: string;\n metrics: {\n traffic?: number;\n conversions?: number;\n revenue?: number;\n rankings?: number;\n };\n}\n\nexport interface PortfolioMetricsTimelineData {\n dataPoints: PortfolioMetricsDataPoint[];\n baselineDate: string;\n title?: string;\n description?: string;\n}\n\nexport interface PortfolioFunnelStage {\n label: string;\n value: number;\n icon?: string;\n color?: string;\n}\n\nexport interface PortfolioConversionFunnelData {\n stages: PortfolioFunnelStage[];\n title?: string;\n description?: string;\n}\n\nexport interface PortfolioDetailsData {\n industry: string;\n location?: string;\n website?: string;\n timeline?: string;\n launchDate?: string;\n budgetRange?: string;\n}\n\nexport interface PortfolioSeoData {\n metaTitle: string;\n metaDescription: string;\n keywords: string[];\n ogImage?: SanityImageRef;\n}\n\nexport interface PortfolioCTAData {\n headline: string;\n description: string;\n buttonText: string;\n buttonUrl: string;\n style?: 'primary' | 'glass';\n}\n\n// ---------------------------------------------------------------------------\n// Portfolio Sections\n// ---------------------------------------------------------------------------\n\nexport const PORTFOLIO_SECTION_TYPES = [\n 'portfolioHero',\n 'portfolioChallenges',\n 'portfolioStrategy',\n 'portfolioResults',\n 'portfolioTechStack',\n 'portfolioServices',\n 'portfolioTestimonial',\n 'portfolioGallery',\n 'portfolioVideo',\n 'portfolioTeam',\n 'portfolioFeatureSpotlight',\n 'portfolioBeforeAfter',\n 'portfolioMetricsTimeline',\n 'portfolioConversionFunnel',\n 'portfolioPerformance',\n 'portfolioSpeedComparison',\n 'portfolioSiteArchitecture',\n 'portfolioDesignSystem',\n 'portfolioDetails',\n 'portfolioSeo',\n 'portfolioCTA',\n] as const;\n\nexport type PortfolioSectionType = (typeof PORTFOLIO_SECTION_TYPES)[number];\n\nexport type PortfolioSectionData =\n | PortfolioHeroData\n | PortfolioChallengesData\n | PortfolioStrategyData\n | PortfolioResultsData\n | PortfolioTechStackData\n | PortfolioServicesData\n | PortfolioTestimonialData\n | PortfolioGalleryData\n | PortfolioVideoData\n | PortfolioTeamData\n | PortfolioFeatureSpotlightData\n | PortfolioBeforeAfterData\n | PortfolioMetricsTimelineData\n | PortfolioConversionFunnelData\n | PortfolioDetailsData\n | PortfolioSeoData\n | PortfolioCTAData;\n\nexport interface PortfolioSectionDataMap {\n portfolioHero: PortfolioHeroData;\n portfolioChallenges: PortfolioChallengesData;\n portfolioStrategy: PortfolioStrategyData;\n portfolioResults: PortfolioResultsData;\n portfolioTechStack: PortfolioTechStackData;\n portfolioServices: PortfolioServicesData;\n portfolioTestimonial: PortfolioTestimonialData;\n portfolioGallery: PortfolioGalleryData;\n portfolioVideo: PortfolioVideoData;\n portfolioTeam: PortfolioTeamData;\n portfolioFeatureSpotlight: PortfolioFeatureSpotlightData;\n portfolioBeforeAfter: PortfolioBeforeAfterData;\n portfolioMetricsTimeline: PortfolioMetricsTimelineData;\n portfolioConversionFunnel: PortfolioConversionFunnelData;\n portfolioDetails: PortfolioDetailsData;\n portfolioSeo: PortfolioSeoData;\n portfolioCTA: PortfolioCTAData;\n}\n\nexport interface PortfolioSection<T extends PortfolioSectionType = PortfolioSectionType> {\n sectionType: T;\n displayName: string;\n data: T extends keyof PortfolioSectionDataMap ? PortfolioSectionDataMap[T] : PortfolioSectionData;\n}\n\n// ---------------------------------------------------------------------------\n// Portfolio Item (mirrors public API response)\n// ---------------------------------------------------------------------------\n\nexport interface PortfolioItem {\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 kpis: PortfolioKPI[];\n details: PortfolioDetailsData | null;\n seo: PortfolioSeoData | null;\n featured: boolean;\n order?: number;\n published_at: string | null;\n /** Sonor project ID (present for items generated from a managed project) */\n project_id: string | null;\n hero_screenshots: {\n desktop?: string;\n tablet?: string;\n mobile?: string;\n } | null;\n baseline_metrics: MetricsSnapshot | null;\n current_metrics: MetricsSnapshot | null;\n metrics_last_refreshed_at: string | null;\n}\n\nexport interface PortfolioItemFull extends PortfolioItem {\n sections: PortfolioSection[];\n metricsDelta: MetricsDelta[];\n}\n\n// ---------------------------------------------------------------------------\n// API Responses\n// ---------------------------------------------------------------------------\n\nexport interface PortfolioListResponse {\n items: PortfolioItem[];\n total: number;\n limit: number;\n offset: number;\n}\n\nexport interface PortfolioConfigResponse {\n brand: BrandConfig;\n orgName: string;\n orgSlug: string;\n}\n"]}
@@ -11,13 +11,13 @@ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
11
11
  var Image__default = /*#__PURE__*/_interopDefault(Image);
12
12
  var Link__default = /*#__PURE__*/_interopDefault(Link);
13
13
 
14
- function PortfolioCard({ item, className = "" }) {
15
- const thumbnailSrc = item.hero_screenshots?.desktop || item.hero_image;
14
+ function PortfolioCard({ item, className = "", basePath = "/work" }) {
15
+ const thumbnailSrc = item.hero_screenshots?.desktop || item.hero_image || (item.project_id ? `/portfolio-previews/${item.slug}.png` : void 0);
16
16
  const kpis = Array.isArray(item.kpis) ? item.kpis : [];
17
17
  const services = Array.isArray(item.services) ? item.services : [];
18
18
  const kpiPreview = kpis.slice(0, 3);
19
19
  const servicePreview = services.slice(0, 3);
20
- return /* @__PURE__ */ jsxRuntime.jsx(Link__default.default, { href: `/portfolio/${item.slug}`, className: `block group ${className}`, children: /* @__PURE__ */ jsxRuntime.jsx(chunkKEOHORIH_cjs.GlassCard, { padding: "sm", hover: true, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
20
+ return /* @__PURE__ */ jsxRuntime.jsx(Link__default.default, { href: `${basePath}/${item.slug}`, className: `block group ${className}`, children: /* @__PURE__ */ jsxRuntime.jsx(chunkKEOHORIH_cjs.GlassCard, { padding: "sm", hover: true, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
21
21
  thumbnailSrc && /* @__PURE__ */ jsxRuntime.jsxs(
22
22
  "div",
23
23
  {
@@ -43,7 +43,7 @@ function PortfolioCard({ item, className = "" }) {
43
43
  color: "#ffffff",
44
44
  backdropFilter: "blur(8px)"
45
45
  },
46
- children: item.category
46
+ children: item.category?.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase())
47
47
  }
48
48
  )
49
49
  ]
@@ -120,7 +120,7 @@ function PortfolioCard({ item, className = "" }) {
120
120
  ] })
121
121
  ] }) }) });
122
122
  }
123
- function PortfolioGrid({ items, className = "" }) {
123
+ function PortfolioGrid({ items, className = "", basePath = "/work" }) {
124
124
  return /* @__PURE__ */ jsxRuntime.jsx(chunkIKBK7HYX_cjs.ScrollReveal, { stagger: 0.1, children: /* @__PURE__ */ jsxRuntime.jsx(
125
125
  "div",
126
126
  {
@@ -128,12 +128,12 @@ function PortfolioGrid({ items, className = "" }) {
128
128
  style: {
129
129
  gap: "var(--sk-grid-gap, 24px)"
130
130
  },
131
- children: items.map((item) => /* @__PURE__ */ jsxRuntime.jsx(PortfolioCard, { item }, item.id))
131
+ children: items.map((item) => /* @__PURE__ */ jsxRuntime.jsx(PortfolioCard, { item, basePath }, item.id))
132
132
  }
133
133
  ) });
134
134
  }
135
135
 
136
136
  exports.PortfolioCard = PortfolioCard;
137
137
  exports.PortfolioGrid = PortfolioGrid;
138
- //# sourceMappingURL=chunk-NAS4K5UR.cjs.map
139
- //# sourceMappingURL=chunk-NAS4K5UR.cjs.map
138
+ //# sourceMappingURL=chunk-OA5ZM4OA.cjs.map
139
+ //# sourceMappingURL=chunk-OA5ZM4OA.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/portfolio/components/PortfolioCard.tsx","../src/portfolio/components/PortfolioGrid.tsx"],"names":["jsx","Link","GlassCard","jsxs","Image","ScrollReveal"],"mappings":"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","file":"chunk-OA5ZM4OA.cjs","sourcesContent":["'use client';\n\nimport React from 'react';\nimport Image from 'next/image';\nimport Link from 'next/link';\nimport type { PortfolioItem } from '../../types';\nimport GlassCard from './primitives/GlassCard';\n\ninterface PortfolioCardProps {\n item: PortfolioItem;\n className?: string;\n /** Base path for portfolio detail links (default: '/work') */\n basePath?: string;\n}\n\nexport default function PortfolioCard({ item, className = '', basePath = '/work' }: PortfolioCardProps) {\n // Use hero_screenshots desktop → hero_image → trifolio screenshot convention\n const thumbnailSrc =\n item.hero_screenshots?.desktop ||\n item.hero_image ||\n (item.project_id ? `/portfolio-previews/${item.slug}.png` : undefined);\n const kpis = Array.isArray(item.kpis) ? item.kpis : [];\n const services = Array.isArray(item.services) ? item.services : [];\n const kpiPreview = kpis.slice(0, 3);\n const servicePreview = services.slice(0, 3);\n\n return (\n <Link href={`${basePath}/${item.slug}`} className={`block group ${className}`}>\n <GlassCard padding=\"sm\" hover>\n <div className=\"flex flex-col gap-4\">\n {/* Thumbnail */}\n {thumbnailSrc && (\n <div\n className=\"relative w-full overflow-hidden rounded-xl\"\n style={{ aspectRatio: '16/10' }}\n >\n <Image\n src={thumbnailSrc}\n alt={item.hero_image_alt || item.title}\n fill\n sizes=\"(max-width: 768px) 100vw, (max-width: 1024px) 50vw, 33vw\"\n className=\"object-cover object-top transition-transform duration-500 group-hover:scale-105\"\n />\n {/* Category badge */}\n <span\n className=\"absolute top-3 left-3 px-3 py-1 rounded-full text-[11px] uppercase tracking-wider font-semibold\"\n style={{\n background: 'color-mix(in srgb, var(--sk-primary, #6366f1) 80%, transparent)',\n color: '#ffffff',\n backdropFilter: 'blur(8px)',\n }}\n >\n {item.category?.replace(/[-_]/g, ' ').replace(/\\b\\w/g, (c: string) => c.toUpperCase())}\n </span>\n </div>\n )}\n\n {/* Text content */}\n <div className=\"flex flex-col gap-2 px-2 pb-2\">\n <h3\n className=\"text-lg font-semibold line-clamp-1\"\n style={{ color: 'var(--sk-text-primary, #ffffff)' }}\n >\n {item.title}\n </h3>\n\n {item.subtitle && (\n <p\n className=\"text-sm line-clamp-2\"\n style={{ color: 'var(--sk-text-tertiary, #71717a)' }}\n >\n {item.subtitle}\n </p>\n )}\n\n {/* KPI preview */}\n {kpiPreview.length > 0 && (\n <div className=\"flex items-center gap-4 mt-1\">\n {kpiPreview.map((kpi, i) => (\n <div key={i} className=\"flex flex-col\">\n <span\n className=\"text-base font-bold\"\n style={{ color: 'var(--sk-primary, #6366f1)' }}\n >\n {kpi.prefix || ''}\n {kpi.value.toLocaleString()}\n {kpi.suffix}\n </span>\n <span\n className=\"text-[10px] uppercase tracking-wider font-medium\"\n style={{ color: 'var(--sk-text-tertiary, #71717a)' }}\n >\n {kpi.label}\n </span>\n </div>\n ))}\n </div>\n )}\n\n {/* Services tags */}\n {servicePreview.length > 0 && (\n <div className=\"flex flex-wrap gap-1.5 mt-2\">\n {servicePreview.map((service) => (\n <span\n key={service}\n className=\"px-2.5 py-0.5 rounded-full text-[11px] font-medium\"\n style={{\n background: 'var(--sk-surface, rgba(255,255,255,0.05))',\n color: 'var(--sk-text-secondary, #a1a1aa)',\n border: '1px solid var(--sk-border, rgba(255,255,255,0.1))',\n }}\n >\n {service}\n </span>\n ))}\n {services.length > 3 && (\n <span\n className=\"px-2.5 py-0.5 rounded-full text-[11px] font-medium\"\n style={{\n background: 'var(--sk-surface, rgba(255,255,255,0.05))',\n color: 'var(--sk-text-tertiary, #71717a)',\n }}\n >\n +{item.services.length - 3}\n </span>\n )}\n </div>\n )}\n </div>\n </div>\n </GlassCard>\n </Link>\n );\n}\n","'use client';\n\nimport React from 'react';\nimport type { PortfolioItem } from '../../types';\nimport ScrollReveal from './primitives/ScrollReveal';\nimport PortfolioCard from './PortfolioCard';\n\ninterface PortfolioGridProps {\n items: PortfolioItem[];\n className?: string;\n /** Base path for portfolio detail links (default: '/work') */\n basePath?: string;\n}\n\nexport default function PortfolioGrid({ items, className = '', basePath = '/work' }: PortfolioGridProps) {\n return (\n <ScrollReveal stagger={0.1}>\n <div\n className={`grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 ${className}`}\n style={{\n gap: 'var(--sk-grid-gap, 24px)',\n }}\n >\n {items.map((item) => (\n <PortfolioCard key={item.id} item={item} basePath={basePath} />\n ))}\n </div>\n </ScrollReveal>\n );\n}\n"]}
@@ -42,10 +42,10 @@ var DEFAULT_BRAND_CONFIG = {
42
42
  }
43
43
  };
44
44
  var DEFAULT_DARK_MODE = {
45
- background: "#0f172a",
46
- backgroundElevated: "#1e293b",
47
- surface: "#1e293b",
48
- surfaceHover: "#334155",
45
+ background: "#1a1a1f",
46
+ backgroundElevated: "#222228",
47
+ surface: "#2a2a30",
48
+ surfaceHover: "#333338",
49
49
  surfaceBorder: "#334155",
50
50
  textPrimary: "#f8fafc",
51
51
  textSecondary: "#cbd5e1",
@@ -92,14 +92,14 @@ function buildDarkVariables(base) {
92
92
  `--sk-primary-rgb: ${primaryRgb};`,
93
93
  `--sk-secondary: ${dark.secondary};`,
94
94
  `--sk-secondary-rgb: ${secondaryRgb};`,
95
- `--sk-bg: ${dark.background || DEFAULT_DARK_MODE.background};`,
96
- `--sk-bg-elevated: ${dark.backgroundElevated || DEFAULT_DARK_MODE.backgroundElevated};`,
97
- `--sk-surface: ${dark.surface || DEFAULT_DARK_MODE.surface};`,
98
- `--sk-surface-hover: ${dark.surfaceHover || DEFAULT_DARK_MODE.surfaceHover};`,
99
- `--sk-border: ${dark.surfaceBorder || DEFAULT_DARK_MODE.surfaceBorder};`,
100
- `--sk-text-primary: ${dark.textPrimary || DEFAULT_DARK_MODE.textPrimary};`,
101
- `--sk-text-secondary: ${dark.textSecondary || DEFAULT_DARK_MODE.textSecondary};`,
102
- `--sk-text-tertiary: ${dark.textTertiary || DEFAULT_DARK_MODE.textTertiary};`
95
+ `--sk-bg: ${dark.background || base.background || DEFAULT_DARK_MODE.background};`,
96
+ `--sk-bg-elevated: ${dark.backgroundElevated || base.backgroundElevated || DEFAULT_DARK_MODE.backgroundElevated};`,
97
+ `--sk-surface: ${dark.surface || base.surface || DEFAULT_DARK_MODE.surface};`,
98
+ `--sk-surface-hover: ${dark.surfaceHover || base.surfaceHover || DEFAULT_DARK_MODE.surfaceHover};`,
99
+ `--sk-border: ${dark.surfaceBorder || base.surfaceBorder || DEFAULT_DARK_MODE.surfaceBorder};`,
100
+ `--sk-text-primary: ${dark.textPrimary || base.textPrimary || DEFAULT_DARK_MODE.textPrimary};`,
101
+ `--sk-text-secondary: ${dark.textSecondary || base.textSecondary || DEFAULT_DARK_MODE.textSecondary};`,
102
+ `--sk-text-tertiary: ${dark.textTertiary || base.textTertiary || DEFAULT_DARK_MODE.textTertiary};`
103
103
  ].join("\n ");
104
104
  }
105
105
  function generateBrandCSS(config) {
@@ -206,5 +206,5 @@ async function AgencySiteKitLayout({
206
206
  }
207
207
 
208
208
  export { AgencySiteKitLayout, DEFAULT_BRAND_CONFIG, DEFAULT_DARK_MODE, generateBrandCSS, hexToRgb, mergeBrandConfig };
209
- //# sourceMappingURL=chunk-QIC6JFFD.js.map
210
- //# sourceMappingURL=chunk-QIC6JFFD.js.map
209
+ //# sourceMappingURL=chunk-OMOF4VR5.js.map
210
+ //# sourceMappingURL=chunk-OMOF4VR5.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/shared/brand.ts","../src/layout/AgencySiteKitLayout.tsx"],"names":[],"mappings":"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","file":"chunk-OMOF4VR5.js","sourcesContent":["/**\n * @sonordev/agency-site-kit — Brand customization system\n *\n * Generates CSS custom properties from a BrandConfig object.\n * Supports light/dark mode with automatic fallbacks.\n */\n\nimport type { BrandConfig } from '../types';\n\n// ---------------------------------------------------------------------------\n// Hex to RGB helper\n// ---------------------------------------------------------------------------\n\n/**\n * Converts a hex color string to an \"R, G, B\" string for use in rgb()/rgba().\n * Supports 3-digit (#abc) and 6-digit (#aabbcc) hex values.\n * Returns null for invalid inputs.\n */\nexport function hexToRgb(hex: string): string | null {\n if (!hex || typeof hex !== 'string') return null;\n\n const cleaned = hex.replace(/^#/, '');\n\n let r: number;\n let g: number;\n let b: number;\n\n if (cleaned.length === 3) {\n r = parseInt(cleaned[0] + cleaned[0], 16);\n g = parseInt(cleaned[1] + cleaned[1], 16);\n b = parseInt(cleaned[2] + cleaned[2], 16);\n } else if (cleaned.length === 6) {\n r = parseInt(cleaned.substring(0, 2), 16);\n g = parseInt(cleaned.substring(2, 4), 16);\n b = parseInt(cleaned.substring(4, 6), 16);\n } else {\n return null;\n }\n\n if (isNaN(r) || isNaN(g) || isNaN(b)) return null;\n\n return `${r}, ${g}, ${b}`;\n}\n\n// ---------------------------------------------------------------------------\n// Default theme values\n// ---------------------------------------------------------------------------\n\nexport const DEFAULT_BRAND_CONFIG: BrandConfig = {\n primary: '#6366f1',\n secondary: '#8b5cf6',\n background: '#ffffff',\n backgroundElevated: '#f8fafc',\n surface: '#f1f5f9',\n surfaceHover: '#e2e8f0',\n surfaceBorder: '#e2e8f0',\n textPrimary: '#0f172a',\n textSecondary: '#475569',\n textTertiary: '#94a3b8',\n fontHeading: \"'Inter', system-ui, -apple-system, sans-serif\",\n fontBody: \"'Inter', system-ui, -apple-system, sans-serif\",\n radius: {\n sm: '0.375rem',\n md: '0.5rem',\n lg: '0.75rem',\n },\n};\n\nexport const DEFAULT_DARK_MODE: Partial<BrandConfig> = {\n background: '#1a1a1f',\n backgroundElevated: '#222228',\n surface: '#2a2a30',\n surfaceHover: '#333338',\n surfaceBorder: '#334155',\n textPrimary: '#f8fafc',\n textSecondary: '#cbd5e1',\n textTertiary: '#64748b',\n};\n\n// ---------------------------------------------------------------------------\n// CSS variable generation\n// ---------------------------------------------------------------------------\n\nfunction buildVariables(config: BrandConfig): string {\n const primaryRgb = hexToRgb(config.primary) || '99, 102, 241';\n const secondaryRgb = hexToRgb(config.secondary) || '139, 92, 246';\n\n const radius = config.radius || DEFAULT_BRAND_CONFIG.radius!;\n\n return [\n // Primary tokens (--sk-*)\n `--sk-primary: ${config.primary};`,\n `--sk-primary-rgb: ${primaryRgb};`,\n `--sk-secondary: ${config.secondary};`,\n `--sk-secondary-rgb: ${secondaryRgb};`,\n `--sk-bg: ${config.background};`,\n `--sk-bg-elevated: ${config.backgroundElevated};`,\n `--sk-surface: ${config.surface};`,\n `--sk-surface-hover: ${config.surfaceHover};`,\n `--sk-border: ${config.surfaceBorder};`,\n `--sk-text-primary: ${config.textPrimary};`,\n `--sk-text-secondary: ${config.textSecondary};`,\n `--sk-text-tertiary: ${config.textTertiary};`,\n `--sk-radius-sm: ${radius.sm};`,\n `--sk-radius: ${radius.md};`,\n `--sk-radius-lg: ${radius.lg};`,\n `--sk-font-heading: ${config.fontHeading};`,\n `--sk-font: ${config.fontBody};`,\n ].join('\\n ');\n}\n\nfunction buildDarkVariables(base: BrandConfig): string {\n const dark = {\n ...DEFAULT_DARK_MODE,\n ...base.darkMode,\n // Primary/secondary carry over unless explicitly overridden\n primary: base.darkMode?.primary || base.primary,\n secondary: base.darkMode?.secondary || base.secondary,\n };\n\n const primaryRgb = hexToRgb(dark.primary!) || hexToRgb(base.primary) || '99, 102, 241';\n const secondaryRgb = hexToRgb(dark.secondary!) || hexToRgb(base.secondary) || '139, 92, 246';\n\n return [\n // Primary tokens (--sk-*)\n `--sk-primary: ${dark.primary};`,\n `--sk-primary-rgb: ${primaryRgb};`,\n `--sk-secondary: ${dark.secondary};`,\n `--sk-secondary-rgb: ${secondaryRgb};`,\n `--sk-bg: ${dark.background || base.background || DEFAULT_DARK_MODE.background};`,\n `--sk-bg-elevated: ${dark.backgroundElevated || base.backgroundElevated || DEFAULT_DARK_MODE.backgroundElevated};`,\n `--sk-surface: ${dark.surface || base.surface || DEFAULT_DARK_MODE.surface};`,\n `--sk-surface-hover: ${dark.surfaceHover || base.surfaceHover || DEFAULT_DARK_MODE.surfaceHover};`,\n `--sk-border: ${dark.surfaceBorder || base.surfaceBorder || DEFAULT_DARK_MODE.surfaceBorder};`,\n `--sk-text-primary: ${dark.textPrimary || base.textPrimary || DEFAULT_DARK_MODE.textPrimary};`,\n `--sk-text-secondary: ${dark.textSecondary || base.textSecondary || DEFAULT_DARK_MODE.textSecondary};`,\n `--sk-text-tertiary: ${dark.textTertiary || base.textTertiary || DEFAULT_DARK_MODE.textTertiary};`,\n ].join('\\n ');\n}\n\n/**\n * Generates a complete CSS string with all --sk-* custom properties.\n *\n * Produces:\n * - `:root` / `[data-theme=\"light\"]` block with light mode values\n * - `[data-theme=\"dark\"]` block with dark mode values\n * - `@media (prefers-color-scheme: dark)` fallback for auto detection\n *\n * Usage: inject the returned string into a `<style>` tag.\n */\nexport function generateBrandCSS(config: BrandConfig): string {\n const merged: BrandConfig = {\n ...DEFAULT_BRAND_CONFIG,\n ...config,\n radius: {\n ...DEFAULT_BRAND_CONFIG.radius!,\n ...config.radius,\n },\n };\n\n const lightVars = buildVariables(merged);\n const darkVars = buildDarkVariables(merged);\n\n return `\n:root, [data-theme=\"light\"] {\n ${lightVars}\n}\n\n[data-theme=\"dark\"] {\n ${darkVars}\n}\n\n@media (prefers-color-scheme: dark) {\n :root:not([data-theme=\"light\"]) {\n ${darkVars}\n }\n}\n`.trim();\n}\n\n/**\n * Merges a partial brand config with defaults, returning a complete BrandConfig.\n */\nexport function mergeBrandConfig(partial: Partial<BrandConfig>): BrandConfig {\n return {\n ...DEFAULT_BRAND_CONFIG,\n ...partial,\n radius: {\n ...DEFAULT_BRAND_CONFIG.radius!,\n ...partial.radius,\n },\n };\n}\n","/**\n * @sonordev/agency-site-kit — AgencySiteKitLayout\n *\n * Server component that wraps children with brand CSS variables.\n * Fetches the brand configuration from the Portal API and injects\n * a <style> tag with all --sk-* custom properties (plus --agency-* aliases for backwards compatibility).\n *\n * Usage in app/layout.tsx:\n *\n * ```tsx\n * import { AgencySiteKitLayout } from '@sonordev/agency-site-kit';\n *\n * export default function RootLayout({ children }) {\n * return (\n * <html lang=\"en\">\n * <body>\n * <AgencySiteKitLayout>\n * {children}\n * </AgencySiteKitLayout>\n * </body>\n * </html>\n * );\n * }\n * ```\n */\n\nimport React from 'react';\nimport { apiGet } from '../shared/api';\nimport { generateBrandCSS, mergeBrandConfig } from '../shared/brand';\nimport type { BrandConfig, PortfolioConfigResponse } from '../types';\n\nfunction portalApiOrigin(apiUrl: string): string {\n try {\n return new URL(apiUrl.replace(/\\/$/, '')).origin\n } catch {\n return 'https://api.sonor.io'\n }\n}\n\nexport interface AgencySiteKitLayoutProps {\n children: React.ReactNode;\n /** Override the API key (defaults to process.env.SONOR_API_KEY) */\n apiKey?: string;\n /** Override the API URL (defaults to process.env.SONOR_API_URL or https://api.sonor.io) */\n apiUrl?: string;\n /** Override brand config instead of fetching from API */\n brandConfig?: Partial<BrandConfig>;\n /** Force a specific theme: \"light\" | \"dark\". Omit for system preference. */\n theme?: 'light' | 'dark';\n /** Additional class name for the wrapper div */\n className?: string;\n}\n\n/**\n * Fetches brand configuration from the Portal API.\n * Returns a merged BrandConfig with defaults applied.\n * Falls back to defaults if the API call fails.\n */\nasync function fetchBrandConfig(\n apiKey?: string,\n apiUrl?: string,\n): Promise<BrandConfig> {\n const response = await apiGet<PortfolioConfigResponse>(\n '/api/public/portfolio/config',\n 3600,\n apiKey,\n apiUrl,\n );\n\n if (response?.brand) {\n return mergeBrandConfig(response.brand);\n }\n\n return mergeBrandConfig({});\n}\n\n/**\n * AgencySiteKitLayout — Server component wrapper\n *\n * Fetches the agency's brand configuration and injects CSS custom properties\n * so all child components can use --sk-* variables for theming.\n *\n * This is an async server component (RSC). It makes one API call at render\n * time, cached via Next.js ISR (default 1 hour revalidation).\n */\nexport async function AgencySiteKitLayout({\n children,\n apiKey,\n apiUrl,\n brandConfig: brandConfigOverride,\n theme,\n className,\n}: AgencySiteKitLayoutProps): Promise<React.ReactElement> {\n // Use override brand config if provided, otherwise fetch from API\n const brandConfig = brandConfigOverride\n ? mergeBrandConfig(brandConfigOverride)\n : await fetchBrandConfig(apiKey, apiUrl);\n\n const cssString = generateBrandCSS(brandConfig);\n\n const resolvedApiUrl =\n apiUrl ??\n process.env.SONOR_API_URL ??\n process.env.UPTRADE_API_URL ??\n 'https://api.sonor.io';\n const preconnectHref = portalApiOrigin(resolvedApiUrl);\n\n return React.createElement(\n React.Fragment,\n null,\n React.createElement('link', {\n rel: 'preconnect',\n href: preconnectHref,\n crossOrigin: 'anonymous',\n }),\n React.createElement('link', {\n rel: 'dns-prefetch',\n href: preconnectHref,\n }),\n React.createElement('style', {\n dangerouslySetInnerHTML: { __html: cssString },\n 'data-agency-site-kit': 'brand',\n }),\n React.createElement(\n 'div',\n {\n 'data-theme': theme || undefined,\n 'data-agency-site-kit': 'root',\n className,\n style: {\n fontFamily: 'var(--sk-font)',\n color: 'var(--sk-text-primary)',\n backgroundColor: 'var(--sk-bg)',\n },\n },\n children,\n ),\n );\n}\n"]}