@burdenoff/website-sdk 2026.1101.1 → 2026.1101.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/README.md +42 -8
- package/dist/analytics/index.js +19 -69
- package/dist/analytics/index.js.map +1 -1
- package/dist/analytics/index.mjs +3 -69
- package/dist/analytics/index.mjs.map +1 -1
- package/dist/avatar-live/index.js +1 -0
- package/dist/avatar-live/index.js.map +1 -1
- package/dist/avatar-live/index.mjs +1 -0
- package/dist/avatar-live/index.mjs.map +1 -1
- package/dist/avatar-live-view/index.js +10 -30
- package/dist/avatar-live-view/index.js.map +1 -1
- package/dist/avatar-live-view/index.mjs +4 -24
- package/dist/avatar-live-view/index.mjs.map +1 -1
- package/dist/chunk-23H4JUL4.js +41 -0
- package/dist/chunk-23H4JUL4.js.map +1 -0
- package/dist/chunk-34RX5P75.mjs +25 -0
- package/dist/chunk-34RX5P75.mjs.map +1 -0
- package/dist/chunk-3LFFY7AZ.js +184 -0
- package/dist/chunk-3LFFY7AZ.js.map +1 -0
- package/dist/chunk-3X26GE4S.js +2794 -0
- package/dist/chunk-3X26GE4S.js.map +1 -0
- package/dist/chunk-45GCA4GG.js +85 -0
- package/dist/chunk-45GCA4GG.js.map +1 -0
- package/dist/chunk-4SAGVLHN.js +876 -0
- package/dist/chunk-4SAGVLHN.js.map +1 -0
- package/dist/chunk-4U6YAII5.js +218 -0
- package/dist/chunk-4U6YAII5.js.map +1 -0
- package/dist/chunk-5GMTBOO6.js +21 -0
- package/dist/chunk-5GMTBOO6.js.map +1 -0
- package/dist/chunk-5JRJ57CL.mjs +329 -0
- package/dist/chunk-5JRJ57CL.mjs.map +1 -0
- package/dist/chunk-6C2DKIC7.js +4 -0
- package/dist/chunk-6C2DKIC7.js.map +1 -0
- package/dist/chunk-6IBFBWC5.mjs +17 -0
- package/dist/chunk-6IBFBWC5.mjs.map +1 -0
- package/dist/chunk-6KY2ZRVI.js +116 -0
- package/dist/chunk-6KY2ZRVI.js.map +1 -0
- package/dist/chunk-6VJT3SM4.mjs +182 -0
- package/dist/chunk-6VJT3SM4.mjs.map +1 -0
- package/dist/chunk-6YZUFMK2.mjs +40 -0
- package/dist/chunk-6YZUFMK2.mjs.map +1 -0
- package/dist/chunk-7A6R7CGG.js +47 -0
- package/dist/chunk-7A6R7CGG.js.map +1 -0
- package/dist/chunk-7PWL4V2R.js +4 -0
- package/dist/chunk-7PWL4V2R.js.map +1 -0
- package/dist/chunk-AA33WDYS.js +161 -0
- package/dist/chunk-AA33WDYS.js.map +1 -0
- package/dist/chunk-AHRGFXV2.js +525 -0
- package/dist/chunk-AHRGFXV2.js.map +1 -0
- package/dist/chunk-B63XLDZH.mjs +109 -0
- package/dist/chunk-B63XLDZH.mjs.map +1 -0
- package/dist/chunk-BX2USEWF.mjs +35 -0
- package/dist/chunk-BX2USEWF.mjs.map +1 -0
- package/dist/chunk-BYSZT35R.js +150 -0
- package/dist/chunk-BYSZT35R.js.map +1 -0
- package/dist/chunk-C6OJIHNO.js +12 -0
- package/dist/chunk-C6OJIHNO.js.map +1 -0
- package/dist/chunk-COOELBX7.mjs +63 -0
- package/dist/chunk-COOELBX7.mjs.map +1 -0
- package/dist/chunk-D2TW7MNC.mjs +2771 -0
- package/dist/chunk-D2TW7MNC.mjs.map +1 -0
- package/dist/chunk-D5KEZ6V6.mjs +3 -0
- package/dist/chunk-D5KEZ6V6.mjs.map +1 -0
- package/dist/chunk-E3GUGJMY.js +46 -0
- package/dist/chunk-E3GUGJMY.js.map +1 -0
- package/dist/chunk-EBZ2HGJ2.mjs +52 -0
- package/dist/chunk-EBZ2HGJ2.mjs.map +1 -0
- package/dist/chunk-EDLTUOIA.mjs +210 -0
- package/dist/chunk-EDLTUOIA.mjs.map +1 -0
- package/dist/chunk-EJQSOT4X.js +4 -0
- package/dist/chunk-EJQSOT4X.js.map +1 -0
- package/dist/chunk-EUPFS6MZ.mjs +2019 -0
- package/dist/chunk-EUPFS6MZ.mjs.map +1 -0
- package/dist/chunk-F3XWDC3B.js +77 -0
- package/dist/chunk-F3XWDC3B.js.map +1 -0
- package/dist/chunk-FFADMVIK.mjs +5127 -0
- package/dist/chunk-FFADMVIK.mjs.map +1 -0
- package/dist/chunk-FJWS7BKT.mjs +3 -0
- package/dist/chunk-FJWS7BKT.mjs.map +1 -0
- package/dist/chunk-FJZJDUOR.mjs +3 -0
- package/dist/chunk-FJZJDUOR.mjs.map +1 -0
- package/dist/chunk-GCFLCO2Z.mjs +145 -0
- package/dist/chunk-GCFLCO2Z.mjs.map +1 -0
- package/dist/chunk-GEXC4LRP.js +61 -0
- package/dist/chunk-GEXC4LRP.js.map +1 -0
- package/dist/chunk-GTRAXQGC.mjs +3 -0
- package/dist/chunk-GTRAXQGC.mjs.map +1 -0
- package/dist/chunk-IKOIRUKW.js +270 -0
- package/dist/chunk-IKOIRUKW.js.map +1 -0
- package/dist/chunk-IXTWOPRN.js +45 -0
- package/dist/chunk-IXTWOPRN.js.map +1 -0
- package/dist/chunk-JDXOXGOK.js +547 -0
- package/dist/chunk-JDXOXGOK.js.map +1 -0
- package/dist/chunk-JSLDWYV7.mjs +558 -0
- package/dist/chunk-JSLDWYV7.mjs.map +1 -0
- package/dist/chunk-JXLSTVBK.js +336 -0
- package/dist/chunk-JXLSTVBK.js.map +1 -0
- package/dist/chunk-KHYXK7FG.mjs +49 -0
- package/dist/chunk-KHYXK7FG.mjs.map +1 -0
- package/dist/chunk-KSMBOWQQ.js +2034 -0
- package/dist/chunk-KSMBOWQQ.js.map +1 -0
- package/dist/chunk-KU2PXHGB.mjs +83 -0
- package/dist/chunk-KU2PXHGB.mjs.map +1 -0
- package/dist/chunk-KZANIV3S.mjs +37 -0
- package/dist/chunk-KZANIV3S.mjs.map +1 -0
- package/dist/chunk-L4BP6YOB.js +20 -0
- package/dist/chunk-L4BP6YOB.js.map +1 -0
- package/dist/chunk-MRKA2OAF.mjs +75 -0
- package/dist/chunk-MRKA2OAF.mjs.map +1 -0
- package/dist/chunk-N5EC53MH.mjs +80 -0
- package/dist/chunk-N5EC53MH.mjs.map +1 -0
- package/dist/chunk-NZANQGDM.mjs +34 -0
- package/dist/chunk-NZANQGDM.mjs.map +1 -0
- package/dist/chunk-O4GS3XPZ.js +10 -0
- package/dist/chunk-O4GS3XPZ.js.map +1 -0
- package/dist/chunk-O76QDGRM.js +5154 -0
- package/dist/chunk-O76QDGRM.js.map +1 -0
- package/dist/chunk-OYEUNZJY.js +114 -0
- package/dist/chunk-OYEUNZJY.js.map +1 -0
- package/dist/chunk-PHXAGQNB.js +74 -0
- package/dist/chunk-PHXAGQNB.js.map +1 -0
- package/dist/chunk-PIHHPP7J.mjs +16 -0
- package/dist/chunk-PIHHPP7J.mjs.map +1 -0
- package/dist/chunk-PXRHVVOU.mjs +499 -0
- package/dist/chunk-PXRHVVOU.mjs.map +1 -0
- package/dist/chunk-QMA6U6AU.js +36 -0
- package/dist/chunk-QMA6U6AU.js.map +1 -0
- package/dist/chunk-R6J7IULF.js +44 -0
- package/dist/chunk-R6J7IULF.js.map +1 -0
- package/dist/chunk-R7G6TWIS.mjs +10 -0
- package/dist/chunk-R7G6TWIS.mjs.map +1 -0
- package/dist/chunk-RUMGFI6L.mjs +544 -0
- package/dist/chunk-RUMGFI6L.mjs.map +1 -0
- package/dist/chunk-S54JWSJF.js +565 -0
- package/dist/chunk-S54JWSJF.js.map +1 -0
- package/dist/chunk-SK5UGG6A.js +2041 -0
- package/dist/chunk-SK5UGG6A.js.map +1 -0
- package/dist/chunk-SKIYDKIS.mjs +70 -0
- package/dist/chunk-SKIYDKIS.mjs.map +1 -0
- package/dist/chunk-SZNS5IK6.mjs +322 -0
- package/dist/chunk-SZNS5IK6.mjs.map +1 -0
- package/dist/chunk-T7JIPZAC.js +66 -0
- package/dist/chunk-T7JIPZAC.js.map +1 -0
- package/dist/chunk-TGYQHO2E.mjs +864 -0
- package/dist/chunk-TGYQHO2E.mjs.map +1 -0
- package/dist/chunk-TT3MHN42.mjs +8 -0
- package/dist/chunk-TT3MHN42.mjs.map +1 -0
- package/dist/chunk-UM55U65W.mjs +268 -0
- package/dist/chunk-UM55U65W.mjs.map +1 -0
- package/dist/chunk-UQ2HALUD.js +72 -0
- package/dist/chunk-UQ2HALUD.js.map +1 -0
- package/dist/chunk-UXWT7G77.js +82 -0
- package/dist/chunk-UXWT7G77.js.map +1 -0
- package/dist/chunk-VNS5WDWO.mjs +7 -0
- package/dist/chunk-VNS5WDWO.mjs.map +1 -0
- package/dist/chunk-VRWZLGR7.js +4 -0
- package/dist/chunk-VRWZLGR7.js.map +1 -0
- package/dist/chunk-VZLDSNH4.mjs +22 -0
- package/dist/chunk-VZLDSNH4.mjs.map +1 -0
- package/dist/chunk-W6P3YETJ.js +92 -0
- package/dist/chunk-W6P3YETJ.js.map +1 -0
- package/dist/chunk-WPKFFQCR.mjs +90 -0
- package/dist/chunk-WPKFFQCR.mjs.map +1 -0
- package/dist/chunk-WTVTI5TO.mjs +22 -0
- package/dist/chunk-WTVTI5TO.mjs.map +1 -0
- package/dist/chunk-XJOBZ4RJ.js +44 -0
- package/dist/chunk-XJOBZ4RJ.js.map +1 -0
- package/dist/chunk-XP2JDNDU.js +325 -0
- package/dist/chunk-XP2JDNDU.js.map +1 -0
- package/dist/chunk-Y4V5CEWD.mjs +112 -0
- package/dist/chunk-Y4V5CEWD.mjs.map +1 -0
- package/dist/chunk-YERN2UWM.js +116 -0
- package/dist/chunk-YERN2UWM.js.map +1 -0
- package/dist/chunk-ZKJQWFAA.mjs +114 -0
- package/dist/chunk-ZKJQWFAA.mjs.map +1 -0
- package/dist/chunk-ZOON3S3Y.mjs +138 -0
- package/dist/chunk-ZOON3S3Y.mjs.map +1 -0
- package/dist/chunk-ZP6QRAYC.mjs +2024 -0
- package/dist/chunk-ZP6QRAYC.mjs.map +1 -0
- package/dist/chunk-ZSTFX7FV.js +9 -0
- package/dist/chunk-ZSTFX7FV.js.map +1 -0
- package/dist/client/index.js +23 -331
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +3 -330
- package/dist/client/index.mjs.map +1 -1
- package/dist/comparison/index.js +28 -111
- package/dist/comparison/index.js.map +1 -1
- package/dist/comparison/index.mjs +2 -107
- package/dist/comparison/index.mjs.map +1 -1
- package/dist/components/contact.js +21 -931
- package/dist/components/contact.js.map +1 -1
- package/dist/components/contact.mjs +12 -909
- package/dist/components/contact.mjs.map +1 -1
- package/dist/components/electron-download.js +12 -124
- package/dist/components/electron-download.js.map +1 -1
- package/dist/components/electron-download.mjs +4 -106
- package/dist/components/electron-download.mjs.map +1 -1
- package/dist/components/explore-cta.js +8 -112
- package/dist/components/explore-cta.js.map +1 -1
- package/dist/components/explore-cta.mjs +3 -114
- package/dist/components/explore-cta.mjs.map +1 -1
- package/dist/components/explore.js +62 -7833
- package/dist/components/explore.js.map +1 -1
- package/dist/components/explore.mjs +13 -7800
- package/dist/components/explore.mjs.map +1 -1
- package/dist/components/launch-countdown.js +8 -83
- package/dist/components/launch-countdown.js.map +1 -1
- package/dist/components/launch-countdown.mjs +3 -85
- package/dist/components/launch-countdown.mjs.map +1 -1
- package/dist/components/newsletter.js +20 -993
- package/dist/components/newsletter.js.map +1 -1
- package/dist/components/newsletter.mjs +11 -971
- package/dist/components/newsletter.mjs.map +1 -1
- package/dist/components/partners.js +24 -1302
- package/dist/components/partners.js.map +1 -1
- package/dist/components/partners.mjs +15 -1276
- package/dist/components/partners.mjs.map +1 -1
- package/dist/components/pricing.js +49 -1249
- package/dist/components/pricing.js.map +1 -1
- package/dist/components/pricing.mjs +8 -1226
- package/dist/components/pricing.mjs.map +1 -1
- package/dist/components/problems-solved.js +8 -150
- package/dist/components/problems-solved.js.map +1 -1
- package/dist/components/problems-solved.mjs +4 -133
- package/dist/components/problems-solved.mjs.map +1 -1
- package/dist/components/resource-links.js +8 -304
- package/dist/components/resource-links.js.map +1 -1
- package/dist/components/resource-links.mjs +5 -308
- package/dist/components/resource-links.mjs.map +1 -1
- package/dist/components/social-links.js +8 -343
- package/dist/components/social-links.js.map +1 -1
- package/dist/components/social-links.mjs +5 -347
- package/dist/components/social-links.mjs.map +1 -1
- package/dist/components/unsubscribe.js +10 -627
- package/dist/components/unsubscribe.js.map +1 -1
- package/dist/components/unsubscribe.mjs +6 -611
- package/dist/components/unsubscribe.mjs.map +1 -1
- package/dist/components/waitlist.js +21 -1001
- package/dist/components/waitlist.js.map +1 -1
- package/dist/components/waitlist.mjs +12 -982
- package/dist/components/waitlist.mjs.map +1 -1
- package/dist/components/website-switcher.js +12 -175
- package/dist/components/website-switcher.js.map +1 -1
- package/dist/components/website-switcher.mjs +4 -157
- package/dist/components/website-switcher.mjs.map +1 -1
- package/dist/content/index.js +118 -3616
- package/dist/content/index.js.map +1 -1
- package/dist/content/index.mjs +12 -3567
- package/dist/content/index.mjs.map +1 -1
- package/dist/hooks/index.js +37 -2321
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/index.mjs +6 -2318
- package/dist/hooks/index.mjs.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +4145 -20599
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3766 -20567
- package/dist/index.mjs.map +1 -1
- package/dist/product/index.js +20 -393
- package/dist/product/index.js.map +1 -1
- package/dist/product/index.mjs +5 -394
- package/dist/product/index.mjs.map +1 -1
- package/dist/seo/index.js +8 -76
- package/dist/seo/index.js.map +1 -1
- package/dist/seo/index.mjs +3 -78
- package/dist/seo/index.mjs.map +1 -1
- package/dist/store/index.js +51 -2650
- package/dist/store/index.js.map +1 -1
- package/dist/store/index.mjs +13 -2619
- package/dist/store/index.mjs.map +1 -1
- package/dist/utils/index.js +69 -134
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/index.mjs +5 -122
- package/dist/utils/index.mjs.map +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/pricing-catalog.ts","../src/components/pricing.tsx"],"names":["useWebSDK","useWebSDKConfig","useProduct","useState","useEffect","useMemo","jsx","jsxs","Button","PageHead"],"mappings":"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","file":"chunk-4SAGVLHN.js","sourcesContent":["/**\n * Pure helpers that turn the billing catalog (`publicPlans`) into what the\n * pricing page shows: which product key to query, the display name of a\n * plan, which currencies a visitor can pick, and the price in that currency.\n *\n * Kept free of React so every rule here is unit-tested directly\n * (see `pricing-catalog.test.ts`).\n */\n\n// ============================================================================\n// Product key\n// ============================================================================\n\nconst UUID_PATTERN =\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/** True when `value` is shaped like a UUID (a product registry id, not a slug). */\nexport function isUuidLike(value: string): boolean {\n return UUID_PATTERN.test(value.trim());\n}\n\n/** A product as returned by `publicProduct` / `publicProducts`. */\nexport interface ProductKeyCandidate {\n id: string;\n slug: string;\n}\n\n/**\n * Thrown when the pricing page is given a product UUID that no public product\n * owns. `publicPlans(productId)` matches on the product SLUG (a plan's\n * `productID` is e.g. `\"bigconsole\"`), so a registry UUID silently returns an\n * empty list — the page must refuse it instead of rendering a blank grid.\n */\nexport class PricingProductKeyError extends Error {\n constructor(productId: string) {\n super(\n `PricingPage was given the product id \"${productId}\", which matches no public product. ` +\n `publicPlans(productId) expects the product slug (for example \"vibecontrols\"); pass product.slug.`,\n );\n this.name = \"PricingProductKeyError\";\n }\n}\n\n/**\n * Resolve the key `publicPlans` expects.\n *\n * - A slug is returned unchanged (trimmed).\n * - A UUID is mapped to its product's slug using `candidates` (the product\n * from `ProductProvider` and/or the `publicProducts` list).\n * - A UUID with no matching candidate returns `null`: the caller must look it\n * up or fail with {@link PricingProductKeyError}.\n */\nexport function resolvePricingProductKey(\n productId: string,\n candidates: ReadonlyArray<ProductKeyCandidate | null | undefined>,\n): string | null {\n const trimmed = productId.trim();\n if (!isUuidLike(trimmed)) return trimmed;\n const wanted = trimmed.toLowerCase();\n const match = candidates.find(\n (c): c is ProductKeyCandidate =>\n !!c && c.id.toLowerCase() === wanted && c.slug.trim().length > 0,\n );\n return match ? match.slug.trim() : null;\n}\n\n// ============================================================================\n// Plan names\n// ============================================================================\n\nconst PERIOD_SUFFIX =\n /\\s*[([]\\s*(monthly|month|annual|annually|yearly|year)\\s*[)\\]]\\s*$/i;\nconst PRODUCT_PREFIX = /^\\S.*?\\s[-–—]\\s+(?=\\S)/;\n\n/**\n * Catalog plan names carry the product and the billing period, e.g.\n * `\"VibeControls - Hobby (Monthly)\"`. The card already sits on the product's\n * own site and the period is chosen by the Monthly/Yearly toggle, so the\n * visitor sees `\"Hobby\"`.\n *\n * Only the FIRST `\" - \"` separates the product, so `\"Acme - Pro - Teams\"`\n * becomes `\"Pro - Teams\"`. A name that would end up empty is returned as is.\n */\nexport function cleanPlanDisplayName(name: string): string {\n const withoutPeriod = name.replace(PERIOD_SUFFIX, \"\").trim();\n const withoutProduct = withoutPeriod.replace(PRODUCT_PREFIX, \"\").trim();\n if (withoutProduct.length > 0) return withoutProduct;\n if (withoutPeriod.length > 0) return withoutPeriod;\n return name.trim();\n}\n\n// ============================================================================\n// Currencies\n// ============================================================================\n\n/** The price-bearing fields of a catalog plan. */\nexport interface PricedPlan {\n price: number;\n currency: string;\n currencyPrices?: Record<string, number> | null;\n}\n\nfunction isFiniteNumber(value: unknown): value is number {\n return typeof value === \"number\" && Number.isFinite(value);\n}\n\n/**\n * The price of `plan` in `currency`, or `null` when the catalog has no price\n * for it. A free plan costs 0 in every currency.\n */\nexport function planPriceIn(plan: PricedPlan, currency: string): number | null {\n const code = currency.toUpperCase();\n if (plan.price === 0) return 0;\n if (plan.currency.toUpperCase() === code) return plan.price;\n const prices = plan.currencyPrices;\n if (!prices) return null;\n const direct = prices[code];\n if (isFiniteNumber(direct)) return direct;\n const key = Object.keys(prices).find((k) => k.toUpperCase() === code);\n const value = key === undefined ? undefined : prices[key];\n return isFiniteNumber(value) ? value : null;\n}\n\n/**\n * The currencies a visitor can switch to: those in which EVERY plan has a\n * price (free plans count as priced everywhere). A currency missing from a\n * single paid plan is left out, so switching never blanks a card. Sorted with\n * the catalog's base currency first, then alphabetically.\n */\nexport function availablePlanCurrencies(\n plans: ReadonlyArray<PricedPlan>,\n): string[] {\n if (plans.length === 0) return [];\n const all = new Set<string>();\n for (const plan of plans) {\n all.add(plan.currency.toUpperCase());\n for (const key of Object.keys(plan.currencyPrices ?? {})) {\n all.add(key.toUpperCase());\n }\n }\n const usable = [...all].filter((code) =>\n plans.every((plan) => planPriceIn(plan, code) !== null),\n );\n const base = plans.find((p) => p.price > 0)?.currency.toUpperCase();\n return usable.sort((a, b) => {\n if (a === base) return -1;\n if (b === base) return 1;\n return a.localeCompare(b);\n });\n}\n\nconst EURO_REGIONS = [\n \"AT\",\n \"BE\",\n \"CY\",\n \"DE\",\n \"EE\",\n \"ES\",\n \"FI\",\n \"FR\",\n \"GR\",\n \"HR\",\n \"IE\",\n \"IT\",\n \"LT\",\n \"LU\",\n \"LV\",\n \"MT\",\n \"NL\",\n \"PT\",\n \"SI\",\n \"SK\",\n];\n\n/** ISO 3166 region → ISO 4217 currency, for the currencies the catalog sells in. */\nconst REGION_CURRENCY: Readonly<Record<string, string>> = Object.freeze({\n AU: \"AUD\",\n BR: \"BRL\",\n CA: \"CAD\",\n CH: \"CHF\",\n CN: \"CNY\",\n DK: \"DKK\",\n GB: \"GBP\",\n HK: \"HKD\",\n IN: \"INR\",\n JP: \"JPY\",\n KR: \"KRW\",\n MX: \"MXN\",\n MY: \"MYR\",\n NO: \"NOK\",\n NZ: \"NZD\",\n PL: \"PLN\",\n SE: \"SEK\",\n SG: \"SGD\",\n TH: \"THB\",\n US: \"USD\",\n ...Object.fromEntries(EURO_REGIONS.map((r) => [r, \"EUR\"])),\n});\n\n/** Time zones that pin a region more reliably than the browser language. */\nconst TIME_ZONE_REGION: Readonly<Record<string, string>> = Object.freeze({\n \"Asia/Kolkata\": \"IN\",\n \"Asia/Calcutta\": \"IN\",\n});\n\n/** What the browser says about where the visitor is. */\nexport interface VisitorLocale {\n timeZone?: string | null;\n languages?: ReadonlyArray<string> | null;\n}\n\nfunction regionOfLanguage(tag: string): string | null {\n const parts = tag.replace(/_/g, \"-\").split(\"-\");\n for (const part of parts.slice(1)) {\n if (/^[A-Za-z]{2}$/.test(part)) return part.toUpperCase();\n }\n return null;\n}\n\n/**\n * The visitor's regional currency when the catalog sells in it, else `null`\n * (keep the base currency). The time zone wins over the browser language,\n * because many visitors browse with an `en-US` browser outside the US.\n */\nexport function detectRegionalCurrency(\n available: ReadonlyArray<string>,\n locale: VisitorLocale,\n): string | null {\n const sellable = new Set(available.map((c) => c.toUpperCase()));\n const regions: string[] = [];\n const tzRegion = locale.timeZone ? TIME_ZONE_REGION[locale.timeZone] : null;\n if (tzRegion) regions.push(tzRegion);\n for (const tag of locale.languages ?? []) {\n const region = regionOfLanguage(tag);\n if (region) regions.push(region);\n }\n for (const region of regions) {\n const currency = REGION_CURRENCY[region];\n if (currency && sellable.has(currency)) return currency;\n }\n return null;\n}\n\n/**\n * Format `amount` in `currency` the same way on the server (prerender) and in\n * every browser: a fixed `en-US` locale, no decimals for whole amounts.\n */\nexport function formatCatalogPrice(amount: number, currency: string): string {\n const code = currency.toUpperCase();\n const whole = amount % 1 === 0;\n try {\n return new Intl.NumberFormat(\"en-US\", {\n style: \"currency\",\n currency: code,\n minimumFractionDigits: whole ? 0 : 2,\n maximumFractionDigits: whole ? 0 : 2,\n }).format(amount);\n } catch {\n // An unknown ISO code makes Intl throw; show the code rather than nothing.\n return `${code} ${whole ? amount.toString() : amount.toFixed(2)}`;\n }\n}\n\n// ============================================================================\n// Billing cycle\n// ============================================================================\n\n/**\n * The cards shown for a billing cycle. Monthly shows the monthly plans;\n * yearly shows the yearly plans plus the free plan when the catalog has no\n * yearly free twin (free tiers are usually seeded once, as monthly), so the\n * free tier does not vanish when a visitor flips the toggle.\n */\nexport function plansForBillingCycle<\n T extends { duration: string; priceRaw: number },\n>(plans: ReadonlyArray<T>, cycle: \"monthly\" | \"yearly\"): T[] {\n if (cycle === \"monthly\") {\n return plans.filter((p) => p.duration === \"monthly\");\n }\n const yearly = plans.filter((p) => p.duration === \"yearly\");\n if (yearly.some((p) => p.priceRaw === 0)) return yearly;\n const free = plans.filter(\n (p) => p.duration === \"monthly\" && p.priceRaw === 0,\n );\n return [...free, ...yearly];\n}\n","/**\n * Pricing Component\n * GraphQL-powered pricing component with quota-based features and monthly/yearly toggle\n *\n * @example\n * ```tsx\n * import { PricingPage, PricingSection } from '@burdenoff/website-sdk/components/pricing'\n *\n * // Full page with hero\n * <PricingPage\n * productName=\"BigConsole\"\n * productId=\"bigconsole-123\"\n * appLoginUrl=\"https://app.bigconsole.com/login\"\n * contactUrl=\"/contact\"\n * fallbackFeatures={{\n * basic: ['Up to 5 dashboards', '10 data sources'],\n * professional: ['Unlimited dashboards', 'API access'],\n * enterprise: ['Everything in Professional', 'Dedicated support']\n * }}\n * />\n *\n * // Embeddable section\n * <PricingSection {...props} id=\"pricing\" className=\"bg-muted/30\" />\n * ```\n */\n\n\"use client\";\n\nimport { useEffect, useMemo, useState } from \"react\";\n\nimport {\n PricingProductKeyError,\n availablePlanCurrencies,\n cleanPlanDisplayName,\n detectRegionalCurrency,\n formatCatalogPrice,\n isUuidLike,\n planPriceIn,\n plansForBillingCycle,\n resolvePricingProductKey,\n} from \"./pricing-catalog\";\nimport { useWebSDK, useWebSDKConfig } from \"../client/provider\";\nimport { useProduct } from \"../product/product-provider\";\nimport { PageHead } from \"../seo/page-head\";\nimport { Button } from \"../ui/button\";\n\nimport type { ProductKeyCandidate } from \"./pricing-catalog\";\n\n// Catalog helpers, re-exported so a product website can pin its own copy\n// (planMetadata keys, served plan names) to the same rules the page applies.\nexport {\n PricingProductKeyError,\n availablePlanCurrencies,\n cleanPlanDisplayName,\n isUuidLike,\n planPriceIn,\n resolvePricingProductKey,\n} from \"./pricing-catalog\";\n\n// ============================================================================\n// GRAPHQL QUERY\n// ============================================================================\n\nconst GET_PLANS_QUERY = `\n query GetPublicPlans($productId: String!) {\n publicPlans(productId: $productId) {\n id\n name\n price\n currency\n currencyPrices\n duration\n features {\n id\n quotaId\n context\n quota {\n id\n name\n limits\n }\n }\n isActive\n productID\n createdAt\n }\n }\n`;\n\n/**\n * Used only when a consumer passes a product UUID that the ProductProvider\n * cannot map to a slug (see `resolvePricingProductKey`).\n */\nconst PUBLIC_PRODUCT_KEYS_QUERY = `\n query PricingProductKeys {\n publicProducts(limit: 200) {\n items {\n id\n slug\n }\n }\n }\n`;\n\ninterface PublicProductKeysResponse {\n publicProducts?: { items?: ProductKeyCandidate[] | null } | null;\n}\n\n/** localStorage key remembering the visitor's currency choice. */\nconst CURRENCY_STORAGE_KEY = \"burdenoff.pricing.currency\";\n\nfunction readStoredCurrency(): string | null {\n try {\n if (typeof window === \"undefined\") return null;\n return window.localStorage.getItem(CURRENCY_STORAGE_KEY);\n } catch {\n return null;\n }\n}\n\nfunction storeCurrency(code: string): void {\n try {\n if (typeof window === \"undefined\") return;\n window.localStorage.setItem(CURRENCY_STORAGE_KEY, code);\n } catch {\n // Storage blocked (private window, disabled site data): the choice just\n // isn't remembered across visits.\n }\n}\n\nfunction readVisitorLocale(): {\n timeZone: string | null;\n languages: string[];\n} {\n let timeZone: string | null = null;\n try {\n timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone ?? null;\n } catch {\n timeZone = null;\n }\n const languages =\n typeof navigator === \"undefined\"\n ? []\n : [...(navigator.languages ?? []), navigator.language].filter(\n (l): l is string => typeof l === \"string\" && l.length > 0,\n );\n return { timeZone, languages };\n}\n\n// ============================================================================\n// TYPES & INTERFACES\n// ============================================================================\n\ninterface PlanFeature {\n id: string;\n quotaId?: string | null;\n /**\n * Per-plan quantity for this quota (e.g. `{ quantity: 250, unit: 'profiles' }`).\n * This is the tier-specific value — `quota.limits` below is the SHARED\n * Quota row's value, fixed once at quota-creation time and identical for\n * every plan that links the same quota. See `transformFeatureToLabel`.\n */\n context?: unknown;\n quota?: {\n id: string;\n name: string;\n limits?: unknown;\n } | null;\n}\n\ninterface GatewayPlan {\n id: string;\n name: string;\n price: number;\n currency: string;\n currencyPrices?: Record<string, number> | null;\n duration: string;\n features: PlanFeature[];\n isActive: boolean;\n productID?: string | null;\n createdAt: string;\n}\n\ninterface GetPlansResponse {\n publicPlans: GatewayPlan[];\n}\n\nexport interface PlanCard {\n id: string;\n name: string;\n price: number;\n currency: string;\n currencyPrices?: Record<string, number> | null;\n duration: string;\n features: Array<{\n id: string;\n quotaId?: string | null;\n context?: unknown;\n quota?: {\n id: string;\n name: string;\n limits?: unknown;\n } | null;\n }>;\n isActive: boolean;\n productID?: string | null;\n}\n\ninterface CurrencyConfig {\n code: string;\n symbol: string;\n position: \"prefix\" | \"suffix\";\n}\n\ninterface PlanMetadata {\n tier: \"basic\" | \"professional\" | \"enterprise\";\n displayName: string;\n defaultDescription: string;\n popular: boolean;\n ctaText: string;\n ctaType: \"signup\" | \"contact\";\n /**\n * Curated feature bullets for this specific plan. When provided, these win\n * over the tier-based `fallbackFeatures` and the raw quota list — letting a\n * consumer differentiate plans that share a tier keyword (e.g. Free vs Hobby,\n * Pro vs Team) with concise, accurate copy.\n */\n features?: string[];\n}\n\nexport interface PricingProps {\n /**\n * Product name (used in branding and SEO)\n */\n productName?: string;\n /**\n * Product ID for GraphQL query (falls back to WebSDKConfig.productId)\n */\n productId?: string;\n /**\n * App login URL for signup CTA\n */\n appLoginUrl: string;\n /**\n * Contact page URL for enterprise CTA\n * @default '/contact'\n */\n contactUrl?: string;\n /**\n * Supported currencies\n */\n currencies?: Record<string, CurrencyConfig>;\n /**\n * Billing interval display text\n */\n billingIntervals?: Record<string, string>;\n /**\n * Currency to show first (ISO 4217, e.g. \"INR\"). When omitted the page shows\n * the catalog's base currency until the browser reveals the visitor's\n * region, then switches to that region's currency if the catalog sells in it.\n */\n defaultCurrency?: string;\n /**\n * Show the currency picker when the catalog prices every plan in more than\n * one currency.\n * @default true\n */\n showCurrencySelector?: boolean;\n /**\n * Plan metadata (tier info, CTA text, etc.)\n */\n planMetadata?: Record<string, Partial<PlanMetadata>>;\n /**\n * Fallback features when API doesn't provide them or is unavailable\n */\n fallbackFeatures?: {\n basic?: string[];\n professional?: string[];\n enterprise?: string[];\n };\n /**\n * Enable audience tabs (Individual / Enterprise).\n * When enabled, shows a two-level tab system.\n * @default false\n */\n showAudienceTabs?: boolean;\n /**\n * Enterprise plans to show when Enterprise tab is selected.\n * Each entry defines a static plan card (not fetched from API).\n */\n enterprisePlans?: Array<{\n name: string;\n subtitle: string;\n price: string;\n period: string;\n note?: string;\n button: string;\n buttonLink: string;\n popular: boolean;\n badge?: string;\n features: string[];\n }>;\n /**\n * Enterprise section heading\n * @default 'Enterprise Solutions'\n */\n enterpriseHeading?: string;\n /**\n * Enterprise section subtitle\n */\n enterpriseSubtitle?: string;\n /**\n * Hero section heading (for PricingPage)\n * @default 'Simple, Transparent Pricing'\n */\n heroHeading?: string;\n /**\n * Hero section subtitle (for PricingPage)\n * @default \"Choose the plan that's right for you. No hidden fees, no surprises.\"\n */\n heroSubtitle?: string;\n /**\n * Section heading (for PricingSection)\n * @default 'Pricing Plans'\n */\n sectionHeading?: string;\n /**\n * Section subtitle (for PricingSection)\n * @default 'Choose the perfect plan for your needs'\n */\n sectionSubtitle?: string;\n /**\n * Custom CSS class\n */\n className?: string;\n /**\n * Section ID for anchor links\n */\n id?: string;\n /**\n * SEO: Page title (optional, for PricingPage)\n */\n seoTitle?: string;\n /**\n * SEO: Page description (optional, for PricingPage)\n */\n seoDescription?: string;\n /**\n * SEO: Keywords (optional, for PricingPage)\n */\n seoKeywords?: string;\n /**\n * SEO: OG Image URL (optional, for PricingPage)\n */\n seoImage?: string;\n /**\n * SEO: Canonical URL (optional, for PricingPage)\n */\n seoUrl?: string;\n}\n\n// ============================================================================\n// SERVICE FUNCTIONS\n// ============================================================================\n\n/**\n * Format price for display. A consumer-supplied `currencies` entry wins;\n * every other ISO code is formatted by `Intl` with a fixed locale so the\n * prerendered HTML and the hydrated page agree.\n */\nfunction formatPrice(\n price: number,\n currencyCode: string = \"USD\",\n currencies?: Record<string, CurrencyConfig>,\n): string {\n const currency = currencies?.[currencyCode];\n if (!currency) {\n return formatCatalogPrice(price, currencyCode);\n }\n\n const formattedNumber = price % 1 === 0 ? price.toString() : price.toFixed(2);\n\n return currency.position === \"prefix\"\n ? `${currency.symbol}${formattedNumber}`\n : `${formattedNumber}${currency.symbol}`;\n}\n\n/**\n * Format billing interval for display\n */\nfunction formatInterval(\n duration: string,\n billingIntervals?: Record<string, string>,\n): string {\n const defaultIntervals: Record<string, string> = {\n monthly: \"/month\",\n yearly: \"/year\",\n };\n\n const intervals = billingIntervals || defaultIntervals;\n return intervals[duration] || `/${duration}`;\n}\n\n/**\n * Detect if a plan belongs to the enterprise audience based on name.\n * Plans with \"team\", \"business\", or \"enterprise\" in the name are enterprise.\n *\n * IMPORTANT: this heuristic only has somewhere to route matched plans TO when\n * `showAudienceTabs` is enabled (see `splitPlansByAudience` below) — the\n * separate \"Enterprise\" tab it feeds is only ever rendered in that mode.\n * Callers MUST gate on `showAudienceTabs` before using this, or a plan whose\n * name simply happens to contain \"business\"/\"team\" (a common self-serve tier\n * name, not necessarily a contact-sales tier) silently disappears from the\n * page with no error and no fallback rendering (found live on AssetHandler's\n * /pricing — BOFF-4854 — where a $999/mo \"Business\" self-serve plan was\n * completely invisible because the product renders without audience tabs).\n */\nfunction isEnterprisePlan(planName: string): boolean {\n const name = planName.toLowerCase();\n return (\n name.includes(\"team\") ||\n name.includes(\"business\") ||\n name.includes(\"enterprise\")\n );\n}\n\n/**\n * Decide whether a name-matched \"enterprise audience\" plan should actually be\n * routed to the Enterprise tab.\n *\n * The name heuristic (`isEnterprisePlan`) matches common self-serve tier names\n * like \"Business\" or \"Team\", not only contact-sales tiers. When a price is\n * available, we treat paid tiers as self-serve and keep them in the individual\n * grid; only $0 (custom/quote-based) name-matched plans go to the Enterprise\n * tab. Callers that omit `priceRaw` keep the legacy name-only behavior.\n */\nfunction isEnterpriseAudiencePlan<\n T extends { name: string; priceRaw?: number },\n>(plan: T): boolean {\n if (!isEnterprisePlan(plan.name)) {\n return false;\n }\n if (typeof plan.priceRaw === \"number\") {\n return plan.priceRaw === 0;\n }\n return true;\n}\n\n/**\n * Split the full active plan list into the individual (self-serve) grid vs.\n * the enterprise-audience-tab bucket.\n *\n * When `showAudienceTabs` is false (the default — @see `PricingProps.showAudienceTabs`),\n * there is no \"Enterprise\" tab for a name-matched plan to render under, so\n * every active plan must stay in the individual grid regardless of its name.\n *\n * When `showAudienceTabs` is true, only custom-priced (priceRaw === 0) plans\n * whose names contain \"team\"/\"business\"/\"enterprise\" are moved to the\n * Enterprise tab. Paid self-serve tiers with those same names (e.g.\n * \"$999/mo Business\") stay in the individual grid so they actually render —\n * this is the BOFF-5652 fix.\n *\n * Exported (pure, no React/hooks) so this exact branching can be unit-tested\n * without a DOM/render harness.\n */\nexport function splitPlansByAudience<\n T extends { name: string; priceRaw?: number },\n>(\n plans: T[],\n showAudienceTabs: boolean | undefined,\n): { individualPlans: T[]; enterprisePlans: T[] } {\n if (!showAudienceTabs) {\n return { individualPlans: plans, enterprisePlans: [] };\n }\n return {\n individualPlans: plans.filter((p) => !isEnterpriseAudiencePlan(p)),\n enterprisePlans: plans.filter((p) => isEnterpriseAudiencePlan(p)),\n };\n}\n\n/**\n * Resolve the final set of cards to show under the Enterprise audience tab.\n *\n * Historically this was a strict either/or: any API-fetched, name-matched\n * \"enterprise audience\" plan (see `isEnterprisePlan` — matches \"team\",\n * \"business\", or \"enterprise\" in the plan name) completely replaced the\n * consumer's static `enterprisePlans` prop, on the assumption that a product\n * either sells enterprise tiers self-serve through the billing catalog OR\n * lists them as static \"Contact Sales\" cards, never both.\n *\n * That assumption breaks for a product that self-serve-sells a mid tier\n * whose name happens to match the enterprise heuristic (e.g. a \"Business\"\n * plan sold through the catalog, $399/mo) while ALSO wanting a distinct,\n * uncatalogued, custom-priced \"Enterprise / Contact Sales\" tier above it\n * (sales-negotiated, not backed by a billing plan). Found live on\n * HousingVista's /pricing (BOFF-5007): the API's \"Business (Monthly/Yearly)\"\n * plans satisfied `hasApiEnterprisePlans`, so the static `enterprisePlans`\n * prop carrying the real \"Enterprise\" Contact-Sales card was silently\n * dropped in full — the tab showed Business but no Enterprise card at all.\n *\n * Fix: union the two sources instead of picking one. API plans always win a\n * same-name collision (case-insensitive) so a static entry can't shadow a\n * live catalog plan; anything in the static list that doesn't collide is\n * appended after the API cards. Only fall back to the single built-in\n * default card when NEITHER source has anything to show.\n *\n * Exported (pure, no React/hooks) so this merge/dedup logic can be\n * unit-tested without a DOM/render harness.\n */\nexport function resolveEnterprisePlans<T extends { name: string }>(\n apiEnterprisePlans: T[],\n staticEnterprisePlans: Array<{ name: string }> | undefined,\n defaultEnterpriseCard: { name: string },\n): Array<T | { name: string }> {\n const staticPlans = staticEnterprisePlans || [];\n\n if (apiEnterprisePlans.length === 0 && staticPlans.length === 0) {\n return [defaultEnterpriseCard];\n }\n\n const apiNames = new Set(apiEnterprisePlans.map((p) => p.name.toLowerCase()));\n const dedupedStaticPlans = staticPlans.filter(\n (p) => !apiNames.has(p.name.toLowerCase()),\n );\n\n return [...apiEnterprisePlans, ...dedupedStaticPlans];\n}\n\n/**\n * Detect plan tier from plan name\n */\nfunction detectPlanTier(\n planName: string,\n): \"basic\" | \"professional\" | \"enterprise\" | null {\n const name = planName.toLowerCase();\n\n if (\n name.includes(\"free\") ||\n name.includes(\"basic\") ||\n name.includes(\"starter\") ||\n name.includes(\"hobby\")\n ) {\n return \"basic\";\n } else if (\n name.includes(\"pro\") ||\n name.includes(\"professional\") ||\n name.includes(\"premium\") ||\n name.includes(\"team\")\n ) {\n return \"professional\";\n } else if (name.includes(\"enterprise\") || name.includes(\"business\")) {\n return \"enterprise\";\n }\n\n return null;\n}\n\n/**\n * Get features for a specific plan tier from fallback config\n */\nfunction getFallbackFeaturesForTier(\n tier: \"basic\" | \"professional\" | \"enterprise\",\n fallbackFeatures?: {\n basic?: string[];\n professional?: string[];\n enterprise?: string[];\n },\n): string[] {\n return [...(fallbackFeatures?.[tier] || [])];\n}\n\n/**\n * Get metadata for a plan by name.\n *\n * `priceRaw` is used to disambiguate self-serve tiers from contact-sales\n * tiers that happen to share a name. A paid \"Business\" or \"Enterprise\" plan\n * is self-serve, so it gets a signup CTA and is treated as the professional\n * tier for fallback features/styling. Only $0 (custom/quote-based) plans with\n * enterprise-y names get the Enterprise contact-sales treatment.\n */\nfunction getMetadataForPlan(\n planName: string,\n priceRaw: number,\n planMetadata?: Record<string, Partial<PlanMetadata>>,\n): PlanMetadata {\n const normalizedName = planName.toLowerCase();\n const cleanName = cleanPlanDisplayName(planName);\n\n // Try exact match first — consumers can override any heuristic. Keys may be\n // the full catalog name (\"vibecontrols - pro (monthly)\") or the clean one (\"pro\").\n const meta =\n planMetadata?.[normalizedName] ?? planMetadata?.[cleanName.toLowerCase()];\n if (meta) {\n return {\n tier: meta.tier || \"basic\",\n displayName: meta.displayName || cleanName,\n defaultDescription: meta.defaultDescription || \"Choose this plan\",\n popular: meta.popular ?? false,\n ctaText: meta.ctaText || \"Get Started\",\n ctaType: meta.ctaType || \"signup\",\n features: meta.features,\n };\n }\n\n // Try tier detection\n const tier = detectPlanTier(planName);\n if (tier) {\n const isPaidEnterpriseName =\n tier === \"enterprise\" && typeof priceRaw === \"number\" && priceRaw > 0;\n if (isPaidEnterpriseName) {\n return {\n tier: \"professional\",\n displayName: cleanName,\n defaultDescription: \"Choose this plan\",\n popular: false,\n ctaText: \"Get Started\",\n ctaType: \"signup\",\n };\n }\n\n return {\n tier,\n displayName: cleanName,\n defaultDescription: \"Choose this plan\",\n popular: tier === \"professional\",\n ctaText: tier === \"enterprise\" ? \"Contact Sales\" : \"Get Started\",\n ctaType: tier === \"enterprise\" ? \"contact\" : \"signup\",\n };\n }\n\n // Fallback\n return {\n tier: \"basic\",\n displayName: cleanName,\n defaultDescription: \"Choose this plan\",\n popular: false,\n ctaText: \"Get Started\",\n ctaType: \"signup\",\n };\n}\n\n/**\n * Transform quota limits to a human-readable feature string\n */\n/**\n * Derive a human-readable label from a dotted quota name.\n * e.g. \"vibecontrols.workspaces.count\" → \"Workspaces\"\n * \"vibecontrols.storage.bytes\" → \"Storage\"\n */\nfunction humanizeQuotaName(name: string): string {\n const parts = name.split(\".\");\n // Use the second-to-last segment (the resource), or fall back to the last\n const resource =\n parts.length >= 3 ? parts[parts.length - 2]! : parts[parts.length - 1]!;\n // Capitalize and replace hyphens/underscores with spaces\n return resource\n .replace(/[-_]/g, \" \")\n .replace(/\\b\\w/g, (c) => c.toUpperCase());\n}\n\n/**\n * Transform a plan feature (quota link) into a human-readable string.\n *\n * BOFF-4206: the per-PLAN quantity lives on the SubscriptionFeature's own\n * `context` (e.g. `{ quantity: 250, unit: 'profiles' }`) — `quota.limits` is\n * the SHARED Quota row's value, set once at quota-creation time and\n * IDENTICAL for every plan that links that quota. Reading `quota.limits`\n * here made every tier's card show the same numbers: live on TimeCampus's\n * /pricing, Starter/Growth/Business all rendered Free's \"1 Projects\" and \"2\n * Admin Seats\" instead of their real, higher, per-tier limits. Prefer the\n * per-feature `context.quantity`; fall back to `quota.limits.value` only\n * when a feature genuinely carries no context (legacy rows seeded before\n * this convention, or a quota linked without a context payload).\n *\n * Exported (pure, no React/hooks) so this exact precedence is unit-testable\n * without a DOM/render harness, matching `splitPlansByAudience` /\n * `resolveEnterprisePlans` above.\n */\nexport function transformFeatureToLabel(feature: {\n quotaId?: string | null;\n context?: unknown;\n quota?: { id: string; name: string; limits?: unknown } | null;\n}): string {\n const quota = feature.quota;\n if (!quota) return \"\";\n const { name, limits } = quota;\n\n const featureContext =\n feature.context && typeof feature.context === \"object\"\n ? (feature.context as Record<string, unknown>)\n : null;\n const limitsObj =\n limits && typeof limits === \"object\"\n ? (limits as Record<string, unknown>)\n : null;\n\n const value: number | undefined =\n typeof featureContext?.quantity === \"number\"\n ? featureContext.quantity\n : typeof limitsObj?.value === \"number\"\n ? limitsObj.value\n : undefined;\n const unit: string | undefined =\n (typeof featureContext?.unit === \"string\" && featureContext.unit) ||\n (typeof limitsObj?.unit === \"string\" && limitsObj.unit) ||\n undefined;\n const description: string | undefined =\n (typeof featureContext?.description === \"string\" &&\n featureContext.description) ||\n (typeof limitsObj?.description === \"string\" && limitsObj.description) ||\n undefined;\n const type = limitsObj?.type;\n\n // Use explicit description if provided\n if (description) {\n if (type === \"number\" && typeof value === \"number\") {\n return `${value.toLocaleString()} ${description}`;\n }\n return description;\n }\n\n // Use unit if provided\n if (unit) {\n if (typeof value === \"number\") {\n return `${value.toLocaleString()} ${unit}`;\n }\n return unit;\n }\n\n // Derive label from quota name\n const label = humanizeQuotaName(name);\n\n if (type === \"boolean\") {\n return label;\n }\n\n if (typeof value === \"number\") {\n return `${value.toLocaleString()} ${label}`;\n }\n\n return label;\n}\n\n/**\n * Extract display features from a plan's feature list\n */\nfunction extractDisplayFeatures(\n features: PlanFeature[],\n planName: string,\n fallbackFeatures?: {\n basic?: string[];\n professional?: string[];\n enterprise?: string[];\n },\n): string[] {\n // Prefer curated fallback features when available — they are concise\n // and differentiated per tier, unlike raw quota dumps\n const tier = detectPlanTier(planName);\n if (tier) {\n const tierFeatures = getFallbackFeaturesForTier(tier, fallbackFeatures);\n if (tierFeatures.length > 0) {\n return tierFeatures;\n }\n }\n\n // No fallback: transform quota features from API\n const quotaFeatures = features\n .filter((f) => f.quota)\n .map((f) => transformFeatureToLabel(f));\n\n if (quotaFeatures.length > 0) {\n return quotaFeatures;\n }\n\n return [];\n}\n\n// ============================================================================\n// UI CARD TYPE (internal)\n// ============================================================================\n\ninterface DisplayPlanCard {\n id: string;\n /** Display name (product prefix and period stripped, or planMetadata.displayName). */\n name: string;\n description: string;\n price: string;\n priceRaw: number;\n interval: string;\n duration: string;\n features: string[];\n popular: boolean;\n ctaText: string;\n ctaLink: string;\n tier: \"basic\" | \"professional\" | \"enterprise\" | null;\n}\n\n/**\n * Transform a GatewayPlan into a display card\n */\nfunction transformToDisplayCard(\n plan: GatewayPlan,\n props: PricingProps,\n currency: string | null,\n): DisplayPlanCard {\n const metadata = getMetadataForPlan(\n plan.name,\n plan.price,\n props.planMetadata,\n );\n\n // The selected currency only applies when the catalog prices this plan in\n // it; otherwise the card keeps the plan's own currency (never a blank).\n const selectedPrice = currency ? planPriceIn(plan, currency) : null;\n const formattedPrice =\n currency && selectedPrice !== null\n ? formatPrice(selectedPrice, currency.toUpperCase(), props.currencies)\n : formatPrice(plan.price, plan.currency, props.currencies);\n const interval = formatInterval(plan.duration, props.billingIntervals);\n\n // A curated per-plan list (via planMetadata) wins over the tier fallback and\n // the raw quota dump — this is how Free/Hobby and Pro/Team get distinct copy.\n const features =\n metadata.features && metadata.features.length > 0\n ? metadata.features\n : extractDisplayFeatures(\n plan.features,\n plan.name,\n props.fallbackFeatures,\n );\n\n const contactUrl = props.contactUrl || \"/contact\";\n const ctaLink =\n metadata.ctaType === \"contact\" ? contactUrl : props.appLoginUrl;\n // A $0 contact-sales plan (e.g. \"Enterprise\") is quote-based, not free.\n const isQuoteBased = metadata.ctaType === \"contact\" && plan.price === 0;\n\n return {\n id: plan.id,\n name: metadata.displayName,\n description: metadata.defaultDescription,\n price: isQuoteBased ? \"Custom\" : formattedPrice,\n priceRaw: plan.price,\n interval: isQuoteBased ? \"\" : interval,\n duration: plan.duration,\n features,\n popular: metadata.popular,\n ctaText: metadata.ctaText,\n ctaLink,\n tier: metadata.tier,\n };\n}\n\n// ============================================================================\n// REACT COMPONENTS\n// ============================================================================\n\n/**\n * Pricing Section Component - Embeddable\n */\nexport function PricingSection(props: PricingProps) {\n const {\n sectionHeading = \"Pricing Plans\",\n sectionSubtitle = \"Choose the perfect plan for your needs\",\n id,\n className = \"\",\n } = props;\n\n const client = useWebSDK();\n const config = useWebSDKConfig();\n const { product: contextProduct } = useProduct();\n\n const requestedProductId = (props.productId || config.productId || \"\").trim();\n // A UUID the ProductProvider already knows is mapped to its slug without a\n // network call; anything else is resolved inside the effect below.\n const contextKey = resolvePricingProductKey(requestedProductId, [\n contextProduct,\n ]);\n const productKey = contextKey ?? requestedProductId;\n\n const [catalogPlans, setCatalogPlans] = useState<GatewayPlan[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [billingCycle, setBillingCycle] = useState<\"monthly\" | \"yearly\">(\n \"monthly\",\n );\n const [audience, setAudience] = useState<\"individual\" | \"enterprise\">(\n \"individual\",\n );\n const [selectedCurrency, setSelectedCurrency] = useState<string | null>(\n props.defaultCurrency ? props.defaultCurrency.toUpperCase() : null,\n );\n\n useEffect(() => {\n let cancelled = false;\n\n async function resolveKey(): Promise<string> {\n if (!productKey) {\n throw new Error(\n \"PricingPage needs a product slug: pass productId or set WebSDKConfig.productId.\",\n );\n }\n if (!isUuidLike(productKey)) return productKey;\n const response = await client.query<PublicProductKeysResponse>(\n PUBLIC_PRODUCT_KEYS_QUERY,\n );\n if (response.errors && response.errors.length > 0) {\n throw new Error(response.errors[0]!.message);\n }\n const resolved = resolvePricingProductKey(\n productKey,\n response.data?.publicProducts?.items ?? [],\n );\n if (!resolved) throw new PricingProductKeyError(productKey);\n return resolved;\n }\n\n async function loadPlans() {\n try {\n setLoading(true);\n\n const key = await resolveKey();\n if (cancelled) return;\n\n const response = await client.query<GetPlansResponse>(GET_PLANS_QUERY, {\n productId: key,\n });\n\n if (cancelled) return;\n\n if (response.errors && response.errors.length > 0) {\n throw new Error(response.errors[0]!.message);\n }\n\n if (!response.data?.publicPlans) {\n throw new Error(\"No plans data received\");\n }\n\n // Server already filters by productId — keep the active plans.\n const active = response.data.publicPlans.filter(\n (plan) => plan.isActive,\n );\n if (active.length === 0) {\n // An empty catalog is a misconfiguration (wrong key, unseeded\n // product), never a valid page: say so instead of an empty grid.\n throw new Error(\n `publicPlans returned no active plans for product \"${key}\".`,\n );\n }\n\n setCatalogPlans(active);\n setError(null);\n } catch (err) {\n if (cancelled) return;\n console.error(\"Failed to load plans:\", err);\n setCatalogPlans([]);\n setError(\"Unable to load pricing information. Please try again later.\");\n } finally {\n if (!cancelled) {\n setLoading(false);\n }\n }\n }\n\n loadPlans();\n\n return () => {\n cancelled = true;\n };\n }, [client, productKey]);\n\n const currencyOptions = useMemo(\n () => availablePlanCurrencies(catalogPlans),\n [catalogPlans],\n );\n\n // Pick the visitor's currency once the catalog is known: a remembered\n // choice first, else the regional currency. Runs after hydration so the\n // prerendered HTML (base currency) and the first client render agree.\n useEffect(() => {\n if (selectedCurrency || currencyOptions.length < 2) return;\n const stored = readStoredCurrency()?.toUpperCase();\n if (stored && currencyOptions.includes(stored)) {\n setSelectedCurrency(stored);\n return;\n }\n const regional = detectRegionalCurrency(\n currencyOptions,\n readVisitorLocale(),\n );\n if (regional) setSelectedCurrency(regional);\n }, [currencyOptions, selectedCurrency]);\n\n const activeCurrency =\n selectedCurrency && currencyOptions.includes(selectedCurrency)\n ? selectedCurrency\n : (currencyOptions[0] ?? null);\n\n const allPlans: DisplayPlanCard[] = catalogPlans\n .map((plan) => transformToDisplayCard(plan, props, activeCurrency))\n .sort((a, b) => {\n // Quote-based (Custom) cards go last; the rest ascend by price.\n const aCustom = a.price === \"Custom\" ? 1 : 0;\n const bCustom = b.price === \"Custom\" ? 1 : 0;\n return aCustom - bCustom || a.priceRaw - b.priceRaw;\n });\n\n const showCurrencySelector =\n props.showCurrencySelector !== false && currencyOptions.length > 1;\n\n // Split plans by audience (individual vs enterprise) based on plan name —\n // ONLY when the two-tab layout is actually enabled (see\n // `splitPlansByAudience`'s doc comment for why this must be gated).\n const {\n individualPlans: individualAllPlans,\n enterprisePlans: enterpriseApiPlans,\n } = splitPlansByAudience(allPlans, props.showAudienceTabs);\n\n // Filter individual plans by billing cycle\n const individualPlans = plansForBillingCycle(\n individualAllPlans,\n billingCycle,\n );\n\n // Enterprise plans from API (show all — no billing cycle filter since enterprise is typically monthly)\n const enterpriseCards: DisplayPlanCard[] = enterpriseApiPlans.map((p) => {\n // Mark $0 enterprise plans as \"Custom\" pricing with Contact Sales CTA\n if (p.priceRaw === 0 && p.name.toLowerCase().includes(\"enterprise\")) {\n return {\n ...p,\n price: \"Custom\",\n interval: \"\",\n ctaText: \"Contact Sales\",\n ctaLink: props.contactUrl || \"/contact\",\n };\n }\n return p;\n });\n\n // Merge API-fetched enterprise-audience plans (name-matched \"team\"/\n // \"business\"/\"enterprise\") with any statically-configured enterprisePlans\n // — see `resolveEnterprisePlans` for why this must be a union, not an\n // either/or, and only fall back to the built-in default when both are empty.\n const defaultEnterpriseCard: Record<string, unknown> & { name: string } = {\n name: \"Enterprise\",\n subtitle: \"For organizations that need more\",\n description: \"For organizations that need more\",\n price: \"Custom\",\n period: \"\",\n interval: \"\",\n button: \"Contact Sales\",\n buttonLink: props.contactUrl || \"/contact\",\n ctaText: \"Contact Sales\",\n ctaLink: props.contactUrl || \"/contact\",\n popular: false,\n features: props.fallbackFeatures?.enterprise || [\n \"Unlimited everything\",\n \"SSO & SAML\",\n \"Audit logs\",\n \"Dedicated support\",\n \"Self-hosted option\",\n ],\n };\n const resolvedEnterprisePlans = resolveEnterprisePlans(\n enterpriseCards,\n props.enterprisePlans as unknown as Array<{ name: string }> | undefined,\n defaultEnterpriseCard,\n ) as unknown as Record<string, unknown>[];\n\n // Determine which plans to show based on audience\n const plans =\n props.showAudienceTabs && audience === \"enterprise\"\n ? [] // Enterprise uses separate rendering\n : individualPlans;\n\n if (loading) {\n return (\n <section id={id} className={`py-12 sm:py-16 md:py-20 ${className}`}>\n <div className=\"container mx-auto px-4 sm:px-6\">\n <div className=\"text-center mb-8 sm:mb-12\">\n {sectionHeading ? (\n <h2 className=\"text-2xl sm:text-3xl md:text-4xl font-bold mb-3 sm:mb-4\">\n {sectionHeading}\n </h2>\n ) : (\n // Visually hidden but DOM-present heading so axe's heading-order\n // rule still sees an h2 between the consumer's hero h1 and the\n // h3 plan-card titles. See `getMetadataForPlan` for why a falsy\n // sectionHeading is the canonical \"no visible heading\" signal.\n <h2 className=\"sr-only\">Pricing</h2>\n )}\n <p className=\"text-muted-foreground text-sm sm:text-base md:text-lg\">\n Loading pricing information...\n </p>\n </div>\n <div className=\"grid md:grid-cols-3 gap-6 sm:gap-8\">\n {[1, 2, 3].map((i) => (\n <div\n key={i}\n className=\"bg-card border rounded-lg p-6 animate-pulse\"\n >\n <div className=\"h-6 bg-muted rounded w-1/2 mb-4\"></div>\n <div className=\"h-4 bg-muted rounded w-3/4 mb-6\"></div>\n <div className=\"h-10 bg-muted rounded w-1/3 mb-6\"></div>\n <div className=\"space-y-3 mb-6\">\n {[1, 2, 3, 4].map((j) => (\n <div key={j} className=\"h-4 bg-muted rounded\"></div>\n ))}\n </div>\n <div className=\"h-10 bg-muted rounded\"></div>\n </div>\n ))}\n </div>\n </div>\n </section>\n );\n }\n\n if (error && plans.length === 0) {\n return (\n <section id={id} className={`py-12 sm:py-16 md:py-20 ${className}`}>\n <div className=\"container mx-auto px-4 sm:px-6\">\n <div className=\"text-center\">\n {sectionHeading ? (\n <h2 className=\"text-2xl sm:text-3xl md:text-4xl font-bold mb-4\">\n {sectionHeading}\n </h2>\n ) : (\n <h2 className=\"sr-only\">Pricing</h2>\n )}\n <p className=\"text-muted-foreground mb-8\">{error}</p>\n <Button onClick={() => window.location.reload()}>Retry</Button>\n </div>\n </div>\n </section>\n );\n }\n\n return (\n <section id={id} className={`py-12 sm:py-16 md:py-20 ${className}`}>\n <div className=\"container mx-auto px-4 sm:px-6\">\n <div className=\"text-center mb-8 sm:mb-12\">\n {sectionHeading ? (\n <h2 className=\"text-2xl sm:text-3xl md:text-4xl font-bold mb-3 sm:mb-4\">\n {sectionHeading}\n </h2>\n ) : (\n <h2 className=\"sr-only\">Pricing</h2>\n )}\n <p className=\"text-muted-foreground text-sm sm:text-base md:text-lg mb-6\">\n {sectionSubtitle}\n </p>\n\n {/* Audience Toggle (Individual / Enterprise) */}\n {props.showAudienceTabs && (\n <div className=\"flex justify-center mb-4\">\n <div className=\"inline-flex items-center bg-muted p-1.5 rounded-xl\">\n <button\n type=\"button\"\n onClick={() => setAudience(\"individual\")}\n className={`px-6 sm:px-8 py-2.5 rounded-lg text-sm font-semibold transition-all ${\n audience === \"individual\"\n ? \"bg-background shadow-sm text-foreground\"\n : \"text-muted-foreground hover:text-foreground\"\n }`}\n >\n Individual\n </button>\n <button\n type=\"button\"\n onClick={() => setAudience(\"enterprise\")}\n className={`px-6 sm:px-8 py-2.5 rounded-lg text-sm font-semibold transition-all ${\n audience === \"enterprise\"\n ? \"bg-background shadow-sm text-foreground\"\n : \"text-muted-foreground hover:text-foreground\"\n }`}\n >\n Enterprise\n </button>\n </div>\n </div>\n )}\n\n {/* Billing Cycle Toggle — only for Individual */}\n {(audience === \"individual\" || !props.showAudienceTabs) && (\n <div className=\"flex justify-center\">\n <div className=\"inline-flex items-center bg-muted/60 p-1.5 rounded-xl\">\n <button\n type=\"button\"\n onClick={() => setBillingCycle(\"monthly\")}\n className={`px-5 sm:px-6 py-2 rounded-lg text-sm font-medium transition-all ${\n billingCycle === \"monthly\"\n ? \"bg-background shadow-sm text-foreground\"\n : \"text-muted-foreground hover:text-foreground\"\n }`}\n >\n Monthly\n </button>\n <button\n type=\"button\"\n onClick={() => setBillingCycle(\"yearly\")}\n className={`px-5 sm:px-6 py-2 rounded-lg text-sm font-medium transition-all ${\n billingCycle === \"yearly\"\n ? \"bg-background shadow-sm text-foreground\"\n : \"text-muted-foreground hover:text-foreground\"\n }`}\n >\n Yearly\n </button>\n </div>\n </div>\n )}\n\n {showCurrencySelector && (\n <div className=\"flex justify-center mt-4\">\n <label className=\"inline-flex items-center gap-2 text-sm text-muted-foreground\">\n <span>Currency</span>\n <select\n value={activeCurrency ?? \"\"}\n onChange={(event) => {\n const code = event.target.value;\n setSelectedCurrency(code);\n storeCurrency(code);\n }}\n className=\"h-9 rounded-md border border-input bg-background px-3 text-sm text-foreground focus:outline-none focus:ring-2 focus:ring-ring\"\n data-testid=\"pricing-currency-select\"\n >\n {currencyOptions.map((code) => (\n <option key={code} value={code}>\n {code}\n </option>\n ))}\n </select>\n </label>\n </div>\n )}\n </div>\n\n {/* Enterprise Intro — only when Enterprise is selected */}\n {props.showAudienceTabs && audience === \"enterprise\" && (\n <div className=\"mx-auto max-w-3xl text-center mb-8\">\n <h3 className=\"text-xl sm:text-2xl font-bold text-foreground\">\n {props.enterpriseHeading || \"Enterprise Solutions\"}\n </h3>\n <p className=\"mt-3 text-muted-foreground\">\n {props.enterpriseSubtitle ||\n \"Built for teams, agencies, and organizations that need advanced collaboration, security, support, and scale.\"}\n </p>\n </div>\n )}\n\n <div className=\"grid md:grid-cols-3 gap-6 sm:gap-8 max-w-6xl mx-auto items-stretch\">\n {props.showAudienceTabs && audience === \"enterprise\"\n ? /* Enterprise cards — from API, static prop, or built-in default */\n resolvedEnterprisePlans.map((ep) => {\n // Normalize fields — API uses description/ctaText/ctaLink/interval, static prop uses subtitle/button/buttonLink/period\n const subtitle = (ep.subtitle ||\n ep.description ||\n \"\") as string;\n const btnText = (ep.button ||\n ep.ctaText ||\n \"Get Started\") as string;\n const btnLink = (ep.buttonLink ||\n ep.ctaLink ||\n props.appLoginUrl) as string;\n const period = (ep.period || ep.interval || \"\") as string;\n const note = (ep.note || \"\") as string;\n const badge = (ep.badge || \"\") as string;\n const isPopular = ep.popular as boolean;\n const features = (ep.features || []) as string[];\n const price = String(ep.price);\n\n return (\n <div\n key={ep.name as string}\n className={`bg-card rounded-xl p-6 sm:p-8 transition-all duration-300 flex flex-col ${\n isPopular\n ? \"border-2 border-primary shadow-xl ring-1 ring-primary/20\"\n : \"border hover:shadow-lg hover:border-primary/50\"\n }`}\n >\n <div className=\"text-center mb-6\">\n {isPopular && badge && (\n <span className=\"inline-block bg-primary text-primary-foreground px-4 py-1 rounded-full text-xs font-semibold mb-3\">\n {badge}\n </span>\n )}\n <h3 className=\"text-xl sm:text-2xl font-bold mb-2\">\n {ep.name as string}\n </h3>\n <p className=\"text-muted-foreground text-sm\">\n {subtitle}\n </p>\n </div>\n\n <div className=\"text-center mb-2\">\n {price === \"Custom\" ? (\n <p className=\"text-3xl sm:text-4xl font-bold\">Custom</p>\n ) : (\n <p className=\"flex items-baseline justify-center gap-1\">\n <span className=\"text-4xl sm:text-5xl font-extrabold\">\n {price}\n </span>\n <span className=\"text-muted-foreground text-lg\">\n {period}\n </span>\n </p>\n )}\n {note && (\n <p className=\"mt-2 text-sm font-medium text-primary\">\n {note}\n </p>\n )}\n </div>\n\n <ul className=\"space-y-3 mb-8 mt-6 flex-1\">\n {features.map((feature) => (\n <li\n key={feature}\n className=\"text-sm flex items-start gap-2\"\n >\n <span className=\"text-primary mt-0.5 flex-shrink-0\">\n ✓\n </span>\n <span>{feature}</span>\n </li>\n ))}\n </ul>\n\n {btnLink.startsWith(\"/\") ? (\n <Button\n className=\"w-full h-11\"\n variant={isPopular ? \"default\" : \"outline\"}\n size=\"lg\"\n asChild\n >\n <a\n href={btnLink}\n onClick={() =>\n window.scrollTo({ top: 0, behavior: \"smooth\" })\n }\n >\n {btnText}\n </a>\n </Button>\n ) : (\n <Button\n className=\"w-full h-11\"\n variant={isPopular ? \"default\" : \"outline\"}\n size=\"lg\"\n asChild\n >\n <a\n href={btnLink}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n {btnText}\n </a>\n </Button>\n )}\n </div>\n );\n })\n : /* Individual API-fetched cards */\n plans.map((plan) => (\n <div\n key={plan.id}\n className={`bg-card rounded-xl p-6 sm:p-8 transition-all duration-300 flex flex-col ${\n plan.popular\n ? \"border-2 border-primary shadow-xl ring-1 ring-primary/20\"\n : \"border hover:shadow-lg hover:border-primary/50\"\n }`}\n >\n <div className=\"text-center mb-6\">\n {plan.popular && (\n <span className=\"inline-block bg-primary text-primary-foreground px-4 py-1 rounded-full text-xs font-semibold mb-3\">\n Most Popular\n </span>\n )}\n <h3 className=\"text-xl sm:text-2xl font-bold mb-2\">\n {plan.name}\n </h3>\n <p className=\"text-muted-foreground text-sm\">\n {plan.description}\n </p>\n </div>\n\n {/* The amount sits in a <p> on purpose: product sites build\n /pricing.md (their llms.txt corpus) with an extractor that\n keeps only h1-h6/p/li/blockquote/figcaption/dt/dd text, so\n a price in a bare <div>/<span> vanished from it (BOFF-7178). */}\n <div className=\"text-center mb-8\">\n {plan.price === \"Custom\" ? (\n <p className=\"text-3xl sm:text-4xl font-bold\">Custom</p>\n ) : (\n <p className=\"flex items-baseline justify-center gap-1\">\n <span className=\"text-4xl sm:text-5xl font-bold\">\n {plan.price}\n </span>\n <span className=\"text-muted-foreground text-lg\">\n {plan.interval}\n </span>\n </p>\n )}\n </div>\n\n <ul className=\"space-y-3 mb-8 flex-1\">\n {plan.features.map((feature, index) => (\n <li\n key={index}\n className=\"text-sm flex items-start gap-2\"\n >\n <span className=\"text-primary mt-0.5 flex-shrink-0\">\n ✓\n </span>\n <span>{feature}</span>\n </li>\n ))}\n </ul>\n\n {plan.ctaLink.startsWith(\"/\") ? (\n <Button\n className=\"w-full h-11\"\n variant={plan.popular ? \"default\" : \"outline\"}\n size=\"lg\"\n asChild\n >\n <a\n href={plan.ctaLink}\n onClick={() =>\n window.scrollTo({ top: 0, behavior: \"smooth\" })\n }\n >\n {plan.ctaText}\n </a>\n </Button>\n ) : (\n <Button\n className=\"w-full h-11\"\n variant={plan.popular ? \"default\" : \"outline\"}\n size=\"lg\"\n asChild\n >\n <a\n href={plan.ctaLink}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n {plan.ctaText}\n </a>\n </Button>\n )}\n </div>\n ))}\n </div>\n </div>\n </section>\n );\n}\n\n/**\n * Pricing Page Component - Full Page with Hero\n */\nexport function PricingPage(props: PricingProps) {\n const {\n productName,\n heroHeading = \"Simple, Transparent Pricing\",\n heroSubtitle = \"Choose the plan that's right for you. No hidden fees, no surprises.\",\n seoTitle,\n seoDescription,\n seoKeywords,\n seoImage,\n seoUrl,\n } = props;\n\n return (\n <div className=\"w-full\">\n {/* SEO Meta Tags */}\n {seoTitle && (\n <PageHead\n title={seoTitle}\n description={seoDescription || heroSubtitle}\n productName={productName}\n keywords={seoKeywords}\n image={seoImage}\n url={seoUrl}\n />\n )}\n\n {/* Hero Section */}\n <section className=\"bg-gradient-to-b from-background to-muted/20 py-12 sm:py-16 md:py-20 lg:py-24\">\n <div className=\"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8\">\n <div className=\"text-center max-w-3xl mx-auto\">\n <h1 className=\"text-3xl sm:text-4xl md:text-5xl font-bold mb-4 sm:mb-6\">\n {heroHeading}\n </h1>\n <p className=\"text-lg sm:text-xl text-muted-foreground leading-relaxed\">\n {heroSubtitle}\n </p>\n </div>\n </div>\n </section>\n\n {/* Pricing Section */}\n <PricingSection {...props} />\n </div>\n );\n}\n"]}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkGEXC4LRP_js = require('./chunk-GEXC4LRP.js');
|
|
4
|
+
var chunkBYSZT35R_js = require('./chunk-BYSZT35R.js');
|
|
5
|
+
var chunkJXLSTVBK_js = require('./chunk-JXLSTVBK.js');
|
|
6
|
+
var react = require('react');
|
|
7
|
+
var DOMPurify = require('isomorphic-dompurify');
|
|
8
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
9
|
+
|
|
10
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
11
|
+
|
|
12
|
+
var DOMPurify__default = /*#__PURE__*/_interopDefault(DOMPurify);
|
|
13
|
+
|
|
14
|
+
var PUBLIC_CONTENT_PAGE_QUERY = `
|
|
15
|
+
query PublicContentPage($slug: String!, $productId: String) {
|
|
16
|
+
publicContentPage(slug: $slug, productId: $productId) {
|
|
17
|
+
id
|
|
18
|
+
title
|
|
19
|
+
slug
|
|
20
|
+
content
|
|
21
|
+
contentFormat
|
|
22
|
+
seoTitle
|
|
23
|
+
seoDescription
|
|
24
|
+
seoKeywords
|
|
25
|
+
lastUpdated
|
|
26
|
+
version
|
|
27
|
+
excerpt
|
|
28
|
+
thumbnail
|
|
29
|
+
author
|
|
30
|
+
publishedAt
|
|
31
|
+
readingTime
|
|
32
|
+
featured
|
|
33
|
+
category
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
`;
|
|
37
|
+
var PUBLIC_RESOLVED_CONTENT_PAGE_QUERY = `
|
|
38
|
+
query PublicResolvedContentPage($slug: String!, $productId: String!) {
|
|
39
|
+
publicResolvedContentPage(slug: $slug, productId: $productId) {
|
|
40
|
+
id
|
|
41
|
+
title
|
|
42
|
+
slug
|
|
43
|
+
content
|
|
44
|
+
contentFormat
|
|
45
|
+
seoTitle
|
|
46
|
+
seoDescription
|
|
47
|
+
seoKeywords
|
|
48
|
+
lastUpdated
|
|
49
|
+
version
|
|
50
|
+
excerpt
|
|
51
|
+
thumbnail
|
|
52
|
+
author
|
|
53
|
+
publishedAt
|
|
54
|
+
readingTime
|
|
55
|
+
featured
|
|
56
|
+
category
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
`;
|
|
60
|
+
var PUBLIC_CONTENT_PAGES_QUERY = `
|
|
61
|
+
query PublicContentPages($productId: String!, $limit: Int, $offset: Int) {
|
|
62
|
+
publicContentPages(productId: $productId, limit: $limit, offset: $offset) {
|
|
63
|
+
items {
|
|
64
|
+
id
|
|
65
|
+
title
|
|
66
|
+
slug
|
|
67
|
+
lastUpdated
|
|
68
|
+
tags
|
|
69
|
+
seoDescription
|
|
70
|
+
excerpt
|
|
71
|
+
thumbnail
|
|
72
|
+
author
|
|
73
|
+
publishedAt
|
|
74
|
+
readingTime
|
|
75
|
+
featured
|
|
76
|
+
category
|
|
77
|
+
}
|
|
78
|
+
total
|
|
79
|
+
hasMore
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
`;
|
|
83
|
+
function resolveContentPageGate(resolvedProductId, hasProvider, productLoading) {
|
|
84
|
+
if (resolvedProductId) return "fetch";
|
|
85
|
+
return hasProvider && productLoading ? "wait" : "unavailable";
|
|
86
|
+
}
|
|
87
|
+
function useContentPage(slug, options) {
|
|
88
|
+
const client = chunkJXLSTVBK_js.useWebSDK();
|
|
89
|
+
const config = chunkJXLSTVBK_js.useWebSDKConfig();
|
|
90
|
+
const {
|
|
91
|
+
product,
|
|
92
|
+
loading: productLoading,
|
|
93
|
+
error: productError,
|
|
94
|
+
hasProvider
|
|
95
|
+
} = chunkBYSZT35R_js.useProduct();
|
|
96
|
+
const resolvedProductId = product?.id || config.productId;
|
|
97
|
+
const gate = resolveContentPageGate(
|
|
98
|
+
resolvedProductId,
|
|
99
|
+
hasProvider,
|
|
100
|
+
productLoading
|
|
101
|
+
);
|
|
102
|
+
const enabled = options?.enabled !== false;
|
|
103
|
+
const [data, setData] = react.useState(null);
|
|
104
|
+
const [loading, setLoading] = react.useState(enabled);
|
|
105
|
+
const [error, setError] = react.useState(null);
|
|
106
|
+
const fetchPage = react.useCallback(async () => {
|
|
107
|
+
if (!enabled) {
|
|
108
|
+
setLoading(false);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
if (gate === "wait") return;
|
|
112
|
+
if (gate === "unavailable") {
|
|
113
|
+
setData(null);
|
|
114
|
+
setError(
|
|
115
|
+
productError ?? "Product unavailable \u2014 cannot load content page"
|
|
116
|
+
);
|
|
117
|
+
setLoading(false);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
setLoading(true);
|
|
121
|
+
setError(null);
|
|
122
|
+
try {
|
|
123
|
+
const query = options?.resolve ? PUBLIC_RESOLVED_CONTENT_PAGE_QUERY : PUBLIC_CONTENT_PAGE_QUERY;
|
|
124
|
+
const queryName = options?.resolve ? "publicResolvedContentPage" : "publicContentPage";
|
|
125
|
+
const result = await client.query(
|
|
126
|
+
query,
|
|
127
|
+
{
|
|
128
|
+
slug,
|
|
129
|
+
productId: resolvedProductId
|
|
130
|
+
}
|
|
131
|
+
);
|
|
132
|
+
if (result.errors?.length) {
|
|
133
|
+
setError(result.errors[0]?.message ?? "Unknown error");
|
|
134
|
+
setData(null);
|
|
135
|
+
} else {
|
|
136
|
+
setData(result.data?.[queryName] ?? null);
|
|
137
|
+
}
|
|
138
|
+
} catch (err) {
|
|
139
|
+
setError(err instanceof Error ? err.message : "Failed to fetch content");
|
|
140
|
+
setData(null);
|
|
141
|
+
} finally {
|
|
142
|
+
setLoading(false);
|
|
143
|
+
}
|
|
144
|
+
}, [
|
|
145
|
+
client,
|
|
146
|
+
enabled,
|
|
147
|
+
gate,
|
|
148
|
+
productError,
|
|
149
|
+
resolvedProductId,
|
|
150
|
+
slug,
|
|
151
|
+
options?.resolve
|
|
152
|
+
]);
|
|
153
|
+
react.useEffect(() => {
|
|
154
|
+
fetchPage();
|
|
155
|
+
}, [fetchPage]);
|
|
156
|
+
return { data, loading, error, refetch: fetchPage };
|
|
157
|
+
}
|
|
158
|
+
function useContentPages(options) {
|
|
159
|
+
const client = chunkJXLSTVBK_js.useWebSDK();
|
|
160
|
+
const config = chunkJXLSTVBK_js.useWebSDKConfig();
|
|
161
|
+
const [data, setData] = react.useState(null);
|
|
162
|
+
const [loading, setLoading] = react.useState(true);
|
|
163
|
+
const [error, setError] = react.useState(null);
|
|
164
|
+
const fetchPages = react.useCallback(async () => {
|
|
165
|
+
setLoading(true);
|
|
166
|
+
setError(null);
|
|
167
|
+
try {
|
|
168
|
+
const result = await client.query(PUBLIC_CONTENT_PAGES_QUERY, {
|
|
169
|
+
productId: config.productId,
|
|
170
|
+
limit: options?.limit ?? 50,
|
|
171
|
+
offset: options?.offset ?? 0
|
|
172
|
+
});
|
|
173
|
+
if (result.errors?.length) {
|
|
174
|
+
setError(result.errors[0]?.message ?? "Unknown error");
|
|
175
|
+
setData(null);
|
|
176
|
+
} else {
|
|
177
|
+
setData(result.data?.publicContentPages ?? null);
|
|
178
|
+
}
|
|
179
|
+
} catch (err) {
|
|
180
|
+
setError(
|
|
181
|
+
err instanceof Error ? err.message : "Failed to fetch content pages"
|
|
182
|
+
);
|
|
183
|
+
setData(null);
|
|
184
|
+
} finally {
|
|
185
|
+
setLoading(false);
|
|
186
|
+
}
|
|
187
|
+
}, [client, config.productId, options?.limit, options?.offset]);
|
|
188
|
+
react.useEffect(() => {
|
|
189
|
+
fetchPages();
|
|
190
|
+
}, [fetchPages]);
|
|
191
|
+
return { data, loading, error, refetch: fetchPages };
|
|
192
|
+
}
|
|
193
|
+
function ContentRenderer({
|
|
194
|
+
content,
|
|
195
|
+
format = "markdown",
|
|
196
|
+
className
|
|
197
|
+
}) {
|
|
198
|
+
if (format === "markdown") {
|
|
199
|
+
return /* @__PURE__ */ jsxRuntime.jsx(chunkGEXC4LRP_js.Markdown, { className, children: content });
|
|
200
|
+
}
|
|
201
|
+
if (format === "html") {
|
|
202
|
+
const sanitized = DOMPurify__default.default.sanitize(content);
|
|
203
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
204
|
+
"div",
|
|
205
|
+
{
|
|
206
|
+
className,
|
|
207
|
+
dangerouslySetInnerHTML: { __html: sanitized }
|
|
208
|
+
}
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className, children: /* @__PURE__ */ jsxRuntime.jsx("pre", { style: { whiteSpace: "pre-wrap", fontFamily: "inherit" }, children: content }) });
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
exports.ContentRenderer = ContentRenderer;
|
|
215
|
+
exports.useContentPage = useContentPage;
|
|
216
|
+
exports.useContentPages = useContentPages;
|
|
217
|
+
//# sourceMappingURL=chunk-4U6YAII5.js.map
|
|
218
|
+
//# sourceMappingURL=chunk-4U6YAII5.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/content/content-provider.tsx"],"names":["useWebSDK","useWebSDKConfig","useProduct","useState","useCallback","useEffect","jsx","Markdown","DOMPurify"],"mappings":";;;;;;;;;;;;;AA2FA,IAAM,yBAAA,GAA4B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA;AAwBlC,IAAM,kCAAA,GAAqC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA;AAwB3C,IAAM,0BAAA,GAA6B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA;AAwC5B,SAAS,sBAAA,CACd,iBAAA,EACA,WAAA,EACA,cAAA,EACiB;AACjB,EAAA,IAAI,mBAAmB,OAAO,OAAA;AAG9B,EAAA,OAAO,WAAA,IAAe,iBAAiB,MAAA,GAAS,aAAA;AAClD;AAUO,SAAS,cAAA,CACd,MACA,OAAA,EAMA;AACA,EAAA,MAAM,SAASA,0BAAA,EAAU;AACzB,EAAA,MAAM,SAASC,gCAAA,EAAgB;AAC/B,EAAA,MAAM;AAAA,IACJ,OAAA;AAAA,IACA,OAAA,EAAS,cAAA;AAAA,IACT,KAAA,EAAO,YAAA;AAAA,IACP;AAAA,MACEC,2BAAA,EAAW;AACf,EAAA,MAAM,iBAAA,GAAoB,OAAA,EAAS,EAAA,IAAM,MAAA,CAAO,SAAA;AAChD,EAAA,MAAM,IAAA,GAAO,sBAAA;AAAA,IACX,iBAAA;AAAA,IACA,WAAA;AAAA,IACA;AAAA,GACF;AACA,EAAA,MAAM,OAAA,GAAU,SAAS,OAAA,KAAY,KAAA;AACrC,EAAA,MAAM,CAAC,IAAA,EAAM,OAAO,CAAA,GAAIC,eAAiC,IAAI,CAAA;AAC7D,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAIA,eAAS,OAAO,CAAA;AAC9C,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAIA,eAAwB,IAAI,CAAA;AAEtD,EAAA,MAAM,SAAA,GAAYC,kBAAY,YAAY;AACxC,IAAA,IAAI,CAAC,OAAA,EAAS;AACZ,MAAA,UAAA,CAAW,KAAK,CAAA;AAChB,MAAA;AAAA,IACF;AACA,IAAA,IAAI,SAAS,MAAA,EAAQ;AAErB,IAAA,IAAI,SAAS,aAAA,EAAe;AAI1B,MAAA,OAAA,CAAQ,IAAI,CAAA;AACZ,MAAA,QAAA;AAAA,QACE,YAAA,IAAgB;AAAA,OAClB;AACA,MAAA,UAAA,CAAW,KAAK,CAAA;AAChB,MAAA;AAAA,IACF;AAEA,IAAA,UAAA,CAAW,IAAI,CAAA;AACf,IAAA,QAAA,CAAS,IAAI,CAAA;AAEb,IAAA,IAAI;AACF,MAAA,MAAM,KAAA,GAAQ,OAAA,EAAS,OAAA,GACnB,kCAAA,GACA,yBAAA;AAEJ,MAAA,MAAM,SAAA,GAAY,OAAA,EAAS,OAAA,GACvB,2BAAA,GACA,mBAAA;AAEJ,MAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,KAAA;AAAA,QAC1B,KAAA;AAAA,QACA;AAAA,UACE,IAAA;AAAA,UACA,SAAA,EAAW;AAAA;AACb,OACF;AAEA,MAAA,IAAI,MAAA,CAAO,QAAQ,MAAA,EAAQ;AACzB,QAAA,QAAA,CAAS,MAAA,CAAO,MAAA,CAAO,CAAC,CAAA,EAAG,WAAW,eAAe,CAAA;AACrD,QAAA,OAAA,CAAQ,IAAI,CAAA;AAAA,MACd,CAAA,MAAO;AACL,QAAA,OAAA,CAAQ,MAAA,CAAO,IAAA,GAAO,SAAS,CAAA,IAAK,IAAI,CAAA;AAAA,MAC1C;AAAA,IACF,SAAS,GAAA,EAAK;AACZ,MAAA,QAAA,CAAS,GAAA,YAAe,KAAA,GAAQ,GAAA,CAAI,OAAA,GAAU,yBAAyB,CAAA;AACvE,MAAA,OAAA,CAAQ,IAAI,CAAA;AAAA,IACd,CAAA,SAAE;AACA,MAAA,UAAA,CAAW,KAAK,CAAA;AAAA,IAClB;AAAA,EACF,CAAA,EAAG;AAAA,IACD,MAAA;AAAA,IACA,OAAA;AAAA,IACA,IAAA;AAAA,IACA,YAAA;AAAA,IACA,iBAAA;AAAA,IACA,IAAA;AAAA,IACA,OAAA,EAAS;AAAA,GACV,CAAA;AAED,EAAAC,eAAA,CAAU,MAAM;AACd,IAAA,SAAA,EAAU;AAAA,EACZ,CAAA,EAAG,CAAC,SAAS,CAAC,CAAA;AAEd,EAAA,OAAO,EAAE,IAAA,EAAM,OAAA,EAAS,KAAA,EAAO,SAAS,SAAA,EAAU;AACpD;AAUO,SAAS,gBAAgB,OAAA,EAQ9B;AACA,EAAA,MAAM,SAASL,0BAAA,EAAU;AACzB,EAAA,MAAM,SAASC,gCAAA,EAAgB;AAC/B,EAAA,MAAM,CAAC,IAAA,EAAM,OAAO,CAAA,GAAIE,eAAqC,IAAI,CAAA;AACjE,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAIA,eAAS,IAAI,CAAA;AAC3C,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAIA,eAAwB,IAAI,CAAA;AAEtD,EAAA,MAAM,UAAA,GAAaC,kBAAY,YAAY;AACzC,IAAA,UAAA,CAAW,IAAI,CAAA;AACf,IAAA,QAAA,CAAS,IAAI,CAAA;AAEb,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,KAAA,CAEzB,0BAAA,EAA4B;AAAA,QAC7B,WAAW,MAAA,CAAO,SAAA;AAAA,QAClB,KAAA,EAAO,SAAS,KAAA,IAAS,EAAA;AAAA,QACzB,MAAA,EAAQ,SAAS,MAAA,IAAU;AAAA,OAC5B,CAAA;AAED,MAAA,IAAI,MAAA,CAAO,QAAQ,MAAA,EAAQ;AACzB,QAAA,QAAA,CAAS,MAAA,CAAO,MAAA,CAAO,CAAC,CAAA,EAAG,WAAW,eAAe,CAAA;AACrD,QAAA,OAAA,CAAQ,IAAI,CAAA;AAAA,MACd,CAAA,MAAO;AACL,QAAA,OAAA,CAAQ,MAAA,CAAO,IAAA,EAAM,kBAAA,IAAsB,IAAI,CAAA;AAAA,MACjD;AAAA,IACF,SAAS,GAAA,EAAK;AACZ,MAAA,QAAA;AAAA,QACE,GAAA,YAAe,KAAA,GAAQ,GAAA,CAAI,OAAA,GAAU;AAAA,OACvC;AACA,MAAA,OAAA,CAAQ,IAAI,CAAA;AAAA,IACd,CAAA,SAAE;AACA,MAAA,UAAA,CAAW,KAAK,CAAA;AAAA,IAClB;AAAA,EACF,CAAA,EAAG,CAAC,MAAA,EAAQ,MAAA,CAAO,WAAW,OAAA,EAAS,KAAA,EAAO,OAAA,EAAS,MAAM,CAAC,CAAA;AAE9D,EAAAC,eAAA,CAAU,MAAM;AACd,IAAA,UAAA,EAAW;AAAA,EACb,CAAA,EAAG,CAAC,UAAU,CAAC,CAAA;AAEf,EAAA,OAAO,EAAE,IAAA,EAAM,OAAA,EAAS,KAAA,EAAO,SAAS,UAAA,EAAW;AACrD;AAeO,SAAS,eAAA,CAAgB;AAAA,EAC9B,OAAA;AAAA,EACA,MAAA,GAAS,UAAA;AAAA,EACT;AACF,CAAA,EAIG;AACD,EAAA,IAAI,WAAW,UAAA,EAAY;AACzB,IAAA,uBAAOC,cAAA,CAACC,yBAAA,EAAA,EAAS,SAAA,EAAuB,QAAA,EAAA,OAAA,EAAQ,CAAA;AAAA,EAClD;AAEA,EAAA,IAAI,WAAW,MAAA,EAAQ;AACrB,IAAA,MAAM,SAAA,GAAYC,0BAAA,CAAU,QAAA,CAAS,OAAO,CAAA;AAC5C,IAAA,uBACEF,cAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,SAAA;AAAA,QACA,uBAAA,EAAyB,EAAE,MAAA,EAAQ,SAAA;AAAU;AAAA,KAC/C;AAAA,EAEJ;AAGA,EAAA,uBACEA,cAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EACH,QAAA,kBAAAA,cAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAO,EAAE,UAAA,EAAY,UAAA,EAAY,UAAA,EAAY,SAAA,EAAU,EACzD,mBACH,CAAA,EACF,CAAA;AAEJ","file":"chunk-4U6YAII5.js","sourcesContent":["/**\n * Content Provider\n *\n * React hooks for fetching public content pages from the global tenant service.\n * Content pages include privacy policies, terms of service, blogs, changelogs, etc.\n */\n\"use client\";\n\nimport { useState, useEffect, useCallback } from \"react\";\n\nimport DOMPurify from \"isomorphic-dompurify\";\n\nimport { Markdown } from \"./Markdown\";\nimport { useWebSDK, useWebSDKConfig } from \"../client/provider\";\nimport { useProduct } from \"../product/product-provider\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * A customer's written approval of a case study (BOFF-9971). Public callers\n * receive only the approval date; who approved it and where the approval is\n * filed stay with the people who manage content pages.\n */\nexport interface CustomerSignOffData {\n signedAt: string;\n signedBy?: string | null;\n documentRef?: string | null;\n}\n\nexport interface ContentPageData {\n id: string;\n title: string;\n slug: string;\n content: string;\n contentFormat: string;\n seoTitle?: string | null;\n seoDescription?: string | null;\n seoKeywords?: string | null;\n lastUpdated: string;\n version: number;\n tags?: string[];\n excerpt?: string | null;\n thumbnail?: string | null;\n author?: string | null;\n publishedAt?: string | null;\n readingTime?: number | null;\n featured?: boolean;\n category?: string | null;\n customerSignOff?: CustomerSignOffData | null;\n}\n\nexport interface ContentPageListItem {\n id: string;\n title: string;\n slug: string;\n lastUpdated: string;\n tags?: string[];\n excerpt?: string | null;\n thumbnail?: string | null;\n author?: string | null;\n publishedAt?: string | null;\n readingTime?: number | null;\n featured?: boolean;\n category?: string | null;\n seoDescription?: string | null;\n customerSignOff?: CustomerSignOffData | null;\n}\n\nexport interface ContentPageListData {\n items: ContentPageListItem[];\n total: number;\n hasMore: boolean;\n}\n\nexport interface UseContentPageOptions {\n /** Whether to use template variable replacement (resolvedContentPage) */\n resolve?: boolean;\n /**\n * Set to `false` to skip the request entirely — e.g. when the page was\n * already fetched at build time. The hook then settles immediately with\n * `data: null`, `loading: false`. Defaults to `true`.\n */\n enabled?: boolean;\n}\n\n// ============================================================================\n// GraphQL Queries\n// ============================================================================\n\nconst PUBLIC_CONTENT_PAGE_QUERY = `\n query PublicContentPage($slug: String!, $productId: String) {\n publicContentPage(slug: $slug, productId: $productId) {\n id\n title\n slug\n content\n contentFormat\n seoTitle\n seoDescription\n seoKeywords\n lastUpdated\n version\n excerpt\n thumbnail\n author\n publishedAt\n readingTime\n featured\n category\n }\n }\n`;\n\nconst PUBLIC_RESOLVED_CONTENT_PAGE_QUERY = `\n query PublicResolvedContentPage($slug: String!, $productId: String!) {\n publicResolvedContentPage(slug: $slug, productId: $productId) {\n id\n title\n slug\n content\n contentFormat\n seoTitle\n seoDescription\n seoKeywords\n lastUpdated\n version\n excerpt\n thumbnail\n author\n publishedAt\n readingTime\n featured\n category\n }\n }\n`;\n\nconst PUBLIC_CONTENT_PAGES_QUERY = `\n query PublicContentPages($productId: String!, $limit: Int, $offset: Int) {\n publicContentPages(productId: $productId, limit: $limit, offset: $offset) {\n items {\n id\n title\n slug\n lastUpdated\n tags\n seoDescription\n excerpt\n thumbnail\n author\n publishedAt\n readingTime\n featured\n category\n }\n total\n hasMore\n }\n }\n`;\n\n// ============================================================================\n// Hooks\n// ============================================================================\n\n/**\n * Decides what `useContentPage` should do about the product-id dependency.\n *\n * The product id comes from `useProduct()` (a `publicProduct(slug)` lookup)\n * or falls back to `config.productId`. While that lookup is in flight there\n * is nothing to fetch — but once it has settled (or no ProductProvider is\n * mounted and no config id exists) waiting is indistinguishable from hanging:\n * the hook must settle with an error so consumers render their fallback\n * instead of a permanent loading skeleton (BOFF-6190).\n */\nexport type ContentPageGate = \"wait\" | \"fetch\" | \"unavailable\";\n\nexport function resolveContentPageGate(\n resolvedProductId: string | undefined | null,\n hasProvider: boolean,\n productLoading: boolean,\n): ContentPageGate {\n if (resolvedProductId) return \"fetch\";\n // Only an in-flight provider lookup is a real reason to keep waiting.\n // Without a provider the default context reports loading forever.\n return hasProvider && productLoading ? \"wait\" : \"unavailable\";\n}\n\n/**\n * Hook to fetch a single public content page by slug.\n *\n * @example\n * ```tsx\n * const { data, loading, error } = useContentPage('privacy-policy');\n * ```\n */\nexport function useContentPage(\n slug: string,\n options?: UseContentPageOptions,\n): {\n data: ContentPageData | null;\n loading: boolean;\n error: string | null;\n refetch: () => void;\n} {\n const client = useWebSDK();\n const config = useWebSDKConfig();\n const {\n product,\n loading: productLoading,\n error: productError,\n hasProvider,\n } = useProduct();\n const resolvedProductId = product?.id || config.productId;\n const gate = resolveContentPageGate(\n resolvedProductId,\n hasProvider,\n productLoading,\n );\n const enabled = options?.enabled !== false;\n const [data, setData] = useState<ContentPageData | null>(null);\n const [loading, setLoading] = useState(enabled);\n const [error, setError] = useState<string | null>(null);\n\n const fetchPage = useCallback(async () => {\n if (!enabled) {\n setLoading(false);\n return;\n }\n if (gate === \"wait\") return; // Product lookup still in flight\n\n if (gate === \"unavailable\") {\n // The product lookup settled without an id (or there is no provider\n // and no config.productId): the fetch can never start. Settle with an\n // error so consumers render their fallback, not a permanent skeleton.\n setData(null);\n setError(\n productError ?? \"Product unavailable — cannot load content page\",\n );\n setLoading(false);\n return;\n }\n\n setLoading(true);\n setError(null);\n\n try {\n const query = options?.resolve\n ? PUBLIC_RESOLVED_CONTENT_PAGE_QUERY\n : PUBLIC_CONTENT_PAGE_QUERY;\n\n const queryName = options?.resolve\n ? \"publicResolvedContentPage\"\n : \"publicContentPage\";\n\n const result = await client.query<Record<string, ContentPageData | null>>(\n query,\n {\n slug,\n productId: resolvedProductId,\n },\n );\n\n if (result.errors?.length) {\n setError(result.errors[0]?.message ?? \"Unknown error\");\n setData(null);\n } else {\n setData(result.data?.[queryName] ?? null);\n }\n } catch (err) {\n setError(err instanceof Error ? err.message : \"Failed to fetch content\");\n setData(null);\n } finally {\n setLoading(false);\n }\n }, [\n client,\n enabled,\n gate,\n productError,\n resolvedProductId,\n slug,\n options?.resolve,\n ]);\n\n useEffect(() => {\n fetchPage();\n }, [fetchPage]);\n\n return { data, loading, error, refetch: fetchPage };\n}\n\n/**\n * Hook to fetch a list of public content pages for the current product.\n *\n * @example\n * ```tsx\n * const { data, loading } = useContentPages({ limit: 10 });\n * ```\n */\nexport function useContentPages(options?: {\n limit?: number;\n offset?: number;\n}): {\n data: ContentPageListData | null;\n loading: boolean;\n error: string | null;\n refetch: () => void;\n} {\n const client = useWebSDK();\n const config = useWebSDKConfig();\n const [data, setData] = useState<ContentPageListData | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n\n const fetchPages = useCallback(async () => {\n setLoading(true);\n setError(null);\n\n try {\n const result = await client.query<{\n publicContentPages: ContentPageListData;\n }>(PUBLIC_CONTENT_PAGES_QUERY, {\n productId: config.productId,\n limit: options?.limit ?? 50,\n offset: options?.offset ?? 0,\n });\n\n if (result.errors?.length) {\n setError(result.errors[0]?.message ?? \"Unknown error\");\n setData(null);\n } else {\n setData(result.data?.publicContentPages ?? null);\n }\n } catch (err) {\n setError(\n err instanceof Error ? err.message : \"Failed to fetch content pages\",\n );\n setData(null);\n } finally {\n setLoading(false);\n }\n }, [client, config.productId, options?.limit, options?.offset]);\n\n useEffect(() => {\n fetchPages();\n }, [fetchPages]);\n\n return { data, loading, error, refetch: fetchPages };\n}\n\n/**\n * Content renderer component.\n *\n * - `markdown` (default): renders with the shared {@link Markdown} component\n * (react-markdown + remark-gfm + rehype-sanitize).\n * - `html`: sanitized with DOMPurify before rendering.\n * - anything else: plain-text fallback\n *\n * @example\n * ```tsx\n * <ContentRenderer content={page.content} format={page.contentFormat} />\n * ```\n */\nexport function ContentRenderer({\n content,\n format = \"markdown\",\n className,\n}: {\n content: string;\n format?: string;\n className?: string;\n}) {\n if (format === \"markdown\") {\n return <Markdown className={className}>{content}</Markdown>;\n }\n\n if (format === \"html\") {\n const sanitized = DOMPurify.sanitize(content);\n return (\n <div\n className={className}\n dangerouslySetInnerHTML={{ __html: sanitized }}\n />\n );\n }\n\n // Plain text fallback\n return (\n <div className={className}>\n <pre style={{ whiteSpace: \"pre-wrap\", fontFamily: \"inherit\" }}>\n {content}\n </pre>\n </div>\n );\n}\n"]}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/utils/site-chrome.ts
|
|
4
|
+
var FOOTERLESS_ROUTES = ["/explore"];
|
|
5
|
+
function normalizeRoutePath(pathname) {
|
|
6
|
+
const trimmed = pathname.toLowerCase().replace(/\/+$/, "");
|
|
7
|
+
return trimmed === "" ? "/" : trimmed;
|
|
8
|
+
}
|
|
9
|
+
function isFooterlessPath(pathname) {
|
|
10
|
+
return FOOTERLESS_ROUTES.includes(normalizeRoutePath(pathname));
|
|
11
|
+
}
|
|
12
|
+
function shouldRenderSiteFooter(pathname) {
|
|
13
|
+
return !isFooterlessPath(pathname);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
exports.FOOTERLESS_ROUTES = FOOTERLESS_ROUTES;
|
|
17
|
+
exports.isFooterlessPath = isFooterlessPath;
|
|
18
|
+
exports.normalizeRoutePath = normalizeRoutePath;
|
|
19
|
+
exports.shouldRenderSiteFooter = shouldRenderSiteFooter;
|
|
20
|
+
//# sourceMappingURL=chunk-5GMTBOO6.js.map
|
|
21
|
+
//# sourceMappingURL=chunk-5GMTBOO6.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/site-chrome.ts"],"names":[],"mappings":";;;AAiCO,IAAM,iBAAA,GAAuC,CAAC,UAAU;AAWxD,SAAS,mBAAmB,QAAA,EAA0B;AAC3D,EAAA,MAAM,UAAU,QAAA,CAAS,WAAA,EAAY,CAAE,OAAA,CAAQ,QAAQ,EAAE,CAAA;AACzD,EAAA,OAAO,OAAA,KAAY,KAAK,GAAA,GAAM,OAAA;AAChC;AASO,SAAS,iBAAiB,QAAA,EAA2B;AAC1D,EAAA,OAAO,iBAAA,CAAkB,QAAA,CAAS,kBAAA,CAAmB,QAAQ,CAAC,CAAA;AAChE;AAGO,SAAS,uBAAuB,QAAA,EAA2B;AAChE,EAAA,OAAO,CAAC,iBAAiB,QAAQ,CAAA;AACnC","file":"chunk-5GMTBOO6.js","sourcesContent":["/**\n * Which routes a host site should render WITHOUT its marketing footer.\n *\n * ## Why this lives in the SDK\n *\n * It was written 39 times. The footer belongs to each website, so the obvious\n * place for the rule is each website — and eight agents asked to add it produced\n * five different helpers (`footerless-routes.ts`, `footer-visibility.ts`,\n * `footer-routes.ts`, `explore-route.ts`, plus six copies inlined straight into\n * a layout). All five behave the same today. The next person to change the rule\n * has to find all five, and will not.\n *\n * The rule is really a property of the SDK's own surfaces: `/explore` is\n * footerless because `ExplorePage` sizes itself to one viewport, which is a fact\n * about this package, not about any site. So it is declared here, once, and the\n * sites ask.\n *\n * ## Why `/explore` and nothing else\n *\n * `ExplorePage` renders a full-viewport chat pane (`100dvh` minus the site\n * chrome above it), so anything after it is below the fold by construction. On a\n * phone these footers are 1100-1330px — roughly 70% of the document — and that\n * bulk is what allowed a restored scroll offset to open the page on nothing but\n * footer, with zero pixels of the chat on screen.\n *\n * The SDK's OTHER pages are the opposite case and must keep their footer:\n * `/concepts`, `/concept/:slug`, `/answers` and `/answer/:slug` are ordinary\n * indexed content pages whose footer carries the site's internal links.\n * Stripping it would cost hundreds of pages their internal linking, and nobody\n * would notice for weeks.\n */\n\n/** Routes that render without the site footer. Exact paths, not prefixes. */\nexport const FOOTERLESS_ROUTES: readonly string[] = [\"/explore\"];\n\n/**\n * Normalise a router pathname for exact comparison.\n *\n * React Router matches case-insensitively (`caseSensitive` defaults to false)\n * and tolerates a trailing slash, so `/Explore/` reaches the same route element\n * as `/explore` and has to be treated the same way here. Half the fleet's\n * hand-written versions compared case-sensitively and would have rendered a\n * footer under the chat pane on `/Explore`.\n */\nexport function normalizeRoutePath(pathname: string): string {\n const trimmed = pathname.toLowerCase().replace(/\\/+$/, \"\");\n return trimmed === \"\" ? \"/\" : trimmed;\n}\n\n/**\n * True when this pathname is one of the SDK's full-screen surfaces.\n *\n * Deliberately an exact-set test. `startsWith(\"/explore\")` would also swallow a\n * future `/explore-beta`, and `includes(\"explore\")` would swallow far more —\n * including `/concept/explore-your-data`, which is a real shape of concept slug.\n */\nexport function isFooterlessPath(pathname: string): boolean {\n return FOOTERLESS_ROUTES.includes(normalizeRoutePath(pathname));\n}\n\n/** Inverse of {@link isFooterlessPath}, for layouts that read better positively. */\nexport function shouldRenderSiteFooter(pathname: string): boolean {\n return !isFooterlessPath(pathname);\n}\n"]}
|