@sonordev/agency-site-kit 0.4.0 → 0.4.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.
- package/dist/{chunk-L7E3UHQC.cjs → chunk-2Y2RJB3A.cjs} +16 -13
- package/dist/chunk-2Y2RJB3A.cjs.map +1 -0
- package/dist/{chunk-4N7ENJ7L.cjs → chunk-6OWFFV6N.cjs} +14 -12
- package/dist/chunk-6OWFFV6N.cjs.map +1 -0
- package/dist/chunk-7D4H36UF.js +13 -0
- package/dist/chunk-7D4H36UF.js.map +1 -0
- package/dist/{chunk-UNTOEWQL.js → chunk-7FFZX7C5.js} +10 -7
- package/dist/chunk-7FFZX7C5.js.map +1 -0
- package/dist/{chunk-7ZHRMWJU.cjs → chunk-A5WCMHAE.cjs} +2 -2
- package/dist/{chunk-7ZHRMWJU.cjs.map → chunk-A5WCMHAE.cjs.map} +1 -1
- package/dist/chunk-FM25LB67.js +50 -0
- package/dist/chunk-FM25LB67.js.map +1 -0
- package/dist/{chunk-KXLROAO5.js → chunk-FT2SNEYP.js} +2 -2
- package/dist/{chunk-KXLROAO5.js.map → chunk-FT2SNEYP.js.map} +1 -1
- package/dist/{chunk-XLK77USA.js → chunk-K2BGTDSN.js} +2 -2
- package/dist/{chunk-XLK77USA.js.map → chunk-K2BGTDSN.js.map} +1 -1
- package/dist/chunk-X76U7VLF.cjs +57 -0
- package/dist/chunk-X76U7VLF.cjs.map +1 -0
- package/dist/{chunk-NIZJLONK.js → chunk-X7UCJ45A.js} +14 -12
- package/dist/chunk-X7UCJ45A.js.map +1 -0
- package/dist/chunk-ZBPF7TLZ.cjs +16 -0
- package/dist/chunk-ZBPF7TLZ.cjs.map +1 -0
- package/dist/format-C-axf13G.d.cts +68 -0
- package/dist/format-poUpLtQ2.d.ts +68 -0
- package/dist/index.cjs +36 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -2
- package/dist/index.d.ts +4 -2
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/dist/layout/index.d.cts +1 -1
- package/dist/layout/index.d.ts +1 -1
- package/dist/portfolio/client.cjs +25 -6
- package/dist/portfolio/client.cjs.map +1 -1
- package/dist/portfolio/client.d.cts +20 -1
- package/dist/portfolio/client.d.ts +20 -1
- package/dist/portfolio/client.js +22 -5
- package/dist/portfolio/client.js.map +1 -1
- package/dist/portfolio/components/PortfolioErrorFallback.cjs +1 -1
- package/dist/portfolio/components/PortfolioErrorFallback.cjs.map +1 -1
- package/dist/portfolio/components/PortfolioErrorFallback.js +1 -1
- package/dist/portfolio/index.cjs +36 -10
- package/dist/portfolio/index.d.cts +12 -4
- package/dist/portfolio/index.d.ts +12 -4
- package/dist/portfolio/index.js +5 -3
- package/dist/portfolio/sections/ChallengesSection.cjs +1 -1
- package/dist/portfolio/sections/ChallengesSection.cjs.map +1 -1
- package/dist/portfolio/sections/ChallengesSection.d.cts +1 -1
- package/dist/portfolio/sections/ChallengesSection.d.ts +1 -1
- package/dist/portfolio/sections/ChallengesSection.js +1 -1
- package/dist/portfolio/sections/ChallengesSection.js.map +1 -1
- package/dist/portfolio/sections/DetailsSection.d.cts +1 -1
- package/dist/portfolio/sections/DetailsSection.d.ts +1 -1
- package/dist/portfolio/sections/HeroSection.cjs +3 -3
- package/dist/portfolio/sections/HeroSection.cjs.map +1 -1
- package/dist/portfolio/sections/HeroSection.d.cts +1 -1
- package/dist/portfolio/sections/HeroSection.d.ts +1 -1
- package/dist/portfolio/sections/HeroSection.js +3 -3
- package/dist/portfolio/sections/HeroSection.js.map +1 -1
- package/dist/portfolio/sections/PerformanceSection.cjs +1 -64
- package/dist/portfolio/sections/PerformanceSection.cjs.map +1 -1
- package/dist/portfolio/sections/PerformanceSection.js +1 -64
- package/dist/portfolio/sections/PerformanceSection.js.map +1 -1
- package/dist/portfolio/sections/ResultsSection.cjs +4 -2
- package/dist/portfolio/sections/ResultsSection.cjs.map +1 -1
- package/dist/portfolio/sections/ResultsSection.d.cts +2 -2
- package/dist/portfolio/sections/ResultsSection.d.ts +2 -2
- package/dist/portfolio/sections/ResultsSection.js +3 -1
- package/dist/portfolio/sections/ResultsSection.js.map +1 -1
- package/dist/portfolio/sections/ServicesSection.d.cts +1 -1
- package/dist/portfolio/sections/ServicesSection.d.ts +1 -1
- package/dist/portfolio/sections/SiteArchitectureSection.cjs +1 -1
- package/dist/portfolio/sections/SiteArchitectureSection.cjs.map +1 -1
- package/dist/portfolio/sections/SiteArchitectureSection.js +1 -1
- package/dist/portfolio/sections/SiteArchitectureSection.js.map +1 -1
- package/dist/portfolio/sections/SpeedComparisonSection.cjs +1 -1
- package/dist/portfolio/sections/SpeedComparisonSection.cjs.map +1 -1
- package/dist/portfolio/sections/SpeedComparisonSection.js +1 -1
- package/dist/portfolio/sections/SpeedComparisonSection.js.map +1 -1
- package/dist/portfolio/sections/TechStackSection.d.cts +1 -1
- package/dist/portfolio/sections/TechStackSection.d.ts +1 -1
- package/dist/portfolio/sections/TestimonialSection.d.cts +1 -1
- package/dist/portfolio/sections/TestimonialSection.d.ts +1 -1
- package/dist/portfolio/server.cjs +40 -14
- package/dist/portfolio/server.d.cts +3 -1
- package/dist/portfolio/server.d.ts +3 -1
- package/dist/portfolio/server.js +3 -1
- package/dist/{types-0tVPy4pU.d.cts → types-He5A9okM.d.cts} +1 -1
- package/dist/{types-0tVPy4pU.d.ts → types-He5A9okM.d.ts} +1 -1
- package/package.json +3 -3
- package/dist/chunk-4N7ENJ7L.cjs.map +0 -1
- package/dist/chunk-L7E3UHQC.cjs.map +0 -1
- package/dist/chunk-NIZJLONK.js.map +0 -1
- package/dist/chunk-UNTOEWQL.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/portfolio/components/PortfolioPage.tsx","../src/portfolio/components/PortfolioSchema.tsx","../src/portfolio/components/PortfolioCard.tsx","../src/portfolio/components/PortfolioGrid.tsx","../src/portfolio/components/PortfolioIndex.tsx","../src/portfolio/components/PortfolioSkeletons.tsx","../src/types.ts"],"names":["jsx","jsxs"],"mappings":";;;;;;;;;;;;;;;;;;;AA+DA,IAAM,eAAA,GAAkB,QAAQ,MAAM,OAAO,8DAA8D,CAAA,EAAG,EAAE,GAAA,EAAK,IAAA,EAAM,CAAA;AAC3H,IAAM,cAAA,GAAiB,QAAQ,MAAM,OAAO,6DAA6D,CAAA,EAAG,EAAE,GAAA,EAAK,IAAA,EAAM,CAAA;AACzH,IAAM,YAAA,GAAe,QAAQ,MAAM,OAAO,2DAA2D,CAAA,EAAG,EAAE,GAAA,EAAK,IAAA,EAAM,CAAA;AACrH,IAAM,WAAA,GAAc,QAAQ,MAAM,OAAO,0DAA0D,CAAA,EAAG,EAAE,GAAA,EAAK,IAAA,EAAM,CAAA;AACnH,IAAM,uBAAA,GAA0B,QAAQ,MAAM,OAAO,sEAAsE,CAAA,EAAG,EAAE,GAAA,EAAK,IAAA,EAAM,CAAA;AAC3I,IAAM,kBAAA,GAAqB,QAAQ,MAAM,OAAO,iEAAiE,CAAA,EAAG,EAAE,GAAA,EAAK,IAAA,EAAM,CAAA;AACjI,IAAM,sBAAA,GAAyB,QAAQ,MAAM,OAAO,qEAAqE,CAAA,EAAG,EAAE,GAAA,EAAK,IAAA,EAAM,CAAA;AACzI,IAAM,uBAAA,GAA0B,QAAQ,MAAM,OAAO,sEAAsE,CAAA,EAAG,EAAE,GAAA,EAAK,IAAA,EAAM,CAAA;AAC3I,IAAM,kBAAA,GAAqB,QAAQ,MAAM,OAAO,iEAAiE,CAAA,EAAG,EAAE,GAAA,EAAK,IAAA,EAAM,CAAA;AACjI,IAAM,uBAAA,GAA0B,QAAQ,MAAM,OAAO,sEAAsE,CAAA,EAAG,EAAE,GAAA,EAAK,IAAA,EAAM,CAAA;AAC3I,IAAM,UAAA,GAAa,QAAQ,MAAM,OAAO,yDAAyD,CAAA,EAAG,EAAE,GAAA,EAAK,IAAA,EAAM,CAAA;AAcjH,SAAS,aAAA,CAAc,OAAA,EAA2B,IAAA,EAAyB,KAAA,EAAe;AACxF,EAAA,MAAM,GAAA,GAAM,CAAA,EAAG,OAAA,CAAQ,WAAW,IAAI,KAAK,CAAA,CAAA;AAE3C,EAAA,QAAQ,QAAQ,WAAA;AAAa,IAC3B,KAAK,eAAA;AACH,MAAA,uBACE,GAAA;AAAA,QAAC,WAAA;AAAA,QAAA;AAAA,UAEC,MAAM,OAAA,CAAQ,IAAA;AAAA,UACd,WAAA,EAAa,KAAK,gBAAA,IAAoB,MAAA;AAAA,UACtC,OAAA,EAAS,KAAK,QAAA,IAAY;AAAA,SAAA;AAAA,QAHrB;AAAA,OAIP;AAAA,IAEJ,KAAK,qBAAA;AACH,MAAA,uBAAO,GAAA,CAAC,iBAAA,EAAA,EAA4B,IAAA,EAAM,OAAA,CAAQ,QAAnB,GAAoD,CAAA;AAAA,IACrF,KAAK,mBAAA;AACH,MAAA,uBAAO,GAAA,CAAC,eAAA,EAAA,EAA0B,IAAA,EAAM,OAAA,CAAQ,QAAnB,GAAkD,CAAA;AAAA,IACjF,KAAK,kBAAA;AACH,MAAA,uBACE,GAAA;AAAA,QAAC,cAAA;AAAA,QAAA;AAAA,UAEC,MAAM,OAAA,CAAQ,IAAA;AAAA,UACd,cAAc,IAAA,CAAK,YAAA;AAAA,UACnB,oBAAoB,IAAA,CAAK;AAAA,SAAA;AAAA,QAHpB;AAAA,OAIP;AAAA,IAEJ,KAAK,oBAAA;AACH,MAAA,uBAAO,GAAA,CAAC,gBAAA,EAAA,EAA2B,IAAA,EAAM,OAAA,CAAQ,QAAnB,GAAmD,CAAA;AAAA,IACnF,KAAK,mBAAA;AACH,MAAA,uBAAO,GAAA,CAAC,eAAA,EAAA,EAA0B,IAAA,EAAM,OAAA,CAAQ,QAAnB,GAAkD,CAAA;AAAA,IACjF,KAAK,sBAAA;AACH,MAAA,uBAAO,GAAA,CAAC,kBAAA,EAAA,EAA6B,IAAA,EAAM,OAAA,CAAQ,QAAnB,GAAqD,CAAA;AAAA,IACvF,KAAK,kBAAA;AACH,MAAA,uBAAO,GAAA,CAAC,cAAA,EAAA,EAAyB,IAAA,EAAM,OAAA,CAAQ,QAAnB,GAAiD,CAAA;AAAA,IAC/E,KAAK,gBAAA;AACH,MAAA,uBAAO,GAAA,CAAC,YAAA,EAAA,EAAuB,IAAA,EAAM,OAAA,CAAQ,QAAnB,GAA+C,CAAA;AAAA,IAC3E,KAAK,eAAA;AACH,MAAA,uBAAO,GAAA,CAAC,WAAA,EAAA,EAAsB,IAAA,EAAM,OAAA,CAAQ,QAAnB,GAA8C,CAAA;AAAA,IACzE,KAAK,2BAAA;AACH,MAAA,uBAAO,GAAA,CAAC,uBAAA,EAAA,EAAkC,IAAA,EAAM,OAAA,CAAQ,QAAnB,GAA0D,CAAA;AAAA,IACjG,KAAK,sBAAA;AACH,MAAA,uBAAO,GAAA,CAAC,kBAAA,EAAA,EAA6B,IAAA,EAAM,OAAA,CAAQ,QAAnB,GAAqD,CAAA;AAAA,IACvF,KAAK,0BAAA;AACH,MAAA,uBAAO,GAAA,CAAC,sBAAA,EAAA,EAAiC,IAAA,EAAM,OAAA,CAAQ,QAAnB,GAAyD,CAAA;AAAA,IAC/F,KAAK,2BAAA;AACH,MAAA,uBAAO,GAAA,CAAC,uBAAA,EAAA,EAAkC,IAAA,EAAM,OAAA,CAAQ,QAAnB,GAA0D,CAAA;AAAA,IACjG,KAAK,sBAAA;AACH,MAAA,uBAAO,GAAA,CAAC,kBAAA,EAAA,EAA6B,IAAA,EAAM,OAAA,CAAQ,QAAnB,GAAgC,CAAA;AAAA,IAClE,KAAK,0BAAA;AACH,MAAA,uBAAO,GAAA,CAAC,sBAAA,EAAA,EAAiC,IAAA,EAAM,OAAA,CAAQ,QAAnB,GAAgC,CAAA;AAAA,IACtE,KAAK,2BAAA;AACH,MAAA,2BAAQ,uBAAA,EAAA,EAAkC,IAAA,EAAM,6BAA6B,OAAA,CAAQ,IAAI,KAApD,GAA8D,CAAA;AAAA,IACrG,KAAK,uBAAA;AACH,MAAA,uBAAO,GAAA,CAAC,mBAAA,EAAA,EAA8B,IAAA,EAAM,OAAA,CAAQ,QAAnB,GAAgC,CAAA;AAAA,IACnE,KAAK,kBAAA;AACH,MAAA,uBAAO,GAAA,CAAC,cAAA,EAAA,EAAyB,IAAA,EAAM,OAAA,CAAQ,QAAnB,GAAiD,CAAA;AAAA,IAC/E,KAAK,cAAA;AACH,MAAA,uBAAO,GAAA,CAAC,UAAA,EAAA,EAAqB,IAAA,EAAM,OAAA,CAAQ,QAAnB,GAA6C,CAAA;AAAA,IACvE,KAAK,cAAA;AAEH,MAAA,OAAO,IAAA;AAAA,IACT;AACE,MAAA,OAAO,IAAA;AAAA;AAEb;AAMA,IAAM,aAAA,GAAwC;AAAA,EAC5C,aAAA,EAAe,CAAA;AAAA,EACf,mBAAA,EAAqB,CAAA;AAAA,EACrB,iBAAA,EAAmB,CAAA;AAAA,EACnB,gBAAA,EAAkB,CAAA;AAAA,EAClB,oBAAA,EAAsB,CAAA;AAAA,EACtB,wBAAA,EAA0B,CAAA;AAAA,EAC1B,yBAAA,EAA2B,CAAA;AAAA,EAC3B,kBAAA,EAAoB,CAAA;AAAA,EACpB,qBAAA,EAAuB,CAAA;AAAA,EACvB,iBAAA,EAAmB,CAAA;AAAA,EACnB,oBAAA,EAAsB,EAAA;AAAA,EACtB,oBAAA,EAAsB,EAAA;AAAA,EACtB,wBAAA,EAA0B,EAAA;AAAA,EAC1B,yBAAA,EAA2B,EAAA;AAAA,EAC3B,gBAAA,EAAkB,EAAA;AAAA,EAClB,YAAA,EAAc,EAAA;AAAA,EACd,YAAA,EAAc;AAChB,CAAA;AAEe,SAAR,aAAA,CAA+B,EAAE,IAAA,EAAK,EAAuB;AAMlE,EAAA,MAAM,iBAAiB,IAAA,CAAK,eAAA,GACxB,CAAC,GAAG,IAAA,CAAK,QAAQ,CAAA,CAAE,IAAA;AAAA,IACjB,CAAC,CAAA,EAAG,CAAA,KAAA,CACD,CAAA,CAAE,WAAA,KAAgB,eAAA,GAAkB,CAAA,GAAI,CAAA,KACxC,CAAA,CAAE,WAAA,KAAgB,eAAA,GAAkB,CAAA,GAAI,CAAA;AAAA,GAC7C,GACA,CAAC,GAAG,IAAA,CAAK,QAAQ,CAAA,CAAE,IAAA;AAAA,IACjB,CAAC,CAAA,EAAG,CAAA,KAAA,CAAO,aAAA,CAAc,CAAA,CAAE,WAAW,CAAA,IAAK,EAAA,KAAO,aAAA,CAAc,CAAA,CAAE,WAAW,CAAA,IAAK,EAAA;AAAA,GACpF;AAEJ,EAAA,uBACE,IAAA,CAAC,SAAA,EAAA,EAAQ,SAAA,EAAU,yDAAA,EAChB,QAAA,EAAA;AAAA,IAAA,cAAA,CAAe,GAAA,CAAI,CAAC,OAAA,EAAS,KAAA,KAAU;AACtC,MAAA,MAAM,QAAA,GAAW,aAAA,CAAc,OAAA,EAAS,IAAA,EAAM,KAAK,CAAA;AACnD,MAAA,IAAI,CAAC,UAAU,OAAO,IAAA;AACtB,MAAA,uBACE,GAAA;AAAA,QAAC,KAAA;AAAA,QAAA;AAAA,UAEC,SAAA,EAAU,QAAA;AAAA,UACV,0BAAwB,OAAA,CAAQ,WAAA;AAAA,UAE/B,QAAA,EAAA;AAAA,SAAA;AAAA,QAJI,CAAA,QAAA,EAAW,OAAA,CAAQ,WAAW,CAAA,CAAA,EAAI,KAAK,CAAA;AAAA,OAK9C;AAAA,IAEJ,CAAC,CAAA;AAAA,oBAED,GAAA,CAAC,kBAAA,EAAA,EAAmB,IAAA,EAAM,IAAA,CAAK,IAAA,EAAM;AAAA,GAAA,EACvC,CAAA;AAEJ;AC5KA,SAAS,OAAO,KAAA,EAAwB;AACtC,EAAA,OAAO,KAAK,SAAA,CAAU,KAAK,CAAA,CAAE,OAAA,CAAQ,MAAM,SAAS,CAAA;AACtD;AAEe,SAAR,eAAA,CAAiC,EAAE,IAAA,EAAM,GAAA,EAAK,WAAU,EAAyB;AACtF,EAAA,MAAM,QAAA,GAAW,IAAA,CAAK,GAAA,EAAK,SAAA,IAAa,IAAA,CAAK,KAAA;AAC7C,EAAA,MAAM,WAAA,GAAc,IAAA,CAAK,GAAA,EAAK,eAAA,IAAmB,IAAA,CAAK,WAAA;AAEtD,EAAA,MAAM,QAAA,GAAW;AAAA,IACf,GAAI,IAAA,CAAK,GAAA,EAAK,QAAA,IAAY,EAAC;AAAA,IAC3B,IAAA,CAAK,QAAA;AAAA,IACL,GAAG,IAAA,CAAK;AAAA,GACV,CAAE,OAAO,OAAO,CAAA;AAEhB,EAAA,MAAM,MAAM,SAAA,GACR;AAAA,IACE,OAAA,EAAS,cAAA;AAAA,IACT,MAAM,SAAA,CAAU,IAAA;AAAA,IAChB,GAAI,UAAU,GAAA,GAAM,EAAE,KAAK,SAAA,CAAU,GAAA,KAAQ,EAAC;AAAA,IAC9C,GAAI,SAAA,CAAU,IAAA,GACV,EAAE,IAAA,EAAM,EAAE,OAAA,EAAS,aAAA,EAAe,GAAA,EAAK,SAAA,CAAU,IAAA,EAAK,KACtD;AAAC,GACP,GACA,MAAA;AAEJ,EAAA,MAAM,MAAA,GAAS;AAAA,IACb,UAAA,EAAY,oBAAA;AAAA,IACZ,OAAA,EAAS,SAAA;AAAA,IACT,QAAA;AAAA,IACA,GAAI,WAAA,GAAc,EAAE,WAAA,KAAgB,EAAC;AAAA,IACrC,GAAI,IAAA,CAAK,UAAA,GAAa,EAAE,KAAA,EAAO,CAAC,IAAA,CAAK,UAAU,CAAA,EAAE,GAAI,EAAC;AAAA,IACtD,GAAA;AAAA,IACA,gBAAA,EAAkB,EAAE,OAAA,EAAS,SAAA,EAAW,OAAO,GAAA,EAAI;AAAA,IACnD,GAAI,KAAK,YAAA,GAAe,EAAE,eAAe,IAAA,CAAK,YAAA,KAAiB,EAAC;AAAA,IAChE,GAAI,IAAA,CAAK,yBAAA,IAA6B,IAAA,CAAK,YAAA,GACvC,EAAE,YAAA,EAAc,IAAA,CAAK,yBAAA,IAA6B,IAAA,CAAK,YAAA,EAAa,GACpE,EAAC;AAAA,IACL,GAAI,QAAA,CAAS,MAAA,GAAS,EAAE,QAAA,EAAU,SAAS,IAAA,CAAK,IAAI,CAAA,EAAE,GAAI,EAAC;AAAA,IAC3D,GAAI,MAAM,EAAE,MAAA,EAAQ,KAAK,SAAA,EAAW,GAAA,KAAQ,EAAC;AAAA,IAC7C,GAAI,IAAA,CAAK,OAAA,EAAS,QAAA,GAAW,EAAE,OAAO,IAAA,CAAK,OAAA,CAAQ,QAAA,EAAS,GAAI;AAAC,GACnE;AAEA,EAAA,uBACEA,GAAAA;AAAA,IAAC,QAAA;AAAA,IAAA;AAAA,MACC,IAAA,EAAK,qBAAA;AAAA,MACL,uBAAA,EAAyB,EAAE,MAAA,EAAQ,MAAA,CAAO,MAAM,CAAA;AAAE;AAAA,GACpD;AAEJ;ACxEe,SAAR,cAA+B,EAAE,IAAA,EAAM,YAAY,EAAA,EAAI,QAAA,GAAW,SAAQ,EAAuB;AAEtG,EAAA,MAAM,YAAA,GACJ,IAAA,CAAK,gBAAA,EAAkB,OAAA,IACvB,IAAA,CAAK,UAAA,KACJ,IAAA,CAAK,UAAA,GAAa,CAAA,oBAAA,EAAuB,IAAA,CAAK,IAAI,CAAA,IAAA,CAAA,GAAS,MAAA,CAAA;AAC9D,EAAA,MAAM,IAAA,GAAO,MAAM,OAAA,CAAQ,IAAA,CAAK,IAAI,CAAA,GAAI,IAAA,CAAK,OAAO,EAAC;AACrD,EAAA,MAAM,QAAA,GAAW,MAAM,OAAA,CAAQ,IAAA,CAAK,QAAQ,CAAA,GAAI,IAAA,CAAK,WAAW,EAAC;AACjE,EAAA,MAAM,UAAA,GAAa,IAAA,CAAK,KAAA,CAAM,CAAA,EAAG,CAAC,CAAA;AAClC,EAAA,MAAM,cAAA,GAAiB,QAAA,CAAS,KAAA,CAAM,CAAA,EAAG,CAAC,CAAA;AAE1C,EAAA,uBACEA,GAAAA,CAAC,IAAA,EAAA,EAAK,IAAA,EAAM,CAAA,EAAG,QAAQ,CAAA,CAAA,EAAI,IAAA,CAAK,IAAI,CAAA,CAAA,EAAI,SAAA,EAAW,CAAA,YAAA,EAAe,SAAS,CAAA,CAAA,EACzE,QAAA,kBAAAA,GAAAA,CAAC,SAAA,EAAA,EAAU,OAAA,EAAQ,IAAA,EAAK,KAAA,EAAK,IAAA,EAC3B,QAAA,kBAAAC,IAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,qBAAA,EAEZ,QAAA,EAAA;AAAA,IAAA,YAAA,oBACCA,IAAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,SAAA,EAAU,4CAAA;AAAA,QACV,KAAA,EAAO,EAAE,WAAA,EAAa,OAAA,EAAQ;AAAA,QAE9B,QAAA,EAAA;AAAA,0BAAAD,GAAAA;AAAA,YAAC,KAAA;AAAA,YAAA;AAAA,cACC,GAAA,EAAK,YAAA;AAAA,cACL,GAAA,EAAK,IAAA,CAAK,cAAA,IAAkB,IAAA,CAAK,KAAA;AAAA,cACjC,IAAA,EAAI,IAAA;AAAA,cACJ,KAAA,EAAM,0DAAA;AAAA,cACN,SAAA,EAAU;AAAA;AAAA,WACZ;AAAA,0BAEAA,GAAAA;AAAA,YAAC,MAAA;AAAA,YAAA;AAAA,cACC,SAAA,EAAU,iGAAA;AAAA,cACV,KAAA,EAAO;AAAA,gBACL,UAAA,EAAY,iEAAA;AAAA,gBACZ,KAAA,EAAO,SAAA;AAAA,gBACP,cAAA,EAAgB;AAAA,eAClB;AAAA,cAEC,QAAA,EAAA,IAAA,CAAK,QAAA,EAAU,OAAA,CAAQ,OAAA,EAAS,GAAG,CAAA,CAAE,OAAA,CAAQ,OAAA,EAAS,CAAC,CAAA,KAAc,CAAA,CAAE,WAAA,EAAa;AAAA;AAAA;AACvF;AAAA;AAAA,KACF;AAAA,oBAIFC,IAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,+BAAA,EACb,QAAA,EAAA;AAAA,sBAAAD,GAAAA;AAAA,QAAC,IAAA;AAAA,QAAA;AAAA,UACC,SAAA,EAAU,oCAAA;AAAA,UACV,KAAA,EAAO,EAAE,KAAA,EAAO,iCAAA,EAAkC;AAAA,UAEjD,QAAA,EAAA,IAAA,CAAK;AAAA;AAAA,OACR;AAAA,MAEC,IAAA,CAAK,4BACJA,GAAAA;AAAA,QAAC,GAAA;AAAA,QAAA;AAAA,UACC,SAAA,EAAU,sBAAA;AAAA,UACV,KAAA,EAAO,EAAE,KAAA,EAAO,kCAAA,EAAmC;AAAA,UAElD,QAAA,EAAA,IAAA,CAAK;AAAA;AAAA,OACR;AAAA,MAID,WAAW,MAAA,GAAS,CAAA,oBACnBA,GAAAA,CAAC,SAAI,SAAA,EAAU,8BAAA,EACZ,QAAA,EAAA,UAAA,CAAW,GAAA,CAAI,CAAC,GAAA,EAAK,CAAA,qBACpBC,IAAAA,CAAC,KAAA,EAAA,EAAY,WAAU,eAAA,EACrB,QAAA,EAAA;AAAA,wBAAAA,IAAAA;AAAA,UAAC,MAAA;AAAA,UAAA;AAAA,YACC,SAAA,EAAU,qBAAA;AAAA,YACV,KAAA,EAAO,EAAE,KAAA,EAAO,4BAAA,EAA6B;AAAA,YAE5C,QAAA,EAAA;AAAA,cAAA,GAAA,CAAI,MAAA,IAAU,EAAA;AAAA,cACd,GAAA,CAAI,MAAM,cAAA,EAAe;AAAA,cACzB,GAAA,CAAI;AAAA;AAAA;AAAA,SACP;AAAA,wBACAD,GAAAA;AAAA,UAAC,MAAA;AAAA,UAAA;AAAA,YACC,SAAA,EAAU,kDAAA;AAAA,YACV,KAAA,EAAO,EAAE,KAAA,EAAO,kCAAA,EAAmC;AAAA,YAElD,QAAA,EAAA,GAAA,CAAI;AAAA;AAAA;AACP,OAAA,EAAA,EAdQ,CAeV,CACD,CAAA,EACH,CAAA;AAAA,MAID,eAAe,MAAA,GAAS,CAAA,oBACvBC,IAAAA,CAAC,KAAA,EAAA,EAAI,WAAU,6BAAA,EACZ,QAAA,EAAA;AAAA,QAAA,cAAA,CAAe,GAAA,CAAI,CAAC,OAAA,qBACnBD,GAAAA;AAAA,UAAC,MAAA;AAAA,UAAA;AAAA,YAEC,SAAA,EAAU,oDAAA;AAAA,YACV,KAAA,EAAO;AAAA,cACL,UAAA,EAAY,2CAAA;AAAA,cACZ,KAAA,EAAO,mCAAA;AAAA,cACP,MAAA,EAAQ;AAAA,aACV;AAAA,YAEC,QAAA,EAAA;AAAA,WAAA;AAAA,UARI;AAAA,SAUR,CAAA;AAAA,QACA,QAAA,CAAS,MAAA,GAAS,CAAA,oBACjBC,IAAAA;AAAA,UAAC,MAAA;AAAA,UAAA;AAAA,YACC,SAAA,EAAU,oDAAA;AAAA,YACV,KAAA,EAAO;AAAA,cACL,UAAA,EAAY,2CAAA;AAAA,cACZ,KAAA,EAAO;AAAA,aACT;AAAA,YACD,QAAA,EAAA;AAAA,cAAA,GAAA;AAAA,cACG,IAAA,CAAK,SAAS,MAAA,GAAS;AAAA;AAAA;AAAA;AAC3B,OAAA,EAEJ;AAAA,KAAA,EAEJ;AAAA,GAAA,EACF,GACF,CAAA,EACF,CAAA;AAEJ;ACvHe,SAAR,cAA+B,EAAE,KAAA,EAAO,YAAY,EAAA,EAAI,QAAA,GAAW,SAAQ,EAAuB;AACvG,EAAA,uBACED,GAAAA,CAAC,YAAA,EAAA,EAAa,OAAA,EAAS,KACrB,QAAA,kBAAAA,GAAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAW,kDAAkD,SAAS,CAAA,CAAA;AAAA,MACtE,KAAA,EAAO;AAAA,QACL,GAAA,EAAK;AAAA,OACP;AAAA,MAEC,QAAA,EAAA,KAAA,CAAM,GAAA,CAAI,CAAC,IAAA,qBACVA,GAAAA,CAAC,aAAA,EAAA,EAA4B,IAAA,EAAY,QAAA,EAAA,EAArB,IAAA,CAAK,EAAoC,CAC9D;AAAA;AAAA,GACH,EACF,CAAA;AAEJ;ACSA,SAAS,YAAA,CAAa;AAAA,EACpB,IAAA;AAAA,EACA,KAAA;AAAA,EACA;AACF,CAAA,EAIG;AACD,EAAA,uBACEA,GAAAA;AAAA,IAAC,GAAA;AAAA,IAAA;AAAA,MACC,IAAA;AAAA,MACA,cAAA,EAAc,SAAS,MAAA,GAAS,MAAA;AAAA,MAChC,SAAA,EAAU,gEAAA;AAAA,MACV,KAAA,EAAO;AAAA,QACL,UAAA,EAAY,SACR,iEAAA,GACA,2CAAA;AAAA,QACJ,KAAA,EAAO,SAAS,4BAAA,GAA+B,mCAAA;AAAA,QAC/C,MAAA,EAAQ,CAAA,UAAA,EACN,MAAA,GACI,iEAAA,GACA,yCACN,CAAA;AAAA,OACF;AAAA,MAEC,QAAA,EAAA;AAAA;AAAA,GACH;AAEJ;AAEA,SAAS,QAAA,CAAS,EAAE,IAAA,EAAM,KAAA,EAAO,UAAS,EAAuD;AAC/F,EAAA,IAAI,QAAA,EAAU;AACZ,IAAA,uBACEA,GAAAA;AAAA,MAAC,MAAA;AAAA,MAAA;AAAA,QACC,eAAA,EAAc,MAAA;AAAA,QACd,SAAA,EAAU,qDAAA;AAAA,QACV,KAAA,EAAO;AAAA,UACL,UAAA,EAAY,2CAAA;AAAA,UACZ,KAAA,EAAO,kCAAA;AAAA,UACP,MAAA,EAAQ;AAAA,SACV;AAAA,QAEC,QAAA,EAAA;AAAA;AAAA,KACH;AAAA,EAEJ;AACA,EAAA,uBACEA,GAAAA;AAAA,IAAC,GAAA;AAAA,IAAA;AAAA,MACC,IAAA;AAAA,MACA,SAAA,EAAU,4DAAA;AAAA,MACV,KAAA,EAAO;AAAA,QACL,UAAA,EAAY,2CAAA;AAAA,QACZ,KAAA,EAAO,mCAAA;AAAA,QACP,MAAA,EAAQ;AAAA,OACV;AAAA,MAEC,QAAA,EAAA;AAAA;AAAA,GACH;AAEJ;AAEA,eAAO,cAAA,CAAsC;AAAA,EAC3C,QAAA,GAAW,OAAA;AAAA,EACX,QAAA,GAAW,IAAA;AAAA,EACX,IAAA,GAAO,CAAA;AAAA,EACP,QAAA,GAAW,EAAA;AAAA,EACX,OAAA;AAAA,EACA,UAAA;AAAA,EACA,cAAA,GAAiB,IAAA;AAAA,EACjB,SAAA,GAAY;AACd,CAAA,EAAwB;AACtB,EAAA,MAAM,cAAc,IAAA,CAAK,GAAA,CAAI,GAAG,IAAA,CAAK,KAAA,CAAM,IAAI,CAAC,CAAA;AAChD,EAAA,MAAM,UAAA,GAAa,MAAM,sBAAA,EAAuB;AAGhD,EAAA,MAAM,cAAA,GAAiB,QAAA,GACnB,UAAA,CAAW,IAAA,CAAK,CAAC,CAAA,KAAM,eAAA,CAAgB,CAAC,CAAA,KAAM,QAAQ,CAAA,IAAK,IAAA,GAC3D,IAAA;AAEJ,EAAA,MAAM,EAAE,KAAA,EAAO,KAAA,EAAM,GAAI,MAAM,iBAAA,CAAkB;AAAA,IAC/C,UAAU,cAAA,IAAkB,MAAA;AAAA,IAC5B,KAAA,EAAO,QAAA;AAAA,IACP,MAAA,EAAA,CAAS,cAAc,CAAA,IAAK;AAAA,GAC7B,CAAA;AAED,EAAA,MAAM,UAAA,GAAa,KAAK,GAAA,CAAI,CAAA,EAAG,KAAK,IAAA,CAAK,KAAA,GAAQ,QAAQ,CAAC,CAAA;AAC1D,EAAA,MAAM,QAAA,GAAW,CAAC,CAAA,KAAc;AAC9B,IAAA,MAAM,IAAA,GAAO,iBACT,CAAA,EAAG,QAAQ,aAAa,eAAA,CAAgB,cAAc,CAAC,CAAA,CAAA,GACvD,QAAA;AACJ,IAAA,OAAO,KAAK,CAAA,GAAI,IAAA,GAAO,CAAA,EAAG,IAAI,SAAS,CAAC,CAAA,CAAA;AAAA,EAC1C,CAAA;AAEA,EAAA,uBACEA,GAAAA,CAAC,SAAA,EAAA,EAAQ,SAAA,EAAW,CAAA,OAAA,EAAU,SAAS,CAAA,CAAA,EACrC,QAAA,kBAAAC,IAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,wBAAA,EACZ,QAAA,EAAA;AAAA,IAAA,OAAA;AAAA,IAGA,cAAA,IAAkB,UAAA,CAAW,MAAA,GAAS,CAAA,oBACrCA,KAAC,KAAA,EAAA,EAAI,YAAA,EAAW,sBAAA,EAAuB,SAAA,EAAU,4BAAA,EAC/C,QAAA,EAAA;AAAA,sBAAAD,GAAAA,CAAC,gBAAa,IAAA,EAAM,QAAA,EAAU,OAAM,KAAA,EAAM,MAAA,EAAQ,CAAC,cAAA,EAAgB,CAAA;AAAA,MAClE,UAAA,CAAW,GAAA,CAAI,CAAC,CAAA,qBACfA,GAAAA;AAAA,QAAC,YAAA;AAAA,QAAA;AAAA,UAEC,MAAM,CAAA,EAAG,QAAQ,CAAA,UAAA,EAAa,eAAA,CAAgB,CAAC,CAAC,CAAA,CAAA;AAAA,UAChD,KAAA,EAAO,CAAA;AAAA,UACP,QAAQ,cAAA,KAAmB;AAAA,SAAA;AAAA,QAHtB;AAAA,OAKR;AAAA,KAAA,EACH,CAAA;AAAA,IAGD,KAAA,CAAM,MAAA,GAAS,CAAA,mBACdA,GAAAA,CAAC,iBAAc,KAAA,EAAc,QAAA,EAAoB,CAAA,GAEjD,UAAA,oBACEC,IAAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,SAAA,EAAU,gEAAA;AAAA,QACV,KAAA,EAAO;AAAA,UACL,UAAA,EAAY,2CAAA;AAAA,UACZ,MAAA,EAAQ;AAAA,SACV;AAAA,QAEA,QAAA,EAAA;AAAA,0BAAAD,GAAAA;AAAA,YAAC,MAAA;AAAA,YAAA;AAAA,cACC,SAAA,EAAU,uBAAA;AAAA,cACV,KAAA,EAAO,EAAE,KAAA,EAAO,iCAAA,EAAkC;AAAA,cACnD,QAAA,EAAA;AAAA;AAAA,WAED;AAAA,0BACAA,GAAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,WAAU,KAAA,EAAO,EAAE,KAAA,EAAO,kCAAA,EAAmC,EAC1E,QAAA,EAAA,cAAA,GACG,CAAA,qBAAA,EAAwB,cAAc,UACtC,6CAAA,EACN;AAAA;AAAA;AAAA,KACF;AAAA,IAKH,UAAA,GAAa,qBACZC,IAAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,YAAA,EAAW,iBAAA;AAAA,QACX,SAAA,EAAU,8CAAA;AAAA,QAEV,QAAA,EAAA;AAAA,0BAAAD,GAAAA;AAAA,YAAC,QAAA;AAAA,YAAA;AAAA,cACC,IAAA,EAAM,QAAA,CAAS,WAAA,GAAc,CAAC,CAAA;AAAA,cAC9B,KAAA,EAAM,iBAAA;AAAA,cACN,UAAU,WAAA,IAAe;AAAA;AAAA,WAC3B;AAAA,0BACAC,KAAC,MAAA,EAAA,EAAK,SAAA,EAAU,WAAU,KAAA,EAAO,EAAE,KAAA,EAAO,kCAAA,EAAmC,EAAG,QAAA,EAAA;AAAA,YAAA,OAAA;AAAA,YACxE,WAAA;AAAA,YAAY,MAAA;AAAA,YAAK;AAAA,WAAA,EACzB,CAAA;AAAA,0BACAD,GAAAA;AAAA,YAAC,QAAA;AAAA,YAAA;AAAA,cACC,IAAA,EAAM,QAAA,CAAS,WAAA,GAAc,CAAC,CAAA;AAAA,cAC9B,KAAA,EAAM,aAAA;AAAA,cACN,UAAU,WAAA,IAAe;AAAA;AAAA;AAC3B;AAAA;AAAA;AACF,GAAA,EAEJ,CAAA,EACF,CAAA;AAEJ;AChMA,IAAM,SAAA,GAAY;AAAA;AAAA;AAAA;AAAA,CAAA;AAMlB,SAAS,cAAA,GAAiB;AACxB,EAAA,uBACEA,GAAAA,CAAC,OAAA,EAAA,EAAM,MAAK,cAAA,EAAe,UAAA,EAAW,WACnC,QAAA,EAAA,SAAA,EACH,CAAA;AAEJ;AAGO,SAAS,sBAAA,CAAuB,EAAE,KAAA,GAAQ,CAAA,EAAE,EAAuB;AACxE,EAAA,uBACEC,IAAAA,CAAC,SAAA,EAAA,EAAQ,SAAA,EAAU,cAAA,EACjB,QAAA,EAAA;AAAA,oBAAAD,IAAC,cAAA,EAAA,EAAe,CAAA;AAAA,oBAChBC,IAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,wBAAA,EACb,QAAA,EAAA;AAAA,sBAAAD,GAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,2BAAA,EAA4B,CAAA;AAAA,sBAC3CA,GAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,4BAAA,EAA6B,CAAA;AAAA,sBAC5CA,IAAC,KAAA,EAAA,EAAI,SAAA,EAAU,wDACZ,QAAA,EAAA,KAAA,CAAM,IAAA,CAAK,EAAE,MAAA,EAAQ,KAAA,EAAO,CAAA,CAAE,GAAA,CAAI,CAAC,CAAA,EAAG,CAAA,qBACrCC,IAAAA,CAAC,KAAA,EAAA,EAAY,WAAU,qBAAA,EACrB,QAAA,EAAA;AAAA,wBAAAD,GAAAA,CAAC,SAAI,SAAA,EAAU,oBAAA,EAAqB,OAAO,EAAE,WAAA,EAAa,SAAQ,EAAG,CAAA;AAAA,wBACrEA,GAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,uBAAA,EAAwB,CAAA;AAAA,wBACvCA,GAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,uBAAA,EAAwB;AAAA,OAAA,EAAA,EAH/B,CAIV,CACD,CAAA,EACH;AAAA,KAAA,EACF;AAAA,GAAA,EACF,CAAA;AAEJ;AAGO,SAAS,qBAAA,GAAwB;AACtC,EAAA,uBACEC,IAAAA,CAAC,SAAA,EAAA,EAAQ,SAAA,EAAU,QAAA,EACjB,QAAA,EAAA;AAAA,oBAAAD,IAAC,cAAA,EAAA,EAAe,CAAA;AAAA,oBAChBA,IAAC,KAAA,EAAA,EAAI,SAAA,EAAU,gCACb,QAAA,kBAAAC,IAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,oDAAA,EACb,QAAA,EAAA;AAAA,sBAAAA,IAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,qBAAA,EACb,QAAA,EAAA;AAAA,wBAAAD,GAAAA,CAAC,SAAI,SAAA,EAAU,sBAAA,EAAuB,OAAO,EAAE,YAAA,EAAc,UAAS,EAAG,CAAA;AAAA,wBACzEA,GAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,yBAAA,EAA0B,CAAA;AAAA,wBACzCA,GAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,wBAAA,EAAyB,CAAA;AAAA,wBACxCA,GAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,wBAAA,EAAyB,CAAA;AAAA,wBACxCA,GAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,uBAAA,EAAwB;AAAA,OAAA,EACzC,CAAA;AAAA,sBACAA,IAAC,KAAA,EAAA,EAAI,SAAA,EAAU,sBAAqB,KAAA,EAAO,EAAE,WAAA,EAAa,MAAA,EAAO,EAAG;AAAA,KAAA,EACtE,CAAA,EACF;AAAA,GAAA,EACF,CAAA;AAEJ;;;AC2PO,IAAM,uBAAA,GAA0B;AAAA,EACrC,eAAA;AAAA,EACA,qBAAA;AAAA,EACA,mBAAA;AAAA,EACA,kBAAA;AAAA,EACA,oBAAA;AAAA,EACA,mBAAA;AAAA,EACA,sBAAA;AAAA,EACA,kBAAA;AAAA,EACA,gBAAA;AAAA,EACA,eAAA;AAAA,EACA,2BAAA;AAAA,EACA,sBAAA;AAAA,EACA,0BAAA;AAAA,EACA,2BAAA;AAAA,EACA,sBAAA;AAAA,EACA,0BAAA;AAAA,EACA,2BAAA;AAAA,EACA,uBAAA;AAAA,EACA,kBAAA;AAAA,EACA,cAAA;AAAA,EACA;AACF","file":"chunk-UNTOEWQL.js","sourcesContent":["/**\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 * Sections MUST be imported via `@sonordev/agency-site-kit/portfolio/sections/*`\n * (self-referencing subpaths, external in tsup's server build), never via\n * relative paths: a relative import would bundle the client sections into\n * this server chunk, the 'use client' boundary would be lost, and the whole\n * PortfolioItemFull would serialize into the RSC flight payload again.\n *\n * Whether a section is a server or client component is decided by the\n * section file itself (its 'use client' directive; tsup routes it to the\n * matching build). Server sections are imported statically — they ship zero\n * client JS, so there is nothing to lazy-load. Client sections load through\n * next/dynamic so each becomes its own browser chunk, fetched only when the\n * case study actually renders that section type.\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, client — it animates)\nimport HeroSection from '@sonordev/agency-site-kit/portfolio/sections/HeroSection';\n\n// Server sections — no 'use client' in their files, so they render on the\n// server and ship ZERO component JS to the browser. Static imports: there is\n// no client chunk to defer.\nimport ChallengesSection from '@sonordev/agency-site-kit/portfolio/sections/ChallengesSection';\nimport ResultsSection from '@sonordev/agency-site-kit/portfolio/sections/ResultsSection';\nimport TechStackSection from '@sonordev/agency-site-kit/portfolio/sections/TechStackSection';\nimport ServicesSection from '@sonordev/agency-site-kit/portfolio/sections/ServicesSection';\nimport TestimonialSection from '@sonordev/agency-site-kit/portfolio/sections/TestimonialSection';\nimport SpeedComparisonSection from '@sonordev/agency-site-kit/portfolio/sections/SpeedComparisonSection';\nimport DesignSystemSection from '@sonordev/agency-site-kit/portfolio/sections/DesignSystemSection';\nimport DetailsSection from '@sonordev/agency-site-kit/portfolio/sections/DetailsSection';\n\n// Client sections — animated/interactive; lazily loaded so each becomes its\n// own browser chunk, fetched only when rendered.\nconst StrategySection = dynamic(() => import('@sonordev/agency-site-kit/portfolio/sections/StrategySection'), { ssr: true });\nconst GallerySection = dynamic(() => import('@sonordev/agency-site-kit/portfolio/sections/GallerySection'), { ssr: true });\nconst VideoSection = dynamic(() => import('@sonordev/agency-site-kit/portfolio/sections/VideoSection'), { ssr: true });\nconst TeamSection = dynamic(() => import('@sonordev/agency-site-kit/portfolio/sections/TeamSection'), { ssr: true });\nconst FeatureSpotlightSection = dynamic(() => import('@sonordev/agency-site-kit/portfolio/sections/FeatureSpotlightSection'), { ssr: true });\nconst BeforeAfterSection = dynamic(() => import('@sonordev/agency-site-kit/portfolio/sections/BeforeAfterSection'), { ssr: true });\nconst MetricsTimelineSection = dynamic(() => import('@sonordev/agency-site-kit/portfolio/sections/MetricsTimelineSection'), { ssr: true });\nconst ConversionFunnelSection = dynamic(() => import('@sonordev/agency-site-kit/portfolio/sections/ConversionFunnelSection'), { ssr: true });\nconst PerformanceSection = dynamic(() => import('@sonordev/agency-site-kit/portfolio/sections/PerformanceSection'), { ssr: true });\nconst SiteArchitectureSection = dynamic(() => import('@sonordev/agency-site-kit/portfolio/sections/SiteArchitectureSection'), { ssr: true });\nconst CTASection = dynamic(() => import('@sonordev/agency-site-kit/portfolio/sections/CTASection'), { ssr: true });\n\ninterface PortfolioPageProps {\n item: PortfolioItemFull;\n}\n\n// Defense-in-depth only — the enforcing sanitize pass runs in\n// getPortfolioItem (portfolio/server.ts), the last genuinely-server-side hop.\n// See src/portfolio/sanitize.ts for the single source of truth and the story.\nimport { sanitizeSiteArchitectureData } from '../sanitize';\n\n// Engagement-tracking island (client) — crosses the boundary via subpath.\nimport PortfolioAnalytics from '@sonordev/agency-site-kit/portfolio/components/PortfolioAnalytics';\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 (\n <ResultsSection\n key={key}\n data={section.data as PortfolioResultsData}\n metricsDelta={item.metricsDelta}\n metricsRefreshedAt={item.metrics_last_refreshed_at}\n />\n );\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={sanitizeSiteArchitectureData(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\n// order. Keys must match PortfolioSectionType exactly (note 'portfolioCTA' —\n// a lowercased 'portfolioCta' key silently fell through to the ?? 99 fallback\n// for years). Unknown/future types sort last via the fallback.\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: 14,\n portfolioSeo: 15,\n portfolioCTA: 16,\n};\n\nexport default function PortfolioPage({ item }: PortfolioPageProps) {\n // Section order: once an agency has deliberately reordered in the\n // dashboard (item.sectionsOrdered), the array order IS the narrative —\n // respect it, with the hero pinned first (it's the LCP shell). Items\n // never reordered keep the canonical order below, so nothing shifts\n // out from under existing case studies.\n const sortedSections = item.sectionsOrdered\n ? [...item.sections].sort(\n (a, b) =>\n (a.sectionType === 'portfolioHero' ? 0 : 1) -\n (b.sectionType === 'portfolioHero' ? 0 : 1),\n )\n : [...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\n key={`wrapper-${section.sectionType}-${index}`}\n className=\"w-full\"\n data-portfolio-section={section.sectionType}\n >\n {rendered}\n </div>\n );\n })}\n {/* Engagement tracking — childless idle-deferred sibling, never a wrapper */}\n <PortfolioAnalytics slug={item.slug} />\n </article>\n );\n}\n","/**\n * PortfolioSchema — JSON-LD structured data for a case study page.\n *\n * Server component: renders a single <script type=\"application/ld+json\">\n * describing the case study as a schema.org Article (there is no dedicated\n * CaseStudy type; Article is what Google documents for editorial content).\n * Ships zero client JS and adds rich-result eligibility + entity clarity for\n * the agency's portfolio pages.\n *\n * Usage (in the case study page, next to <PortfolioPage />):\n *\n * ```tsx\n * <PortfolioSchema\n * item={item}\n * url={`https://youragency.com/work/${item.slug}`}\n * publisher={{ name: 'Your Agency', url: 'https://youragency.com' }}\n * />\n * ```\n *\n * Only reads fields from the already-sanitized PortfolioItemFull (see\n * getPortfolioItem / sanitize.ts) — never section internals.\n */\n\nimport React from 'react';\nimport type { PortfolioItemFull } from '../../types';\n\nexport interface PortfolioSchemaProps {\n item: PortfolioItemFull;\n /** Canonical URL of this case study page. */\n url: string;\n /** The agency, emitted as author + publisher Organization. */\n publisher?: {\n name: string;\n url?: string;\n /** Absolute URL to a logo image. */\n logo?: string;\n };\n}\n\n/** JSON-LD-safe serialization — escape `<` so `</script>` can never break out. */\nfunction jsonLd(value: unknown): string {\n return JSON.stringify(value).replace(/</g, '\\\\u003c');\n}\n\nexport default function PortfolioSchema({ item, url, publisher }: PortfolioSchemaProps) {\n const headline = item.seo?.metaTitle || item.title;\n const description = item.seo?.metaDescription || item.description;\n\n const keywords = [\n ...(item.seo?.keywords ?? []),\n item.category,\n ...item.services,\n ].filter(Boolean);\n\n const org = publisher\n ? {\n '@type': 'Organization',\n name: publisher.name,\n ...(publisher.url ? { url: publisher.url } : {}),\n ...(publisher.logo\n ? { logo: { '@type': 'ImageObject', url: publisher.logo } }\n : {}),\n }\n : undefined;\n\n const schema = {\n '@context': 'https://schema.org',\n '@type': 'Article',\n headline,\n ...(description ? { description } : {}),\n ...(item.hero_image ? { image: [item.hero_image] } : {}),\n url,\n mainEntityOfPage: { '@type': 'WebPage', '@id': url },\n ...(item.published_at ? { datePublished: item.published_at } : {}),\n ...(item.metrics_last_refreshed_at || item.published_at\n ? { dateModified: item.metrics_last_refreshed_at || item.published_at }\n : {}),\n ...(keywords.length ? { keywords: keywords.join(', ') } : {}),\n ...(org ? { author: org, publisher: org } : {}),\n ...(item.details?.industry ? { about: item.details.industry } : {}),\n };\n\n return (\n <script\n type=\"application/ld+json\"\n dangerouslySetInnerHTML={{ __html: jsonLd(schema) }}\n />\n );\n}\n","// Shared component — no directive on purpose: hook-free, so it renders\n// server-side (zero JS) from PortfolioIndex and stays usable from client\n// code via the /portfolio/client barrel.\nimport React from 'react';\nimport Image from 'next/image';\nimport Link from 'next/link';\nimport type { PortfolioItem } from '../../types';\nimport GlassCard from '@sonordev/agency-site-kit/portfolio/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","// Shared component — no directive on purpose: hook-free, so it renders\n// server-side (zero JS) from PortfolioIndex and stays usable from client\n// code via the /portfolio/client barrel.\nimport React from 'react';\nimport type { PortfolioItem } from '../../types';\nimport ScrollReveal from '@sonordev/agency-site-kit/portfolio/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","/**\n * PortfolioIndex — turnkey, server-rendered /work index.\n *\n * Fetches items + categories and renders: category nav (real links —\n * crawlable, no client state), the portfolio grid, pagination, and an\n * intentional empty state. Ships zero component JS.\n *\n * Category filtering is route-based for SEO:\n * /work → all items\n * /work/category/[category] → one category (slug via slugifyCategory)\n * Pagination via ?page= on either route.\n */\n\nimport React from 'react';\nimport {\n getPortfolioCategories,\n getPortfolioItems,\n slugifyCategory,\n} from '../server';\nimport PortfolioGrid from './PortfolioGrid';\n\nexport interface PortfolioIndexProps {\n /** Route prefix for case studies + category links. Defaults to \"/work\". */\n basePath?: string;\n /** Active category SLUG (from the [category] route param). */\n category?: string | null;\n /** 1-based page number (from ?page=). */\n page?: number;\n /** Items per page. Defaults to 12. */\n pageSize?: number;\n /** Optional heading block rendered above the category nav. */\n heading?: React.ReactNode;\n /** Override the empty state. */\n emptyState?: React.ReactNode;\n /** Hide the category nav (e.g. single-category portfolios). */\n showCategories?: boolean;\n className?: string;\n}\n\nfunction CategoryLink({\n href,\n label,\n active,\n}: {\n href: string;\n label: string;\n active: boolean;\n}) {\n return (\n <a\n href={href}\n aria-current={active ? 'page' : undefined}\n className=\"px-4 py-1.5 rounded-full text-sm font-medium transition-colors\"\n style={{\n background: active\n ? 'color-mix(in srgb, var(--sk-primary, #6366f1) 15%, transparent)'\n : 'var(--sk-surface, rgba(255,255,255,0.05))',\n color: active ? 'var(--sk-primary, #6366f1)' : 'var(--sk-text-secondary, #a1a1aa)',\n border: `1px solid ${\n active\n ? 'color-mix(in srgb, var(--sk-primary, #6366f1) 25%, transparent)'\n : 'var(--sk-border, rgba(255,255,255,0.1))'\n }`,\n }}\n >\n {label}\n </a>\n );\n}\n\nfunction PageLink({ href, label, disabled }: { href: string; label: string; disabled: boolean }) {\n if (disabled) {\n return (\n <span\n aria-disabled=\"true\"\n className=\"px-4 py-2 rounded-xl text-sm font-medium opacity-40\"\n style={{\n background: 'var(--sk-surface, rgba(255,255,255,0.05))',\n color: 'var(--sk-text-tertiary, #71717a)',\n border: '1px solid var(--sk-border, rgba(255,255,255,0.1))',\n }}\n >\n {label}\n </span>\n );\n }\n return (\n <a\n href={href}\n className=\"px-4 py-2 rounded-xl text-sm font-medium transition-colors\"\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 {label}\n </a>\n );\n}\n\nexport default async function PortfolioIndex({\n basePath = '/work',\n category = null,\n page = 1,\n pageSize = 12,\n heading,\n emptyState,\n showCategories = true,\n className = '',\n}: PortfolioIndexProps) {\n const currentPage = Math.max(1, Math.floor(page));\n const categories = await getPortfolioCategories();\n\n // Route param is a slug; the API filter wants the original category value.\n const activeCategory = category\n ? categories.find((c) => slugifyCategory(c) === category) ?? null\n : null;\n\n const { items, total } = await getPortfolioItems({\n category: activeCategory ?? undefined,\n limit: pageSize,\n offset: (currentPage - 1) * pageSize,\n });\n\n const totalPages = Math.max(1, Math.ceil(total / pageSize));\n const pageHref = (p: number) => {\n const base = activeCategory\n ? `${basePath}/category/${slugifyCategory(activeCategory)}`\n : basePath;\n return p <= 1 ? base : `${base}?page=${p}`;\n };\n\n return (\n <section className={`w-full ${className}`}>\n <div className=\"max-w-7xl mx-auto px-6\">\n {heading}\n\n {/* Category nav — real links, crawlable */}\n {showCategories && categories.length > 1 && (\n <nav aria-label=\"Portfolio categories\" className=\"flex flex-wrap gap-2 mb-10\">\n <CategoryLink href={basePath} label=\"All\" active={!activeCategory} />\n {categories.map((c) => (\n <CategoryLink\n key={c}\n href={`${basePath}/category/${slugifyCategory(c)}`}\n label={c}\n active={activeCategory === c}\n />\n ))}\n </nav>\n )}\n\n {items.length > 0 ? (\n <PortfolioGrid items={items} basePath={basePath} />\n ) : (\n emptyState ?? (\n <div\n className=\"flex flex-col items-center gap-3 py-24 rounded-2xl text-center\"\n style={{\n background: 'var(--sk-surface, rgba(255,255,255,0.03))',\n border: '1px dashed var(--sk-border, rgba(255,255,255,0.1))',\n }}\n >\n <span\n className=\"text-lg font-semibold\"\n style={{ color: 'var(--sk-text-primary, #ffffff)' }}\n >\n Case studies coming soon\n </span>\n <span className=\"text-sm\" style={{ color: 'var(--sk-text-tertiary, #71717a)' }}>\n {activeCategory\n ? `Nothing published in ${activeCategory} yet.`\n : \"We're writing up our latest work right now.\"}\n </span>\n </div>\n )\n )}\n\n {/* Pagination */}\n {totalPages > 1 && (\n <nav\n aria-label=\"Portfolio pages\"\n className=\"flex items-center justify-center gap-3 mt-12\"\n >\n <PageLink\n href={pageHref(currentPage - 1)}\n label=\"← Previous\"\n disabled={currentPage <= 1}\n />\n <span className=\"text-sm\" style={{ color: 'var(--sk-text-tertiary, #71717a)' }}>\n Page {currentPage} of {totalPages}\n </span>\n <PageLink\n href={pageHref(currentPage + 1)}\n label=\"Next →\"\n disabled={currentPage >= totalPages}\n />\n </nav>\n )}\n </div>\n </section>\n );\n}\n","/**\n * Loading skeletons for portfolio routes — pure CSS pulse, server-safe,\n * zero JS. Drop into `loading.tsx`:\n *\n * ```tsx\n * export { PortfolioIndexSkeleton as default } from '@sonordev/agency-site-kit/portfolio';\n * ```\n */\n\nimport React from 'react';\n\nconst PULSE_CSS = `\n.sk-skeleton{animation:sk-skeleton-pulse 1.6s ease-in-out infinite;background:var(--sk-surface,rgba(255,255,255,0.06));border:1px solid var(--sk-border,rgba(255,255,255,0.08));border-radius:16px}\n@keyframes sk-skeleton-pulse{0%,100%{opacity:.55}50%{opacity:1}}\n@media (prefers-reduced-motion:reduce){.sk-skeleton{animation:none}}\n`;\n\nfunction SkeletonStyles() {\n return (\n <style href=\"sk-skeletons\" precedence=\"default\">\n {PULSE_CSS}\n </style>\n );\n}\n\n/** Grid of pulsing card placeholders for the /work index. */\nexport function PortfolioIndexSkeleton({ cards = 6 }: { cards?: number }) {\n return (\n <section className=\"w-full py-16\">\n <SkeletonStyles />\n <div className=\"max-w-7xl mx-auto px-6\">\n <div className=\"sk-skeleton h-9 w-56 mb-4\" />\n <div className=\"sk-skeleton h-5 w-80 mb-10\" />\n <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6\">\n {Array.from({ length: cards }).map((_, i) => (\n <div key={i} className=\"flex flex-col gap-3\">\n <div className=\"sk-skeleton w-full\" style={{ aspectRatio: '16/10' }} />\n <div className=\"sk-skeleton h-5 w-3/4\" />\n <div className=\"sk-skeleton h-4 w-1/2\" />\n </div>\n ))}\n </div>\n </div>\n </section>\n );\n}\n\n/** Hero-shaped placeholder for /work/[slug] case study pages. */\nexport function PortfolioPageSkeleton() {\n return (\n <article className=\"w-full\">\n <SkeletonStyles />\n <div className=\"max-w-7xl mx-auto px-6 py-24\">\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-12 items-start\">\n <div className=\"flex flex-col gap-5\">\n <div className=\"sk-skeleton h-7 w-32\" style={{ borderRadius: '9999px' }} />\n <div className=\"sk-skeleton h-12 w-full\" />\n <div className=\"sk-skeleton h-12 w-4/5\" />\n <div className=\"sk-skeleton h-5 w-full\" />\n <div className=\"sk-skeleton h-5 w-2/3\" />\n </div>\n <div className=\"sk-skeleton w-full\" style={{ aspectRatio: '11/5' }} />\n </div>\n </div>\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 * True once the agency deliberately reordered sections in the dashboard.\n * PortfolioPage respects the sections array order when set (hero pinned\n * first); otherwise it applies the canonical order.\n */\n sectionsOrdered?: boolean;\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"]}
|