@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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/pricing-catalog.ts","../../src/client/security.ts","../../src/client/graphql-client.ts","../../src/client/provider.tsx","../../src/utils/product-slug.ts","../../src/product/product-provider.tsx","../../src/seo/page-head.tsx","../../src/utils/cn.ts","../../src/ui/button.tsx","../../src/components/pricing.tsx"],"names":["createContext","useContext","Helmet","jsx","twMerge","clsx","cva","React","Slot","useState","useEffect","useMemo","jsxs"],"mappings":"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","file":"pricing.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","const LOOPBACK_HOSTS = new Set([\"localhost\", \"127.0.0.1\", \"::1\"]);\nconst RESERVED_HEADERS = new Set([\n \"authorization\",\n \"cookie\",\n \"x-actor-id\",\n \"x-actor-type\",\n \"x-consent-granted-by\",\n \"x-consent-scope\",\n \"x-consent-granted-at\",\n \"x-consent-expires-at\",\n]);\n\nexport function validateGatewayUrl(value: string): string {\n let url: URL;\n\n try {\n url = new URL(value);\n } catch {\n throw new Error(\"Gateway URL must be a valid absolute URL\");\n }\n\n if (url.protocol === \"https:\") {\n return url.toString();\n }\n\n if (\n url.protocol === \"http:\" &&\n LOOPBACK_HOSTS.has(url.hostname.toLowerCase())\n ) {\n return url.toString();\n }\n\n throw new Error(\"Gateway URL must use HTTPS unless it targets localhost\");\n}\n\nexport function sanitizeHeaders(\n headers?: Record<string, string>,\n): Record<string, string> | undefined {\n if (!headers) return undefined;\n\n return Object.fromEntries(\n Object.entries(headers).filter(\n ([key]) => !RESERVED_HEADERS.has(key.toLowerCase()),\n ),\n );\n}\n\n/**\n * Hostnames whose Cloudflare Access application gates the GraphQL gateway.\n *\n * Only the alpha gateways are gated; the production gateways are public, so a token is never\n * attached there even if one is configured.\n */\nconst CF_ACCESS_GATED_HOSTS = new Set([\n \"alphagraphql.burdenoff.com\",\n \"alphagraphqlworkspaces.burdenoff.com\",\n]);\n\n/**\n * Service-token headers for a gated gateway, or nothing.\n *\n * Returns an empty object unless the target really is a gated host AND both halves of the token\n * are present — a half-configured token would send a malformed credential to every request.\n */\nexport function cfAccessHeaders(\n gatewayUrl: string,\n cfAccess?: { clientId?: string; clientSecret?: string },\n): Record<string, string> {\n const clientId = cfAccess?.clientId?.trim();\n const clientSecret = cfAccess?.clientSecret?.trim();\n if (!clientId || !clientSecret) return {};\n\n let host: string;\n try {\n host = new URL(gatewayUrl).hostname.toLowerCase();\n } catch {\n return {};\n }\n if (!CF_ACCESS_GATED_HOSTS.has(host)) return {};\n\n return {\n \"CF-Access-Client-Id\": clientId,\n \"CF-Access-Client-Secret\": clientSecret,\n };\n}\n","/**\n * Lightweight GraphQL client for the Burdenoff Web SDK.\n * Uses native fetch - zero external dependencies.\n * All calls are unauthenticated (public gateway operations only).\n */\n\nimport {\n cfAccessHeaders,\n sanitizeHeaders,\n validateGatewayUrl,\n} from \"./security\";\n\nexport interface WebSDKClientConfig {\n /** Global public gateway URL (e.g. \"https://api.burdenoff.com/global/graphql\") */\n gatewayUrl: string;\n /** Platform product ID for scoping operations */\n productId: string;\n /** Product slug (used for public product queries) */\n productSlug?: string;\n /** reCAPTCHA site key (if not fetched from product config) */\n recaptchaSiteKey?: string;\n /** Rybbit analytics site ID */\n rybbitSiteId?: string;\n /** Custom headers to include in all requests */\n headers?: Record<string, string>;\n /**\n * Cloudflare Access service token, for the ALPHA gateway hosts only.\n *\n * `alphagraphql.burdenoff.com` sits behind a Cloudflare Access application, so an\n * unauthenticated browser XHR is answered with a 302 to the Access login on a different\n * origin — which the browser reports as an opaque CORS failure rather than a 401. Attaching\n * the service-token headers is how every app shell already passes that gate (fe-libs\n * `installGatewayFetchRewrite`); the website SDK previously had no equivalent, so contact,\n * newsletter, waitlist and explore all failed on alpha.\n *\n * Vite only inlines `VITE_*` into APP source, never into a bundled library, so the host app\n * must read `import.meta.env` and pass the values in. Production gateways are not gated and\n * receive no headers.\n */\n cfAccess?: { clientId?: string; clientSecret?: string };\n /**\n * Per-request timeout in ms. Kept below the gateway's operation timeout so a\n * stalled request surfaces as an error the UI can render, instead of leaving\n * a page stuck on loading skeletons forever. Default 12000.\n */\n timeoutMs?: number;\n /**\n * Extra attempts for transient failures (timeout / network / 5xx / 429) on\n * QUERIES ONLY. Mutations are never retried — a retried contact-form or\n * waitlist submit would double-submit. Default 1 (2 attempts total).\n */\n maxRetries?: number;\n}\n\n/**\n * Per-call overrides for {@link WebSDKClient.query}.\n *\n * The defaults (5-minute response cache + in-flight deduplication) are right\n * for the read-once content queries most SDK components make, and wrong for a\n * poll loop: the second poll of the same `(query, variables)` pair would be\n * served straight from the cache and the UI would freeze on the first\n * response forever. Pass `{ cache: false }` for anything polled.\n */\nexport interface WebSDKQueryOptions {\n /**\n * Use the response cache and in-flight deduplication. Default `true`.\n * `false` skips the cache read, the dedupe map AND the cache write, so the\n * call always hits the network and never poisons a later cached read.\n */\n cache?: boolean;\n /** Override the client-wide request timeout for this call only. */\n timeoutMs?: number;\n}\n\n/** Per-call overrides for {@link WebSDKClient.mutate}. */\nexport interface WebSDKMutateOptions {\n /** Override the client-wide request timeout for this call only. */\n timeoutMs?: number;\n}\n\nexport interface GraphQLResponse<T = Record<string, unknown>> {\n data?: T;\n errors?: Array<{\n message: string;\n locations?: Array<{ line: number; column: number }>;\n path?: Array<string | number>;\n extensions?: Record<string, unknown>;\n }>;\n}\n\nexport class WebSDKClient {\n private config: WebSDKClientConfig;\n private queryCache = new Map<\n string,\n { data: GraphQLResponse<unknown>; ts: number }\n >();\n private inflight = new Map<string, Promise<GraphQLResponse<unknown>>>();\n private static CACHE_TTL = 5 * 60 * 1000; // 5 minutes\n private static MAX_CACHE_ENTRIES = 100;\n private static DEFAULT_TIMEOUT_MS = 12_000;\n private static DEFAULT_MAX_RETRIES = 1;\n private static RETRY_BASE_DELAY_MS = 400;\n\n constructor(config: WebSDKClientConfig) {\n this.config = {\n ...config,\n gatewayUrl: validateGatewayUrl(config.gatewayUrl),\n headers: sanitizeHeaders(config.headers),\n };\n }\n\n getConfig(): WebSDKClientConfig {\n return this.config;\n }\n\n /**\n * Execute a GraphQL query (with deduplication and caching).\n *\n * Pass `{ cache: false }` to bypass the cache entirely — required for poll\n * loops, where every tick must reach the network.\n */\n async query<T = Record<string, unknown>>(\n query: string,\n variables?: Record<string, unknown>,\n options?: WebSDKQueryOptions,\n ): Promise<GraphQLResponse<T>> {\n // Uncached path: no cache read, no in-flight dedupe, no cache write.\n // Deliberately bypasses the dedupe map too — two identical polls issued a\n // few hundred ms apart must both hit the network, otherwise the second one\n // resolves with the first one's (already stale) snapshot.\n if (options?.cache === false) {\n return this.request<T>(query, variables, true, options.timeoutMs);\n }\n\n const cacheKey = JSON.stringify({ query, variables });\n\n // Return cached response if still valid, evict if expired\n const cached = this.queryCache.get(cacheKey);\n if (cached) {\n if (Date.now() - cached.ts < WebSDKClient.CACHE_TTL) {\n return cached.data as GraphQLResponse<T>;\n }\n this.queryCache.delete(cacheKey);\n }\n\n // Deduplicate: if same query is already in-flight, reuse the promise\n const existing = this.inflight.get(cacheKey);\n if (existing) {\n return existing as Promise<GraphQLResponse<T>>;\n }\n\n const promise = this.request<T>(query, variables, true, options?.timeoutMs)\n .then((result) => {\n // Cache successful responses only\n if (result.data && !result.errors?.length) {\n if (this.queryCache.size >= WebSDKClient.MAX_CACHE_ENTRIES) {\n const oldestEntry = this.queryCache.keys().next().value;\n if (oldestEntry) {\n this.queryCache.delete(oldestEntry);\n }\n }\n this.queryCache.set(cacheKey, { data: result, ts: Date.now() });\n }\n return result;\n })\n .finally(() => {\n this.inflight.delete(cacheKey);\n });\n\n this.inflight.set(cacheKey, promise);\n return promise;\n }\n\n /**\n * Execute a GraphQL mutation\n */\n async mutate<T = Record<string, unknown>>(\n mutation: string,\n variables?: Record<string, unknown>,\n options?: WebSDKMutateOptions,\n ): Promise<GraphQLResponse<T>> {\n // retry=false: a retried mutation could double-submit (contact form,\n // waitlist signup, order). Only the timeout applies here.\n return this.request<T>(mutation, variables, false, options?.timeoutMs);\n }\n\n /** HTTP statuses worth another attempt — transient server/edge conditions. */\n private static isRetriableStatus(status: number): boolean {\n return status === 429 || status === 408 || (status >= 500 && status < 600);\n }\n\n private static sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n\n /**\n * Perform the HTTP call with a hard timeout. Every outcome resolves to a\n * GraphQLResponse — callers never hang, so a UI's `loading` state always\n * settles into data or a renderable error.\n */\n private async attempt<T>(\n query: string,\n variables: Record<string, unknown> | undefined,\n timeoutMs: number,\n ): Promise<{ res: GraphQLResponse<T>; retriable: boolean }> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n try {\n const response = await fetch(this.config.gatewayUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-product-id\": this.config.productId,\n ...this.config.headers,\n ...cfAccessHeaders(this.config.gatewayUrl, this.config.cfAccess),\n },\n body: JSON.stringify({ query, variables }),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n console.error(\n \"[WebSDK] GraphQL request failed\",\n response.status,\n response.statusText,\n );\n return {\n res: {\n errors: [\n {\n message: `HTTP ${response.status}: ${response.statusText}`,\n extensions: { code: `HTTP_${response.status}` },\n },\n ],\n },\n retriable: WebSDKClient.isRetriableStatus(response.status),\n };\n }\n\n return {\n res: (await response.json()) as GraphQLResponse<T>,\n retriable: false,\n };\n } catch (error) {\n const aborted =\n error instanceof Error &&\n (error.name === \"AbortError\" || controller.signal.aborted);\n const message = aborted\n ? `Request timed out after ${timeoutMs}ms`\n : error instanceof Error\n ? error.message\n : \"Network error\";\n console.error(\"[WebSDK] Network error\", message);\n return {\n res: {\n errors: [\n {\n message,\n extensions: { code: aborted ? \"TIMEOUT\" : \"NETWORK_ERROR\" },\n },\n ],\n },\n // Transport-level failures are worth one more try; a genuine outage\n // just fails twice quickly and still renders an error state.\n retriable: true,\n };\n } finally {\n clearTimeout(timer);\n }\n }\n\n private async request<T>(\n query: string,\n variables?: Record<string, unknown>,\n retry = true,\n timeoutOverrideMs?: number,\n ): Promise<GraphQLResponse<T>> {\n const timeoutMs =\n timeoutOverrideMs ??\n this.config.timeoutMs ??\n WebSDKClient.DEFAULT_TIMEOUT_MS;\n const maxRetries = retry\n ? (this.config.maxRetries ?? WebSDKClient.DEFAULT_MAX_RETRIES)\n : 0;\n\n let last: GraphQLResponse<T> | undefined;\n for (let i = 0; i <= maxRetries; i++) {\n const { res, retriable } = await this.attempt<T>(\n query,\n variables,\n timeoutMs,\n );\n if (!retriable || i === maxRetries) return res;\n last = res;\n // Small backoff with jitter so a burst of clients doesn't retry in lockstep.\n await WebSDKClient.sleep(\n WebSDKClient.RETRY_BASE_DELAY_MS * (i + 1) + Math.random() * 200,\n );\n }\n return last as GraphQLResponse<T>;\n }\n}\n","/**\n * React context provider for the Web SDK client.\n * Wraps the application with WebSDKClient configuration.\n */\n\"use client\";\n\nimport { createContext, useContext, useMemo } from \"react\";\nimport type { ReactNode } from \"react\";\n\nimport { resolveGatewayUrl } from \"./gateway-resolver\";\nimport { WebSDKClient, type WebSDKClientConfig } from \"./graphql-client\";\n\nconst WebSDKContext = createContext<WebSDKClient | null>(null);\n\nexport interface WebSDKProviderProps {\n /** SDK client configuration */\n config: WebSDKClientConfig;\n /** Child components */\n children: ReactNode;\n}\n\n/**\n * Provider component that makes the WebSDKClient available to all child components.\n *\n * @example\n * ```tsx\n * <WebSDKProvider config={{\n * gatewayUrl: \"https://api.burdenoff.com/global/graphql\",\n * productId: \"your-product-id\",\n * productSlug: \"your-product\",\n * }}>\n * <App />\n * </WebSDKProvider>\n * ```\n */\nexport function WebSDKProvider({ config, children }: WebSDKProviderProps) {\n const client = useMemo(() => {\n const resolvedConfig = {\n ...config,\n gatewayUrl: resolveGatewayUrl(config.gatewayUrl),\n };\n return new WebSDKClient(resolvedConfig);\n }, [config]);\n\n return (\n <WebSDKContext.Provider value={client}>{children}</WebSDKContext.Provider>\n );\n}\n\n/**\n * Hook to access the WebSDKClient instance.\n * Must be used within a WebSDKProvider.\n */\nexport function useWebSDK(): WebSDKClient {\n const client = useContext(WebSDKContext);\n if (!client) {\n throw new Error(\"useWebSDK must be used within a WebSDKProvider\");\n }\n return client;\n}\n\n/**\n * Hook to access the SDK configuration.\n * Useful for components that need config values like productId, recaptchaSiteKey, etc.\n */\nexport function useWebSDKConfig(): WebSDKClientConfig {\n const client = useWebSDK();\n return client.getConfig();\n}\n","/**\n * Product slug aliases for products whose machine slug is being renamed\n * (today: `botlit` → `botmanor`, see botmanor-migration PLAN.md Stage 0 / WP-E).\n *\n * - Readers accept both slugs: compare with {@link isSameProductSlug}, look up\n * with {@link productSlugVariants}.\n * - Writers emit {@link emitProductSlug}, which follows {@link PRODUCT_SLUG_EMIT}\n * (`\"current\"` since PLAN.md Stage 5a, so writers emit `botmanor`).\n *\n * Mirrors `@burdenoff/be-sdk/product-alias` (product-slug concern only) so the\n * browser bundle does not pull in the backend SDK.\n */\n\n/** One product whose slug is being renamed. Both values are lowercase. */\nexport interface ProductSlugRename {\n readonly legacy: string;\n readonly current: string;\n}\n\n/** Every product slug rename in flight. */\nexport const PRODUCT_SLUG_RENAMES: readonly ProductSlugRename[] = Object.freeze(\n [Object.freeze({ legacy: \"botlit\", current: \"botmanor\" })],\n);\n\n/** Legacy slug → current slug, e.g. `{ botlit: \"botmanor\" }`. */\nexport const PRODUCT_SLUG_ALIASES: Readonly<Record<string, string>> =\n Object.freeze(\n Object.fromEntries(PRODUCT_SLUG_RENAMES.map((r) => [r.legacy, r.current])),\n );\n\n/** Which spelling a writer emits. */\nexport type ProductSlugGeneration = \"legacy\" | \"current\";\n\n/**\n * Which slug writers emit. Flipped to `\"current\"` at PLAN.md Stage 5a: stored\n * product slugs are migrated to `botmanor`, and every reader accepts both.\n */\nexport const PRODUCT_SLUG_EMIT: Readonly<{\n productSlug: ProductSlugGeneration;\n}> = Object.freeze({ productSlug: \"current\" });\n\nfunction findRename(slug: string): ProductSlugRename | undefined {\n const wanted = slug.toLowerCase();\n return PRODUCT_SLUG_RENAMES.find(\n (r) => r.legacy === wanted || r.current === wanted,\n );\n}\n\n/** `botlit` (any case) → `botmanor`; every other slug is returned unchanged. */\nexport function normalizeProductSlug(slug: string): string;\nexport function normalizeProductSlug(\n slug: string | null | undefined,\n): string | null | undefined;\nexport function normalizeProductSlug(\n slug: string | null | undefined,\n): string | null | undefined {\n if (slug == null) return slug;\n return findRename(slug)?.current ?? slug;\n}\n\n/** Old spelling of a renamed slug (`botmanor` → `botlit`); every other slug unchanged. */\nexport function legacyProductSlug(slug: string): string {\n return findRename(slug)?.legacy ?? slug;\n}\n\n/** Every slug a reader must accept for `slug`, the given one first. */\nexport function productSlugVariants(slug: string): string[] {\n // Canonical lowercase keys come after the given spelling, so \"BotLit\" yields both keys.\n return [\n ...new Set([slug, normalizeProductSlug(slug), legacyProductSlug(slug)]),\n ];\n}\n\n/** True when `a` and `b` are the same product under the rename. */\nexport function isSameProductSlug(\n a: string | null | undefined,\n b: string | null | undefined,\n): boolean {\n if (a == null || b == null) return a === b;\n return a === b || normalizeProductSlug(a) === normalizeProductSlug(b);\n}\n\n/** The slug writers should use today, from either spelling. */\nexport function emitProductSlug(slug: string): string {\n return PRODUCT_SLUG_EMIT.productSlug === \"current\"\n ? normalizeProductSlug(slug)\n : legacyProductSlug(slug);\n}\n","/**\n * Product Provider\n *\n * React context and hooks for accessing product metadata from the global tenant service.\n * Fetches product info (name, logo, SEO, contact, reCAPTCHA key, etc.) on mount\n * and provides it to all child components.\n */\n\"use client\";\n\nimport {\n createContext,\n useContext,\n useState,\n useLayoutEffect,\n useCallback,\n useMemo,\n} from \"react\";\nimport type { ReactNode } from \"react\";\n\nimport { useWebSDK, useWebSDKConfig } from \"../client/provider\";\nimport { productSlugVariants } from \"../utils/product-slug\";\n\nimport type { WebSDKClient } from \"../client/graphql-client\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface ProductData {\n id: string;\n name: string;\n slug: string;\n description?: string | null;\n status: string;\n logoUrl?: string | null;\n website?: string | null;\n appUrl?: string | null;\n docsUrl?: string | null;\n contactEmail?: string | null;\n supportEmail?: string | null;\n contactPhone?: string | null;\n contactAddress?: string | null;\n seoTitle?: string | null;\n seoDescription?: string | null;\n seoKeywords?: string | null;\n ogImage?: string | null;\n socialLinks?: Record<string, string> | null;\n /**\n * Resource links for the website footer \"Resources\" column\n * (JSON: { docs, community, careers, team, companies, ... }).\n * Same shape as socialLinks. Free-form keys; the ResourceLinks\n * component renders known keys and silently ignores the rest.\n * Backed by Product.resourceLinks on the backend (global-tenant-svc).\n */\n resourceLinks?: Record<string, string> | null;\n recaptchaSiteKey?: string | null;\n rybbitSiteId?: string | null;\n notificationEmail?: string | null;\n metadata?: Record<string, unknown> | null;\n}\n\nexport interface ProductProviderProps {\n /** Child components */\n children: ReactNode;\n /** Optional fallback product data (used while loading) */\n fallback?: Partial<ProductData>;\n}\n\n// ============================================================================\n// GraphQL Query\n// ============================================================================\n\n/**\n * Body of fields requested for every product fetch — shared by the primary\n * query and the legacy-backend fallback below. Kept as a string fragment\n * (not a real GraphQL `fragment` block) so we can append/strip\n * `resourceLinks` without touching anything else.\n */\nconst PRODUCT_FIELDS_BASE = `\n id\n name\n slug\n description\n status\n logoUrl\n website\n appUrl\n docsUrl\n contactEmail\n supportEmail\n contactPhone\n contactAddress\n seoTitle\n seoDescription\n seoKeywords\n ogImage\n socialLinks\n recaptchaSiteKey\n rybbitSiteId\n notificationEmail\n metadata\n`;\n\n/**\n * Primary query — includes the recently-added `resourceLinks` field.\n * Backends that have run the corresponding `global-tenant-svc` migration\n * (PR #64) expose this field; older ones don't, in which case\n * GraphQL validation rejects the whole query with\n * `Cannot query field \"resourceLinks\" on type \"PublicProduct\"`. The fetch\n * helper detects that specific error and transparently retries the\n * fallback below, so the SDK works on both pre- and post-migration\n * deployments without consumer-side gating.\n */\nconst PUBLIC_PRODUCT_QUERY = `\n query PublicProduct($slug: String!) {\n publicProduct(slug: $slug) {\n ${PRODUCT_FIELDS_BASE}\n resourceLinks\n }\n }\n`;\n\n/**\n * Fallback query for older backend deployments. Identical to the primary\n * minus `resourceLinks`. Consumers see `product.resourceLinks === undefined`\n * and any consumer (e.g. `<ResourceLinks/>`) falls through to prop / default\n * resolution paths.\n */\nconst PUBLIC_PRODUCT_QUERY_FALLBACK = `\n query PublicProductFallback($slug: String!) {\n publicProduct(slug: $slug) {\n ${PRODUCT_FIELDS_BASE}\n }\n }\n`;\n\n// ============================================================================\n// Context\n// ============================================================================\n\ninterface ProductContextValue {\n product: ProductData | null;\n loading: boolean;\n error: string | null;\n hasProvider: boolean;\n refetch: () => void;\n}\n\nconst ProductContext = createContext<ProductContextValue>({\n product: null,\n loading: true,\n error: null,\n hasProvider: false,\n refetch: () => {},\n});\n\n// ============================================================================\n// Provider\n// ============================================================================\n\n/** Result of {@link fetchPublicProduct}. */\nexport interface PublicProductFetchResult {\n product: ProductData | null;\n error: string | null;\n}\n\nasync function queryPublicProduct(\n client: Pick<WebSDKClient, \"query\">,\n slug: string,\n) {\n const result = await client.query<{ publicProduct: ProductData | null }>(\n PUBLIC_PRODUCT_QUERY,\n { slug },\n );\n\n // Backwards-compat fallback: if the backend's `publicProduct` schema\n // doesn't yet expose `resourceLinks` (deployed before\n // `global-tenant-svc` PR #64), the primary query fails validation\n // with a \"Cannot query field …\" error and `result.data` is null.\n // Retry the fallback query that omits `resourceLinks`; consumers\n // gracefully see `product.resourceLinks === undefined` and fall\n // through their own resolution paths.\n const schemaMissingResourceLinks = result.errors?.some((e) =>\n /Cannot query field [\"']?resourceLinks[\"']?/i.test(e?.message ?? \"\"),\n );\n if (!schemaMissingResourceLinks) return result;\n return client.query<{ publicProduct: ProductData | null }>(\n PUBLIC_PRODUCT_QUERY_FALLBACK,\n { slug },\n );\n}\n\n/**\n * Fetch a product's public metadata by slug.\n *\n * A renamed product (`botlit` → `botmanor`) is looked up under the configured\n * slug first, then under its twin, so a site keeps resolving its product on\n * both sides of the tenant-service slug migration. The first error stops the\n * lookup; \"not found\" under every spelling returns `{ product: null, error: null }`.\n */\nexport async function fetchPublicProduct(\n client: Pick<WebSDKClient, \"query\">,\n slug: string,\n): Promise<PublicProductFetchResult> {\n for (const candidate of productSlugVariants(slug)) {\n const result = await queryPublicProduct(client, candidate);\n if (result.errors?.length) {\n return {\n product: null,\n error: result.errors[0]?.message ?? \"Unknown error\",\n };\n }\n if (result.data?.publicProduct) {\n return { product: result.data.publicProduct, error: null };\n }\n }\n return { product: null, error: null };\n}\n\n/**\n * ProductProvider fetches and provides product metadata to child components.\n * Uses the product slug from WebSDKConfig to fetch from the public API.\n *\n * @example\n * ```tsx\n * <WebSDKProvider config={sdkConfig}>\n * <ProductProvider>\n * <App />\n * </ProductProvider>\n * </WebSDKProvider>\n * ```\n */\nexport function ProductProvider({ children, fallback }: ProductProviderProps) {\n const client = useWebSDK();\n const config = useWebSDKConfig();\n const [product, setProduct] = useState<ProductData | null>(\n fallback ? (fallback as ProductData) : null,\n );\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const fetchProduct = useCallback(async () => {\n if (!config.productSlug) {\n setLoading(false);\n return;\n }\n\n setLoading(true);\n setError(null);\n\n try {\n const outcome = await fetchPublicProduct(client, config.productSlug);\n if (outcome.error) {\n setError(outcome.error);\n } else if (outcome.product) {\n setProduct(outcome.product);\n }\n } catch (err) {\n setError(err instanceof Error ? err.message : \"Failed to fetch product\");\n } finally {\n setLoading(false);\n }\n }, [client, config.productSlug]);\n\n // Use useLayoutEffect for earliest possible fetch after commit (before paint).\n // Runs when slug or client changes. Client-side query cache + dedup ensures\n // no duplicate network requests even if React StrictMode fires this twice.\n useLayoutEffect(() => {\n fetchProduct();\n }, [fetchProduct]);\n\n const value = useMemo(\n () => ({\n product,\n loading,\n error,\n hasProvider: true,\n refetch: fetchProduct,\n }),\n [product, loading, error, fetchProduct],\n );\n\n return (\n <ProductContext.Provider value={value}>{children}</ProductContext.Provider>\n );\n}\n\n// ============================================================================\n// Hooks\n// ============================================================================\n\n/**\n * Hook to access the current product data.\n *\n * @example\n * ```tsx\n * const { product, loading } = useProduct();\n * if (loading) return <Loading />;\n * return <h1>{product?.name}</h1>;\n * ```\n */\nexport function useProduct(): ProductContextValue {\n return useContext(ProductContext);\n}\n\n/**\n * Hook to get product-specific configuration.\n * Merges SDK config with fetched product data.\n * Useful for getting reCAPTCHA keys, analytics IDs, etc.\n *\n * @example\n * ```tsx\n * const { recaptchaSiteKey, rybbitSiteId } = useProductConfig();\n * ```\n */\nexport function useProductConfig(): {\n recaptchaSiteKey: string | null;\n rybbitSiteId: string | null;\n contactEmail: string | null;\n supportEmail: string | null;\n notificationEmail: string | null;\n productName: string | null;\n logoUrl: string | null;\n} {\n const { product } = useProduct();\n const config = useWebSDKConfig();\n\n return {\n // Prefer product-level config, fall back to SDK config\n recaptchaSiteKey:\n product?.recaptchaSiteKey ?? config.recaptchaSiteKey ?? null,\n rybbitSiteId: product?.rybbitSiteId ?? config.rybbitSiteId ?? null,\n contactEmail: product?.contactEmail ?? null,\n supportEmail: product?.supportEmail ?? null,\n notificationEmail:\n product?.notificationEmail ?? product?.contactEmail ?? null,\n productName: product?.name ?? null,\n logoUrl: product?.logoUrl ?? null,\n };\n}\n","/**\n * PageHead Component\n * Manages dynamic meta tags for SEO optimization on a per-page basis.\n * Supports JSON-LD structured data, Open Graph, Twitter Cards, favicons, and robots directives.\n *\n * @example\n * ```tsx\n * <PageHead\n * title=\"About Us\"\n * description=\"Learn about our mission and team\"\n * productName=\"MyProduct\"\n * keywords=\"about, team, mission\"\n * image=\"/og-about.png\"\n * url=\"https://myproduct.com/about\"\n * favicon=\"/favicon.ico\"\n * robots=\"index, follow\"\n * locale=\"en_US\"\n * twitterHandle=\"@burdenoff\"\n * themeColor=\"#1a1a2e\"\n * structuredData={{\n * \"@context\": \"https://schema.org\",\n * \"@type\": \"WebPage\",\n * name: \"About Us\",\n * }}\n * />\n * ```\n */\n\nimport { Helmet } from \"react-helmet-async\";\n\nexport interface PageHeadProps {\n /** Page title */\n title: string;\n /** Meta description */\n description?: string;\n /** Product/brand name (appended to title) */\n productName?: string;\n /** SEO keywords */\n keywords?: string;\n /** Open Graph image URL */\n image?: string;\n /** Page canonical URL */\n url?: string;\n /** Favicon URL */\n favicon?: string;\n /** Robots directive (e.g., \"index, follow\") */\n robots?: string;\n /** Open Graph locale (e.g., \"en_US\") */\n locale?: string;\n /** Twitter handle (e.g., \"@burdenoff\") */\n twitterHandle?: string;\n /** Theme color for mobile browsers */\n themeColor?: string;\n /**\n * Value for `<meta name=\"author\">`. Omitted by default.\n *\n * Each product site already declares its author in `index.html` as the\n * legal entity that publicly holds that product (BUR-SOP-029, \"Holder is\n * what you publish\"), and that entity differs between products. A default\n * here would add a second, conflicting author tag to every route, so the\n * tag is only rendered when a site passes the value explicitly.\n */\n author?: string;\n /** JSON-LD structured data objects */\n structuredData?: Record<string, unknown> | Array<Record<string, unknown>>;\n /** Additional meta tags */\n additionalMeta?: Array<{\n name?: string;\n property?: string;\n content: string;\n }>;\n}\n\n/**\n * Build a self-referencing canonical URL from the current location.\n *\n * Query strings and hashes are dropped (they never identify a distinct\n * canonical document here) and the trailing slash is normalised away for\n * everything but the site root. Returns `undefined` outside a DOM so the\n * component degrades to its previous behaviour under plain string rendering.\n */\nfunction canonicalFromLocation(): string | undefined {\n if (typeof window === \"undefined\" || !window.location) return undefined;\n const { origin, pathname } = window.location;\n if (!origin || origin === \"null\") return undefined;\n const path = pathname === \"/\" ? \"/\" : pathname.replace(/\\/+$/, \"\");\n return `${origin}${path}`;\n}\n\n/**\n * Absolutise an Open Graph image URL.\n *\n * Already-absolute URLs pass through. A root-relative path resolves against\n * the origin, and any other relative path against the page URL. Without a\n * resolvable base the original value is returned unchanged.\n */\nfunction resolveImageUrl(\n image: string | undefined,\n base: string | undefined,\n): string | undefined {\n if (!image || /^https?:\\/\\//i.test(image)) return image;\n if (!base) return image;\n try {\n return new URL(image, base).toString();\n } catch {\n return image;\n }\n}\n\n/**\n * JSON-LD as the body of a `<script>` element.\n *\n * `JSON.stringify` leaves `<` alone, and a script body ends at the first `</script` whatever\n * the quoting around it. The prerender serialises the live DOM with script text verbatim,\n * so a `</script><script>…` inside any string field (a caption, a title) would close the\n * JSON-LD block in the static HTML and run what follows. `\\u003c` is the same character to\n * every JSON parser; U+2028 / U+2029 are escaped for engines that still read them as line\n * breaks.\n */\nfunction serializeJsonLd(data: Record<string, unknown>): string {\n return JSON.stringify(data)\n .replace(/</g, \"\\\\u003c\")\n .replace(/\\u2028/g, \"\\\\u2028\")\n .replace(/\\u2029/g, \"\\\\u2029\");\n}\n\nexport function PageHead({\n title,\n description,\n productName,\n keywords,\n image,\n url,\n favicon,\n robots = \"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\",\n locale = \"en_US\",\n twitterHandle,\n themeColor = \"#000000\",\n author,\n structuredData,\n additionalMeta,\n}: PageHeadProps) {\n const fullTitle =\n productName && !title.includes(productName)\n ? `${title} | ${productName}`\n : title;\n\n // Self-referencing canonical by default.\n //\n // Without this, a route that renders PageHead but omits `url` emits no\n // canonical at all, so the static `<link rel=\"canonical\">` in index.html\n // survives and every such page declares itself a duplicate of the home\n // page — Google then drops /pricing, /privacy, /terms and friends from the\n // index. Deriving it from the current location keeps each page pointing at\n // itself; callers that genuinely need a cross-canonical still pass `url`.\n //\n // During prerender the DOM location already reflects the route being\n // rendered, so this resolves correctly ahead of hydration.\n const resolvedUrl = url ?? canonicalFromLocation();\n\n // Root-relative images (\"/og.png\") belong to the ORIGIN, not to the current\n // path — concatenating them onto the page URL yields /features/og.png and a\n // broken share card. Anything else is resolved relative to the page itself.\n const fullImageUrl = resolveImageUrl(image, resolvedUrl);\n\n // Normalize structured data to an array\n const structuredDataItems: Array<Record<string, unknown>> = structuredData\n ? Array.isArray(structuredData)\n ? structuredData\n : [structuredData]\n : [];\n\n // Every conditional below is a `? … : null` ternary, never `x && <el />`.\n // `&&` forwards the left operand when it is falsy, so an empty-string prop\n // (keywords is legitimately \"\" on generated answer pages) would reach Helmet\n // as a string child. react-helmet-async skips any falsy child, but its\n // maintained fork @dr.pogodin/react-helmet skips only null/undefined and\n // throws `\"string\" is not a valid <Helmet> descendant` — which crashes the\n // whole page at render time. `null` is skipped by both.\n return (\n <Helmet>\n {/* Primary Meta Tags */}\n <title>{fullTitle}</title>\n <meta name=\"title\" content={fullTitle} />\n {description ? <meta name=\"description\" content={description} /> : null}\n {keywords ? <meta name=\"keywords\" content={keywords} /> : null}\n {author ? <meta name=\"author\" content={author} /> : null}\n <meta name=\"robots\" content={robots} />\n\n {/* Canonical URL */}\n {resolvedUrl ? <link rel=\"canonical\" href={resolvedUrl} /> : null}\n\n {/* Favicon */}\n {favicon ? <link rel=\"icon\" href={favicon} /> : null}\n\n {/* Open Graph / Facebook */}\n <meta property=\"og:type\" content=\"website\" />\n <meta property=\"og:title\" content={fullTitle} />\n {description ? (\n <meta property=\"og:description\" content={description} />\n ) : null}\n {fullImageUrl ? (\n <meta property=\"og:image\" content={fullImageUrl} />\n ) : null}\n {resolvedUrl ? <meta property=\"og:url\" content={resolvedUrl} /> : null}\n <meta property=\"og:locale\" content={locale} />\n {productName ? (\n <meta property=\"og:site_name\" content={productName} />\n ) : null}\n\n {/* Twitter Card */}\n <meta name=\"twitter:card\" content=\"summary_large_image\" />\n <meta name=\"twitter:title\" content={fullTitle} />\n {description ? (\n <meta name=\"twitter:description\" content={description} />\n ) : null}\n {fullImageUrl ? (\n <meta name=\"twitter:image\" content={fullImageUrl} />\n ) : null}\n {twitterHandle ? (\n <meta name=\"twitter:creator\" content={twitterHandle} />\n ) : null}\n\n {/* Theme Color */}\n <meta name=\"theme-color\" content={themeColor} />\n\n {/* JSON-LD Structured Data */}\n {structuredDataItems.map((data, index) => (\n <script key={`ld-${index}`} type=\"application/ld+json\">\n {serializeJsonLd(data)}\n </script>\n ))}\n\n {/* Additional Meta Tags */}\n {additionalMeta?.map((meta, index) => (\n <meta\n key={`meta-${index}`}\n {...(meta.name ? { name: meta.name } : {})}\n {...(meta.property ? { property: meta.property } : {})}\n content={meta.content}\n />\n ))}\n </Helmet>\n );\n}\n","/**\n * Utility function to merge Tailwind CSS classes\n * Combines clsx and tailwind-merge for optimal class handling\n */\n\nimport { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Merge multiple class names intelligently\n * - Handles conditional classes via clsx\n * - Deduplicates and resolves Tailwind conflicts via tailwind-merge\n *\n * @example\n * ```ts\n * cn('px-4 py-2', 'bg-blue-500', { 'text-white': isActive })\n * // => 'px-4 py-2 bg-blue-500 text-white' (if isActive is true)\n * ```\n */\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n","import * as React from \"react\";\n\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\n\nimport { cn } from \"../utils/cn\";\n\nconst buttonVariants = cva(\n \"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0\",\n {\n variants: {\n variant: {\n default:\n \"bg-primary text-primary-foreground shadow hover:bg-primary/90\",\n destructive:\n \"bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90\",\n outline:\n \"border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground\",\n secondary:\n \"bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80\",\n ghost: \"hover:bg-accent hover:text-accent-foreground\",\n link: \"text-primary underline-offset-4 hover:underline\",\n },\n size: {\n default: \"h-9 px-4 py-2\",\n sm: \"h-8 rounded-md px-3 text-xs\",\n lg: \"h-10 rounded-md px-8\",\n icon: \"h-9 w-9\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n },\n);\n\nexport interface ButtonProps\n extends\n React.ButtonHTMLAttributes<HTMLButtonElement>,\n VariantProps<typeof buttonVariants> {\n asChild?: boolean;\n}\n\nconst Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n ({ className, variant, size, asChild = false, ...props }, ref) => {\n const Comp = asChild ? Slot : \"button\";\n return (\n <Comp\n className={cn(buttonVariants({ variant, size, className }))}\n ref={ref}\n {...props}\n />\n );\n },\n);\nButton.displayName = \"Button\";\n\nexport { Button, buttonVariants };\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"]}
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"pricing.js"}
|