@burdenoff/website-sdk 2026.926.1 → 2026.927.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/components/contact.js.map +1 -1
  2. package/dist/components/contact.mjs.map +1 -1
  3. package/dist/components/explore.js +1 -1
  4. package/dist/components/explore.js.map +1 -1
  5. package/dist/components/explore.mjs +1 -1
  6. package/dist/components/explore.mjs.map +1 -1
  7. package/dist/components/newsletter.js.map +1 -1
  8. package/dist/components/newsletter.mjs.map +1 -1
  9. package/dist/components/partners.js.map +1 -1
  10. package/dist/components/partners.mjs.map +1 -1
  11. package/dist/components/resource-links.js.map +1 -1
  12. package/dist/components/resource-links.mjs.map +1 -1
  13. package/dist/components/social-links.js.map +1 -1
  14. package/dist/components/social-links.mjs.map +1 -1
  15. package/dist/components/waitlist.js.map +1 -1
  16. package/dist/components/waitlist.mjs.map +1 -1
  17. package/dist/content/index.js.map +1 -1
  18. package/dist/content/index.mjs.map +1 -1
  19. package/dist/hooks/index.js +1 -1
  20. package/dist/hooks/index.js.map +1 -1
  21. package/dist/hooks/index.mjs +1 -1
  22. package/dist/hooks/index.mjs.map +1 -1
  23. package/dist/index.js +1 -1
  24. package/dist/index.js.map +1 -1
  25. package/dist/index.mjs +1 -1
  26. package/dist/index.mjs.map +1 -1
  27. package/dist/product/index.js.map +1 -1
  28. package/dist/product/index.mjs.map +1 -1
  29. package/dist/store/index.js.map +1 -1
  30. package/dist/store/index.mjs.map +1 -1
  31. package/dist/utils/index.d.mts +6 -4
  32. package/dist/utils/index.d.ts +6 -4
  33. package/dist/utils/index.js +1 -1
  34. package/dist/utils/index.js.map +1 -1
  35. package/dist/utils/index.mjs +1 -1
  36. package/dist/utils/index.mjs.map +1 -1
  37. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/client/security.ts","../../src/client/graphql-client.ts","../../src/client/provider.tsx","../../src/components/explore-types.ts","../../src/utils/product-slug.ts","../../src/data/products.ts","../../src/utils/recaptcha-v3.ts","../../src/hooks/use-explore-chat.ts","../../src/hooks/use-speech-input.ts","../../src/hooks/use-speech-output.ts","../../src/hooks/use-progressive-reveal.ts"],"names":["createContext","useContext","useMemo","useState","useRef","useCallback","useEffect","open","id"],"mappings":"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","file":"index.js","sourcesContent":["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 * Explore chat — TypeScript mirror of the public `/explore` GraphQL contract.\n *\n * Canonical source: `~/products/dev/platform/context/explore/CONTRACT.md` §1\n * (global-support-svc, served through the global PUBLIC gateway with\n * `@rbac(public: true, scopeType: \"global\")`).\n *\n * Nothing in this file may drift from that document. If a shape here looks\n * wrong, fix CONTRACT.md first and tell the backend workstreams — never patch\n * it locally.\n *\n * Nullability note: the contract marks `title`, `description`, `product`,\n * `imageUrl`, `partialContent`, `errorCode`, `completedAt`, `productSlug`,\n * `recaptchaSiteKey` and `remainingToday` as nullable, so every one of them is\n * `?: T | null` here — a GraphQL `null` and an omitted selection both have to\n * be representable.\n */\n\n// ============================================================================\n// Enums (string unions — the wire format is the enum name)\n// ============================================================================\n\nexport type ExploreMessageRole = \"USER\" | \"ASSISTANT\";\n\nexport type ExploreMessageStatus =\n \"PENDING\" | \"RUNNING\" | \"COMPLETED\" | \"FAILED\";\n\n/** `enum ExploreFeedbackRating` — the visitor's verdict on one answer. */\nexport type ExploreFeedbackRating = \"HELPFUL\" | \"NOT_HELPFUL\";\n\n/**\n * The reasons offered alongside `NOT_HELPFUL`, in the order the chooser shows them.\n *\n * `reason` is `String` on the wire, not an enum, so this list is the SDK's half of the\n * agreement rather than something the schema enforces — the same treatment\n * {@link ExploreCtaKind} gets. Use the union when you are *producing* a reason; read one\n * back as a plain string.\n */\nexport const EXPLORE_FEEDBACK_REASONS = [\n \"INACCURATE\",\n \"INCOMPLETE\",\n \"OFF_TOPIC\",\n \"HARD_TO_FOLLOW\",\n \"OTHER\",\n] as const;\n\nexport type ExploreFeedbackReason = (typeof EXPLORE_FEEDBACK_REASONS)[number];\n\n/**\n * The `ExploreCta.kind` values enumerated in CONTRACT.md §1.\n *\n * `ExploreCtaLink.kind` is deliberately typed `string`, not this union: the\n * field is `String!` on the wire, and a kind added server-side must render\n * (falling back to the neutral CTA style) rather than break the build of 38\n * websites. Use this union when you are *producing* a kind.\n */\nexport type ExploreCtaKind =\n | \"CONTACT\"\n | \"PARTNERS\"\n | \"PRICING\"\n | \"SERVICES\"\n | \"WAITLIST\"\n | \"STORE\"\n | \"APP\"\n | \"DOCS\"\n | \"OTHER\";\n\n/**\n * Error codes surfaced through `extensions.code`. `HTTP_429` is what the\n * gateway emits when it rate-limits at the edge; the SDK client also produces\n * `TIMEOUT` / `NETWORK_ERROR` for transport failures.\n */\nexport type ExploreErrorCode =\n | \"EXPLORE_DISABLED\"\n | \"RATE_LIMITED\"\n | \"EXPLORE_GLOBAL_LIMIT\"\n | \"CAPTCHA_REQUIRED\"\n | \"CAPTCHA_FAILED\"\n | \"MESSAGE_TOO_LONG\"\n | \"MESSAGE_EMPTY\"\n | \"CONVERSATION_LIMIT\"\n | \"CONVERSATION_BUSY\"\n | \"UPSTREAM_UNAVAILABLE\"\n | \"NOT_FOUND\"\n // Handover-only. `INVALID_EMAIL` is the server's verdict on an address the client's own\n // pre-flight let through, and has to be shown rather than folded into a generic failure.\n | \"INVALID_EMAIL\"\n | \"ESCALATION_FAILED\"\n | \"HTTP_429\"\n | \"TIMEOUT\"\n | \"NETWORK_ERROR\";\n\n// ============================================================================\n// Object types\n// ============================================================================\n\n/** `type ExploreReference` — a cited source page for an answer. */\nexport interface ExploreReference {\n url: string;\n title?: string | null;\n description?: string | null;\n product?: string | null;\n imageUrl?: string | null;\n /** 1-based citation number; the answer text carries a matching `[n]` marker. */\n index?: number | null;\n}\n\n/**\n * `type ExploreCta` — a suggested next step attached to an answer.\n *\n * Named `ExploreCtaLink` in the SDK because `ExploreCta` is taken by the\n * exported nav component (`<ExploreCta variant=\"header\" />`). Same shape,\n * different name; the GraphQL type is unchanged.\n */\nexport interface ExploreCtaLink {\n /** One of {@link ExploreCtaKind}; unknown values render with the neutral style. */\n kind: string;\n label: string;\n url: string;\n product?: string | null;\n}\n\n/**\n * `type ExploreFeedback` — what is on record for one answer, as the server holds it.\n *\n * A rating REPLACES the previous one (the server upserts on `messageId`), so this is the\n * visitor's current opinion and never a history. `reason` is null for `HELPFUL` by\n * definition, and typed `string` rather than the union because it is `String` on the wire —\n * a reason added server-side must read back, not break the build of 38 websites.\n */\nexport interface ExploreFeedback {\n rating: ExploreFeedbackRating;\n /** One of {@link EXPLORE_FEEDBACK_REASONS}. Null for `HELPFUL`. */\n reason?: string | null;\n comment?: string | null;\n createdAt: string;\n}\n\n/** `type ExploreMessage`. `content` is `\"\"` while the turn is PENDING. */\nexport interface ExploreMessage {\n id: string;\n conversationId: string;\n role: ExploreMessageRole;\n status: ExploreMessageStatus;\n /** Final answer, markdown. Empty string until the turn completes. */\n content: string;\n /** Streaming preview while PENDING/RUNNING. */\n partialContent?: string | null;\n /** Human-readable progress lines, e.g. \"Searching vibecontrols…\". */\n activity: string[];\n references: ExploreReference[];\n ctas: ExploreCtaLink[];\n /** Up to three suggested next questions, derived from what the answer covered. */\n followUps?: string[] | null;\n /**\n * False when the corpus could not answer. A grounded answer always cites, so an answer\n * with no source is the honest \"we do not have this\" signal rather than a hedge.\n */\n answered?: boolean | null;\n /** Files the visitor attached to this turn. Present on USER messages only. */\n attachments?: ExploreAttachment[] | null;\n /**\n * The rating this visitor already gave this answer, or null for an unrated one.\n *\n * Selected only by the reads scoped to a `conversationToken`, so a polled or restored\n * transcript shows what was already rated instead of offering the row again. It is always\n * null on a SHARED transcript — that read is performed by someone else, and the rater's\n * verdict and free-text note are not theirs to receive. See\n * `EXPLORE_OWN_MESSAGE_FIELDS`.\n */\n feedback?: ExploreFeedback | null;\n errorCode?: string | null;\n createdAt: string;\n completedAt?: string | null;\n}\n\n/** `type ExploreConversation`. `token` is the client's only handle. */\nexport interface ExploreConversation {\n id: string;\n token: string;\n productSlug?: string | null;\n turnCount: number;\n createdAt: string;\n}\n\n/** `type ExploreProduct` — one product the harness can answer about. */\nexport interface ExploreProduct {\n slug: string;\n name: string;\n tagline?: string | null;\n website: string;\n}\n\n/** `type ExploreLimits` — server-owned caps the composer enforces locally. */\nexport interface ExploreLimits {\n maxMessageLength: number;\n maxTurnsPerConversation: number;\n messagesPerDay: number;\n historyTurns: number;\n}\n\n/**\n * `type ExploreConceptHighlight` — one published concept illustration, offered\n * as a way into the conversation before the visitor has typed anything.\n *\n * Every field is non-null on the wire: a highlight with no picture or no\n * destination is not a highlight, so the server drops it rather than sending a\n * half-built one. `product` is the slug, `productName` the display casing the\n * product's own pages use.\n *\n * The pictures are the published concept mockups — illustrations of an idea,\n * not screenshots of shipping software — which is why every surface that shows\n * one also labels it (`ExploreStrings.conceptIllustration`).\n */\nexport interface ExploreConceptHighlight {\n product: string;\n productName: string;\n title: string;\n url: string;\n imageUrl: string;\n alt: string;\n}\n\n/**\n * `type ExploreCatalog` — everything the page needs before the first message.\n *\n * `captchaRequired` + `recaptchaSiteKey` are what turn reCAPTCHA v3 on: it is\n * a backend secret change, and no website redeploy is involved.\n */\nexport interface ExploreCatalog {\n enabled: boolean;\n products: ExploreProduct[];\n examplePrompts: string[];\n welcomeTitle: string;\n welcomeBody: string;\n limits: ExploreLimits;\n captchaRequired: boolean;\n recaptchaSiteKey?: string | null;\n /**\n * At most 8, the site's own product first. Non-null and non-nullable per the\n * contract, so a gateway that knows the field always sends an array.\n *\n * A gateway that does NOT know it yet does not answer without it — it refuses\n * the whole document (`GRAPHQL_VALIDATION_FAILED`), which is why the hook\n * retries with `EXPLORE_CATALOG_QUERY_WITHOUT_HIGHLIGHTS` instead of falling\n * back to the built-in catalog. That retry is the one response that reaches\n * `normalizeCatalog` with this field absent, and the hardening there is what\n * keeps `catalog.conceptHighlights.filter(...)` from throwing in render.\n */\n conceptHighlights: ExploreConceptHighlight[];\n}\n\n/** `input SendExploreMessageInput`. */\nexport interface SendExploreMessageInput {\n /** Omit to start a new conversation. */\n conversationToken?: string;\n /** The site the visitor is on — a soft hint, not a hard filter. */\n productSlug?: string;\n message: string;\n /** Only sent when `catalog.captchaRequired`. */\n recaptchaToken?: string;\n locale?: string;\n pageUrl?: string;\n /**\n * Files attached as extra context. At most 5, each at most 10 MB — re-enforced server-side,\n * so these are the limits the composer shows, not the limits that hold.\n */\n attachments?: ExploreAttachmentInput[];\n}\n\n/** `input ExploreAttachmentInput`. `textContent` is null for a format the browser cannot read. */\nexport interface ExploreAttachmentInput {\n name: string;\n mimeType: string;\n size: number;\n textContent: string | null;\n}\n\n/** `type ExploreAttachment` — what the server accepted, echoed back. */\nexport interface ExploreAttachment {\n name: string;\n mimeType: string;\n size: number;\n /** False when only the filename and type could be used as context. */\n textExtracted: boolean;\n}\n\n/**\n * `type SendExploreMessageResult`.\n *\n * **`assistantMessage.id` is the id to poll** — it comes back `PENDING`.\n */\nexport interface SendExploreMessageResult {\n conversation: ExploreConversation;\n userMessage: ExploreMessage;\n assistantMessage: ExploreMessage;\n remainingToday?: number | null;\n}\n\n// ============================================================================\n// GraphQL documents\n//\n// Only the fields the UI actually renders are requested — a narrower selection\n// set is cheaper to poll (this runs once every ~1.5s per active visitor) and\n// keeps the contract surface the websites depend on small.\n// ============================================================================\n\n/**\n * Selection set for an `ExploreMessage` that ANYONE may read. Inlined as a\n * template string rather than a GraphQL `fragment` so each document stays a\n * single self-contained operation (the SDK client posts raw strings and has no\n * fragment registry).\n *\n * Deliberately WITHOUT `feedback` — see `EXPLORE_OWN_MESSAGE_FIELDS`.\n */\nconst EXPLORE_MESSAGE_FIELDS = /* GraphQL */ `\n id\n conversationId\n role\n status\n content\n partialContent\n activity\n references {\n url\n title\n description\n product\n imageUrl\n index\n }\n ctas {\n kind\n label\n url\n product\n }\n followUps\n answered\n attachments {\n name\n mimeType\n size\n textExtracted\n }\n errorCode\n createdAt\n completedAt\n`;\n\n/**\n * The same message PLUS the visitor's own rating of it.\n *\n * Used by the reads that are scoped to a `conversationToken`, so the person\n * making them is the person who rated the answer — that is what lets the row\n * come back already-rated after a reload instead of re-offering the choice.\n *\n * `EXPLORE_SHARED_CONVERSATION_QUERY` deliberately does NOT use this one. A\n * share link is the one read performed by somebody ELSE, and it exists to be\n * forwarded; `comment` is where a visitor types the specifics that made the\n * answer wrong for them — the price they are actually being charged, an\n * invoice number, a name — so it must not travel with a transcript sent to a\n * colleague. The server omits it there too; this is the client half of the\n * same rule, and keeping the selection sets separate is what stops a future\n * \"why are there two of these?\" tidy-up from re-merging them.\n */\nconst EXPLORE_OWN_MESSAGE_FIELDS = /* GraphQL */ `\n ${EXPLORE_MESSAGE_FIELDS}\n feedback {\n rating\n reason\n comment\n createdAt\n }\n`;\n\n/**\n * Everything on `ExploreCatalog` that every deployed gateway has served since\n * the surface shipped. Shared by both catalog documents below so the pair can\n * never drift: the ONLY difference between them is `conceptHighlights`.\n */\nconst EXPLORE_CATALOG_ESTABLISHED_FIELDS = /* GraphQL */ `\n enabled\n products {\n slug\n name\n tagline\n website\n }\n examplePrompts\n welcomeTitle\n welcomeBody\n limits {\n maxMessageLength\n maxTurnsPerConversation\n messagesPerDay\n historyTurns\n }\n captchaRequired\n recaptchaSiteKey`;\n\n/** The field added with the welcome strip — the only thing a new gateway has that an old one does not. */\nconst EXPLORE_CATALOG_HIGHLIGHT_FIELD = /* GraphQL */ `\n conceptHighlights {\n product\n productName\n title\n url\n imageUrl\n alt\n }`;\n\nfunction exploreCatalogDocument(extraFields: string): string {\n return /* GraphQL */ `\n query ExploreCatalog($productSlug: String) {\n exploreCatalog(productSlug: $productSlug) {${EXPLORE_CATALOG_ESTABLISHED_FIELDS}${extraFields}\n }\n }\n`;\n}\n\n/** Loaded once per page. Safe to serve from the SDK's 5-minute query cache. */\nexport const EXPLORE_CATALOG_QUERY = exploreCatalogDocument(\n EXPLORE_CATALOG_HIGHLIGHT_FIELD,\n);\n\n/**\n * The same catalog WITHOUT `conceptHighlights`, for a gateway whose supergraph\n * predates the field.\n *\n * This is not belt-and-braces. GraphQL validation is all-or-nothing: a gateway\n * that does not know the field rejects the ENTIRE document and returns no\n * `data` at all, so selecting `conceptHighlights` against an older supergraph\n * costs the whole catalog — `enabled`, `captchaRequired`, `recaptchaSiteKey`,\n * the welcome copy and `limits` included — and not merely the strip. Measured\n * against production:\n *\n * {\"errors\":[{\"message\":\"Cannot query field \\\\\"conceptHighlights\\\\\" on type\n * \\\\\"ExploreCatalog\\\\\".\",\"extensions\":{\"code\":\"GRAPHQL_VALIDATION_FAILED\"}}]}\n *\n * That window is guaranteed even with perfect deploy ordering, because the prod\n * gateway's CDN edge serves a stale supergraph for ~30-40 minutes after the\n * subgraph publishes. `useExploreChat` therefore retries with this document\n * (see `catalogRejectedTheHighlightField`) rather than dropping to the built-in\n * fallback, whose hardcoded `enabled: true` would ignore the server-side kill\n * switch and whose `captchaRequired: false` would let every send be rejected\n * server-side while the composer looked fine.\n */\nexport const EXPLORE_CATALOG_QUERY_WITHOUT_HIGHLIGHTS =\n exploreCatalogDocument(\"\");\n\n/**\n * The poll. MUST be issued with `client.query(..., { cache: false })` — the\n * default 5-minute cache would return the first PENDING snapshot forever and\n * the answer would never appear.\n */\nexport const EXPLORE_MESSAGE_QUERY = /* GraphQL */ `\n query ExploreMessage($conversationToken: String!, $id: ID!) {\n exploreMessage(conversationToken: $conversationToken, id: $id) {\n ${EXPLORE_OWN_MESSAGE_FIELDS}\n }\n }\n`;\n\n/** Submit half of the submit + poll pair. */\nexport const SEND_EXPLORE_MESSAGE_MUTATION = /* GraphQL */ `\n mutation SendExploreMessage($input: SendExploreMessageInput!) {\n sendExploreMessage(input: $input) {\n conversation {\n id\n token\n productSlug\n turnCount\n createdAt\n }\n userMessage {\n ${EXPLORE_OWN_MESSAGE_FIELDS}\n }\n assistantMessage {\n ${EXPLORE_OWN_MESSAGE_FIELDS}\n }\n remainingToday\n }\n }\n`;\n\n// ============================================================================\n// Response envelopes (what `client.query`/`client.mutate` resolve `data` to)\n// ============================================================================\n\nexport interface ExploreCatalogQueryData {\n exploreCatalog: ExploreCatalog;\n}\n\nexport interface ExploreMessageQueryData {\n exploreMessage: ExploreMessage | null;\n}\n\nexport interface SendExploreMessageMutationData {\n sendExploreMessage: SendExploreMessageResult;\n}\n\n/**\n * Record that the visitor followed a link the answer offered.\n *\n * Fire-and-forget: the browser is navigating away as this goes out, so the result is never\n * waited on and a failure is never surfaced. The server reports `recorded: false` rather\n * than erroring for anything it cannot attribute.\n */\nexport const RECORD_EXPLORE_CLICK_MUTATION = /* GraphQL */ `\n mutation RecordExploreClick($input: RecordExploreClickInput!) {\n recordExploreClick(input: $input) {\n recorded\n }\n }\n`;\n\n/** `input SubmitExploreFeedbackInput`. */\nexport interface SubmitExploreFeedbackInput {\n conversationToken: string;\n messageId: string;\n rating: ExploreFeedbackRating;\n /** Only meaningful with `NOT_HELPFUL`; stored null for `HELPFUL`. */\n reason?: ExploreFeedbackReason | null;\n /** Optional free text. Hard-capped server-side — never trusted from the client. */\n comment?: string | null;\n}\n\n/** `type SubmitExploreFeedbackResult`. */\nexport interface SubmitExploreFeedbackResult {\n /**\n * False — NOT an error — when the message is not a COMPLETED assistant turn, or when the\n * supplied `conversationToken` does not own it. Same posture as `recordExploreClick`: the\n * server declines to attribute rather than raising FORBIDDEN at a marketing visitor.\n */\n recorded: boolean;\n /** What is now on record, when the rating was taken. Null when `recorded` is false. */\n feedback?: ExploreFeedback | null;\n}\n\n/**\n * Rate one answer.\n *\n * Unlike {@link RECORD_EXPLORE_CLICK_MUTATION} this one IS awaited: the visitor is still on\n * the page and the control they just pressed has to end up telling the truth about whether\n * anything was recorded. The rating upserts on `messageId`, so re-rating is legal and a\n * second submit is never an error.\n */\nexport const SUBMIT_EXPLORE_FEEDBACK_MUTATION = /* GraphQL */ `\n mutation SubmitExploreFeedback($input: SubmitExploreFeedbackInput!) {\n submitExploreFeedback(input: $input) {\n recorded\n feedback {\n rating\n reason\n comment\n createdAt\n }\n }\n }\n`;\n\nexport interface SubmitExploreFeedbackMutationData {\n submitExploreFeedback: SubmitExploreFeedbackResult;\n}\n\n/**\n * How many characters the comment field accepts before it stops taking more.\n *\n * A courtesy so the box does not invite an essay it will lose, NOT a contract: the server\n * owns the real cap and may truncate or reject below this. Nothing here may be read as the\n * limit that holds.\n */\nexport const EXPLORE_FEEDBACK_COMMENT_SOFT_MAX = 500;\n\n// ============================================================================\n// Handing the conversation to a human (CONTRACT.md §1, \"Handing a conversation\n// to a human\")\n// ============================================================================\n\n/**\n * `input EscalateExploreConversationInput`.\n *\n * `email` is the only thing the visitor is asked for. It is **personal data**: it belongs in\n * this input and nowhere else — never in `localStorage`, never in an `ExploreMessage`, never\n * in an analytics call. See the note on {@link ESCALATE_EXPLORE_CONVERSATION_MUTATION}.\n */\nexport interface EscalateExploreConversationInput {\n /** The thread being handed over. A share (`?c=`) secret is NOT one. */\n conversationToken: string;\n /** Validated strictly server-side; a bad address comes back as `INVALID_EMAIL`. */\n email: string;\n /** Optional free text. Hard-capped server-side at 2000 and TRUNCATED, never rejected. */\n note?: string | null;\n /** Only when `exploreCatalog.captchaRequired`. Same v3 action as a chat message. */\n recaptchaToken?: string;\n /** `PlatformProduct` id of the site, so the ticket reaches that product's support inbox. */\n productId?: string;\n}\n\n/** `type ExploreEscalation` — the handover on record, as the SERVER holds it. */\nexport interface ExploreEscalation {\n /**\n * Where the reply is actually going.\n *\n * On a repeat submit this is the address the FIRST handover used, not the one just typed\n * into the box — so this is the value to show back, never the local draft.\n */\n email: string;\n createdAt: string;\n}\n\n/** `type EscalateExploreConversationResult`. */\nexport interface EscalateExploreConversationResult {\n /**\n * False when a handover already existed — the double-clicked button, the retried request,\n * the reloaded page.\n *\n * **Not an error.** The conversation is with a human either way, and the UI must read as\n * success on both values. The server enforces one handover per conversation with a UNIQUE\n * index rather than a check-then-insert, so two concurrent submits produce one ticket and\n * one of them gets this.\n */\n created: boolean;\n escalation: ExploreEscalation;\n}\n\n/**\n * Hand this conversation to a person.\n *\n * Awaited and NOT fire-and-forget (unlike {@link RECORD_EXPLORE_CLICK_MUTATION}): it is the\n * one explore write that throws, because it ends with a visitor being told a person has\n * their question. \"We passed it on\" must never be shown for a handover that did not happen.\n *\n * **The address the visitor types is personal data.** It travels in these variables and\n * stops there. Do not persist it, do not fold it into the transcript (`messages` is what the\n * hook writes to localStorage), and do not pass it to `onSend` or any analytics call — the\n * server already has everything a support agent needs.\n */\nexport const ESCALATE_EXPLORE_CONVERSATION_MUTATION = /* GraphQL */ `\n mutation EscalateExploreConversation(\n $input: EscalateExploreConversationInput!\n ) {\n escalateExploreConversation(input: $input) {\n created\n escalation {\n email\n createdAt\n }\n }\n }\n`;\n\nexport interface EscalateExploreConversationMutationData {\n escalateExploreConversation: EscalateExploreConversationResult;\n}\n\n/**\n * How many characters the note field accepts before it stops taking more.\n *\n * Mirrors the server's hard cap so the box does not invite text it will silently truncate.\n * Same status as {@link EXPLORE_FEEDBACK_COMMENT_SOFT_MAX}: a courtesy, not the limit that\n * holds.\n */\nexport const EXPLORE_ESCALATION_NOTE_SOFT_MAX = 2000;\n\n/**\n * A dot-atom local part, real DNS labels, an alphabetic TLD — the shape CONTRACT.md says the\n * server insists on.\n *\n * Deliberately no more permissive than that and deliberately no stricter: this exists to\n * catch the typo before a round-trip, not to be the authority. The server is the authority,\n * and the two DO disagree (an address this accepts can still come back `INVALID_EMAIL`),\n * which is why the form surfaces the server's verdict as well as this one.\n */\nconst DELIVERABLE_EMAIL =\n /^[A-Za-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[A-Za-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?\\.)+[A-Za-z]{2,}$/;\n\n/**\n * Whether `value` is worth sending to the mutation at all.\n *\n * A local pre-flight, never a promise of deliverability. Keep it a SUBSET of what the server\n * accepts: a client that rejects something the server would have taken costs the visitor\n * their answer with nobody able to see why.\n */\nexport function looksLikeDeliverableEmail(value: string): boolean {\n const trimmed = value.trim();\n // 254 is the RFC 5321 ceiling on a forward path; anything longer cannot be delivered\n // anywhere, so it is a typo (or a paste of something that is not an address).\n if (trimmed.length === 0 || trimmed.length > 254) return false;\n return DELIVERABLE_EMAIL.test(trimmed);\n}\n\n/**\n * Mint a read-only link to this conversation.\n *\n * The returned secret is NOT the conversation token — it opens a read-only view and cannot\n * be used to continue the thread, so it is safe to put in a URL that gets forwarded.\n */\nexport const CREATE_EXPLORE_SHARE_LINK_MUTATION = /* GraphQL */ `\n mutation CreateExploreShareLink($conversationToken: String!) {\n createExploreShareLink(conversationToken: $conversationToken) {\n shareToken\n }\n }\n`;\n\n/**\n * Load a conversation someone shared, read-only.\n *\n * Takes the share secret from the `?c=` parameter, NOT a conversation token — the recipient\n * gets the transcript and no ability to continue or modify the thread.\n */\nexport const EXPLORE_SHARED_CONVERSATION_QUERY = /* GraphQL */ `\n query ExploreSharedConversation($shareToken: String!) {\n exploreSharedConversation(shareToken: $shareToken) {\n ${EXPLORE_MESSAGE_FIELDS}\n }\n }\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 * It stays `\"legacy\"` until the stage that flips the slug (PLAN.md 5a); the\n * flip is that one line plus a release.\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/** Which slug writers emit: the old one until the rename stage flips it. */\nexport const PRODUCT_SLUG_EMIT: Readonly<{\n productSlug: ProductSlugGeneration;\n}> = Object.freeze({ productSlug: \"legacy\" });\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","import { emitProductSlug } from \"../utils/product-slug\";\n\nimport type { WebsiteSwitcherProduct } from \"../components/website-switcher\";\n\n/**\n * Canonical Burdenoff ecosystem product list used by WebsiteSwitcher.\n *\n * Sourced from `~/products/websites/burdenoff-website/src/data/products.ts`.\n * Keep this in sync when products are added, renamed, or moved.\n */\nexport const ECOSYSTEM_PRODUCTS: WebsiteSwitcherProduct[] = [\n {\n slug: \"workspaces\",\n name: \"Workspaces\",\n websiteUrl: \"https://burdenoff.com\",\n },\n {\n slug: \"vibecontrols\",\n name: \"VibeControls\",\n websiteUrl: \"https://vibecontrols.com\",\n },\n // FluidGrids ships on www.fluidgrids.ai — fluidgrids.com only redirects there. The explore\n // catalog and the site's own sitemap use this origin, and so must the fallback catalog\n // built from this list, or /explore rejects FluidGrids concept images until it loads.\n {\n slug: \"fluidgrids\",\n name: \"FluidGrids\",\n websiteUrl: \"https://www.fluidgrids.ai\",\n },\n // BotManor ships on www.botmanor.com (the apex redirects there; the old botlit.ai domain\n // still serves the same site but is no longer the name we link to).\n // The slug follows the botlit → botmanor rename switch (utils/product-slug.ts).\n {\n slug: emitProductSlug(\"botmanor\"),\n name: \"BotManor\",\n websiteUrl: \"https://www.botmanor.com\",\n },\n {\n slug: \"bigconsole\",\n name: \"BigConsole\",\n websiteUrl: \"https://bigconsole.com\",\n },\n {\n slug: \"semanticfed\",\n name: \"SemanticFed\",\n websiteUrl: \"https://semanticfed.com\",\n },\n {\n slug: \"intelwatchtower\",\n name: \"IntelWatchtower\",\n websiteUrl: \"https://intelwatchtower.com\",\n },\n { slug: \"buildmyiq\", name: \"BuildMyIQ\", websiteUrl: \"https://buildmyiq.com\" },\n {\n slug: \"assethandler\",\n name: \"AssetHandler\",\n websiteUrl: \"https://assethandler.com\",\n },\n {\n slug: \"crewfoundry\",\n name: \"CrewFoundry\",\n websiteUrl: \"https://crewfoundry.com\",\n },\n {\n slug: \"planmagnet\",\n name: \"PlanMagnet\",\n websiteUrl: \"https://planmagnet.com\",\n },\n {\n slug: \"eventfullymanaged\",\n name: \"EventfullyManaged\",\n websiteUrl: \"https://eventfullymanaged.com\",\n },\n {\n slug: \"headshotmarketing\",\n name: \"HeadshotMarketing\",\n websiteUrl: \"https://headshotmarketing.com\",\n },\n {\n slug: \"technospam\",\n name: \"TechnoSpam\",\n websiteUrl: \"https://technospam.com\",\n },\n { slug: \"kadaikodi\", name: \"Kadaikodi\", websiteUrl: \"https://kadaikodi.com\" },\n {\n slug: \"movethewheels\",\n name: \"MoveTheWheels\",\n websiteUrl: \"https://movethewheels.com\",\n },\n {\n slug: \"healthybowl\",\n name: \"Healthy Bowl\",\n websiteUrl: \"https://gethealthybowl.com\",\n },\n { slug: \"ggnomad\", name: \"GGNomad\", websiteUrl: \"https://ggnomad.com\" },\n {\n slug: \"timecampus\",\n name: \"TimeCampus\",\n websiteUrl: \"https://timecampus.com\",\n },\n {\n slug: \"manufacturedops\",\n name: \"ManufacturedOps\",\n websiteUrl: \"https://manufacturedops.com\",\n },\n {\n slug: \"theglobalfuel\",\n name: \"TheGlobalFuel\",\n websiteUrl: \"https://theglobalfuel.com\",\n },\n {\n slug: \"housingvista\",\n name: \"HousingVista\",\n websiteUrl: \"https://housingvista.com\",\n },\n {\n slug: \"proserviceworld\",\n name: \"ProServiceWorld\",\n websiteUrl: \"https://proserviceworld.com\",\n },\n {\n slug: \"mybodyshield\",\n name: \"MyBodyShield\",\n websiteUrl: \"https://mybodyshield.com\",\n },\n {\n slug: \"artistrybase\",\n name: \"ArtistryBase\",\n websiteUrl: \"https://artistrybase.com\",\n },\n {\n slug: \"theculturepost\",\n name: \"TheCulturePost\",\n websiteUrl: \"https://theculturepost.com\",\n },\n { slug: \"collabkin\", name: \"CollabKin\", websiteUrl: \"https://collabkin.com\" },\n {\n slug: \"comradecircle\",\n name: \"ComradeCircle\",\n websiteUrl: \"https://comradecircle.com\",\n },\n {\n slug: \"brainyrich\",\n name: \"BrainyRich\",\n websiteUrl: \"https://brainyrich.com\",\n },\n {\n slug: \"cosmicintersection\",\n name: \"CosmicIntersection\",\n websiteUrl: \"https://cosmicintersection.com\",\n },\n {\n slug: \"govcitizenhub\",\n name: \"GovCitizenHub\",\n websiteUrl: \"https://govcitizenhub.com\",\n },\n {\n slug: \"adaptercloud\",\n name: \"AdapterCloud\",\n websiteUrl: \"https://adaptercloud.com\",\n },\n {\n slug: \"subscriberbot\",\n name: \"Subscriber Bot\",\n websiteUrl: \"https://subscriberbot.com\",\n },\n {\n slug: \"hookmovies\",\n name: \"HookMovies\",\n websiteUrl: \"https://hookmovies.com\",\n },\n {\n slug: \"coolandlovely\",\n name: \"CoolAndLovely\",\n websiteUrl: \"https://coolandlovely.com\",\n },\n {\n slug: \"atheletebridge\",\n name: \"AthleteBridge\",\n websiteUrl: \"https://atheletebridge.com\",\n },\n {\n slug: \"labsofscience\",\n name: \"LabsOfScience\",\n websiteUrl: \"https://labsofscience.com\",\n },\n {\n slug: \"ecoimpacthub\",\n name: \"EcoImpactHub\",\n websiteUrl: \"https://ecoimpacthub.com\",\n },\n];\n","/**\n * reCAPTCHA v3 (invisible, score-based) loader and token minter.\n *\n * Deliberately dependency-free: the SDK already ships the `react-google-recaptcha`\n * peer for the **v2 checkbox** used by `ContactPage`, and the two widgets cannot\n * share a script tag — v3 needs `?render=<siteKey>` on the URL, v2 must not have\n * it. This module injects its own v3 script and leaves the v2 integration alone.\n *\n * The Explore chat turns v3 on purely from the backend catalog\n * (`exploreCatalog.captchaRequired && recaptchaSiteKey`), so nothing is loaded\n * and no third-party request is made on sites where captcha is off.\n */\n\n/** The slice of the `grecaptcha` global this module uses. */\nexport interface RecaptchaV3 {\n /** Runs the callback once the API is fully initialised. */\n ready(callback: () => void): void;\n /** Mints a single-use token for `action`. */\n execute(siteKey: string, options: { action: string }): Promise<string>;\n}\n\ndeclare global {\n interface Window {\n grecaptcha?: RecaptchaV3;\n }\n}\n\nconst SCRIPT_ID = \"boff-recaptcha-v3\";\nconst SCRIPT_SRC = \"https://www.google.com/recaptcha/api.js?render=\";\n\n/** Default action name reported to reCAPTCHA for Explore chat submissions. */\nexport const EXPLORE_RECAPTCHA_ACTION = \"explore_chat\";\n\n/**\n * One in-flight/settled promise per site key. Guarantees the script tag is\n * injected at most once even when several components mount at the same time,\n * and lets later callers await the same load instead of racing it.\n */\nconst loaders = new Map<string, Promise<RecaptchaV3>>();\n\nfunction isBrowser(): boolean {\n return typeof window !== \"undefined\" && typeof document !== \"undefined\";\n}\n\n/**\n * Load the reCAPTCHA v3 script for `siteKey` and resolve once `grecaptcha` is\n * ready to mint tokens.\n *\n * - Injects the script at most once per site key (subsequent calls reuse the\n * same promise).\n * - Adopts an already-present script tag (e.g. injected by the host site)\n * rather than adding a second one.\n * - Rejects rather than hanging when the script fails to load, so the caller\n * can decide whether to submit without a token or surface an error.\n */\nexport function loadRecaptchaV3(siteKey: string): Promise<RecaptchaV3> {\n if (!siteKey) {\n return Promise.reject(new Error(\"reCAPTCHA site key is required\"));\n }\n if (!isBrowser()) {\n return Promise.reject(\n new Error(\"reCAPTCHA v3 can only be loaded in a browser\"),\n );\n }\n\n const existing = loaders.get(siteKey);\n if (existing) return existing;\n\n const promise = new Promise<RecaptchaV3>((resolve, reject) => {\n const ready = () => {\n const api = window.grecaptcha;\n if (!api) {\n reject(new Error(\"reCAPTCHA loaded but grecaptcha is unavailable\"));\n return;\n }\n // `ready` queues until the API has finished initialising internally;\n // calling `execute` before that throws.\n api.ready(() => resolve(api));\n };\n\n // Already loaded by this module, another SDK instance, or the host site.\n if (window.grecaptcha) {\n ready();\n return;\n }\n\n const scriptId = `${SCRIPT_ID}-${siteKey}`;\n const found = document.getElementById(scriptId);\n if (found) {\n found.addEventListener(\"load\", ready, { once: true });\n found.addEventListener(\n \"error\",\n () => reject(new Error(\"Failed to load reCAPTCHA v3\")),\n { once: true },\n );\n return;\n }\n\n const script = document.createElement(\"script\");\n script.id = scriptId;\n script.src = `${SCRIPT_SRC}${encodeURIComponent(siteKey)}`;\n script.async = true;\n script.defer = true;\n script.addEventListener(\"load\", ready, { once: true });\n script.addEventListener(\n \"error\",\n () => {\n // Drop the memo so a later attempt can retry a transient CDN failure\n // instead of being stuck with a permanently rejected promise.\n loaders.delete(siteKey);\n reject(new Error(\"Failed to load reCAPTCHA v3\"));\n },\n { once: true },\n );\n document.head.appendChild(script);\n });\n\n loaders.set(siteKey, promise);\n return promise;\n}\n\n/**\n * Mint a single-use reCAPTCHA v3 token, loading the script on first use.\n *\n * Tokens are valid for ~2 minutes and are consumed by one verification, so\n * this must be called per submission — never cached.\n */\nexport async function getRecaptchaV3Token(\n siteKey: string,\n action: string = EXPLORE_RECAPTCHA_ACTION,\n): Promise<string> {\n const api = await loadRecaptchaV3(siteKey);\n return api.execute(siteKey, { action });\n}\n","/**\n * useExploreChat — the `/explore` conversation state machine.\n *\n * Implements the client half of CONTRACT.md §1: a **submit + poll** exchange.\n * `sendExploreMessage` returns immediately with a PENDING assistant message;\n * the answer arrives by polling `exploreMessage(conversationToken, id)` roughly\n * every 1.5s until it reports COMPLETED or FAILED. Both gateway hops cap a\n * subgraph call at 20s while a turn takes 5-60s, which is why there is no\n * request that simply waits for the answer.\n *\n * Design rules this hook exists to enforce:\n *\n * 1. **The catalog never gates the first paint.** `catalog` is populated with a\n * static fallback synchronously, so `ExplorePage` can render its `<h1>` on\n * the very first render. The websites' prerender waits for an `h1`; if that\n * heading depended on a network round-trip, 38 static builds would capture a\n * blank page.\n * 2. **Polls bypass the query cache.** `client.query(..., { cache: false })` —\n * the default 5-minute cache would hand every later poll the first PENDING\n * snapshot and the answer would never appear.\n * 3. **Nothing outlives the component.** Unmount and `stop()` both cancel the\n * poll chain through a generation counter, so a late response can never\n * write into an unmounted tree.\n */\n\"use client\";\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\n\nimport { useWebSDK, useWebSDKConfig } from \"../client/provider\";\nimport {\n EXPLORE_CATALOG_QUERY,\n EXPLORE_CATALOG_QUERY_WITHOUT_HIGHLIGHTS,\n EXPLORE_MESSAGE_QUERY,\n EXPLORE_SHARED_CONVERSATION_QUERY,\n CREATE_EXPLORE_SHARE_LINK_MUTATION,\n ESCALATE_EXPLORE_CONVERSATION_MUTATION,\n RECORD_EXPLORE_CLICK_MUTATION,\n SEND_EXPLORE_MESSAGE_MUTATION,\n SUBMIT_EXPLORE_FEEDBACK_MUTATION,\n} from \"../components/explore-types\";\nimport { ECOSYSTEM_PRODUCTS } from \"../data/products\";\nimport { getRecaptchaV3Token } from \"../utils/recaptcha-v3\";\n\nimport type { GraphQLResponse } from \"../client/graphql-client\";\nimport type {\n EscalateExploreConversationInput,\n EscalateExploreConversationMutationData,\n ExploreCatalog,\n ExploreCatalogQueryData,\n ExploreFeedbackRating,\n ExploreFeedbackReason,\n ExploreLimits,\n ExploreMessage,\n ExploreMessageQueryData,\n SendExploreMessageInput,\n SendExploreMessageMutationData,\n SubmitExploreFeedbackInput,\n SubmitExploreFeedbackMutationData,\n} from \"../components/explore-types\";\nimport type { ExploreAttachmentPayload } from \"../utils/explore-attachments\";\n\n// ============================================================================\n// Public types\n// ============================================================================\n\n/**\n * Where the conversation is right now.\n *\n * - `loading` — catalog request in flight (the fallback catalog is already\n * rendered, so this is a refinement, not a blank screen).\n * - `ready` — idle; the composer accepts input.\n * - `sending` — `sendExploreMessage` in flight.\n * - `streaming` — polling the assistant turn; `activity` is live.\n * - `error` — the last turn failed; `retry()` is meaningful and the\n * composer stays usable.\n * - `limited` — rate limited; the composer is disabled.\n * - `disabled` — `exploreCatalog.enabled === false`.\n */\nexport type ExploreChatPhase =\n | \"loading\"\n | \"ready\"\n | \"sending\"\n | \"streaming\"\n | \"error\"\n | \"limited\"\n | \"disabled\";\n\n/** Coarse bucket the UI switches its banner copy and affordances on. */\nexport type ExploreChatErrorKind =\n | \"rate-limit\"\n | \"captcha\"\n | \"timeout\"\n // The question was answerable, but not across this many products at once. Distinct from\n // \"timeout\" because the remedy is different: narrow the question, do not retry it.\n | \"scope\"\n | \"network\"\n | \"disabled\"\n | \"validation\"\n | \"unknown\";\n\nexport interface ExploreChatError {\n /** UI bucket. */\n kind: ExploreChatErrorKind;\n /** Raw `extensions.code` (or a client-side code such as `TIMEOUT`). */\n code: string;\n /** Human-readable, safe to render. */\n message: string;\n /** Whether re-sending the same message could plausibly succeed. */\n retryable: boolean;\n}\n\n/**\n * What came of handing the conversation to a person.\n *\n * `sent` and `already-handed-over` are BOTH successes. The second is the double-clicked\n * button, the retried request and the reloaded page: the server keeps one handover per\n * conversation, so a repeat submit returns the first one rather than filing a second ticket.\n * A visitor who is worried enough to ask for a human must never be told \"you already did\n * that\" — the conversation is with a person either way, and the UI says so either way.\n */\nexport type ExploreEscalateOutcome =\n | \"sent\"\n | \"already-handed-over\"\n /** The SERVER rejected the address (`INVALID_EMAIL`), after the client let it through. */\n | \"invalid-email\"\n | \"rate-limited\"\n | \"captcha\"\n /** No conversation token — nothing to hand over. The form should not have been offered. */\n | \"no-conversation\"\n | \"failed\";\n\nexport interface ExploreEscalateResult {\n outcome: ExploreEscalateOutcome;\n /**\n * The address the SERVER says the reply is going to, on `sent` and `already-handed-over`;\n * null otherwise.\n *\n * On `already-handed-over` this is the address the FIRST handover used, which may not be\n * the one just typed — and it is the one the reply actually reaches, so it is the one to\n * show back.\n */\n email: string | null;\n /** When the handover was filed, as the server holds it. Null unless it exists. */\n createdAt: string | null;\n}\n\nexport interface UseExploreChatOptions {\n /** Slug of the product site this page is on — a soft hint to the harness. */\n productSlug?: string;\n /** Full URL of the page, forwarded as `pageUrl`. */\n pageUrl?: string;\n /** BCP-47 locale, forwarded as `locale`. */\n locale?: string;\n /** Poll cadence in ms. Default 1500 (CONTRACT.md §1). */\n pollIntervalMs?: number;\n /** Give up on a turn after this long. Default 90000 (the edge poll timeout). */\n pollTimeoutMs?: number;\n /**\n * Prompts to offer when `exploreCatalog` supplies none. Resolution order is\n * catalog → this option → {@link DEFAULT_EXPLORE_EXAMPLE_PROMPTS}, so a site\n * can ship product-specific prompts without waiting on the backend and still\n * be overridden centrally once the catalog carries them.\n */\n examplePrompts?: string[];\n /**\n * Welcome copy for the fallback catalog, used only when `exploreCatalog` cannot be reached.\n *\n * The hook's own built-in fallback is English. `ExplorePage` passes the strings for the\n * visitor's locale, so a catalog outage degrades to a translated page rather than an\n * English one — the whole point of shipping six locales is that the failure paths speak\n * them too.\n */\n fallbackWelcome?: { title: string; body: string };\n /** localStorage key for the ACTIVE conversation. Default `boff.explore.v1`. */\n storageKey?: string;\n /** Persist the conversation across reloads within the tab. Default true. */\n persist?: boolean;\n /** Called with the message text each time a send is accepted locally. */\n onSend?: (message: string) => void;\n}\n\nexport interface UseExploreChatResult {\n phase: ExploreChatPhase;\n /** Never null — starts as the baked or static fallback catalog, upgraded by the server. */\n catalog: ExploreCatalog;\n /**\n * The last catalog that came from the server, raw, or null if only the fallback has been\n * seen. ExplorePage writes it into the page so the site's prerender captures it.\n */\n serverCatalogRaw: ExploreCatalog | null;\n messages: ExploreMessage[];\n /** Latest progress line of the in-flight turn, or null when idle. */\n activity: string | null;\n /** Messages left today for this visitor, when the server reports it. */\n remainingToday: number | null;\n error: ExploreChatError | null;\n /** Product the visitor pinned the conversation to, or null for \"all\". */\n focusProduct: string | null;\n setFocusProduct: (slug: string | null) => void;\n send: (\n message: string,\n attachments?: readonly ExploreAttachmentPayload[],\n ) => Promise<void>;\n /** Stop polling and keep whatever text has streamed so far. */\n stop: () => void;\n /** Re-send the last message after a failed turn. */\n retry: () => void;\n /** Clear the conversation and the persisted session. */\n reset: () => void;\n /** Past conversations held in THIS browser, newest first. */\n history: ArchivedExploreConversation[];\n /** Reopen a past conversation and continue it. */\n openConversation: (token: string) => void;\n /** Forget one past conversation (local only). */\n deleteConversation: (token: string) => void;\n /** Forget every past conversation held in this browser. */\n clearHistory: () => void;\n /**\n * Record that the visitor followed a link this answer offered. Fire-and-forget — never\n * awaited, never throws, and must never delay the navigation it is reporting on.\n */\n recordClick: (\n messageId: string,\n kind: \"CTA\" | \"REFERENCE\",\n url: string,\n ) => void;\n /**\n * Rate one answer. Resolves `true` only when the server reports `recorded: true`.\n *\n * The opposite posture to {@link recordClick}, and deliberately so: the visitor is still on\n * the page and is watching the control they pressed, so the result has to come back far\n * enough for the UI to tell the truth about it. It still never throws — a transport\n * failure, a GraphQL error and an unattributable message all resolve `false`.\n */\n submitFeedback: (\n messageId: string,\n rating: ExploreFeedbackRating,\n detail?: { reason?: ExploreFeedbackReason | null; comment?: string | null },\n ) => Promise<boolean>;\n /**\n * Hand this conversation to a person, so they pick it up with the transcript attached.\n *\n * Awaited, like {@link submitFeedback} and for the same reason — the visitor is watching\n * the control they pressed — but this one has more to say than true/false: \"we could not\n * deliver to that address\", \"you are sending these too fast\" and \"it is already with a\n * human\" are three different things to a worried visitor, and one boolean flattens them\n * into a shrug. It never throws; every outcome comes back in\n * {@link ExploreEscalateResult}.\n *\n * `email` is personal data. It goes into the mutation variables and nowhere else — not\n * into `messages` (which is what this hook persists), not into `onSend`, not into any\n * analytics call.\n */\n escalate: (email: string, note?: string) => Promise<ExploreEscalateResult>;\n /**\n * Whether there is a conversation a person could actually be handed.\n *\n * False on a fresh page and false in a read-only shared view (a `?c=` secret is not a\n * conversation token), which are exactly the cases where {@link escalate} could only\n * answer `no-conversation`. Offer the form on this, rather than offering one that cannot\n * work.\n */\n canEscalate: boolean;\n /**\n * Mint a read-only link to this conversation, or null when there is nothing to share yet.\n * The secret returned is not the conversation token and cannot continue the thread.\n */\n createShareLink: () => Promise<string | null>;\n /**\n * True when the transcript on screen came from a shared link rather than this visitor's own\n * conversation. A shared view is read-only: the share secret cannot continue the thread.\n */\n isSharedView: boolean;\n canSend: boolean;\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst DEFAULT_STORAGE_KEY = \"boff.explore.v1\";\n/** Where past conversations live. Derived from the active key so a custom key stays paired. */\nconst HISTORY_SUFFIX = \".history\";\n/**\n * How many past conversations to keep, and how many messages of each. Bounded on purpose:\n * localStorage is a few MB per origin and shared with everything else the site stores, so an\n * unbounded archive would eventually start throwing QuotaExceededError on write.\n */\nconst MAX_ARCHIVED_CONVERSATIONS = 15;\nconst MAX_ARCHIVED_MESSAGES = 20;\nconst DEFAULT_POLL_INTERVAL_MS = 1_500;\n/** Matches `EXPLORE_AGENT_TIMEOUT_MS` (CONTRACT.md §3) — the edge gives up too. */\nconst DEFAULT_POLL_TIMEOUT_MS = 90_000;\n/** Per-poll HTTP timeout. Short: a stuck poll must not stall the chain. */\nconst POLL_REQUEST_TIMEOUT_MS = 10_000;\n/** The submit hop enqueues a job; generous but well under the gateway's 20s. */\nconst SEND_REQUEST_TIMEOUT_MS = 18_000;\n/** Transient poll failures tolerated before the turn is declared dead. */\nconst MAX_CONSECUTIVE_POLL_FAILURES = 4;\n/** Cap on what goes into localStorage — a conversation must not grow unbounded. */\nconst MAX_PERSISTED_MESSAGES = 40;\n\n/** Fallback limits, mirroring the server defaults in CONTRACT.md §3. */\nexport const FALLBACK_EXPLORE_LIMITS: ExploreLimits = {\n maxMessageLength: 2000,\n maxTurnsPerConversation: 20,\n messagesPerDay: 100,\n historyTurns: 6,\n};\n\n/**\n * Prompts shown when neither the catalog nor the consumer supplies any.\n * Deliberately product-agnostic — this SDK renders on 38 different sites.\n *\n * Problem-first, and kept in step with `support.explore.examplePrompts.*` in\n * global-support-svc. These render precisely when the catalog call failed, so\n * if they disagree with the server the page silently reverts to whatever this\n * file last said — which is how the whole surface can go back to being a\n * brochure without a single copy change being reviewed.\n */\nexport const DEFAULT_EXPLORE_EXAMPLE_PROMPTS: string[] = [\n \"Every course we run lives in a different spreadsheet.\",\n \"We only hear a machine is down after the line has stopped.\",\n \"Members quietly stop showing up and we never know why.\",\n \"Which of your products fits a problem like mine?\",\n];\n\nconst FALLBACK_WELCOME_TITLE = \"What problem are you trying to solve?\";\nconst FALLBACK_WELCOME_BODY =\n \"Describe the problem, in your own words. You can also ask about a specific product or compare a few. Every answer comes straight from our documentation.\";\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction buildFallbackCatalog(\n examplePrompts?: string[],\n welcome?: { title: string; body: string },\n): ExploreCatalog {\n return {\n enabled: true,\n products: ECOSYSTEM_PRODUCTS.map((p) => ({\n slug: p.slug,\n name: p.name,\n tagline: null,\n website: p.websiteUrl,\n })),\n examplePrompts: examplePrompts?.length\n ? examplePrompts\n : DEFAULT_EXPLORE_EXAMPLE_PROMPTS,\n welcomeTitle: welcome?.title ?? FALLBACK_WELCOME_TITLE,\n welcomeBody: welcome?.body ?? FALLBACK_WELCOME_BODY,\n limits: FALLBACK_EXPLORE_LIMITS,\n captchaRequired: false,\n recaptchaSiteKey: null,\n // Empty, and deliberately not synthesised from `ECOSYSTEM_PRODUCTS`: a concept highlight\n // needs a real published illustration URL, and this SDK has no registry of them. An\n // invented path would render 38 broken frames on the one screen every prerender captures.\n // The strip renders nothing at all when the list is empty, which is the honest fallback.\n conceptHighlights: [],\n };\n}\n\n/** The element a prerendered /explore carries its catalog in. */\nexport const BAKED_CATALOG_ELEMENT_ID = \"boff-explore-catalog\";\n\n/**\n * The language the live catalog is localised to. The server localises exploreCatalog from the\n * browser's Accept-Language, so a copy baked by an English prerender is the wrong copy for a\n * visitor whose browser asks for Spanish.\n */\nexport function bakedCatalogLocale(): string {\n if (typeof navigator === \"undefined\") return \"\";\n return (navigator.language || \"\").toLowerCase().split(\"-\")[0] ?? \"\";\n}\n\ninterface BakedCatalogElement {\n product: string;\n locale: string;\n text: string;\n}\n\nfunction snapshotBakedElement(): BakedCatalogElement | null {\n if (typeof document === \"undefined\") return null;\n const el = document.getElementById(BAKED_CATALOG_ELEMENT_ID);\n // A real <script type=\"application/json\"> only. Anything else that happens to carry the id —\n // sanitised CMS HTML rendered on the same page, say — is not ours to read.\n if (\n !el ||\n el.tagName !== \"SCRIPT\" ||\n el.getAttribute(\"type\") !== \"application/json\"\n )\n return null;\n return {\n product: el.getAttribute(\"data-product\") ?? \"\",\n locale: el.getAttribute(\"data-locale\") ?? \"\",\n text: el.textContent ?? \"\",\n };\n}\n\n/**\n * The baked catalog as it stood when this module first evaluated.\n *\n * Reading it from ExplorePage's state initialiser was not early enough on 35 of the 39 host\n * sites. They lazy-load /explore behind a <Suspense> boundary, so React's FIRST commit is the\n * route fallback — and that commit clears the prerendered page, this element with it, ~400ms\n * before ExplorePage renders for the first time. (Reproduced in Chrome by an independent\n * review; the first version's test put the element outside React's container, where no commit\n * could remove it, and so could not see this.) Every host's main.tsx imports the SDK root\n * statically before calling createRoot, and the SDK is one module (tsup splitting: false, no\n * sideEffects field), so this line runs while the static page is still intact.\n *\n * This keeps the catalog; it cannot stop that fallback commit from blanking the page for the\n * ~350ms the route chunk takes. Measured on vibecontrols: lazy, the strip, header and composer\n * all vanished at ~600ms; with ExplorePage imported eagerly, nothing vanished. So the hosts\n * import it eagerly — it only wraps this component, which their entry chunk already carries.\n */\nconst BAKED_AT_LOAD: BakedCatalogElement | null = snapshotBakedElement();\n\n/** Enough of the shape to render without throwing; normalizeCatalog fills the rest. */\nfunction isCatalogShape(value: unknown): value is ExploreCatalog {\n if (!value || typeof value !== \"object\") return false;\n const c = value as Record<string, unknown>;\n const listOrAbsent = (v: unknown) =>\n v === undefined || v === null || Array.isArray(v);\n const textOrAbsent = (v: unknown) =>\n v === undefined || v === null || typeof v === \"string\";\n return (\n (c[\"enabled\"] === undefined || typeof c[\"enabled\"] === \"boolean\") &&\n listOrAbsent(c[\"products\"]) &&\n listOrAbsent(c[\"examplePrompts\"]) &&\n listOrAbsent(c[\"conceptHighlights\"]) &&\n textOrAbsent(c[\"welcomeTitle\"]) &&\n textOrAbsent(c[\"welcomeBody\"])\n );\n}\n\n/**\n * The catalog the static page was prerendered with, if it carries a usable one for this product\n * and this visitor's language. The live element is preferred (a site that imports ExplorePage\n * eagerly still has it at first render); otherwise the load-time snapshot.\n *\n * Every host mounts with createRoot, which REPLACES the prerendered page rather than adopting\n * it; starting from the fallback catalog tore down the concept strip and the product's own\n * intro the static page was showing, and put them back 1–2s later — or never, if the catalog\n * call failed. Any mismatch or parse failure returns null, and the page behaves as it did\n * before this existed.\n */\nfunction readBakedCatalog(\n productSlug: string | undefined,\n): ExploreCatalog | null {\n const baked = snapshotBakedElement() ?? BAKED_AT_LOAD;\n if (!baked) return null;\n if (baked.product !== (productSlug ?? \"\")) return null;\n if (baked.locale && baked.locale !== bakedCatalogLocale()) return null;\n try {\n const parsed: unknown = JSON.parse(baked.text);\n return isCatalogShape(parsed) ? parsed : null;\n } catch {\n return null;\n }\n}\n\n/**\n * JSON that is safe inside a <script> element. `JSON.stringify` leaves `</script>` and `<!--`\n * intact, and the catalog carries free text from the backend (titles, prompts), so the\n * characters that could end the element or open a comment are escaped as \\u sequences —\n * which JSON.parse turns straight back into the same characters.\n */\nexport function serializeBakedCatalog(raw: ExploreCatalog): string {\n return JSON.stringify(raw)\n .replace(/</g, \"\\\\u003c\")\n .replace(/>/g, \"\\\\u003e\")\n .replace(/&/g, \"\\\\u0026\")\n .replace(/\\u2028/g, \"\\\\u2028\")\n .replace(/\\u2029/g, \"\\\\u2029\");\n}\n\n/**\n * Harden a server catalog against partial/degraded responses so a missing\n * field can never blank the page or produce `maxMessageLength: 0` (which would\n * silently disable the composer).\n */\nfunction normalizeCatalog(\n raw: ExploreCatalog,\n fallback: ExploreCatalog,\n): ExploreCatalog {\n return {\n enabled: raw.enabled !== false,\n products: raw.products?.length ? raw.products : fallback.products,\n examplePrompts: raw.examplePrompts?.length\n ? raw.examplePrompts\n : fallback.examplePrompts,\n welcomeTitle: raw.welcomeTitle || fallback.welcomeTitle,\n welcomeBody: raw.welcomeBody || fallback.welcomeBody,\n limits: {\n maxMessageLength:\n raw.limits?.maxMessageLength || fallback.limits.maxMessageLength,\n maxTurnsPerConversation:\n raw.limits?.maxTurnsPerConversation ||\n fallback.limits.maxTurnsPerConversation,\n messagesPerDay:\n raw.limits?.messagesPerDay || fallback.limits.messagesPerDay,\n historyTurns: raw.limits?.historyTurns || fallback.limits.historyTurns,\n },\n captchaRequired: raw.captchaRequired === true,\n recaptchaSiteKey: raw.recaptchaSiteKey ?? null,\n // `[ExploreConceptHighlight!]!` on the wire, so a gateway that KNOWS the field always\n // sends an array. The one response that arrives without it is the retry against an older\n // supergraph (`EXPLORE_CATALOG_QUERY_WITHOUT_HIGHLIGHTS`) — that gateway does not answer\n // the full document at all, it refuses it. Either way\n // `catalog.conceptHighlights.filter(...)` on `undefined` would throw inside render,\n // blanking the welcome screen over a decorative strip.\n conceptHighlights: raw.conceptHighlights?.length\n ? raw.conceptHighlights\n : fallback.conceptHighlights,\n };\n}\n\n/**\n * Codes that mean \"this gateway would not accept the document\", as opposed to\n * \"the request failed\". `HTTP_400` is in the set because a gateway may surface\n * a validation failure as a status instead of a body — production answers 200\n * with `GRAPHQL_VALIDATION_FAILED`, but that is a server choice, not a promise,\n * and `WebSDKClient` collapses any non-2xx into `HTTP_<status>` with the\n * original message discarded.\n */\nconst CATALOG_DOCUMENT_REJECTED_CODES = new Set([\n \"GRAPHQL_VALIDATION_FAILED\",\n \"HTTP_400\",\n]);\n\n/**\n * True when the catalog came back with nothing usable AND the reason looks like\n * the gateway refusing the document rather than failing to serve it.\n *\n * GraphQL validation is all-or-nothing, so against a supergraph that predates\n * `conceptHighlights` there is no partial response to salvage: no `data`, and\n * the loss is the WHOLE catalog — the server-side `enabled` kill switch,\n * `captchaRequired`/`recaptchaSiteKey`, the reviewed welcome copy and `limits`.\n * One retry without the field recovers all of it.\n */\nexport function catalogRejectedTheHighlightField(\n res: GraphQLResponse<ExploreCatalogQueryData>,\n): boolean {\n if (res.data?.exploreCatalog) return false;\n return (res.errors ?? []).some((error) => {\n const code = error.extensions?.code;\n return (\n (typeof code === \"string\" && CATALOG_DOCUMENT_REJECTED_CODES.has(code)) ||\n error.message.includes(\"conceptHighlights\")\n );\n });\n}\n\nconst RATE_LIMIT_CODES = new Set([\n \"RATE_LIMITED\",\n \"HTTP_429\",\n \"EXPLORE_GLOBAL_LIMIT\",\n]);\nconst CAPTCHA_CODES = new Set([\"CAPTCHA_FAILED\", \"CAPTCHA_REQUIRED\"]);\nconst NETWORK_CODES = new Set([\n \"NETWORK_ERROR\",\n \"UPSTREAM_UNAVAILABLE\",\n \"CONVERSATION_BUSY\",\n \"PROVIDER_ERROR\",\n \"QUEUE_FULL\",\n]);\nconst VALIDATION_CODES = new Set([\n \"MESSAGE_TOO_LONG\",\n \"MESSAGE_EMPTY\",\n \"CONVERSATION_LIMIT\",\n \"INVALID_INPUT\",\n]);\n\n/**\n * Map a `extensions.code` (or a client-side transport code) onto the bucket the\n * UI renders. Unknown codes are treated as retryable — a transient backend\n * condition we have not enumerated is far more likely than a permanent one.\n */\n/**\n * Map an `extensions.code` from `escalateExploreConversation` onto the outcome the form\n * renders.\n *\n * Separate from {@link classifyExploreError} on purpose. That one buckets a failed ANSWER\n * and its buckets carry copy about retrying a question — none of which is true here, and\n * `INVALID_EMAIL` has no bucket there at all (it would land in `unknown` and be shown as\n * \"something went wrong\", which is the one message that leaves the visitor unable to fix\n * the one thing they could actually fix).\n *\n * `EXPLORE_GLOBAL_LIMIT` is deliberately NOT treated as rate limiting here: the contract\n * keeps a handover off the fleet-wide LLM budget, so seeing it would mean something\n * unexpected, not \"you are going too fast\".\n */\nexport function escalateOutcomeForCode(\n code: string | null | undefined,\n): ExploreEscalateOutcome {\n switch (code) {\n case \"INVALID_EMAIL\":\n return \"invalid-email\";\n case \"RATE_LIMITED\":\n case \"HTTP_429\":\n return \"rate-limited\";\n case \"CAPTCHA_REQUIRED\":\n case \"CAPTCHA_FAILED\":\n return \"captcha\";\n // The token owns nothing, or owns a different conversation. The server answers NOT_FOUND\n // rather than FORBIDDEN so the endpoint is not an oracle for whether a thread exists;\n // either way there is nothing this visitor can do about it, so it reads as a failure.\n case \"NOT_FOUND\":\n return \"failed\";\n default:\n return \"failed\";\n }\n}\n\nexport function classifyExploreError(\n code: string | null | undefined,\n serverMessage?: string | null,\n): ExploreChatError {\n const c = code ?? \"UNKNOWN\";\n if (RATE_LIMIT_CODES.has(c)) {\n return {\n kind: \"rate-limit\",\n code: c,\n message:\n \"You've reached the message limit for now. Please try again later.\",\n retryable: false,\n };\n }\n if (CAPTCHA_CODES.has(c)) {\n return {\n kind: \"captcha\",\n code: c,\n message:\n \"We couldn't verify that you're human. Please try sending that again.\",\n retryable: true,\n };\n }\n // TOOL_BUDGET is NOT a timeout and must not be described as one. Measured in production:\n // the failing turn ended in 33s against a 75s budget — it did not run out of time, it ran\n // out of searches. Retrying is also the wrong advice: the same question searches the same\n // way and fails identically, so \"try again\" is a button that cannot work. The honest reply\n // names the actual limit and offers the narrowing that does work.\n if (c === \"TOOL_BUDGET\") {\n return {\n kind: \"scope\",\n code: c,\n message:\n \"That question spans more of our products than I can look through in one go. Try asking about two or three specific products.\",\n retryable: false,\n };\n }\n if (c === \"TIMEOUT\") {\n return {\n kind: \"timeout\",\n code: c,\n message: \"That answer took too long to come back. Try asking again.\",\n retryable: true,\n };\n }\n if (NETWORK_CODES.has(c)) {\n return {\n kind: \"network\",\n code: c,\n message:\n \"We couldn't reach the assistant just now. Check your connection and try again.\",\n retryable: true,\n };\n }\n if (c === \"EXPLORE_DISABLED\") {\n return {\n kind: \"disabled\",\n code: c,\n message: \"Explore is unavailable at the moment. Please check back soon.\",\n retryable: false,\n };\n }\n if (VALIDATION_CODES.has(c)) {\n return {\n kind: \"validation\",\n code: c,\n message: serverMessage || \"That message couldn't be sent.\",\n retryable: false,\n };\n }\n if (c === \"EMPTY_ANSWER\") {\n return {\n kind: \"unknown\",\n code: c,\n message:\n \"No answer came back for that one. Try rephrasing, or ask something more specific.\",\n retryable: true,\n };\n }\n return {\n kind: \"unknown\",\n code: c,\n message: serverMessage || \"Something went wrong. Please try again.\",\n retryable: true,\n };\n}\n\ninterface PersistedExploreState {\n conversationToken: string | null;\n messages: ExploreMessage[];\n focusProduct: string | null;\n}\n\n/** One past conversation, as shown in the history list and restored on click. */\nexport interface ArchivedExploreConversation {\n token: string;\n /** First thing the visitor asked — the human-readable label for the thread. */\n title: string;\n /** ISO timestamp of the last message in the thread. */\n updatedAt: string;\n messageCount: number;\n focusProduct: string | null;\n messages: ExploreMessage[];\n}\n\nfunction historyKey(key: string): string {\n return `${key}${HISTORY_SUFFIX}`;\n}\n\nfunction readArchive(key: string): ArchivedExploreConversation[] {\n if (typeof window === \"undefined\") return [];\n try {\n const raw = window.localStorage.getItem(historyKey(key));\n if (!raw) return [];\n const parsed: unknown = JSON.parse(raw);\n if (!Array.isArray(parsed)) return [];\n return parsed.filter(\n (entry): entry is ArchivedExploreConversation =>\n typeof entry === \"object\" &&\n entry !== null &&\n typeof (entry as ArchivedExploreConversation).token === \"string\" &&\n Array.isArray((entry as ArchivedExploreConversation).messages),\n );\n } catch {\n return [];\n }\n}\n\nfunction writeArchive(\n key: string,\n entries: ArchivedExploreConversation[],\n): void {\n if (typeof window === \"undefined\") return;\n try {\n if (entries.length === 0) {\n window.localStorage.removeItem(historyKey(key));\n return;\n }\n window.localStorage.setItem(historyKey(key), JSON.stringify(entries));\n } catch {\n /* private mode or quota — the archive is a convenience, never load-bearing */\n }\n}\n\n/**\n * Fold the given thread into the archive, newest first.\n *\n * Upsert by token rather than append: the same conversation is archived after every completed\n * turn so an abruptly closed tab still leaves a record, and appending would fill the list with\n * duplicates of one thread.\n */\nfunction upsertArchive(\n key: string,\n thread: {\n token: string | null;\n messages: ExploreMessage[];\n focusProduct: string | null;\n },\n): ArchivedExploreConversation[] {\n if (!thread.token) return readArchive(key);\n const real = thread.messages.filter(\n (m) => m.content.trim() !== \"\" || m.role === \"USER\",\n );\n if (real.length === 0) return readArchive(key);\n\n const firstUser = real.find((m) => m.role === \"USER\");\n const entry: ArchivedExploreConversation = {\n token: thread.token,\n title: (firstUser?.content ?? \"Conversation\").trim().slice(0, 80),\n updatedAt: real[real.length - 1]?.createdAt ?? new Date().toISOString(),\n messageCount: real.length,\n focusProduct: thread.focusProduct,\n messages: real.slice(-MAX_ARCHIVED_MESSAGES),\n };\n\n const rest = readArchive(key).filter((e) => e.token !== entry.token);\n const next = [entry, ...rest].slice(0, MAX_ARCHIVED_CONVERSATIONS);\n writeArchive(key, next);\n return next;\n}\n\nfunction readPersisted(key: string): PersistedExploreState | null {\n // SSR / prerender: there is no localStorage, and reading one during the\n // first client render would desync hydration anyway.\n if (typeof window === \"undefined\") return null;\n try {\n const raw = window.localStorage.getItem(key);\n if (!raw) return null;\n const parsed: unknown = JSON.parse(raw);\n if (!parsed || typeof parsed !== \"object\") return null;\n const record = parsed as Partial<PersistedExploreState>;\n return {\n conversationToken:\n typeof record.conversationToken === \"string\"\n ? record.conversationToken\n : null,\n messages: Array.isArray(record.messages)\n ? record.messages.slice(-MAX_PERSISTED_MESSAGES)\n : [],\n focusProduct:\n typeof record.focusProduct === \"string\" ? record.focusProduct : null,\n };\n } catch {\n /* corrupt or unavailable storage — start a fresh conversation */\n return null;\n }\n}\n\n/**\n * A turn that never produced any text is worth dropping rather than rendering\n * as an empty bubble — used by `stop()` and by the failure paths.\n */\n/**\n * The `?c=` share secret, or null.\n *\n * Read from `window.location` rather than from a router: the explore page ships to 39\n * separate marketing sites, and only some of them mount it under a router whose search\n * params this hook could reach.\n */\nfunction readShareToken(): string | null {\n if (typeof window === \"undefined\") return null;\n try {\n return new URL(window.location.href).searchParams.get(\"c\");\n } catch {\n // A malformed href — fall through to the normal path rather than blanking the page.\n return null;\n }\n}\n\nfunction visibleText(message: ExploreMessage): string {\n return message.content || message.partialContent || \"\";\n}\n\nfunction countUserTurns(messages: ExploreMessage[]): number {\n return messages.filter((m) => m.role === \"USER\").length;\n}\n\n// ============================================================================\n// Hook\n// ============================================================================\n\nexport function useExploreChat(\n options: UseExploreChatOptions = {},\n): UseExploreChatResult {\n const {\n productSlug,\n pageUrl,\n locale,\n examplePrompts,\n fallbackWelcome,\n pollIntervalMs = DEFAULT_POLL_INTERVAL_MS,\n pollTimeoutMs = DEFAULT_POLL_TIMEOUT_MS,\n storageKey = DEFAULT_STORAGE_KEY,\n persist = true,\n onSend,\n } = options;\n\n const client = useWebSDK();\n // The site's `PlatformProduct` id, so a handover reaches THAT product's support inbox\n // rather than a generic one. Optional in the contract: a site that has not configured one\n // still gets its conversation to a person.\n const { productId } = useWebSDKConfig();\n\n // The fallback catalog is the initial value AND the merge base for the\n // server response. Keyed on the prompt CONTENT, not the array identity —\n // a consumer passing an inline `examplePrompts={[...]}` literal would\n // otherwise rebuild it every render and re-fire the catalog effect.\n const promptKey = JSON.stringify(examplePrompts ?? []);\n // Same reasoning for the welcome copy: keyed on content so an inline object literal from the\n // caller does not rebuild the catalog every render.\n const welcomeKey = JSON.stringify(fallbackWelcome ?? null);\n const fallbackCatalog = useMemo(\n () =>\n buildFallbackCatalog(\n JSON.parse(promptKey) as string[],\n (JSON.parse(welcomeKey) as { title: string; body: string } | null) ??\n undefined,\n ),\n [promptKey, welcomeKey],\n );\n\n // The raw server catalog the static page was prerendered with, read once, before React's\n // first commit replaces that page (see readBakedCatalog).\n const [bakedRaw] = useState<ExploreCatalog | null>(() =>\n readBakedCatalog(productSlug),\n );\n const [catalog, setCatalog] = useState<ExploreCatalog>(() =>\n bakedRaw ? normalizeCatalog(bakedRaw, fallbackCatalog) : fallbackCatalog,\n );\n // The last catalog that came from the SERVER (baked or fetched), as raw JSON, for\n // ExplorePage to write into the page so the next prerender captures it. Null while only\n // the fallback has been seen — a fallback is never baked, or a page prerendered during a\n // catalog outage would pin every visitor to it.\n const [serverCatalogRaw, setServerCatalogRaw] =\n useState<ExploreCatalog | null>(bakedRaw);\n const [messages, setMessages] = useState<ExploreMessage[]>([]);\n // A baked catalog is a loaded one: start in the state the static page was captured in.\n const [phase, setPhase] = useState<ExploreChatPhase>(() =>\n bakedRaw ? (bakedRaw.enabled === false ? \"disabled\" : \"ready\") : \"loading\",\n );\n const [error, setError] = useState<ExploreChatError | null>(null);\n const [remainingToday, setRemainingToday] = useState<number | null>(null);\n const [focusProduct, setFocusProductState] = useState<string | null>(null);\n const [turnCount, setTurnCount] = useState(0);\n // Mirror of `messages` for callbacks that must not re-create on every message.\n const messagesRef = useRef<ExploreMessage[]>([]);\n const [archive, setArchive] = useState<ArchivedExploreConversation[]>([]);\n\n const [hydrated, setHydrated] = useState(false);\n\n // Refs mirror the state that async callbacks read, so `send`/`poll` never\n // close over a stale value and never need to be re-created mid-flight.\n const mountedRef = useRef(true);\n const pollGenerationRef = useRef(0);\n const pollTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const conversationTokenRef = useRef<string | null>(null);\n /**\n * Render-visible mirror of `conversationTokenRef`.\n *\n * The ref alone cannot gate a control: a ref write does not re-render, so a form offered\n * on `conversationTokenRef.current !== null` would still be absent on the render after the\n * first message landed. Everything that assigns the token goes through\n * {@link rememberConversationToken} so the two cannot drift.\n */\n const [hasConversation, setHasConversation] = useState(false);\n /**\n * The `?c=` share token, read ONCE and synchronously.\n *\n * It cannot be discovered inside the share effect and then relied on by the hydration\n * effect: both run in the same commit, so the hydration effect would still close over\n * `isSharedView === false` and load the visitor's own stored thread underneath the shared\n * one. On a slow link that shows their old chat first and then swaps it; if the share\n * lookup fails, they are left staring at their old conversation next to a \"not found\"\n * error. Reading it here makes both effects agree on the first render.\n */\n const shareTokenRef = useRef<string | null | undefined>(undefined);\n if (shareTokenRef.current === undefined) {\n shareTokenRef.current = readShareToken();\n }\n // Derived, not state: the token comes from the URL, and leaving the shared view is a\n // full navigation (the \"New chat\" button drops `?c=` via `location.href`), so there is no\n // in-place transition for a setter to model.\n const isSharedView = shareTokenRef.current !== null;\n const catalogRef = useRef<ExploreCatalog>(catalog);\n const turnCountRef = useRef(0);\n const focusProductRef = useRef<string | null>(null);\n const lastSentTextRef = useRef<string | null>(null);\n // Retried alongside the text. Without this a retry after a network failure would re-ask the\n // question with the documents silently dropped, and the visitor would get a confidently\n // wrong answer with nothing on screen saying their files were not used.\n const lastSentAttachmentsRef = useRef<readonly ExploreAttachmentPayload[]>(\n [],\n );\n const inFlightRef = useRef(false);\n\n catalogRef.current = catalog;\n\n /**\n * The single writer for the conversation token.\n *\n * Async callbacks read the ref (so they never close over a stale value); render reads the\n * state (so a control can appear the moment a conversation exists). Writing both here is\n * what keeps \"we have a thread\" from being true in one and false in the other.\n */\n const rememberConversationToken = useCallback((token: string | null) => {\n conversationTokenRef.current = token;\n setHasConversation(token !== null);\n }, []);\n\n const clearPollTimer = useCallback(() => {\n if (pollTimerRef.current !== null) {\n clearTimeout(pollTimerRef.current);\n pollTimerRef.current = null;\n }\n }, []);\n\n /** Invalidate any running poll chain. Late responses become no-ops. */\n const cancelPolling = useCallback(() => {\n pollGenerationRef.current += 1;\n clearPollTimer();\n }, [clearPollTimer]);\n\n useEffect(() => {\n mountedRef.current = true;\n return () => {\n mountedRef.current = false;\n pollGenerationRef.current += 1;\n if (pollTimerRef.current !== null) {\n clearTimeout(pollTimerRef.current);\n pollTimerRef.current = null;\n }\n };\n }, []);\n\n // --------------------------------------------------------------------------\n // A shared link (`?c=<shareToken>`).\n //\n // Runs BEFORE the localStorage hydration below and short-circuits it: someone opening a\n // link they were sent must see that conversation, not whatever they happened to ask this\n // site last week. The secret in the URL is a read-only share token, never a conversation\n // token, so this view cannot continue the thread — which is why `isSharedView` is exposed\n // and the composer is disabled on it.\n // --------------------------------------------------------------------------\n useEffect(() => {\n const shareToken = shareTokenRef.current;\n if (!shareToken) return;\n\n let cancelled = false;\n setHydrated(true);\n\n void client\n .query<{ exploreSharedConversation?: ExploreMessage[] | null }>(\n EXPLORE_SHARED_CONVERSATION_QUERY,\n { shareToken },\n { cache: false },\n )\n .then((res) => {\n if (cancelled || !mountedRef.current) return;\n const shared = res?.data?.exploreSharedConversation;\n if (shared && shared.length > 0) {\n setMessages(shared);\n return;\n }\n // A token that resolves to nothing is an expired or wrong link. Say so rather than\n // showing an empty chat that looks like the feature is broken.\n setError({\n kind: \"unknown\",\n code: \"SHARE_NOT_FOUND\",\n message:\n \"That shared conversation could not be found. The link may have expired.\",\n // Nothing to retry: a bad token will stay bad.\n retryable: false,\n });\n })\n .catch(() => {\n if (cancelled || !mountedRef.current) return;\n setError({\n kind: \"unknown\",\n code: \"SHARE_NOT_FOUND\",\n message: \"That shared conversation could not be loaded.\",\n retryable: true,\n });\n });\n\n return () => {\n cancelled = true;\n };\n }, [client]);\n\n // --------------------------------------------------------------------------\n // Hydrate from localStorage (effect, not lazy state, so the FIRST client\n // render matches the prerendered HTML and hydration stays clean).\n // --------------------------------------------------------------------------\n useEffect(() => {\n // A shared link owns the transcript; never let stored history replace it. The ref, not\n // the state: this effect and the share effect run in the same commit.\n if (shareTokenRef.current !== null) return;\n if (!persist) {\n setHydrated(true);\n return;\n }\n setArchive(readArchive(storageKey));\n const stored = readPersisted(storageKey);\n if (stored) {\n rememberConversationToken(stored.conversationToken);\n focusProductRef.current = stored.focusProduct;\n // A turn that was still streaming when the tab was closed can never be\n // resumed from here — the poll chain is gone. Land it as-is.\n const settled = stored.messages.flatMap<ExploreMessage>((m) => {\n if (m.role !== \"ASSISTANT\") return [m];\n if (m.status !== \"PENDING\" && m.status !== \"RUNNING\") return [m];\n const text = visibleText(m);\n if (!text) return [];\n return [{ ...m, status: \"COMPLETED\", content: text }];\n });\n setMessages(settled);\n setFocusProductState(stored.focusProduct);\n const turns = countUserTurns(settled);\n turnCountRef.current = turns;\n setTurnCount(turns);\n }\n setHydrated(true);\n }, [persist, rememberConversationToken, storageKey]);\n\n // --------------------------------------------------------------------------\n // Persist (after hydration, so the initial empty state never wipes storage).\n // --------------------------------------------------------------------------\n useEffect(() => {\n if (!persist || !hydrated || typeof window === \"undefined\") return;\n try {\n const payload: PersistedExploreState = {\n conversationToken: conversationTokenRef.current,\n messages: messages.slice(-MAX_PERSISTED_MESSAGES),\n focusProduct,\n };\n // Nothing worth restoring (fresh page, or just after reset()): drop the\n // key entirely rather than leaving an empty husk behind.\n if (\n !payload.conversationToken &&\n payload.messages.length === 0 &&\n !payload.focusProduct\n ) {\n window.localStorage.removeItem(storageKey);\n return;\n }\n window.localStorage.setItem(storageKey, JSON.stringify(payload));\n } catch {\n /* private mode / quota exceeded — persistence is a nicety, not a feature */\n }\n }, [persist, hydrated, storageKey, messages, focusProduct]);\n\n // Mirror messages into a ref for callbacks, and fold a settled thread into the archive.\n // Archiving on every settled turn (not only on reset) is what makes an abruptly closed tab\n // still leave a record the visitor can come back to.\n useEffect(() => {\n messagesRef.current = messages;\n if (!persist || !hydrated) return;\n const settled = messages.some(\n (m) =>\n m.role === \"ASSISTANT\" &&\n (m.status === \"COMPLETED\" || m.status === \"FAILED\"),\n );\n if (!settled) return;\n setArchive(\n upsertArchive(storageKey, {\n token: conversationTokenRef.current,\n messages,\n focusProduct: focusProductRef.current,\n }),\n );\n }, [messages, persist, hydrated, storageKey]);\n\n // --------------------------------------------------------------------------\n // Catalog. Cached (the default) — it is per-page-load configuration, not a\n // poll. A failure keeps the fallback so the page NEVER renders blank.\n // --------------------------------------------------------------------------\n useEffect(() => {\n let cancelled = false;\n const load = async () => {\n const variables = { productSlug: productSlug ?? null };\n let res = await client.query<ExploreCatalogQueryData>(\n EXPLORE_CATALOG_QUERY,\n variables,\n );\n if (cancelled || !mountedRef.current) return;\n\n // A gateway whose supergraph predates `conceptHighlights` does not answer this\n // document without the field — it refuses the document outright, so `res.data` is\n // undefined and the loss is the entire catalog, not the strip. Retry once with the\n // field dropped rather than spending the whole window on `buildFallbackCatalog()`,\n // which hardcodes `enabled: true` (ignoring the only kill switch that needs no\n // redeploy) and `captchaRequired: false` (every send then rejected server-side while\n // the composer looks fine). The window is unavoidable even with perfect deploy\n // ordering: the prod gateway's CDN edge serves a stale supergraph for ~30-40 min\n // after the subgraph publishes.\n if (catalogRejectedTheHighlightField(res)) {\n res = await client.query<ExploreCatalogQueryData>(\n EXPLORE_CATALOG_QUERY_WITHOUT_HIGHLIGHTS,\n variables,\n );\n if (cancelled || !mountedRef.current) return;\n }\n\n const raw = res.data?.exploreCatalog;\n if (res.errors?.length || !raw) {\n console.warn(\n \"[WebSDK] exploreCatalog unavailable — keeping the catalog the page started with\",\n res.errors?.[0]?.message,\n );\n setPhase((prev) => (prev === \"loading\" ? \"ready\" : prev));\n return;\n }\n\n // Over the baked catalog when there is one: the retry against an older supergraph\n // answers without conceptHighlights, and merged over the fallback that dropped the\n // strip the static page had just shown.\n const next = normalizeCatalog(\n raw,\n bakedRaw\n ? normalizeCatalog(bakedRaw, fallbackCatalog)\n : fallbackCatalog,\n );\n setCatalog(next);\n setServerCatalogRaw(raw);\n catalogRef.current = next;\n setPhase((prev) => {\n if (!next.enabled) return \"disabled\";\n // \"disabled\" is only ever set from catalog.enabled, so a live enabled:true is the\n // authority. Without this a page that started from a baked enabled:false stayed off\n // after the live catalog said otherwise, until the site was rebuilt.\n return prev === \"loading\" || prev === \"disabled\" ? \"ready\" : prev;\n });\n };\n void load();\n return () => {\n cancelled = true;\n };\n }, [client, productSlug, fallbackCatalog, bakedRaw]);\n\n // --------------------------------------------------------------------------\n // Poll chain\n // --------------------------------------------------------------------------\n const startPolling = useCallback(\n (conversationToken: string, messageId: string) => {\n pollGenerationRef.current += 1;\n const generation = pollGenerationRef.current;\n const startedAt = Date.now();\n let consecutiveFailures = 0;\n\n const isStale = () =>\n !mountedRef.current || generation !== pollGenerationRef.current;\n\n const finishWithError = (chatError: ExploreChatError) => {\n inFlightRef.current = false;\n setMessages((prev) =>\n prev.flatMap<ExploreMessage>((m) => {\n if (m.id !== messageId) return [m];\n const text = visibleText(m);\n if (!text) return [];\n return [{ ...m, status: \"FAILED\", content: text }];\n }),\n );\n setError(chatError);\n setPhase(chatError.kind === \"rate-limit\" ? \"limited\" : \"error\");\n };\n\n const tick = async () => {\n if (isStale()) return;\n\n if (Date.now() - startedAt > pollTimeoutMs) {\n finishWithError(classifyExploreError(\"TIMEOUT\"));\n return;\n }\n\n // `cache: false` is load-bearing — see CONTRACT.md §4.\n const res = await client.query<ExploreMessageQueryData>(\n EXPLORE_MESSAGE_QUERY,\n { conversationToken, id: messageId },\n { cache: false, timeoutMs: POLL_REQUEST_TIMEOUT_MS },\n );\n if (isStale()) return;\n\n const first = res.errors?.[0];\n if (first) {\n const code =\n typeof first.extensions?.code === \"string\"\n ? first.extensions.code\n : undefined;\n const classified = classifyExploreError(code, first.message);\n // Hard stops: retrying a rate limit or a disabled feature is pointless.\n if (!classified.retryable) {\n finishWithError(classified);\n return;\n }\n consecutiveFailures += 1;\n if (consecutiveFailures >= MAX_CONSECUTIVE_POLL_FAILURES) {\n finishWithError(classified);\n return;\n }\n pollTimerRef.current = setTimeout(() => void tick(), pollIntervalMs);\n return;\n }\n\n const message = res.data?.exploreMessage;\n if (!message) {\n // Can happen briefly right after submit; tolerate a few misses.\n consecutiveFailures += 1;\n if (consecutiveFailures >= MAX_CONSECUTIVE_POLL_FAILURES) {\n finishWithError(classifyExploreError(\"NOT_FOUND\"));\n return;\n }\n pollTimerRef.current = setTimeout(() => void tick(), pollIntervalMs);\n return;\n }\n\n consecutiveFailures = 0;\n // Replace in place so `partialContent` renders as it streams.\n setMessages((prev) =>\n prev.map((m) => (m.id === messageId ? message : m)),\n );\n\n if (message.status === \"COMPLETED\") {\n inFlightRef.current = false;\n setError(null);\n setPhase(\"ready\");\n return;\n }\n if (message.status === \"FAILED\") {\n finishWithError(\n classifyExploreError(message.errorCode ?? \"UPSTREAM_UNAVAILABLE\"),\n );\n return;\n }\n\n setPhase(\"streaming\");\n pollTimerRef.current = setTimeout(() => void tick(), pollIntervalMs);\n };\n\n // First poll after one interval — the turn cannot possibly be done sooner.\n pollTimerRef.current = setTimeout(() => void tick(), pollIntervalMs);\n },\n [client, pollIntervalMs, pollTimeoutMs],\n );\n\n // --------------------------------------------------------------------------\n // send\n // --------------------------------------------------------------------------\n const submit = useCallback(\n async (\n text: string,\n optimisticId: string,\n captchaAttempt: number,\n attachments: readonly ExploreAttachmentPayload[],\n ): Promise<void> => {\n const activeCatalog = catalogRef.current;\n\n let recaptchaToken: string | undefined;\n if (activeCatalog.captchaRequired && activeCatalog.recaptchaSiteKey) {\n try {\n recaptchaToken = await getRecaptchaV3Token(\n activeCatalog.recaptchaSiteKey,\n );\n } catch (err) {\n console.warn(\"[WebSDK] reCAPTCHA v3 token could not be minted\", err);\n // Send without a token and let the backend decide — it owns the\n // policy, and a blocked third-party script must not silently kill\n // the whole feature.\n }\n }\n\n const input: SendExploreMessageInput = {\n message: text,\n ...(conversationTokenRef.current\n ? { conversationToken: conversationTokenRef.current }\n : {}),\n ...(focusProductRef.current || productSlug\n ? { productSlug: focusProductRef.current ?? productSlug }\n : {}),\n ...(recaptchaToken ? { recaptchaToken } : {}),\n ...(locale ? { locale } : {}),\n ...(pageUrl ? { pageUrl } : {}),\n ...(attachments.length > 0 ? { attachments: [...attachments] } : {}),\n };\n\n const res = await client.mutate<SendExploreMessageMutationData>(\n SEND_EXPLORE_MESSAGE_MUTATION,\n { input },\n { timeoutMs: SEND_REQUEST_TIMEOUT_MS },\n );\n if (!mountedRef.current) return;\n\n const first = res.errors?.[0];\n const result = res.data?.sendExploreMessage;\n\n if (first || !result) {\n const code =\n typeof first?.extensions?.code === \"string\"\n ? first.extensions.code\n : undefined;\n const classified = classifyExploreError(code, first?.message);\n\n // CAPTCHA_FAILED: one silent re-mint + re-send before bothering the\n // visitor. v3 tokens expire in ~2 minutes and are single-use, so a\n // stale one is a routine, self-healing failure.\n if (classified.kind === \"captcha\" && captchaAttempt === 0) {\n await submit(text, optimisticId, 1, attachments);\n return;\n }\n\n inFlightRef.current = false;\n setError(classified);\n setPhase(classified.kind === \"rate-limit\" ? \"limited\" : \"error\");\n return;\n }\n\n rememberConversationToken(result.conversation.token);\n turnCountRef.current = result.conversation.turnCount;\n setTurnCount(result.conversation.turnCount);\n setRemainingToday(result.remainingToday ?? null);\n\n const userMessage = result.userMessage;\n const assistantMessage = result.assistantMessage;\n\n // Swap the optimistic user bubble for the server's, then append the\n // PENDING assistant turn that the poll chain will fill in. The `some`\n // guard keeps the visitor's words on screen even in the impossible case\n // where the optimistic bubble is already gone.\n setMessages((prev) => {\n const replaced = prev.some((m) => m.id === optimisticId)\n ? prev.map((m) => (m.id === optimisticId ? userMessage : m))\n : [...prev, userMessage];\n return [...replaced, assistantMessage];\n });\n\n if (\n assistantMessage.status === \"COMPLETED\" ||\n assistantMessage.status === \"FAILED\"\n ) {\n inFlightRef.current = false;\n if (assistantMessage.status === \"FAILED\") {\n const classified = classifyExploreError(\n assistantMessage.errorCode ?? \"UPSTREAM_UNAVAILABLE\",\n );\n setError(classified);\n setPhase(\"error\");\n } else {\n setPhase(\"ready\");\n }\n return;\n }\n\n setPhase(\"streaming\");\n startPolling(result.conversation.token, assistantMessage.id);\n },\n [\n client,\n locale,\n pageUrl,\n productSlug,\n rememberConversationToken,\n startPolling,\n ],\n );\n\n const send = useCallback(\n async (\n message: string,\n attachments: readonly ExploreAttachmentPayload[] = [],\n ): Promise<void> => {\n const text = message.trim();\n // A question is still required: files are extra context for a question, not a question\n // on their own. The model has no way to guess what to do with a bare document.\n if (!text) return;\n if (inFlightRef.current) return;\n\n const activeCatalog = catalogRef.current;\n if (!activeCatalog.enabled) {\n setError(classifyExploreError(\"EXPLORE_DISABLED\"));\n setPhase(\"disabled\");\n return;\n }\n if (text.length > activeCatalog.limits.maxMessageLength) {\n setError({\n kind: \"validation\",\n code: \"MESSAGE_TOO_LONG\",\n message: `That message is ${text.length} characters — the limit is ${activeCatalog.limits.maxMessageLength}.`,\n retryable: false,\n });\n return;\n }\n if (\n turnCountRef.current >= activeCatalog.limits.maxTurnsPerConversation\n ) {\n setError({\n kind: \"validation\",\n code: \"CONVERSATION_LIMIT\",\n message: `This conversation has reached its ${activeCatalog.limits.maxTurnsPerConversation}-message limit. Start a new chat to keep going.`,\n retryable: false,\n });\n return;\n }\n\n inFlightRef.current = true;\n lastSentTextRef.current = text;\n lastSentAttachmentsRef.current = attachments;\n setError(null);\n setPhase(\"sending\");\n cancelPolling();\n\n // Optimistic user bubble — the composer clears instantly instead of\n // waiting a network round-trip for the visitor's own words.\n const optimistic: ExploreMessage = {\n id: makeOptimisticId(),\n conversationId: conversationTokenRef.current ?? \"pending\",\n role: \"USER\",\n status: \"COMPLETED\",\n content: text,\n partialContent: null,\n activity: [],\n references: [],\n ctas: [],\n errorCode: null,\n createdAt: new Date().toISOString(),\n completedAt: new Date().toISOString(),\n };\n setMessages((prev) => [...prev, optimistic]);\n turnCountRef.current += 1;\n setTurnCount(turnCountRef.current);\n onSend?.(text);\n\n try {\n await submit(text, optimistic.id, 0, attachments);\n } catch (err) {\n if (!mountedRef.current) return;\n console.error(\"[WebSDK] Explore send failed\", err);\n inFlightRef.current = false;\n setError(classifyExploreError(\"NETWORK_ERROR\"));\n setPhase(\"error\");\n }\n },\n [cancelPolling, onSend, submit],\n );\n\n const stop = useCallback(() => {\n cancelPolling();\n inFlightRef.current = false;\n setMessages((prev) =>\n prev.flatMap<ExploreMessage>((m) => {\n if (m.role !== \"ASSISTANT\") return [m];\n if (m.status !== \"PENDING\" && m.status !== \"RUNNING\") return [m];\n const text = visibleText(m);\n // Nothing streamed yet — drop the placeholder instead of leaving an\n // empty bubble behind.\n if (!text) return [];\n return [\n {\n ...m,\n status: \"COMPLETED\",\n content: text,\n completedAt: new Date().toISOString(),\n },\n ];\n }),\n );\n setPhase((prev) =>\n prev === \"sending\" || prev === \"streaming\" ? \"ready\" : prev,\n );\n }, [cancelPolling]);\n\n const retry = useCallback(() => {\n const text = lastSentTextRef.current;\n if (!text) return;\n cancelPolling();\n inFlightRef.current = false;\n // Drop the failed turn (and the user bubble that produced it) so the retry\n // does not stack a duplicate pair into the transcript.\n setMessages((prev) => {\n const next = [...prev];\n const last = next[next.length - 1];\n if (last?.role === \"ASSISTANT\" && last.status === \"FAILED\") next.pop();\n const prior = next[next.length - 1];\n if (prior?.role === \"USER\" && prior.content === text) next.pop();\n return next;\n });\n turnCountRef.current = Math.max(0, turnCountRef.current - 1);\n setTurnCount(turnCountRef.current);\n void send(text, lastSentAttachmentsRef.current);\n }, [cancelPolling, send]);\n\n const reset = useCallback(() => {\n // Archive the thread being left behind, so \"New chat\" never destroys history.\n setArchive(\n upsertArchive(storageKey, {\n token: conversationTokenRef.current,\n messages: messagesRef.current,\n focusProduct: focusProductRef.current,\n }),\n );\n cancelPolling();\n inFlightRef.current = false;\n rememberConversationToken(null);\n lastSentTextRef.current = null;\n lastSentAttachmentsRef.current = [];\n turnCountRef.current = 0;\n setTurnCount(0);\n setMessages([]);\n setError(null);\n setRemainingToday(null);\n setPhase(catalogRef.current.enabled ? \"ready\" : \"disabled\");\n if (typeof window !== \"undefined\") {\n try {\n window.localStorage.removeItem(storageKey);\n } catch {\n /* storage unavailable — nothing to clear */\n }\n }\n }, [cancelPolling, rememberConversationToken, storageKey]);\n\n /**\n * Reopen a past conversation. The messages come from the local archive rather than the server:\n * the backend expires a job's state, and a visitor looking at their own history should see it\n * whether or not the server still holds anything. The token is restored too, so the next\n * message continues the SAME server-side conversation.\n */\n const openConversation = useCallback(\n (token: string) => {\n const entry = readArchive(storageKey).find((e) => e.token === token);\n if (!entry) return;\n // Archive whatever is on screen first, or switching away would lose it.\n upsertArchive(storageKey, {\n token: conversationTokenRef.current,\n messages: messagesRef.current,\n focusProduct: focusProductRef.current,\n });\n cancelPolling();\n inFlightRef.current = false;\n rememberConversationToken(entry.token);\n turnCountRef.current = entry.messages.filter(\n (m) => m.role === \"USER\",\n ).length;\n setTurnCount(turnCountRef.current);\n setMessages(entry.messages);\n focusProductRef.current = entry.focusProduct;\n setFocusProductState(entry.focusProduct);\n setError(null);\n setPhase(catalogRef.current.enabled ? \"ready\" : \"disabled\");\n setArchive(readArchive(storageKey));\n },\n [cancelPolling, rememberConversationToken, storageKey],\n );\n\n /** Forget one past conversation. Local only — see the page's storage notice. */\n const deleteConversation = useCallback(\n (token: string) => {\n const next = readArchive(storageKey).filter((e) => e.token !== token);\n writeArchive(storageKey, next);\n setArchive(next);\n },\n [storageKey],\n );\n\n /** Forget every past conversation held in this browser. */\n const clearHistory = useCallback(() => {\n writeArchive(storageKey, []);\n setArchive([]);\n }, [storageKey]);\n\n const setFocusProduct = useCallback((slug: string | null) => {\n focusProductRef.current = slug;\n setFocusProductState(slug);\n }, []);\n\n const activity = useMemo<string | null>(() => {\n for (let i = messages.length - 1; i >= 0; i -= 1) {\n const m = messages[i];\n if (!m || m.role !== \"ASSISTANT\") continue;\n if (m.status !== \"PENDING\" && m.status !== \"RUNNING\") return null;\n const lines = m.activity ?? [];\n return lines.length ? (lines[lines.length - 1] ?? null) : null;\n }\n return null;\n }, [messages]);\n\n const canSend =\n (phase === \"ready\" || phase === \"error\") &&\n catalog.enabled &&\n turnCount < catalog.limits.maxTurnsPerConversation;\n\n /**\n * Tell the server the visitor followed one of the links this answer offered.\n *\n * Deliberately fire-and-forget and never awaited: the browser is navigating away as this\n * goes out, so anything that blocked on it would delay the click the visitor actually\n * made. Failures are swallowed — analytics must never break navigation.\n */\n const createShareLink = useCallback(async (): Promise<string | null> => {\n const token = conversationTokenRef.current;\n if (!token) return null;\n try {\n const result = await client.mutate<{\n createExploreShareLink?: { shareToken?: string | null } | null;\n }>(CREATE_EXPLORE_SHARE_LINK_MUTATION, { conversationToken: token });\n return result?.data?.createExploreShareLink?.shareToken ?? null;\n } catch {\n // Sharing is a convenience; a failure must not take the conversation down with it.\n return null;\n }\n }, [client]);\n\n const recordClick = useCallback(\n (messageId: string, kind: \"CTA\" | \"REFERENCE\", url: string): void => {\n const token = conversationTokenRef.current;\n if (!token) return;\n try {\n void client\n .mutate(RECORD_EXPLORE_CLICK_MUTATION, {\n input: { conversationToken: token, messageId, kind, url },\n })\n .catch(() => {\n /* the visitor is mid-navigation; there is nothing useful to do here */\n });\n } catch {\n /* client unavailable — same reasoning */\n }\n },\n [client],\n );\n\n /**\n * Rate one answer, and fold the server's record back into the transcript.\n *\n * The fold matters beyond the control that triggered it: `messages` is what the persist\n * effect writes to localStorage, so a rating stored here survives a reload of the same\n * conversation and not just this render. The optimistic state in the UI covers the moment\n * before this resolves; this covers everything after.\n */\n const submitFeedback = useCallback(\n async (\n messageId: string,\n rating: ExploreFeedbackRating,\n detail?: {\n reason?: ExploreFeedbackReason | null;\n comment?: string | null;\n },\n ): Promise<boolean> => {\n const token = conversationTokenRef.current;\n // No conversation of our own (a fresh page, or a read-only shared view, whose `?c=`\n // secret is not a conversation token): the server could only answer `recorded: false`.\n if (!token) return false;\n\n const comment = detail?.comment?.trim();\n // Annotated, not inferred. `client.mutate` types its variables as an open\n // `Record<string, unknown>`, so an input literal passed inline is unchecked — renaming\n // `messageId` to `id`, or adding a key the server does not accept, would compile\n // cleanly and fail only at runtime against the live gateway. Naming the type is what\n // makes CONTRACT.md compile-checked. `sendMessage` above does the same.\n const input: SubmitExploreFeedbackInput = {\n conversationToken: token,\n messageId,\n rating,\n // Only meaningful with NOT_HELPFUL. The server stores null for HELPFUL either\n // way; sending it would just be noise the backend has to defend against.\n reason: rating === \"NOT_HELPFUL\" ? (detail?.reason ?? null) : null,\n comment: comment ? comment : null,\n };\n try {\n const res = await client.mutate<SubmitExploreFeedbackMutationData>(\n SUBMIT_EXPLORE_FEEDBACK_MUTATION,\n { input },\n );\n const result = res?.data?.submitExploreFeedback;\n if (!result?.recorded) return false;\n\n const recorded = result.feedback;\n if (recorded && mountedRef.current) {\n setMessages((current) =>\n current.map((message) =>\n message.id === messageId\n ? { ...message, feedback: recorded }\n : message,\n ),\n );\n }\n return true;\n } catch {\n // `client.mutate` resolves every outcome rather than throwing, so this is belt and\n // braces for a client that is not there at all. Either way: not recorded.\n return false;\n }\n },\n [client],\n );\n\n /**\n * Hand the conversation to a person.\n *\n * The visitor pressed a control and is waiting on it, so this is awaited and reports what\n * actually happened rather than a boolean: `already-handed-over` in particular is a\n * SUCCESS the caller has to be able to tell apart from a failure, and `invalid-email` is\n * the server's verdict on an address the form's own check let through.\n *\n * `email` never leaves this function except in the mutation variables. It is not folded\n * into `messages` (the persist effect writes those to localStorage), not written to\n * storage directly, and not passed to `onSend`. Anyone adding \"just log the address\" here\n * is putting personal data in a place nothing ever deletes it from.\n */\n const escalate = useCallback(\n async (email: string, note?: string): Promise<ExploreEscalateResult> => {\n const token = conversationTokenRef.current;\n // A read-only shared view (its `?c=` secret is not a conversation token) and a page\n // where nothing has been asked yet both land here. The server could only answer\n // NOT_FOUND, so do not spend a request finding that out.\n if (!token) {\n return { outcome: \"no-conversation\", email: null, createdAt: null };\n }\n\n const attempt = async (\n captchaAttempt: number,\n ): Promise<ExploreEscalateResult> => {\n const activeCatalog = catalogRef.current;\n\n // Exactly what `submit` does, including the default action — the contract puts the\n // whole explore surface on ONE v3 action on purpose, because an action mismatch is a\n // silent 100% rejection.\n let recaptchaToken: string | undefined;\n if (activeCatalog.captchaRequired && activeCatalog.recaptchaSiteKey) {\n try {\n recaptchaToken = await getRecaptchaV3Token(\n activeCatalog.recaptchaSiteKey,\n );\n } catch (err) {\n console.warn(\n \"[WebSDK] reCAPTCHA v3 token could not be minted\",\n err,\n );\n // Send without one and let the backend decide: it owns the policy, and a\n // blocked third-party script must not be what stops someone reaching a human.\n }\n }\n\n // Annotated, not inferred — `client.mutate` types its variables as an open\n // `Record<string, unknown>`, so an inline literal is unchecked and a renamed field\n // would compile cleanly and fail against the live gateway. Same rule as\n // `submitFeedback` and `submit`.\n const input: EscalateExploreConversationInput = {\n conversationToken: token,\n email: email.trim(),\n note: note?.trim() ? note.trim() : null,\n ...(recaptchaToken ? { recaptchaToken } : {}),\n ...(productId ? { productId } : {}),\n };\n\n try {\n const res =\n await client.mutate<EscalateExploreConversationMutationData>(\n ESCALATE_EXPLORE_CONVERSATION_MUTATION,\n { input },\n );\n\n const first = res?.errors?.[0];\n const result = res?.data?.escalateExploreConversation;\n\n if (first || !result) {\n const code =\n typeof first?.extensions?.code === \"string\"\n ? first.extensions.code\n : \"ESCALATION_FAILED\";\n\n // One silent re-mint, exactly as `submit` does: a v3 token is single-use and\n // expires in ~2 minutes, so a rejected one is routine and self-healing.\n if (code === \"CAPTCHA_FAILED\" && captchaAttempt === 0) {\n return attempt(1);\n }\n\n return {\n outcome: escalateOutcomeForCode(code),\n email: null,\n createdAt: null,\n };\n }\n\n return {\n // `created: false` is the double-click, the retry, the reload. The handover\n // exists, so this is a success — and the address comes from the SERVER, because\n // on a repeat submit the reply goes to whatever the FIRST handover used.\n outcome: result.created ? \"sent\" : \"already-handed-over\",\n email: result.escalation?.email ?? null,\n createdAt: result.escalation?.createdAt ?? null,\n };\n } catch {\n // `client.mutate` resolves every transport outcome rather than throwing, so this\n // is for a client that is not there at all. Either way: nothing was handed over,\n // and this function's contract is that it never throws at the caller.\n return { outcome: \"failed\", email: null, createdAt: null };\n }\n };\n\n return attempt(0);\n },\n [client, productId],\n );\n\n /**\n * A shared view holds a share secret, not a conversation token, so `hasConversation` is\n * already false there. Both are named anyway: the second is the reason, and a later\n * refactor that starts restoring a token under a shared transcript should fail this\n * condition rather than quietly start offering the form.\n */\n const canEscalate = hasConversation && !isSharedView;\n\n return {\n phase,\n catalog,\n serverCatalogRaw,\n messages,\n activity,\n remainingToday,\n error,\n focusProduct,\n setFocusProduct,\n send,\n stop,\n retry,\n reset,\n canSend,\n history: archive,\n openConversation,\n deleteConversation,\n clearHistory,\n recordClick,\n submitFeedback,\n escalate,\n canEscalate,\n createShareLink,\n isSharedView,\n };\n}\n\n/**\n * Client-only id for the optimistic user bubble, swapped for the server's\n * `userMessage.id` as soon as the mutation resolves. The counter only has to\n * make ids unique within a tab, never across one.\n */\nlet optimisticCounter = 0;\n\nfunction makeOptimisticId(): string {\n optimisticCounter += 1;\n return `boff-explore-local-${Date.now()}-${optimisticCounter}`;\n}\n","\"use client\";\n\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\n/**\n * Dictation for the explore composer, on the browser's own speech recogniser.\n *\n * Uses the Web Speech API directly — no dependency, no network call of our own, no audio ever\n * leaving the browser through us. Support is genuinely partial (Chrome, Edge and Safari expose\n * it, most commonly behind the `webkit` prefix; Firefox does not implement it at all), so the\n * caller MUST hide the microphone entirely when `supported` is false rather than offering a\n * control that cannot work.\n *\n * Interim results are surfaced separately from final ones so the composer can show speech\n * appearing live while only committing settled text to the draft.\n *\n * ## Mobile\n *\n * Mobile took three separate fixes, each of which looked like the whole problem:\n *\n * 1. **The prompt never appeared.** `recognition.start()` raises the permission prompt on\n * desktop Chrome but on Android Chrome and iOS fires `not-allowed` without showing\n * anything, so a visitor who had never been asked was told to change a browser setting.\n * `getUserMedia` is what actually prompts, so it is primed first.\n * 2. **Granting still failed.** Recognition opens its OWN capture, and starting it in the same\n * tick that the primed stream is being torn down races that teardown. It now waits for the\n * release, and retries once — Android frequently refuses the first attempt after a\n * permission is newly granted and accepts the second.\n * 3. **`continuous` is not honoured on mobile.** Android Chrome ends the session after a\n * single utterance regardless, and asking for continuous mode makes it fail sooner. Mobile\n * therefore runs single-shot and is restarted from `onend` for as long as the visitor is\n * still dictating, which behaves like continuous without fighting the platform.\n */\n\ninterface SpeechRecognitionAlternativeLike {\n transcript: string;\n}\ninterface SpeechRecognitionResultLike {\n readonly isFinal: boolean;\n readonly length: number;\n [index: number]: SpeechRecognitionAlternativeLike;\n}\ninterface SpeechRecognitionEventLike {\n readonly resultIndex: number;\n readonly results: {\n readonly length: number;\n [index: number]: SpeechRecognitionResultLike;\n };\n}\ninterface SpeechRecognitionErrorEventLike {\n readonly error: string;\n}\ninterface SpeechRecognitionLike {\n lang: string;\n continuous: boolean;\n interimResults: boolean;\n maxAlternatives: number;\n start: () => void;\n stop: () => void;\n abort: () => void;\n onresult: ((event: SpeechRecognitionEventLike) => void) | null;\n onerror: ((event: SpeechRecognitionErrorEventLike) => void) | null;\n onend: (() => void) | null;\n onstart: (() => void) | null;\n}\ntype SpeechRecognitionCtor = new () => SpeechRecognitionLike;\n\ninterface SpeechWindow {\n SpeechRecognition?: SpeechRecognitionCtor;\n webkitSpeechRecognition?: SpeechRecognitionCtor;\n}\n\nfunction getRecognitionCtor(): SpeechRecognitionCtor | null {\n if (typeof window === \"undefined\") return null;\n const w = window as unknown as SpeechWindow;\n return w.SpeechRecognition ?? w.webkitSpeechRecognition ?? null;\n}\n\n/**\n * Coarse pointer means a touch device, which is what actually predicts the recogniser's\n * behaviour here — not the screen size, and not a UA string that lies.\n */\nfunction isTouchDevice(): boolean {\n if (typeof window === \"undefined\" || !window.matchMedia) return false;\n try {\n return window.matchMedia(\"(pointer: coarse)\").matches;\n } catch {\n return false;\n }\n}\n\nconst MIC_DENIED_MESSAGE =\n \"Microphone access was blocked. Allow it in your browser's site settings to dictate.\";\nconst MIC_UNAVAILABLE_MESSAGE =\n \"Dictation isn't available in this browser. You can type instead.\";\n\n/** Let a released capture settle before recognition opens its own. */\nconst RELEASE_SETTLE_MS = 250;\n/** Android often refuses the first start after a fresh grant and accepts the next. */\nconst RETRY_DELAY_MS = 350;\n\nconst sleep = (ms: number): Promise<void> =>\n new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n\n/**\n * Ask for the microphone the only way that reliably raises a prompt on mobile.\n *\n * `confirmed` reports whether access was actually VERIFIED, which is what separates \"you need\n * to grant this\" from \"your browser cannot do this\" further down.\n *\n * Must be called synchronously from the user gesture that requested dictation — awaiting\n * anything first spends the gesture and the browser then rejects the request outright.\n */\nasync function ensureMicrophoneAccess(): Promise<\n | { ok: true; confirmed: boolean; primed: boolean }\n | { ok: false; message: string }\n> {\n const media =\n typeof navigator === \"undefined\" ? undefined : navigator.mediaDevices;\n // Older/unusual browsers without mediaDevices: fall through and let recognition try. It\n // may well work, and refusing here would remove dictation from browsers that support it.\n if (!media?.getUserMedia)\n return { ok: true, confirmed: false, primed: false };\n\n // `granted` means the prompt would not appear anyway, so skip the extra capture — which\n // also avoids the teardown race entirely on repeat use.\n //\n // A `denied` reading is deliberately NOT trusted to short-circuit. The Permissions API\n // reports `denied` for a prompt the visitor merely dismissed, and its microphone support\n // is uneven across mobile browsers — believing it would recreate the exact bug this\n // function exists to fix, telling someone to change a setting instead of asking them.\n // getUserMedia is the authoritative check, and when access really is denied it rejects\n // immediately without showing anything, so the only cost of asking is a settled promise.\n //\n // `permissions` is absent on Safari and `microphone` is not a valid name everywhere, so\n // every part of this is optional and failure is non-fatal.\n try {\n const status = await navigator.permissions?.query({\n name: \"microphone\" as PermissionName,\n });\n if (status?.state === \"granted\")\n return { ok: true, confirmed: true, primed: false };\n } catch {\n /* permissions API unavailable or the name is unsupported — prompt below instead */\n }\n\n try {\n const stream = await media.getUserMedia({ audio: true });\n // Release immediately: this capture existed only to raise the prompt, and leaving it\n // open would show a recording indicator for the whole session — and on mobile would\n // occupy the very microphone recognition is about to ask for.\n for (const track of stream.getTracks()) track.stop();\n return { ok: true, confirmed: true, primed: true };\n } catch (error) {\n const name =\n typeof error === \"object\" && error !== null && \"name\" in error\n ? String((error as { name: unknown }).name)\n : \"\";\n if (name === \"NotAllowedError\" || name === \"SecurityError\") {\n return { ok: false, message: MIC_DENIED_MESSAGE };\n }\n if (name === \"NotFoundError\" || name === \"DevicesNotFoundError\") {\n return { ok: false, message: \"No microphone was found.\" };\n }\n return {\n ok: false,\n message: \"Dictation could not start. You can type instead.\",\n };\n }\n}\n\n/** Messages a visitor can act on. `not-allowed` is the one that needs real explanation. */\nfunction describeError(code: string): string {\n switch (code) {\n case \"not-allowed\":\n case \"service-not-allowed\":\n return MIC_DENIED_MESSAGE;\n case \"no-speech\":\n return \"I didn't catch anything — try again a little closer to the mic.\";\n case \"audio-capture\":\n return \"No microphone was found.\";\n case \"network\":\n return \"Speech recognition needs a network connection.\";\n case \"aborted\":\n return \"\";\n default:\n return \"Dictation stopped unexpectedly. You can type instead.\";\n }\n}\n\nexport interface UseSpeechInputOptions {\n /** Receives settled text as it is recognised. Append it to the draft. */\n onFinalTranscript: (text: string) => void;\n /** BCP-47 tag. Defaults to the browser's language. */\n lang?: string;\n /** Called when the recogniser reports a problem worth showing. */\n onError?: (message: string) => void;\n}\n\nexport interface UseSpeechInputResult {\n /** False when the browser has no speech recogniser — hide the control entirely. */\n supported: boolean;\n listening: boolean;\n /** Words recognised but not yet settled; render as a live hint, do not store. */\n interim: string;\n error: string | null;\n start: () => void;\n stop: () => void;\n toggle: () => void;\n}\n\nexport function useSpeechInput({\n onFinalTranscript,\n lang,\n onError,\n}: UseSpeechInputOptions): UseSpeechInputResult {\n const [supported] = useState(() => getRecognitionCtor() !== null);\n const [listening, setListening] = useState(false);\n const [interim, setInterim] = useState(\"\");\n const [error, setError] = useState<string | null>(null);\n\n const recognitionRef = useRef<SpeechRecognitionLike | null>(null);\n /**\n * Bumped on every start()/stop(). A permission prompt, a settle delay or a retry resolving\n * after the visitor moved on must not open a microphone behind them.\n */\n const startTokenRef = useRef(0);\n /**\n * True only when the microphone was actually VERIFIED for this attempt — permission\n * reported granted, or getUserMedia succeeded. Stays false on browsers with no\n * mediaDevices, where nothing was checked and a recogniser `not-allowed` may still be a\n * real permission problem.\n */\n const micConfirmedRef = useRef(false);\n /** The visitor's intent, as opposed to whether a session happens to be open right now. */\n const wantsListeningRef = useRef(false);\n /** One retry per start(), for the Android \"refuses the first attempt\" case. */\n const retriedRef = useRef(false);\n\n // Held in refs so the recogniser's long-lived handlers always call the latest closures\n // without tearing down and re-creating the recogniser on every render.\n const finalRef = useRef(onFinalTranscript);\n const errorRef = useRef(onError);\n finalRef.current = onFinalTranscript;\n errorRef.current = onError;\n\n const langRef = useRef(lang);\n langRef.current = lang;\n\n const stop = useCallback(() => {\n startTokenRef.current += 1;\n wantsListeningRef.current = false;\n setListening(false);\n setInterim(\"\");\n const recognition = recognitionRef.current;\n if (!recognition) return;\n try {\n recognition.stop();\n } catch {\n /* already stopped */\n }\n }, []);\n\n /** Open a recogniser session. Separate from start() so a retry can reuse it. */\n const openSession = useCallback((token: number) => {\n const Ctor = getRecognitionCtor();\n if (!Ctor || token !== startTokenRef.current) return;\n\n const recognition = new Ctor();\n recognition.lang =\n langRef.current ??\n (typeof navigator === \"undefined\"\n ? \"en-US\"\n : navigator.language || \"en-US\");\n // Continuous mode is a desktop-only affordance. Android Chrome ends the session after a\n // single utterance whatever this says, and asking for continuous makes it fail sooner —\n // mobile is restarted from `onend` instead, which behaves the same to the visitor.\n recognition.continuous = !isTouchDevice();\n recognition.interimResults = true;\n recognition.maxAlternatives = 1;\n\n recognition.onstart = () => {\n if (token !== startTokenRef.current) return;\n // NOTE: the retry budget is deliberately NOT reset here. It belongs to the visitor's\n // press, not to the session — a browser that fires onstart and then immediately\n // errors would otherwise retry forever.\n setError(null);\n setListening(true);\n };\n\n recognition.onresult = (event) => {\n if (token !== startTokenRef.current) return;\n let settled = \"\";\n let pending = \"\";\n for (let i = event.resultIndex; i < event.results.length; i += 1) {\n const result = event.results[i];\n if (!result) continue;\n const text = result[0]?.transcript ?? \"\";\n if (result.isFinal) settled += text;\n else pending += text;\n }\n setInterim(pending);\n if (settled.trim() !== \"\") finalRef.current(settled);\n };\n\n recognition.onerror = (event) => {\n if (token !== startTokenRef.current) return;\n\n // Android commonly refuses the FIRST start after a permission is newly granted and\n // accepts the next one. Retry once, silently, before telling the visitor anything —\n // an error message they can do nothing about is worse than a short pause.\n if (\n (event.error === \"not-allowed\" ||\n event.error === \"service-not-allowed\") &&\n micConfirmedRef.current &&\n !retriedRef.current\n ) {\n retriedRef.current = true;\n void sleep(RETRY_DELAY_MS).then(() => {\n if (token !== startTokenRef.current || !wantsListeningRef.current)\n return;\n openSessionRef.current?.(token);\n });\n return;\n }\n\n // The microphone was verified moments ago, so a `not-allowed` that survived the retry\n // is the SPEECH SERVICE refusing, not a permission the visitor can grant. Sending them\n // to a site setting would point at something that cannot help.\n const message =\n micConfirmedRef.current &&\n (event.error === \"not-allowed\" || event.error === \"service-not-allowed\")\n ? MIC_UNAVAILABLE_MESSAGE\n : describeError(event.error);\n\n wantsListeningRef.current = false;\n setListening(false);\n setInterim(\"\");\n if (message !== \"\") {\n setError(message);\n errorRef.current?.(message);\n }\n };\n\n recognition.onend = () => {\n if (token !== startTokenRef.current) return;\n setInterim(\"\");\n // Single-shot mobile sessions end after each utterance. While the visitor still has\n // dictation switched on, reopen so it behaves like continuous mode.\n if (wantsListeningRef.current && isTouchDevice()) {\n void sleep(120).then(() => {\n if (token !== startTokenRef.current || !wantsListeningRef.current)\n return;\n openSessionRef.current?.(token);\n });\n return;\n }\n setListening(false);\n };\n\n recognitionRef.current = recognition;\n try {\n recognition.start();\n } catch {\n // Chrome throws InvalidStateError if start() races a session that is still closing.\n // The onend handler above will not fire for a session that never began, so settle here.\n wantsListeningRef.current = false;\n setListening(false);\n }\n }, []);\n\n // openSession refers to itself from its own handlers (retry, mobile restart), which a plain\n // const cannot do inside useCallback.\n const openSessionRef = useRef<((token: number) => void) | null>(null);\n openSessionRef.current = openSession;\n\n const start = useCallback(() => {\n if (!getRecognitionCtor()) return;\n\n startTokenRef.current += 1;\n const token = startTokenRef.current;\n wantsListeningRef.current = true;\n retriedRef.current = false;\n micConfirmedRef.current = false;\n\n // Show the pressed state straight away: the permission prompt can sit on screen for a\n // while on mobile, and a mic button that looks inert invites a second tap.\n setError(null);\n setListening(true);\n\n void ensureMicrophoneAccess().then(async (access) => {\n // A stop() or a newer start() landed while the prompt was open — abandon this attempt.\n if (token !== startTokenRef.current) return;\n\n if (!access.ok) {\n wantsListeningRef.current = false;\n setListening(false);\n setError(access.message);\n errorRef.current?.(access.message);\n return;\n }\n\n micConfirmedRef.current = access.confirmed;\n\n // Recognition opens its own capture. Starting in the same tick that the primed stream\n // is being torn down races that teardown and is refused — this is what made \"I granted\n // it and it still says blocked\" happen on mobile.\n if (access.primed) {\n await sleep(RELEASE_SETTLE_MS);\n if (token !== startTokenRef.current) return;\n }\n\n openSessionRef.current?.(token);\n });\n }, []);\n\n const toggle = useCallback(() => {\n if (listening) stop();\n else start();\n }, [listening, start, stop]);\n\n // Never leave the microphone open behind a closed page.\n useEffect(\n () => () => {\n startTokenRef.current += 1;\n wantsListeningRef.current = false;\n const recognition = recognitionRef.current;\n if (!recognition) return;\n recognition.onresult = null;\n recognition.onerror = null;\n recognition.onend = null;\n recognition.onstart = null;\n try {\n recognition.abort();\n } catch {\n /* nothing to abort */\n }\n },\n [],\n );\n\n return { supported, listening, interim, error, start, stop, toggle };\n}\n","\"use client\";\n\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\n/**\n * Read an answer aloud, on the browser's own speech synthesiser.\n *\n * The other half of {@link useSpeechInput}: that one turns a visitor's voice into a\n * question, this one turns our answer back into speech. Same posture — the Web Speech API\n * directly, no dependency, no request of ours, no cost, and nothing leaves the browser.\n *\n * ## Why `supported` can go false after a press\n *\n * `window.speechSynthesis` existing proves nothing. Several browsers expose the object and\n * then never speak: some Android WebViews with no TTS engine installed, Chrome on a machine\n * with no voices, and any engine where the utterance is refused without an `error` event.\n * There is no capability flag for that, so the only honest test is to try once — a press\n * that neither starts nor errors within {@link SILENT_FAILURE_MS}, while\n * `speechSynthesis.speaking` is still false, means this browser cannot do it and the control\n * removes itself. A button that does nothing, twice, is worse than no button.\n *\n * ## One voice per page\n *\n * `speechSynthesis` is a single queue shared by the whole document, so two answers reading\n * at once is not a thing the platform can do — a second `speak()` would simply queue behind\n * the first, minutes later. Every instance of this hook therefore registers with the\n * module-level owner below, and taking the voice stops whoever had it. {@link stopSpeaking}\n * is the same handle for code that is not a hook: the composer calls it when a new message\n * is sent, because an answer still reading aloud over the next question is the complaint\n * people actually make about pages that talk.\n */\n\ninterface SpeechSynthesisVoiceLike {\n readonly lang: string;\n readonly name: string;\n readonly default: boolean;\n}\n\ninterface SpeechSynthesisUtteranceLike {\n text: string;\n lang: string;\n voice: SpeechSynthesisVoiceLike | null;\n onstart: (() => void) | null;\n onend: (() => void) | null;\n onerror: ((event: { error?: string }) => void) | null;\n}\n\ntype SpeechSynthesisUtteranceCtor = new (\n text: string,\n) => SpeechSynthesisUtteranceLike;\n\ninterface SpeechSynthesisLike {\n readonly speaking: boolean;\n speak: (utterance: SpeechSynthesisUtteranceLike) => void;\n cancel: () => void;\n getVoices: () => SpeechSynthesisVoiceLike[];\n}\n\ninterface SpeechOutputWindow {\n speechSynthesis?: SpeechSynthesisLike;\n SpeechSynthesisUtterance?: SpeechSynthesisUtteranceCtor;\n}\n\n/** How long a press has to produce speech before the browser is taken at its word. */\nconst SILENT_FAILURE_MS = 1500;\n\n/**\n * The two API pieces, or null when this browser has no synthesiser at all (every iOS\n * in-app WebView without the entitlement, and anything server-side).\n */\nfunction speechPlatform(): {\n synth: SpeechSynthesisLike;\n Utterance: SpeechSynthesisUtteranceCtor;\n} | null {\n if (typeof window === \"undefined\") return null;\n const scope = window as unknown as SpeechOutputWindow;\n const synth = scope.speechSynthesis;\n const Utterance = scope.SpeechSynthesisUtterance;\n if (!synth || typeof synth.speak !== \"function\") return null;\n if (typeof Utterance !== \"function\") return null;\n return { synth, Utterance };\n}\n\n/** Lowercased, `-` separated: platforms spell the same tag `en_US`, `en-us` and `en-US`. */\nfunction normaliseTag(tag: string): string {\n return tag.trim().toLowerCase().replace(/_/g, \"-\");\n}\n\nfunction primaryOf(tag: string): string {\n return normaliseTag(tag).split(\"-\")[0] ?? \"\";\n}\n\n/**\n * The best voice for `lang`, or null to let the platform choose.\n *\n * Exact tag first (`ta-IN` over `ta-LK`), then the platform's default for the language, then\n * anything speaking it. Null rather than a wrong-language voice: an utterance with no voice\n * and the right `lang` is read by the platform's own fallback, which is closer than reading\n * Tamil with an English voice.\n */\nexport function pickVoice(\n voices: readonly SpeechSynthesisVoiceLike[],\n lang: string,\n): SpeechSynthesisVoiceLike | null {\n const wanted = normaliseTag(lang);\n if (wanted === \"\") return null;\n const primary = primaryOf(wanted);\n\n const exact = voices.filter((voice) => normaliseTag(voice.lang) === wanted);\n const related = voices.filter((voice) => primaryOf(voice.lang) === primary);\n\n return (\n exact.find((voice) => voice.default) ??\n exact[0] ??\n related.find((voice) => voice.default) ??\n related[0] ??\n null\n );\n}\n\n/** Whoever currently holds the page's one voice, and how to take it off them. */\nlet activeOwner: object | null = null;\nlet activeStop: (() => void) | null = null;\n\n/**\n * Stop whatever this SDK is reading aloud, from outside a component.\n *\n * A no-op when nothing of ours is speaking — deliberately, rather than calling\n * `speechSynthesis.cancel()` unconditionally, which would also silence a host page that is\n * using the same queue for its own reasons.\n */\nexport function stopSpeaking(): void {\n activeStop?.();\n}\n\nexport interface UseSpeechOutputOptions {\n /** BCP-47 tag to read in. Omit to let the platform pick its default voice. */\n lang?: string;\n}\n\nexport interface UseSpeechOutputResult {\n /**\n * False when this browser has no synthesiser, or has one that was asked to speak and did\n * not. Render no control at all when it is false — see the note at the top of the file.\n */\n supported: boolean;\n speaking: boolean;\n speak: (text: string) => void;\n stop: () => void;\n toggle: (text: string) => void;\n}\n\nexport function useSpeechOutput({\n lang,\n}: UseSpeechOutputOptions = {}): UseSpeechOutputResult {\n const [available, setAvailable] = useState(() => speechPlatform() !== null);\n const [speaking, setSpeaking] = useState(false);\n\n /** The utterance this instance is waiting on. Also \"do we own the voice right now\". */\n const utteranceRef = useRef<SpeechSynthesisUtteranceLike | null>(null);\n const watchdogRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n /** A stable identity for this instance, so the module-level owner can be compared. */\n const ownerRef = useRef<object>({});\n /** Lets the owner registration call the latest `stop` without re-registering. */\n const stopRef = useRef<() => void>(() => undefined);\n\n const langRef = useRef(lang);\n langRef.current = lang;\n\n const clearWatchdog = useCallback(() => {\n if (watchdogRef.current === null) return;\n clearTimeout(watchdogRef.current);\n watchdogRef.current = null;\n }, []);\n\n /** Forget this turn without touching the platform — for `end`, `error` and the watchdog. */\n const settle = useCallback(() => {\n clearWatchdog();\n utteranceRef.current = null;\n if (activeOwner === ownerRef.current) {\n activeOwner = null;\n activeStop = null;\n }\n setSpeaking(false);\n }, [clearWatchdog]);\n\n const stop = useCallback(() => {\n // Only cancel a queue we are actually using. Unmounting one answer's controls must not\n // silence a different answer that is mid-sentence.\n const owned = utteranceRef.current !== null;\n settle();\n if (owned) speechPlatform()?.synth.cancel();\n }, [settle]);\n\n stopRef.current = stop;\n\n const speak = useCallback(\n (text: string) => {\n const platform = speechPlatform();\n if (!platform) {\n setAvailable(false);\n return;\n }\n const spoken = text.trim();\n if (spoken === \"\") return;\n\n // Take the page's one voice, and clear anything already queued on it.\n if (activeStop && activeOwner !== ownerRef.current) activeStop();\n platform.synth.cancel();\n\n const utterance = new platform.Utterance(spoken);\n const wanted = langRef.current;\n if (wanted !== undefined && wanted !== \"\") {\n // Voices are fetched at press time rather than subscribed to: `getVoices()` is\n // empty until the platform has loaded them, and by the time a visitor has read an\n // answer and reached for this button, it has.\n const voice = pickVoice(platform.synth.getVoices(), wanted);\n utterance.lang = voice?.lang ?? wanted;\n if (voice) utterance.voice = voice;\n }\n\n const mine = (): boolean => utteranceRef.current === utterance;\n utterance.onstart = () => {\n if (!mine()) return;\n clearWatchdog();\n setSpeaking(true);\n };\n utterance.onend = () => {\n if (!mine()) return;\n settle();\n };\n utterance.onerror = (event) => {\n if (!mine()) return;\n const code = typeof event?.error === \"string\" ? event.error : \"\";\n settle();\n // `interrupted`/`canceled` is our own `cancel()` coming back — the visitor pressed\n // stop, or sent another message. Anything else is the engine refusing, and a\n // control that has now failed once is a control worth removing.\n if (code !== \"interrupted\" && code !== \"canceled\") setAvailable(false);\n };\n\n utteranceRef.current = utterance;\n activeOwner = ownerRef.current;\n activeStop = () => {\n stopRef.current();\n };\n // Optimistic, like the microphone: the engine can take a moment to start and a\n // control that looks inert invites a second press.\n setSpeaking(true);\n platform.synth.speak(utterance);\n\n clearWatchdog();\n watchdogRef.current = setTimeout(() => {\n watchdogRef.current = null;\n if (!mine()) return;\n // It is talking and simply never fired `start` — leave it alone.\n if (platform.synth.speaking) return;\n settle();\n setAvailable(false);\n }, SILENT_FAILURE_MS);\n },\n [clearWatchdog, settle],\n );\n\n const toggle = useCallback(\n (text: string) => {\n if (speaking) stop();\n else speak(text);\n },\n [speak, speaking, stop],\n );\n\n // A page that keeps talking after you leave it is the failure people complain about.\n // `pagehide` covers a real navigation and a bfcache freeze alike; unmount covers an SPA\n // route change and the transcript being reset.\n useEffect(() => {\n if (!speaking || typeof window === \"undefined\") return;\n const onHide = () => {\n stopRef.current();\n };\n window.addEventListener(\"pagehide\", onHide);\n return () => window.removeEventListener(\"pagehide\", onHide);\n }, [speaking]);\n\n useEffect(\n () => () => {\n stopRef.current();\n },\n [],\n );\n\n return { supported: available, speaking, speak, stop, toggle };\n}\n","\"use client\";\n\nimport { useEffect, useRef, useState } from \"react\";\n\n/**\n * Reveal an in-flight answer at a readable pace instead of in poll-sized blocks.\n *\n * ## What this is NOT\n *\n * It is not a transport change. `/explore` answers arrive by submit-and-poll, one\n * `exploreMessage` read every ~1.5s (CONTRACT.md §1), and nothing here touches that: no\n * extra request, no shorter interval, no socket. Every character this reveals had already\n * been fetched — the hook only decides *when it is painted*.\n *\n * ## The guarantees\n *\n * - **Only text that arrived.** What it returns is always a PREFIX of the string it was\n * given, so it can neither invent a word nor reorder one. When the answer is rewritten\n * rather than appended to — the final `content` replacing a `partialContent` that was cut\n * mid-token — it jumps to the end rather than showing a prefix of the old answer inside\n * the new one.\n * - **Never behind a finished answer.** `settled` (the turn is COMPLETED or FAILED) returns\n * the whole string immediately, on the render it flips. Nobody waits on an animation for\n * text that is already in hand, and a restored or shared transcript paints in full on its\n * first frame.\n * - **A table is painted as a table or not at all.** A GFM header row without its `| --- |`\n * row renders as a paragraph of pipes, so one is held back until that row arrives, for as\n * long as the answer is still running (CONTRACT.md §7). It is the one construct whose\n * rendering changes retroactively when a LATER line lands.\n * - **Reduced motion is not \"slower\".** `prefers-reduced-motion: reduce` gets the text at\n * once, with no timer started at all.\n * - **No scrolling, no measuring.** The hook returns a string. It reads no box, writes no\n * scroll position and renders no element, so it cannot fight the transcript's\n * `stickToBottom` logic or the pane geometry — the page was fixed once already for\n * showing nothing but the site footer, and the fix lives in effects that this cannot\n * reach.\n *\n * ## The pace\n *\n * Adaptive rather than a fixed characters-per-second: the cursor moves at whatever speed\n * drains the *backlog* in {@link DRAIN_TARGET_MS}, which is a little under the poll\n * interval. A fast turn therefore reads fast and a trickling one still moves, and the\n * cursor catches up with the arrived text before the next chunk lands instead of falling\n * further behind with every poll.\n */\n\n/** Drain whatever has arrived in about this long — just inside the ~1.5s poll interval. */\nconst DRAIN_TARGET_MS = 1200;\n/** A floor, so the last few characters of a chunk still move rather than crawl. */\nconst MIN_CHARS_PER_SECOND = 90;\n/** A ceiling, so a large backlog is caught up with quickly but not in one frame. */\nconst MAX_CHARS_PER_SECOND = 2200;\n/**\n * The most one step may advance by, however long the gap was. A backgrounded tab stops\n * firing frames entirely, and without this the first frame after it comes back would paint\n * the whole answer at once — the snap this exists to remove.\n */\nconst MAX_STEP_MS = 250;\n/**\n * How often a step is allowed to re-render. Every commit re-parses the answer's markdown,\n * and doing that once a frame is wasted work nobody can see; ~22 commits a second reads as\n * continuous.\n */\nconst COMMIT_INTERVAL_MS = 45;\n/** Only used where `requestAnimationFrame` does not exist. */\nconst FALLBACK_STEP_MS = 50;\n\n/** A ``` or ~~~ fence, which opens or closes a verbatim block. */\nconst FENCE = /^ {0,3}(?:```|~~~)/;\n/** A GFM table row. */\nconst TABLE_ROW = /^ {0,3}\\|/;\n/** A block marker with nothing after it yet — a bullet or heading mid-keystroke. */\nconst EMPTY_BLOCK_MARKER = /^ {0,3}(?:[-+>]|#{1,6}|\\d{1,9}[.)])\\s*$/;\n\n/**\n * A character that makes `_` part of a word rather than emphasis. Deliberately wider than\n * ASCII: `día_uno` is one word in Spanish exactly as `day_one` is in English.\n */\nfunction isWordCharacter(char: string | undefined): boolean {\n return char !== undefined && /[\\p{L}\\p{N}]/u.test(char);\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.min(Math.max(value, min), max);\n}\n\nfunction now(): number {\n return typeof performance !== \"undefined\" &&\n typeof performance.now === \"function\"\n ? performance.now()\n : Date.now();\n}\n\n/** The first half of an astral character — a cut here would paint a replacement glyph. */\nfunction isHighSurrogate(code: number): boolean {\n return code >= 0xd800 && code <= 0xdbff;\n}\n\n/**\n * Where an incomplete LINE may be cut: before the first inline construct it has left open.\n *\n * Markdown is not revealable one character at a time as written. Cutting `**Starter**` after\n * `**Star` paints two literal asterisks, and the same half-written `[label](https://…` paints\n * a bracket and a raw URL, which then rearrange themselves a frame later. Holding the cut at\n * the point the construct OPENED costs a few characters of delay and never paints syntax.\n *\n * Scanned per line on purpose. Emphasis that spans a line break is rare, and carrying an\n * unclosed marker across lines would pin the cut arbitrarily far back.\n *\n * ## What is NOT an open construct\n *\n * A marker character in ordinary prose must not pin the cut, because the pin lasts for the\n * REST OF THE LINE: an answer's paragraph is one unwrapped line, so a `~` that never closes\n * would freeze the reveal mid-sentence and then paint the remaining hundred characters in a\n * single commit — the snap this hook exists to remove. So:\n *\n * - **A lone `*`, `_` or `~` opens nothing.** `answerPlainText` reached the same rule from\n * the other side (\"single-delimiter emphasis only when it wraps a run\"): a `* ` bullet, a\n * `snake_case` identifier, a `~30 seconds`, and a `per seat*` footnote mark are all one\n * literal character, and one literal character on screen for a frame is far cheaper than a\n * frozen paragraph. A run of two or more (`**`, `__`, `~~`) is markup often enough to be\n * worth the wait.\n * - **Intraword `_` is not emphasis at all**, per CommonMark — `MAX__TURNS__PER` neither\n * opens nor closes, whatever the run length.\n * - **A run with whitespace after it opens nothing**, also per CommonMark: it is not a\n * left-flanking delimiter run. Whitespace after the END of the arrived text is unknown,\n * not absent, so a line ending in `**` still holds — that is the `**29` case above.\n */\nfunction safeInlineEnd(line: string): number {\n let codeSpan: number | null = null;\n /**\n * The link and image constructs still open, innermost last: a `label` waiting for its `]`,\n * a `destination` waiting for its `)`, and a `paren` the URL itself opened. A stack, not one\n * position, because they nest — `[![alt](poster)](https://…/video.mp4)` is a link whose\n * label is an image, and the image's `)` does not close the link. The bottom entry is the\n * outermost construct, and the only one that is ever the cut.\n */\n const links: Array<{\n kind: \"label\" | \"destination\" | \"paren\";\n start: number;\n }> = [];\n const emphasis: Array<{ marker: string; start: number }> = [];\n\n let at = 0;\n while (at < line.length) {\n const char = line[at] ?? \"\";\n if (char === \"\\\\\") {\n // An escape and whatever it escapes are one unit and carry no meaning of their own.\n at += 2;\n continue;\n }\n const innermost = links[links.length - 1];\n if (innermost !== undefined && innermost.kind !== \"label\") {\n // Inside a URL only parentheses mean anything: its own `(` and `)` nest, and the one\n // that balances the `](` closes the construct. A `_`, a backtick or a bracket in a URL\n // is a character of the URL, and read as markup it would pin the cut inside it.\n if (char === \"(\") links.push({ kind: \"paren\", start: innermost.start });\n else if (char === \")\") links.pop();\n at += 1;\n continue;\n }\n if (char === \"`\") {\n let run = 1;\n while (line[at + run] === \"`\") run += 1;\n codeSpan = codeSpan === null ? at : null;\n at += run;\n continue;\n }\n if (codeSpan !== null) {\n // Inside a code span nothing else is markup.\n at += 1;\n continue;\n }\n if (char === \"[\") {\n // `![` is an image; either way the bracket is what opens it.\n links.push({ kind: \"label\", start: line[at - 1] === \"!\" ? at - 1 : at });\n at += 1;\n continue;\n }\n if (char === \"]\" && innermost !== undefined) {\n // `[3]` is a citation marker and closes here. `](` carries on into the destination,\n // which is still the same construct.\n links.pop();\n if (line[at + 1] === \"(\") {\n links.push({ kind: \"destination\", start: innermost.start });\n at += 2;\n continue;\n }\n at += 1;\n continue;\n }\n if (char === \"*\" || char === \"_\" || char === \"~\") {\n let run = 1;\n while (line[at + run] === char) run += 1;\n const start = at;\n at += run;\n const before = line[start - 1];\n const after = line[start + run];\n\n // See \"What is NOT an open construct\" above for all three of these.\n if (run < 2) continue;\n if (char === \"_\" && isWordCharacter(before) && isWordCharacter(after)) {\n continue;\n }\n\n const marker = char.repeat(run);\n const open = emphasis.findIndex((entry) => entry.marker === marker);\n // Closing first: a run that ends emphasis is usually followed by a space, and the\n // opener guard below would otherwise leave that run open forever.\n if (open !== -1) {\n emphasis.splice(open, 1);\n continue;\n }\n if (after !== undefined && /\\s/.test(after)) continue;\n emphasis.push({ marker, start });\n continue;\n }\n at += 1;\n }\n\n const open = [\n codeSpan,\n links[0]?.start ?? null,\n emphasis[0]?.start ?? null,\n ].filter((value): value is number => value !== null);\n return open.length === 0 ? line.length : Math.min(...open);\n}\n\n/**\n * The largest index at or below `limit` that `text` may be cut at and still render as the\n * markdown it will eventually be.\n *\n * Three block rules, then the inline scan above:\n *\n * - **A half-written fenced block is held at its fence.** Until the closing fence arrives\n * the opening one is just three backticks and a word, and the code inside it is prose.\n * - **A table is held until its delimiter row is complete.** A GFM table is only a table\n * once the `| --- |` row exists; before that the header renders as a paragraph full of\n * pipes, which then becomes a table — the single most jarring thing an answer can do.\n * Complete means a WHOLE line: half of `| --- | --- |` is not a delimiter row, and one\n * that has not been closed by a newline may still grow another column. Afterwards only\n * the incomplete row is held, so the table grows a row at a time.\n * - **A bare block marker is held.** `-`, `>` or `####` with nothing after it yet paints an\n * empty bullet or an empty heading.\n *\n * Reaching the end of the arrived text (`limit >= text.length`) releases all of it but the\n * table, because the cursor being level with the text is the NORMAL end state of a poll, not\n * an edge case: the pace drains a chunk in {@link DRAIN_TARGET_MS}, inside the ~1.5s\n * interval, so every poll ends caught up. A construct that is only ever held while the\n * cursor is behind is therefore held only for the part of a chunk it happens to land in.\n *\n * The table is the one that cannot be left to that, and it is the only one that NEEDS more\n * than the text in hand to render correctly. A header row whose delimiter row is in the next\n * poll would be painted as a paragraph of pipes and then, because a cut never moves\n * backwards, stay one on screen while the rest of the table grows below it. An unclosed\n * fence, by contrast, already renders as the code block it will be (CommonMark runs it to\n * the end of the document), and a `- ` bullet already renders as a bullet.\n *\n * Nothing here decides what a FINISHED answer paints: {@link useProgressiveReveal} returns\n * `content` whole the moment the turn settles, without asking.\n */\nexport function revealBoundary(text: string, limit: number): number {\n if (limit <= 0) return 0;\n\n /** True when the cursor has reached everything that has arrived. */\n const caughtUp = limit >= text.length;\n const head = caughtUp ? text : text.slice(0, limit);\n const lineStart = head.lastIndexOf(\"\\n\") + 1;\n const line = head.slice(lineStart);\n\n // Block state, from the WHOLE lines before the cut.\n let fenceStart: number | null = null;\n let tableStart: number | null = null;\n let tableRows = 0;\n let at = 0;\n while (at < lineStart) {\n const breakAt = head.indexOf(\"\\n\", at);\n const end = breakAt === -1 ? lineStart : breakAt;\n const row = head.slice(at, end);\n if (fenceStart !== null) {\n if (FENCE.test(row)) fenceStart = null;\n } else if (FENCE.test(row)) {\n fenceStart = at;\n } else if (TABLE_ROW.test(row)) {\n if (tableStart === null) {\n tableStart = at;\n tableRows = 0;\n }\n tableRows += 1;\n } else if (tableStart !== null) {\n tableStart = null;\n tableRows = 0;\n }\n at = end + 1;\n }\n\n // The last line is not a whole line: the rest of it may still be on its way. A `|` on it\n // still OPENS a table, and an empty one (the text ends at the newline after the row above)\n // has not closed the table it follows either.\n if (fenceStart === null) {\n const lastIsRow = TABLE_ROW.test(line);\n if (lastIsRow && tableStart === null) {\n // It opens a table. It does not COUNT as one of the two rows a table needs: half of\n // `| --- |` is not a delimiter row.\n tableStart = lineStart;\n tableRows = 0;\n } else if (!lastIsRow && line !== \"\") {\n tableStart = null;\n tableRows = 0;\n }\n // Held even at the end of the arrived text — see the note above.\n if (tableStart !== null && tableRows < 2) return tableStart;\n }\n\n if (caughtUp) return text.length;\n if (fenceStart !== null) return fenceStart;\n if (TABLE_ROW.test(line)) return lineStart;\n if (EMPTY_BLOCK_MARKER.test(line)) return lineStart;\n\n const cut = lineStart + safeInlineEnd(line);\n return isHighSurrogate(text.charCodeAt(cut - 1)) ? cut - 1 : cut;\n}\n\n/**\n * True when the visitor has asked for less movement. Read once per mount rather than per\n * render: `matchMedia` is absent on some engines (and in jsdom), and a setting that changes\n * mid-answer is not worth a subscription.\n *\n * Server-side this answers true, so a prerender serialises the finished text rather than an\n * animation's first frame.\n */\nfunction prefersReducedMotion(): boolean {\n if (typeof window === \"undefined\") return true;\n try {\n return (\n window.matchMedia?.(\"(prefers-reduced-motion: reduce)\").matches === true\n );\n } catch {\n /* an engine whose matchMedia throws on an unknown feature — treat as no preference */\n return false;\n }\n}\n\n/**\n * The part of `text` that is currently on screen.\n *\n * @param text everything that has arrived so far — `content` once the turn completes,\n * `partialContent` while it runs.\n * @param settled true once the turn is COMPLETED or FAILED and `text` will not grow again.\n */\nexport function useProgressiveReveal(text: string, settled: boolean): string {\n const [, setFrame] = useState(0);\n const [reduced] = useState(prefersReducedMotion);\n const immediate = settled || reduced;\n\n /** The text the cursor below points into. */\n const textRef = useRef(text);\n /** How far the cursor has travelled, fractionally — a step is usually part of a character. */\n const cursorRef = useRef(immediate ? text.length : 0);\n /** The last cut actually rendered, so a cut can never move backwards. */\n const cutRef = useRef(immediate ? text.length : 0);\n const committedRef = useRef(0);\n\n // Reconciled during render, not in an effect: what this function returns has to describe\n // the text it was handed THIS render, never the previous answer for a frame.\n if (text !== textRef.current) {\n const revealed = textRef.current.slice(0, Math.floor(cursorRef.current));\n if (revealed !== \"\" && !text.startsWith(revealed)) {\n // Rewritten rather than appended to. Everything on screen must be text that arrived,\n // so finish rather than show the old prefix inside the new answer.\n cursorRef.current = text.length;\n }\n cursorRef.current = Math.min(cursorRef.current, text.length);\n cutRef.current = Math.min(cutRef.current, text.length);\n textRef.current = text;\n }\n if (immediate) cursorRef.current = text.length;\n\n // A settled turn paints whole rather than asking the boundary. `content` is the entire\n // answer, so a table it stops in the middle of is all the table there is ever going to\n // be, and holding it back would hold it back for good.\n const cut = immediate\n ? text.length\n : Math.max(\n cutRef.current,\n revealBoundary(text, Math.floor(cursorRef.current)),\n );\n cutRef.current = cut;\n\n useEffect(() => {\n if (immediate) return;\n if (cursorRef.current >= text.length) return;\n\n let cancelled = false;\n let cancelStep: (() => void) | null = null;\n let previous = now();\n\n const commit = (at: number, force: boolean): void => {\n if (!force && at - committedRef.current < COMMIT_INTERVAL_MS) return;\n committedRef.current = at;\n setFrame((value) => value + 1);\n };\n\n const step = (): void => {\n cancelStep = null;\n if (cancelled) return;\n\n const at = now();\n const elapsed = clamp(at - previous, 0, MAX_STEP_MS);\n previous = at;\n\n const length = textRef.current.length;\n const pending = length - cursorRef.current;\n if (pending <= 0) {\n commit(at, true);\n return;\n }\n\n const perSecond = clamp(\n pending / (DRAIN_TARGET_MS / 1000),\n MIN_CHARS_PER_SECOND,\n MAX_CHARS_PER_SECOND,\n );\n cursorRef.current = Math.min(\n length,\n cursorRef.current + (perSecond * elapsed) / 1000,\n );\n\n const done = cursorRef.current >= length;\n commit(at, done);\n if (!done) schedule();\n };\n\n const schedule = (): void => {\n if (typeof window.requestAnimationFrame === \"function\") {\n const id = window.requestAnimationFrame(step);\n cancelStep = () => window.cancelAnimationFrame(id);\n return;\n }\n const id = setTimeout(step, FALLBACK_STEP_MS);\n cancelStep = () => clearTimeout(id);\n };\n\n schedule();\n return () => {\n cancelled = true;\n cancelStep?.();\n };\n }, [text, immediate]);\n\n return text.slice(0, cut);\n}\n"]}
1
+ {"version":3,"sources":["../../src/client/security.ts","../../src/client/graphql-client.ts","../../src/client/provider.tsx","../../src/components/explore-types.ts","../../src/utils/product-slug.ts","../../src/data/products.ts","../../src/utils/recaptcha-v3.ts","../../src/hooks/use-explore-chat.ts","../../src/hooks/use-speech-input.ts","../../src/hooks/use-speech-output.ts","../../src/hooks/use-progressive-reveal.ts"],"names":["createContext","useContext","useMemo","useState","useRef","useCallback","useEffect","open","id"],"mappings":"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","file":"index.js","sourcesContent":["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 * Explore chat — TypeScript mirror of the public `/explore` GraphQL contract.\n *\n * Canonical source: `~/products/dev/platform/context/explore/CONTRACT.md` §1\n * (global-support-svc, served through the global PUBLIC gateway with\n * `@rbac(public: true, scopeType: \"global\")`).\n *\n * Nothing in this file may drift from that document. If a shape here looks\n * wrong, fix CONTRACT.md first and tell the backend workstreams — never patch\n * it locally.\n *\n * Nullability note: the contract marks `title`, `description`, `product`,\n * `imageUrl`, `partialContent`, `errorCode`, `completedAt`, `productSlug`,\n * `recaptchaSiteKey` and `remainingToday` as nullable, so every one of them is\n * `?: T | null` here — a GraphQL `null` and an omitted selection both have to\n * be representable.\n */\n\n// ============================================================================\n// Enums (string unions — the wire format is the enum name)\n// ============================================================================\n\nexport type ExploreMessageRole = \"USER\" | \"ASSISTANT\";\n\nexport type ExploreMessageStatus =\n \"PENDING\" | \"RUNNING\" | \"COMPLETED\" | \"FAILED\";\n\n/** `enum ExploreFeedbackRating` — the visitor's verdict on one answer. */\nexport type ExploreFeedbackRating = \"HELPFUL\" | \"NOT_HELPFUL\";\n\n/**\n * The reasons offered alongside `NOT_HELPFUL`, in the order the chooser shows them.\n *\n * `reason` is `String` on the wire, not an enum, so this list is the SDK's half of the\n * agreement rather than something the schema enforces — the same treatment\n * {@link ExploreCtaKind} gets. Use the union when you are *producing* a reason; read one\n * back as a plain string.\n */\nexport const EXPLORE_FEEDBACK_REASONS = [\n \"INACCURATE\",\n \"INCOMPLETE\",\n \"OFF_TOPIC\",\n \"HARD_TO_FOLLOW\",\n \"OTHER\",\n] as const;\n\nexport type ExploreFeedbackReason = (typeof EXPLORE_FEEDBACK_REASONS)[number];\n\n/**\n * The `ExploreCta.kind` values enumerated in CONTRACT.md §1.\n *\n * `ExploreCtaLink.kind` is deliberately typed `string`, not this union: the\n * field is `String!` on the wire, and a kind added server-side must render\n * (falling back to the neutral CTA style) rather than break the build of 38\n * websites. Use this union when you are *producing* a kind.\n */\nexport type ExploreCtaKind =\n | \"CONTACT\"\n | \"PARTNERS\"\n | \"PRICING\"\n | \"SERVICES\"\n | \"WAITLIST\"\n | \"STORE\"\n | \"APP\"\n | \"DOCS\"\n | \"OTHER\";\n\n/**\n * Error codes surfaced through `extensions.code`. `HTTP_429` is what the\n * gateway emits when it rate-limits at the edge; the SDK client also produces\n * `TIMEOUT` / `NETWORK_ERROR` for transport failures.\n */\nexport type ExploreErrorCode =\n | \"EXPLORE_DISABLED\"\n | \"RATE_LIMITED\"\n | \"EXPLORE_GLOBAL_LIMIT\"\n | \"CAPTCHA_REQUIRED\"\n | \"CAPTCHA_FAILED\"\n | \"MESSAGE_TOO_LONG\"\n | \"MESSAGE_EMPTY\"\n | \"CONVERSATION_LIMIT\"\n | \"CONVERSATION_BUSY\"\n | \"UPSTREAM_UNAVAILABLE\"\n | \"NOT_FOUND\"\n // Handover-only. `INVALID_EMAIL` is the server's verdict on an address the client's own\n // pre-flight let through, and has to be shown rather than folded into a generic failure.\n | \"INVALID_EMAIL\"\n | \"ESCALATION_FAILED\"\n | \"HTTP_429\"\n | \"TIMEOUT\"\n | \"NETWORK_ERROR\";\n\n// ============================================================================\n// Object types\n// ============================================================================\n\n/** `type ExploreReference` — a cited source page for an answer. */\nexport interface ExploreReference {\n url: string;\n title?: string | null;\n description?: string | null;\n product?: string | null;\n imageUrl?: string | null;\n /** 1-based citation number; the answer text carries a matching `[n]` marker. */\n index?: number | null;\n}\n\n/**\n * `type ExploreCta` — a suggested next step attached to an answer.\n *\n * Named `ExploreCtaLink` in the SDK because `ExploreCta` is taken by the\n * exported nav component (`<ExploreCta variant=\"header\" />`). Same shape,\n * different name; the GraphQL type is unchanged.\n */\nexport interface ExploreCtaLink {\n /** One of {@link ExploreCtaKind}; unknown values render with the neutral style. */\n kind: string;\n label: string;\n url: string;\n product?: string | null;\n}\n\n/**\n * `type ExploreFeedback` — what is on record for one answer, as the server holds it.\n *\n * A rating REPLACES the previous one (the server upserts on `messageId`), so this is the\n * visitor's current opinion and never a history. `reason` is null for `HELPFUL` by\n * definition, and typed `string` rather than the union because it is `String` on the wire —\n * a reason added server-side must read back, not break the build of 38 websites.\n */\nexport interface ExploreFeedback {\n rating: ExploreFeedbackRating;\n /** One of {@link EXPLORE_FEEDBACK_REASONS}. Null for `HELPFUL`. */\n reason?: string | null;\n comment?: string | null;\n createdAt: string;\n}\n\n/** `type ExploreMessage`. `content` is `\"\"` while the turn is PENDING. */\nexport interface ExploreMessage {\n id: string;\n conversationId: string;\n role: ExploreMessageRole;\n status: ExploreMessageStatus;\n /** Final answer, markdown. Empty string until the turn completes. */\n content: string;\n /** Streaming preview while PENDING/RUNNING. */\n partialContent?: string | null;\n /** Human-readable progress lines, e.g. \"Searching vibecontrols…\". */\n activity: string[];\n references: ExploreReference[];\n ctas: ExploreCtaLink[];\n /** Up to three suggested next questions, derived from what the answer covered. */\n followUps?: string[] | null;\n /**\n * False when the corpus could not answer. A grounded answer always cites, so an answer\n * with no source is the honest \"we do not have this\" signal rather than a hedge.\n */\n answered?: boolean | null;\n /** Files the visitor attached to this turn. Present on USER messages only. */\n attachments?: ExploreAttachment[] | null;\n /**\n * The rating this visitor already gave this answer, or null for an unrated one.\n *\n * Selected only by the reads scoped to a `conversationToken`, so a polled or restored\n * transcript shows what was already rated instead of offering the row again. It is always\n * null on a SHARED transcript — that read is performed by someone else, and the rater's\n * verdict and free-text note are not theirs to receive. See\n * `EXPLORE_OWN_MESSAGE_FIELDS`.\n */\n feedback?: ExploreFeedback | null;\n errorCode?: string | null;\n createdAt: string;\n completedAt?: string | null;\n}\n\n/** `type ExploreConversation`. `token` is the client's only handle. */\nexport interface ExploreConversation {\n id: string;\n token: string;\n productSlug?: string | null;\n turnCount: number;\n createdAt: string;\n}\n\n/** `type ExploreProduct` — one product the harness can answer about. */\nexport interface ExploreProduct {\n slug: string;\n name: string;\n tagline?: string | null;\n website: string;\n}\n\n/** `type ExploreLimits` — server-owned caps the composer enforces locally. */\nexport interface ExploreLimits {\n maxMessageLength: number;\n maxTurnsPerConversation: number;\n messagesPerDay: number;\n historyTurns: number;\n}\n\n/**\n * `type ExploreConceptHighlight` — one published concept illustration, offered\n * as a way into the conversation before the visitor has typed anything.\n *\n * Every field is non-null on the wire: a highlight with no picture or no\n * destination is not a highlight, so the server drops it rather than sending a\n * half-built one. `product` is the slug, `productName` the display casing the\n * product's own pages use.\n *\n * The pictures are the published concept mockups — illustrations of an idea,\n * not screenshots of shipping software — which is why every surface that shows\n * one also labels it (`ExploreStrings.conceptIllustration`).\n */\nexport interface ExploreConceptHighlight {\n product: string;\n productName: string;\n title: string;\n url: string;\n imageUrl: string;\n alt: string;\n}\n\n/**\n * `type ExploreCatalog` — everything the page needs before the first message.\n *\n * `captchaRequired` + `recaptchaSiteKey` are what turn reCAPTCHA v3 on: it is\n * a backend secret change, and no website redeploy is involved.\n */\nexport interface ExploreCatalog {\n enabled: boolean;\n products: ExploreProduct[];\n examplePrompts: string[];\n welcomeTitle: string;\n welcomeBody: string;\n limits: ExploreLimits;\n captchaRequired: boolean;\n recaptchaSiteKey?: string | null;\n /**\n * At most 8, the site's own product first. Non-null and non-nullable per the\n * contract, so a gateway that knows the field always sends an array.\n *\n * A gateway that does NOT know it yet does not answer without it — it refuses\n * the whole document (`GRAPHQL_VALIDATION_FAILED`), which is why the hook\n * retries with `EXPLORE_CATALOG_QUERY_WITHOUT_HIGHLIGHTS` instead of falling\n * back to the built-in catalog. That retry is the one response that reaches\n * `normalizeCatalog` with this field absent, and the hardening there is what\n * keeps `catalog.conceptHighlights.filter(...)` from throwing in render.\n */\n conceptHighlights: ExploreConceptHighlight[];\n}\n\n/** `input SendExploreMessageInput`. */\nexport interface SendExploreMessageInput {\n /** Omit to start a new conversation. */\n conversationToken?: string;\n /** The site the visitor is on — a soft hint, not a hard filter. */\n productSlug?: string;\n message: string;\n /** Only sent when `catalog.captchaRequired`. */\n recaptchaToken?: string;\n locale?: string;\n pageUrl?: string;\n /**\n * Files attached as extra context. At most 5, each at most 10 MB — re-enforced server-side,\n * so these are the limits the composer shows, not the limits that hold.\n */\n attachments?: ExploreAttachmentInput[];\n}\n\n/** `input ExploreAttachmentInput`. `textContent` is null for a format the browser cannot read. */\nexport interface ExploreAttachmentInput {\n name: string;\n mimeType: string;\n size: number;\n textContent: string | null;\n}\n\n/** `type ExploreAttachment` — what the server accepted, echoed back. */\nexport interface ExploreAttachment {\n name: string;\n mimeType: string;\n size: number;\n /** False when only the filename and type could be used as context. */\n textExtracted: boolean;\n}\n\n/**\n * `type SendExploreMessageResult`.\n *\n * **`assistantMessage.id` is the id to poll** — it comes back `PENDING`.\n */\nexport interface SendExploreMessageResult {\n conversation: ExploreConversation;\n userMessage: ExploreMessage;\n assistantMessage: ExploreMessage;\n remainingToday?: number | null;\n}\n\n// ============================================================================\n// GraphQL documents\n//\n// Only the fields the UI actually renders are requested — a narrower selection\n// set is cheaper to poll (this runs once every ~1.5s per active visitor) and\n// keeps the contract surface the websites depend on small.\n// ============================================================================\n\n/**\n * Selection set for an `ExploreMessage` that ANYONE may read. Inlined as a\n * template string rather than a GraphQL `fragment` so each document stays a\n * single self-contained operation (the SDK client posts raw strings and has no\n * fragment registry).\n *\n * Deliberately WITHOUT `feedback` — see `EXPLORE_OWN_MESSAGE_FIELDS`.\n */\nconst EXPLORE_MESSAGE_FIELDS = /* GraphQL */ `\n id\n conversationId\n role\n status\n content\n partialContent\n activity\n references {\n url\n title\n description\n product\n imageUrl\n index\n }\n ctas {\n kind\n label\n url\n product\n }\n followUps\n answered\n attachments {\n name\n mimeType\n size\n textExtracted\n }\n errorCode\n createdAt\n completedAt\n`;\n\n/**\n * The same message PLUS the visitor's own rating of it.\n *\n * Used by the reads that are scoped to a `conversationToken`, so the person\n * making them is the person who rated the answer — that is what lets the row\n * come back already-rated after a reload instead of re-offering the choice.\n *\n * `EXPLORE_SHARED_CONVERSATION_QUERY` deliberately does NOT use this one. A\n * share link is the one read performed by somebody ELSE, and it exists to be\n * forwarded; `comment` is where a visitor types the specifics that made the\n * answer wrong for them — the price they are actually being charged, an\n * invoice number, a name — so it must not travel with a transcript sent to a\n * colleague. The server omits it there too; this is the client half of the\n * same rule, and keeping the selection sets separate is what stops a future\n * \"why are there two of these?\" tidy-up from re-merging them.\n */\nconst EXPLORE_OWN_MESSAGE_FIELDS = /* GraphQL */ `\n ${EXPLORE_MESSAGE_FIELDS}\n feedback {\n rating\n reason\n comment\n createdAt\n }\n`;\n\n/**\n * Everything on `ExploreCatalog` that every deployed gateway has served since\n * the surface shipped. Shared by both catalog documents below so the pair can\n * never drift: the ONLY difference between them is `conceptHighlights`.\n */\nconst EXPLORE_CATALOG_ESTABLISHED_FIELDS = /* GraphQL */ `\n enabled\n products {\n slug\n name\n tagline\n website\n }\n examplePrompts\n welcomeTitle\n welcomeBody\n limits {\n maxMessageLength\n maxTurnsPerConversation\n messagesPerDay\n historyTurns\n }\n captchaRequired\n recaptchaSiteKey`;\n\n/** The field added with the welcome strip — the only thing a new gateway has that an old one does not. */\nconst EXPLORE_CATALOG_HIGHLIGHT_FIELD = /* GraphQL */ `\n conceptHighlights {\n product\n productName\n title\n url\n imageUrl\n alt\n }`;\n\nfunction exploreCatalogDocument(extraFields: string): string {\n return /* GraphQL */ `\n query ExploreCatalog($productSlug: String) {\n exploreCatalog(productSlug: $productSlug) {${EXPLORE_CATALOG_ESTABLISHED_FIELDS}${extraFields}\n }\n }\n`;\n}\n\n/** Loaded once per page. Safe to serve from the SDK's 5-minute query cache. */\nexport const EXPLORE_CATALOG_QUERY = exploreCatalogDocument(\n EXPLORE_CATALOG_HIGHLIGHT_FIELD,\n);\n\n/**\n * The same catalog WITHOUT `conceptHighlights`, for a gateway whose supergraph\n * predates the field.\n *\n * This is not belt-and-braces. GraphQL validation is all-or-nothing: a gateway\n * that does not know the field rejects the ENTIRE document and returns no\n * `data` at all, so selecting `conceptHighlights` against an older supergraph\n * costs the whole catalog — `enabled`, `captchaRequired`, `recaptchaSiteKey`,\n * the welcome copy and `limits` included — and not merely the strip. Measured\n * against production:\n *\n * {\"errors\":[{\"message\":\"Cannot query field \\\\\"conceptHighlights\\\\\" on type\n * \\\\\"ExploreCatalog\\\\\".\",\"extensions\":{\"code\":\"GRAPHQL_VALIDATION_FAILED\"}}]}\n *\n * That window is guaranteed even with perfect deploy ordering, because the prod\n * gateway's CDN edge serves a stale supergraph for ~30-40 minutes after the\n * subgraph publishes. `useExploreChat` therefore retries with this document\n * (see `catalogRejectedTheHighlightField`) rather than dropping to the built-in\n * fallback, whose hardcoded `enabled: true` would ignore the server-side kill\n * switch and whose `captchaRequired: false` would let every send be rejected\n * server-side while the composer looked fine.\n */\nexport const EXPLORE_CATALOG_QUERY_WITHOUT_HIGHLIGHTS =\n exploreCatalogDocument(\"\");\n\n/**\n * The poll. MUST be issued with `client.query(..., { cache: false })` — the\n * default 5-minute cache would return the first PENDING snapshot forever and\n * the answer would never appear.\n */\nexport const EXPLORE_MESSAGE_QUERY = /* GraphQL */ `\n query ExploreMessage($conversationToken: String!, $id: ID!) {\n exploreMessage(conversationToken: $conversationToken, id: $id) {\n ${EXPLORE_OWN_MESSAGE_FIELDS}\n }\n }\n`;\n\n/** Submit half of the submit + poll pair. */\nexport const SEND_EXPLORE_MESSAGE_MUTATION = /* GraphQL */ `\n mutation SendExploreMessage($input: SendExploreMessageInput!) {\n sendExploreMessage(input: $input) {\n conversation {\n id\n token\n productSlug\n turnCount\n createdAt\n }\n userMessage {\n ${EXPLORE_OWN_MESSAGE_FIELDS}\n }\n assistantMessage {\n ${EXPLORE_OWN_MESSAGE_FIELDS}\n }\n remainingToday\n }\n }\n`;\n\n// ============================================================================\n// Response envelopes (what `client.query`/`client.mutate` resolve `data` to)\n// ============================================================================\n\nexport interface ExploreCatalogQueryData {\n exploreCatalog: ExploreCatalog;\n}\n\nexport interface ExploreMessageQueryData {\n exploreMessage: ExploreMessage | null;\n}\n\nexport interface SendExploreMessageMutationData {\n sendExploreMessage: SendExploreMessageResult;\n}\n\n/**\n * Record that the visitor followed a link the answer offered.\n *\n * Fire-and-forget: the browser is navigating away as this goes out, so the result is never\n * waited on and a failure is never surfaced. The server reports `recorded: false` rather\n * than erroring for anything it cannot attribute.\n */\nexport const RECORD_EXPLORE_CLICK_MUTATION = /* GraphQL */ `\n mutation RecordExploreClick($input: RecordExploreClickInput!) {\n recordExploreClick(input: $input) {\n recorded\n }\n }\n`;\n\n/** `input SubmitExploreFeedbackInput`. */\nexport interface SubmitExploreFeedbackInput {\n conversationToken: string;\n messageId: string;\n rating: ExploreFeedbackRating;\n /** Only meaningful with `NOT_HELPFUL`; stored null for `HELPFUL`. */\n reason?: ExploreFeedbackReason | null;\n /** Optional free text. Hard-capped server-side — never trusted from the client. */\n comment?: string | null;\n}\n\n/** `type SubmitExploreFeedbackResult`. */\nexport interface SubmitExploreFeedbackResult {\n /**\n * False — NOT an error — when the message is not a COMPLETED assistant turn, or when the\n * supplied `conversationToken` does not own it. Same posture as `recordExploreClick`: the\n * server declines to attribute rather than raising FORBIDDEN at a marketing visitor.\n */\n recorded: boolean;\n /** What is now on record, when the rating was taken. Null when `recorded` is false. */\n feedback?: ExploreFeedback | null;\n}\n\n/**\n * Rate one answer.\n *\n * Unlike {@link RECORD_EXPLORE_CLICK_MUTATION} this one IS awaited: the visitor is still on\n * the page and the control they just pressed has to end up telling the truth about whether\n * anything was recorded. The rating upserts on `messageId`, so re-rating is legal and a\n * second submit is never an error.\n */\nexport const SUBMIT_EXPLORE_FEEDBACK_MUTATION = /* GraphQL */ `\n mutation SubmitExploreFeedback($input: SubmitExploreFeedbackInput!) {\n submitExploreFeedback(input: $input) {\n recorded\n feedback {\n rating\n reason\n comment\n createdAt\n }\n }\n }\n`;\n\nexport interface SubmitExploreFeedbackMutationData {\n submitExploreFeedback: SubmitExploreFeedbackResult;\n}\n\n/**\n * How many characters the comment field accepts before it stops taking more.\n *\n * A courtesy so the box does not invite an essay it will lose, NOT a contract: the server\n * owns the real cap and may truncate or reject below this. Nothing here may be read as the\n * limit that holds.\n */\nexport const EXPLORE_FEEDBACK_COMMENT_SOFT_MAX = 500;\n\n// ============================================================================\n// Handing the conversation to a human (CONTRACT.md §1, \"Handing a conversation\n// to a human\")\n// ============================================================================\n\n/**\n * `input EscalateExploreConversationInput`.\n *\n * `email` is the only thing the visitor is asked for. It is **personal data**: it belongs in\n * this input and nowhere else — never in `localStorage`, never in an `ExploreMessage`, never\n * in an analytics call. See the note on {@link ESCALATE_EXPLORE_CONVERSATION_MUTATION}.\n */\nexport interface EscalateExploreConversationInput {\n /** The thread being handed over. A share (`?c=`) secret is NOT one. */\n conversationToken: string;\n /** Validated strictly server-side; a bad address comes back as `INVALID_EMAIL`. */\n email: string;\n /** Optional free text. Hard-capped server-side at 2000 and TRUNCATED, never rejected. */\n note?: string | null;\n /** Only when `exploreCatalog.captchaRequired`. Same v3 action as a chat message. */\n recaptchaToken?: string;\n /** `PlatformProduct` id of the site, so the ticket reaches that product's support inbox. */\n productId?: string;\n}\n\n/** `type ExploreEscalation` — the handover on record, as the SERVER holds it. */\nexport interface ExploreEscalation {\n /**\n * Where the reply is actually going.\n *\n * On a repeat submit this is the address the FIRST handover used, not the one just typed\n * into the box — so this is the value to show back, never the local draft.\n */\n email: string;\n createdAt: string;\n}\n\n/** `type EscalateExploreConversationResult`. */\nexport interface EscalateExploreConversationResult {\n /**\n * False when a handover already existed — the double-clicked button, the retried request,\n * the reloaded page.\n *\n * **Not an error.** The conversation is with a human either way, and the UI must read as\n * success on both values. The server enforces one handover per conversation with a UNIQUE\n * index rather than a check-then-insert, so two concurrent submits produce one ticket and\n * one of them gets this.\n */\n created: boolean;\n escalation: ExploreEscalation;\n}\n\n/**\n * Hand this conversation to a person.\n *\n * Awaited and NOT fire-and-forget (unlike {@link RECORD_EXPLORE_CLICK_MUTATION}): it is the\n * one explore write that throws, because it ends with a visitor being told a person has\n * their question. \"We passed it on\" must never be shown for a handover that did not happen.\n *\n * **The address the visitor types is personal data.** It travels in these variables and\n * stops there. Do not persist it, do not fold it into the transcript (`messages` is what the\n * hook writes to localStorage), and do not pass it to `onSend` or any analytics call — the\n * server already has everything a support agent needs.\n */\nexport const ESCALATE_EXPLORE_CONVERSATION_MUTATION = /* GraphQL */ `\n mutation EscalateExploreConversation(\n $input: EscalateExploreConversationInput!\n ) {\n escalateExploreConversation(input: $input) {\n created\n escalation {\n email\n createdAt\n }\n }\n }\n`;\n\nexport interface EscalateExploreConversationMutationData {\n escalateExploreConversation: EscalateExploreConversationResult;\n}\n\n/**\n * How many characters the note field accepts before it stops taking more.\n *\n * Mirrors the server's hard cap so the box does not invite text it will silently truncate.\n * Same status as {@link EXPLORE_FEEDBACK_COMMENT_SOFT_MAX}: a courtesy, not the limit that\n * holds.\n */\nexport const EXPLORE_ESCALATION_NOTE_SOFT_MAX = 2000;\n\n/**\n * A dot-atom local part, real DNS labels, an alphabetic TLD — the shape CONTRACT.md says the\n * server insists on.\n *\n * Deliberately no more permissive than that and deliberately no stricter: this exists to\n * catch the typo before a round-trip, not to be the authority. The server is the authority,\n * and the two DO disagree (an address this accepts can still come back `INVALID_EMAIL`),\n * which is why the form surfaces the server's verdict as well as this one.\n */\nconst DELIVERABLE_EMAIL =\n /^[A-Za-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[A-Za-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?\\.)+[A-Za-z]{2,}$/;\n\n/**\n * Whether `value` is worth sending to the mutation at all.\n *\n * A local pre-flight, never a promise of deliverability. Keep it a SUBSET of what the server\n * accepts: a client that rejects something the server would have taken costs the visitor\n * their answer with nobody able to see why.\n */\nexport function looksLikeDeliverableEmail(value: string): boolean {\n const trimmed = value.trim();\n // 254 is the RFC 5321 ceiling on a forward path; anything longer cannot be delivered\n // anywhere, so it is a typo (or a paste of something that is not an address).\n if (trimmed.length === 0 || trimmed.length > 254) return false;\n return DELIVERABLE_EMAIL.test(trimmed);\n}\n\n/**\n * Mint a read-only link to this conversation.\n *\n * The returned secret is NOT the conversation token — it opens a read-only view and cannot\n * be used to continue the thread, so it is safe to put in a URL that gets forwarded.\n */\nexport const CREATE_EXPLORE_SHARE_LINK_MUTATION = /* GraphQL */ `\n mutation CreateExploreShareLink($conversationToken: String!) {\n createExploreShareLink(conversationToken: $conversationToken) {\n shareToken\n }\n }\n`;\n\n/**\n * Load a conversation someone shared, read-only.\n *\n * Takes the share secret from the `?c=` parameter, NOT a conversation token — the recipient\n * gets the transcript and no ability to continue or modify the thread.\n */\nexport const EXPLORE_SHARED_CONVERSATION_QUERY = /* GraphQL */ `\n query ExploreSharedConversation($shareToken: String!) {\n exploreSharedConversation(shareToken: $shareToken) {\n ${EXPLORE_MESSAGE_FIELDS}\n }\n }\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","import { emitProductSlug } from \"../utils/product-slug\";\n\nimport type { WebsiteSwitcherProduct } from \"../components/website-switcher\";\n\n/**\n * Canonical Burdenoff ecosystem product list used by WebsiteSwitcher.\n *\n * Sourced from `~/products/websites/burdenoff-website/src/data/products.ts`.\n * Keep this in sync when products are added, renamed, or moved.\n */\nexport const ECOSYSTEM_PRODUCTS: WebsiteSwitcherProduct[] = [\n {\n slug: \"workspaces\",\n name: \"Workspaces\",\n websiteUrl: \"https://burdenoff.com\",\n },\n {\n slug: \"vibecontrols\",\n name: \"VibeControls\",\n websiteUrl: \"https://vibecontrols.com\",\n },\n // FluidGrids ships on www.fluidgrids.ai — fluidgrids.com only redirects there. The explore\n // catalog and the site's own sitemap use this origin, and so must the fallback catalog\n // built from this list, or /explore rejects FluidGrids concept images until it loads.\n {\n slug: \"fluidgrids\",\n name: \"FluidGrids\",\n websiteUrl: \"https://www.fluidgrids.ai\",\n },\n // BotManor ships on www.botmanor.com (the apex redirects there; the old botlit.ai domain\n // still serves the same site but is no longer the name we link to).\n // The slug follows the botlit → botmanor rename switch (utils/product-slug.ts).\n {\n slug: emitProductSlug(\"botmanor\"),\n name: \"BotManor\",\n websiteUrl: \"https://www.botmanor.com\",\n },\n {\n slug: \"bigconsole\",\n name: \"BigConsole\",\n websiteUrl: \"https://bigconsole.com\",\n },\n {\n slug: \"semanticfed\",\n name: \"SemanticFed\",\n websiteUrl: \"https://semanticfed.com\",\n },\n {\n slug: \"intelwatchtower\",\n name: \"IntelWatchtower\",\n websiteUrl: \"https://intelwatchtower.com\",\n },\n { slug: \"buildmyiq\", name: \"BuildMyIQ\", websiteUrl: \"https://buildmyiq.com\" },\n {\n slug: \"assethandler\",\n name: \"AssetHandler\",\n websiteUrl: \"https://assethandler.com\",\n },\n {\n slug: \"crewfoundry\",\n name: \"CrewFoundry\",\n websiteUrl: \"https://crewfoundry.com\",\n },\n {\n slug: \"planmagnet\",\n name: \"PlanMagnet\",\n websiteUrl: \"https://planmagnet.com\",\n },\n {\n slug: \"eventfullymanaged\",\n name: \"EventfullyManaged\",\n websiteUrl: \"https://eventfullymanaged.com\",\n },\n {\n slug: \"headshotmarketing\",\n name: \"HeadshotMarketing\",\n websiteUrl: \"https://headshotmarketing.com\",\n },\n {\n slug: \"technospam\",\n name: \"TechnoSpam\",\n websiteUrl: \"https://technospam.com\",\n },\n { slug: \"kadaikodi\", name: \"Kadaikodi\", websiteUrl: \"https://kadaikodi.com\" },\n {\n slug: \"movethewheels\",\n name: \"MoveTheWheels\",\n websiteUrl: \"https://movethewheels.com\",\n },\n {\n slug: \"healthybowl\",\n name: \"Healthy Bowl\",\n websiteUrl: \"https://gethealthybowl.com\",\n },\n { slug: \"ggnomad\", name: \"GGNomad\", websiteUrl: \"https://ggnomad.com\" },\n {\n slug: \"timecampus\",\n name: \"TimeCampus\",\n websiteUrl: \"https://timecampus.com\",\n },\n {\n slug: \"manufacturedops\",\n name: \"ManufacturedOps\",\n websiteUrl: \"https://manufacturedops.com\",\n },\n {\n slug: \"theglobalfuel\",\n name: \"TheGlobalFuel\",\n websiteUrl: \"https://theglobalfuel.com\",\n },\n {\n slug: \"housingvista\",\n name: \"HousingVista\",\n websiteUrl: \"https://housingvista.com\",\n },\n {\n slug: \"proserviceworld\",\n name: \"ProServiceWorld\",\n websiteUrl: \"https://proserviceworld.com\",\n },\n {\n slug: \"mybodyshield\",\n name: \"MyBodyShield\",\n websiteUrl: \"https://mybodyshield.com\",\n },\n {\n slug: \"artistrybase\",\n name: \"ArtistryBase\",\n websiteUrl: \"https://artistrybase.com\",\n },\n {\n slug: \"theculturepost\",\n name: \"TheCulturePost\",\n websiteUrl: \"https://theculturepost.com\",\n },\n { slug: \"collabkin\", name: \"CollabKin\", websiteUrl: \"https://collabkin.com\" },\n {\n slug: \"comradecircle\",\n name: \"ComradeCircle\",\n websiteUrl: \"https://comradecircle.com\",\n },\n {\n slug: \"brainyrich\",\n name: \"BrainyRich\",\n websiteUrl: \"https://brainyrich.com\",\n },\n {\n slug: \"cosmicintersection\",\n name: \"CosmicIntersection\",\n websiteUrl: \"https://cosmicintersection.com\",\n },\n {\n slug: \"govcitizenhub\",\n name: \"GovCitizenHub\",\n websiteUrl: \"https://govcitizenhub.com\",\n },\n {\n slug: \"adaptercloud\",\n name: \"AdapterCloud\",\n websiteUrl: \"https://adaptercloud.com\",\n },\n {\n slug: \"subscriberbot\",\n name: \"Subscriber Bot\",\n websiteUrl: \"https://subscriberbot.com\",\n },\n {\n slug: \"hookmovies\",\n name: \"HookMovies\",\n websiteUrl: \"https://hookmovies.com\",\n },\n {\n slug: \"coolandlovely\",\n name: \"CoolAndLovely\",\n websiteUrl: \"https://coolandlovely.com\",\n },\n {\n slug: \"atheletebridge\",\n name: \"AthleteBridge\",\n websiteUrl: \"https://atheletebridge.com\",\n },\n {\n slug: \"labsofscience\",\n name: \"LabsOfScience\",\n websiteUrl: \"https://labsofscience.com\",\n },\n {\n slug: \"ecoimpacthub\",\n name: \"EcoImpactHub\",\n websiteUrl: \"https://ecoimpacthub.com\",\n },\n];\n","/**\n * reCAPTCHA v3 (invisible, score-based) loader and token minter.\n *\n * Deliberately dependency-free: the SDK already ships the `react-google-recaptcha`\n * peer for the **v2 checkbox** used by `ContactPage`, and the two widgets cannot\n * share a script tag — v3 needs `?render=<siteKey>` on the URL, v2 must not have\n * it. This module injects its own v3 script and leaves the v2 integration alone.\n *\n * The Explore chat turns v3 on purely from the backend catalog\n * (`exploreCatalog.captchaRequired && recaptchaSiteKey`), so nothing is loaded\n * and no third-party request is made on sites where captcha is off.\n */\n\n/** The slice of the `grecaptcha` global this module uses. */\nexport interface RecaptchaV3 {\n /** Runs the callback once the API is fully initialised. */\n ready(callback: () => void): void;\n /** Mints a single-use token for `action`. */\n execute(siteKey: string, options: { action: string }): Promise<string>;\n}\n\ndeclare global {\n interface Window {\n grecaptcha?: RecaptchaV3;\n }\n}\n\nconst SCRIPT_ID = \"boff-recaptcha-v3\";\nconst SCRIPT_SRC = \"https://www.google.com/recaptcha/api.js?render=\";\n\n/** Default action name reported to reCAPTCHA for Explore chat submissions. */\nexport const EXPLORE_RECAPTCHA_ACTION = \"explore_chat\";\n\n/**\n * One in-flight/settled promise per site key. Guarantees the script tag is\n * injected at most once even when several components mount at the same time,\n * and lets later callers await the same load instead of racing it.\n */\nconst loaders = new Map<string, Promise<RecaptchaV3>>();\n\nfunction isBrowser(): boolean {\n return typeof window !== \"undefined\" && typeof document !== \"undefined\";\n}\n\n/**\n * Load the reCAPTCHA v3 script for `siteKey` and resolve once `grecaptcha` is\n * ready to mint tokens.\n *\n * - Injects the script at most once per site key (subsequent calls reuse the\n * same promise).\n * - Adopts an already-present script tag (e.g. injected by the host site)\n * rather than adding a second one.\n * - Rejects rather than hanging when the script fails to load, so the caller\n * can decide whether to submit without a token or surface an error.\n */\nexport function loadRecaptchaV3(siteKey: string): Promise<RecaptchaV3> {\n if (!siteKey) {\n return Promise.reject(new Error(\"reCAPTCHA site key is required\"));\n }\n if (!isBrowser()) {\n return Promise.reject(\n new Error(\"reCAPTCHA v3 can only be loaded in a browser\"),\n );\n }\n\n const existing = loaders.get(siteKey);\n if (existing) return existing;\n\n const promise = new Promise<RecaptchaV3>((resolve, reject) => {\n const ready = () => {\n const api = window.grecaptcha;\n if (!api) {\n reject(new Error(\"reCAPTCHA loaded but grecaptcha is unavailable\"));\n return;\n }\n // `ready` queues until the API has finished initialising internally;\n // calling `execute` before that throws.\n api.ready(() => resolve(api));\n };\n\n // Already loaded by this module, another SDK instance, or the host site.\n if (window.grecaptcha) {\n ready();\n return;\n }\n\n const scriptId = `${SCRIPT_ID}-${siteKey}`;\n const found = document.getElementById(scriptId);\n if (found) {\n found.addEventListener(\"load\", ready, { once: true });\n found.addEventListener(\n \"error\",\n () => reject(new Error(\"Failed to load reCAPTCHA v3\")),\n { once: true },\n );\n return;\n }\n\n const script = document.createElement(\"script\");\n script.id = scriptId;\n script.src = `${SCRIPT_SRC}${encodeURIComponent(siteKey)}`;\n script.async = true;\n script.defer = true;\n script.addEventListener(\"load\", ready, { once: true });\n script.addEventListener(\n \"error\",\n () => {\n // Drop the memo so a later attempt can retry a transient CDN failure\n // instead of being stuck with a permanently rejected promise.\n loaders.delete(siteKey);\n reject(new Error(\"Failed to load reCAPTCHA v3\"));\n },\n { once: true },\n );\n document.head.appendChild(script);\n });\n\n loaders.set(siteKey, promise);\n return promise;\n}\n\n/**\n * Mint a single-use reCAPTCHA v3 token, loading the script on first use.\n *\n * Tokens are valid for ~2 minutes and are consumed by one verification, so\n * this must be called per submission — never cached.\n */\nexport async function getRecaptchaV3Token(\n siteKey: string,\n action: string = EXPLORE_RECAPTCHA_ACTION,\n): Promise<string> {\n const api = await loadRecaptchaV3(siteKey);\n return api.execute(siteKey, { action });\n}\n","/**\n * useExploreChat — the `/explore` conversation state machine.\n *\n * Implements the client half of CONTRACT.md §1: a **submit + poll** exchange.\n * `sendExploreMessage` returns immediately with a PENDING assistant message;\n * the answer arrives by polling `exploreMessage(conversationToken, id)` roughly\n * every 1.5s until it reports COMPLETED or FAILED. Both gateway hops cap a\n * subgraph call at 20s while a turn takes 5-60s, which is why there is no\n * request that simply waits for the answer.\n *\n * Design rules this hook exists to enforce:\n *\n * 1. **The catalog never gates the first paint.** `catalog` is populated with a\n * static fallback synchronously, so `ExplorePage` can render its `<h1>` on\n * the very first render. The websites' prerender waits for an `h1`; if that\n * heading depended on a network round-trip, 38 static builds would capture a\n * blank page.\n * 2. **Polls bypass the query cache.** `client.query(..., { cache: false })` —\n * the default 5-minute cache would hand every later poll the first PENDING\n * snapshot and the answer would never appear.\n * 3. **Nothing outlives the component.** Unmount and `stop()` both cancel the\n * poll chain through a generation counter, so a late response can never\n * write into an unmounted tree.\n */\n\"use client\";\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\n\nimport { useWebSDK, useWebSDKConfig } from \"../client/provider\";\nimport {\n EXPLORE_CATALOG_QUERY,\n EXPLORE_CATALOG_QUERY_WITHOUT_HIGHLIGHTS,\n EXPLORE_MESSAGE_QUERY,\n EXPLORE_SHARED_CONVERSATION_QUERY,\n CREATE_EXPLORE_SHARE_LINK_MUTATION,\n ESCALATE_EXPLORE_CONVERSATION_MUTATION,\n RECORD_EXPLORE_CLICK_MUTATION,\n SEND_EXPLORE_MESSAGE_MUTATION,\n SUBMIT_EXPLORE_FEEDBACK_MUTATION,\n} from \"../components/explore-types\";\nimport { ECOSYSTEM_PRODUCTS } from \"../data/products\";\nimport { getRecaptchaV3Token } from \"../utils/recaptcha-v3\";\n\nimport type { GraphQLResponse } from \"../client/graphql-client\";\nimport type {\n EscalateExploreConversationInput,\n EscalateExploreConversationMutationData,\n ExploreCatalog,\n ExploreCatalogQueryData,\n ExploreFeedbackRating,\n ExploreFeedbackReason,\n ExploreLimits,\n ExploreMessage,\n ExploreMessageQueryData,\n SendExploreMessageInput,\n SendExploreMessageMutationData,\n SubmitExploreFeedbackInput,\n SubmitExploreFeedbackMutationData,\n} from \"../components/explore-types\";\nimport type { ExploreAttachmentPayload } from \"../utils/explore-attachments\";\n\n// ============================================================================\n// Public types\n// ============================================================================\n\n/**\n * Where the conversation is right now.\n *\n * - `loading` — catalog request in flight (the fallback catalog is already\n * rendered, so this is a refinement, not a blank screen).\n * - `ready` — idle; the composer accepts input.\n * - `sending` — `sendExploreMessage` in flight.\n * - `streaming` — polling the assistant turn; `activity` is live.\n * - `error` — the last turn failed; `retry()` is meaningful and the\n * composer stays usable.\n * - `limited` — rate limited; the composer is disabled.\n * - `disabled` — `exploreCatalog.enabled === false`.\n */\nexport type ExploreChatPhase =\n | \"loading\"\n | \"ready\"\n | \"sending\"\n | \"streaming\"\n | \"error\"\n | \"limited\"\n | \"disabled\";\n\n/** Coarse bucket the UI switches its banner copy and affordances on. */\nexport type ExploreChatErrorKind =\n | \"rate-limit\"\n | \"captcha\"\n | \"timeout\"\n // The question was answerable, but not across this many products at once. Distinct from\n // \"timeout\" because the remedy is different: narrow the question, do not retry it.\n | \"scope\"\n | \"network\"\n | \"disabled\"\n | \"validation\"\n | \"unknown\";\n\nexport interface ExploreChatError {\n /** UI bucket. */\n kind: ExploreChatErrorKind;\n /** Raw `extensions.code` (or a client-side code such as `TIMEOUT`). */\n code: string;\n /** Human-readable, safe to render. */\n message: string;\n /** Whether re-sending the same message could plausibly succeed. */\n retryable: boolean;\n}\n\n/**\n * What came of handing the conversation to a person.\n *\n * `sent` and `already-handed-over` are BOTH successes. The second is the double-clicked\n * button, the retried request and the reloaded page: the server keeps one handover per\n * conversation, so a repeat submit returns the first one rather than filing a second ticket.\n * A visitor who is worried enough to ask for a human must never be told \"you already did\n * that\" — the conversation is with a person either way, and the UI says so either way.\n */\nexport type ExploreEscalateOutcome =\n | \"sent\"\n | \"already-handed-over\"\n /** The SERVER rejected the address (`INVALID_EMAIL`), after the client let it through. */\n | \"invalid-email\"\n | \"rate-limited\"\n | \"captcha\"\n /** No conversation token — nothing to hand over. The form should not have been offered. */\n | \"no-conversation\"\n | \"failed\";\n\nexport interface ExploreEscalateResult {\n outcome: ExploreEscalateOutcome;\n /**\n * The address the SERVER says the reply is going to, on `sent` and `already-handed-over`;\n * null otherwise.\n *\n * On `already-handed-over` this is the address the FIRST handover used, which may not be\n * the one just typed — and it is the one the reply actually reaches, so it is the one to\n * show back.\n */\n email: string | null;\n /** When the handover was filed, as the server holds it. Null unless it exists. */\n createdAt: string | null;\n}\n\nexport interface UseExploreChatOptions {\n /** Slug of the product site this page is on — a soft hint to the harness. */\n productSlug?: string;\n /** Full URL of the page, forwarded as `pageUrl`. */\n pageUrl?: string;\n /** BCP-47 locale, forwarded as `locale`. */\n locale?: string;\n /** Poll cadence in ms. Default 1500 (CONTRACT.md §1). */\n pollIntervalMs?: number;\n /** Give up on a turn after this long. Default 90000 (the edge poll timeout). */\n pollTimeoutMs?: number;\n /**\n * Prompts to offer when `exploreCatalog` supplies none. Resolution order is\n * catalog → this option → {@link DEFAULT_EXPLORE_EXAMPLE_PROMPTS}, so a site\n * can ship product-specific prompts without waiting on the backend and still\n * be overridden centrally once the catalog carries them.\n */\n examplePrompts?: string[];\n /**\n * Welcome copy for the fallback catalog, used only when `exploreCatalog` cannot be reached.\n *\n * The hook's own built-in fallback is English. `ExplorePage` passes the strings for the\n * visitor's locale, so a catalog outage degrades to a translated page rather than an\n * English one — the whole point of shipping six locales is that the failure paths speak\n * them too.\n */\n fallbackWelcome?: { title: string; body: string };\n /** localStorage key for the ACTIVE conversation. Default `boff.explore.v1`. */\n storageKey?: string;\n /** Persist the conversation across reloads within the tab. Default true. */\n persist?: boolean;\n /** Called with the message text each time a send is accepted locally. */\n onSend?: (message: string) => void;\n}\n\nexport interface UseExploreChatResult {\n phase: ExploreChatPhase;\n /** Never null — starts as the baked or static fallback catalog, upgraded by the server. */\n catalog: ExploreCatalog;\n /**\n * The last catalog that came from the server, raw, or null if only the fallback has been\n * seen. ExplorePage writes it into the page so the site's prerender captures it.\n */\n serverCatalogRaw: ExploreCatalog | null;\n messages: ExploreMessage[];\n /** Latest progress line of the in-flight turn, or null when idle. */\n activity: string | null;\n /** Messages left today for this visitor, when the server reports it. */\n remainingToday: number | null;\n error: ExploreChatError | null;\n /** Product the visitor pinned the conversation to, or null for \"all\". */\n focusProduct: string | null;\n setFocusProduct: (slug: string | null) => void;\n send: (\n message: string,\n attachments?: readonly ExploreAttachmentPayload[],\n ) => Promise<void>;\n /** Stop polling and keep whatever text has streamed so far. */\n stop: () => void;\n /** Re-send the last message after a failed turn. */\n retry: () => void;\n /** Clear the conversation and the persisted session. */\n reset: () => void;\n /** Past conversations held in THIS browser, newest first. */\n history: ArchivedExploreConversation[];\n /** Reopen a past conversation and continue it. */\n openConversation: (token: string) => void;\n /** Forget one past conversation (local only). */\n deleteConversation: (token: string) => void;\n /** Forget every past conversation held in this browser. */\n clearHistory: () => void;\n /**\n * Record that the visitor followed a link this answer offered. Fire-and-forget — never\n * awaited, never throws, and must never delay the navigation it is reporting on.\n */\n recordClick: (\n messageId: string,\n kind: \"CTA\" | \"REFERENCE\",\n url: string,\n ) => void;\n /**\n * Rate one answer. Resolves `true` only when the server reports `recorded: true`.\n *\n * The opposite posture to {@link recordClick}, and deliberately so: the visitor is still on\n * the page and is watching the control they pressed, so the result has to come back far\n * enough for the UI to tell the truth about it. It still never throws — a transport\n * failure, a GraphQL error and an unattributable message all resolve `false`.\n */\n submitFeedback: (\n messageId: string,\n rating: ExploreFeedbackRating,\n detail?: { reason?: ExploreFeedbackReason | null; comment?: string | null },\n ) => Promise<boolean>;\n /**\n * Hand this conversation to a person, so they pick it up with the transcript attached.\n *\n * Awaited, like {@link submitFeedback} and for the same reason — the visitor is watching\n * the control they pressed — but this one has more to say than true/false: \"we could not\n * deliver to that address\", \"you are sending these too fast\" and \"it is already with a\n * human\" are three different things to a worried visitor, and one boolean flattens them\n * into a shrug. It never throws; every outcome comes back in\n * {@link ExploreEscalateResult}.\n *\n * `email` is personal data. It goes into the mutation variables and nowhere else — not\n * into `messages` (which is what this hook persists), not into `onSend`, not into any\n * analytics call.\n */\n escalate: (email: string, note?: string) => Promise<ExploreEscalateResult>;\n /**\n * Whether there is a conversation a person could actually be handed.\n *\n * False on a fresh page and false in a read-only shared view (a `?c=` secret is not a\n * conversation token), which are exactly the cases where {@link escalate} could only\n * answer `no-conversation`. Offer the form on this, rather than offering one that cannot\n * work.\n */\n canEscalate: boolean;\n /**\n * Mint a read-only link to this conversation, or null when there is nothing to share yet.\n * The secret returned is not the conversation token and cannot continue the thread.\n */\n createShareLink: () => Promise<string | null>;\n /**\n * True when the transcript on screen came from a shared link rather than this visitor's own\n * conversation. A shared view is read-only: the share secret cannot continue the thread.\n */\n isSharedView: boolean;\n canSend: boolean;\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst DEFAULT_STORAGE_KEY = \"boff.explore.v1\";\n/** Where past conversations live. Derived from the active key so a custom key stays paired. */\nconst HISTORY_SUFFIX = \".history\";\n/**\n * How many past conversations to keep, and how many messages of each. Bounded on purpose:\n * localStorage is a few MB per origin and shared with everything else the site stores, so an\n * unbounded archive would eventually start throwing QuotaExceededError on write.\n */\nconst MAX_ARCHIVED_CONVERSATIONS = 15;\nconst MAX_ARCHIVED_MESSAGES = 20;\nconst DEFAULT_POLL_INTERVAL_MS = 1_500;\n/** Matches `EXPLORE_AGENT_TIMEOUT_MS` (CONTRACT.md §3) — the edge gives up too. */\nconst DEFAULT_POLL_TIMEOUT_MS = 90_000;\n/** Per-poll HTTP timeout. Short: a stuck poll must not stall the chain. */\nconst POLL_REQUEST_TIMEOUT_MS = 10_000;\n/** The submit hop enqueues a job; generous but well under the gateway's 20s. */\nconst SEND_REQUEST_TIMEOUT_MS = 18_000;\n/** Transient poll failures tolerated before the turn is declared dead. */\nconst MAX_CONSECUTIVE_POLL_FAILURES = 4;\n/** Cap on what goes into localStorage — a conversation must not grow unbounded. */\nconst MAX_PERSISTED_MESSAGES = 40;\n\n/** Fallback limits, mirroring the server defaults in CONTRACT.md §3. */\nexport const FALLBACK_EXPLORE_LIMITS: ExploreLimits = {\n maxMessageLength: 2000,\n maxTurnsPerConversation: 20,\n messagesPerDay: 100,\n historyTurns: 6,\n};\n\n/**\n * Prompts shown when neither the catalog nor the consumer supplies any.\n * Deliberately product-agnostic — this SDK renders on 38 different sites.\n *\n * Problem-first, and kept in step with `support.explore.examplePrompts.*` in\n * global-support-svc. These render precisely when the catalog call failed, so\n * if they disagree with the server the page silently reverts to whatever this\n * file last said — which is how the whole surface can go back to being a\n * brochure without a single copy change being reviewed.\n */\nexport const DEFAULT_EXPLORE_EXAMPLE_PROMPTS: string[] = [\n \"Every course we run lives in a different spreadsheet.\",\n \"We only hear a machine is down after the line has stopped.\",\n \"Members quietly stop showing up and we never know why.\",\n \"Which of your products fits a problem like mine?\",\n];\n\nconst FALLBACK_WELCOME_TITLE = \"What problem are you trying to solve?\";\nconst FALLBACK_WELCOME_BODY =\n \"Describe the problem, in your own words. You can also ask about a specific product or compare a few. Every answer comes straight from our documentation.\";\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction buildFallbackCatalog(\n examplePrompts?: string[],\n welcome?: { title: string; body: string },\n): ExploreCatalog {\n return {\n enabled: true,\n products: ECOSYSTEM_PRODUCTS.map((p) => ({\n slug: p.slug,\n name: p.name,\n tagline: null,\n website: p.websiteUrl,\n })),\n examplePrompts: examplePrompts?.length\n ? examplePrompts\n : DEFAULT_EXPLORE_EXAMPLE_PROMPTS,\n welcomeTitle: welcome?.title ?? FALLBACK_WELCOME_TITLE,\n welcomeBody: welcome?.body ?? FALLBACK_WELCOME_BODY,\n limits: FALLBACK_EXPLORE_LIMITS,\n captchaRequired: false,\n recaptchaSiteKey: null,\n // Empty, and deliberately not synthesised from `ECOSYSTEM_PRODUCTS`: a concept highlight\n // needs a real published illustration URL, and this SDK has no registry of them. An\n // invented path would render 38 broken frames on the one screen every prerender captures.\n // The strip renders nothing at all when the list is empty, which is the honest fallback.\n conceptHighlights: [],\n };\n}\n\n/** The element a prerendered /explore carries its catalog in. */\nexport const BAKED_CATALOG_ELEMENT_ID = \"boff-explore-catalog\";\n\n/**\n * The language the live catalog is localised to. The server localises exploreCatalog from the\n * browser's Accept-Language, so a copy baked by an English prerender is the wrong copy for a\n * visitor whose browser asks for Spanish.\n */\nexport function bakedCatalogLocale(): string {\n if (typeof navigator === \"undefined\") return \"\";\n return (navigator.language || \"\").toLowerCase().split(\"-\")[0] ?? \"\";\n}\n\ninterface BakedCatalogElement {\n product: string;\n locale: string;\n text: string;\n}\n\nfunction snapshotBakedElement(): BakedCatalogElement | null {\n if (typeof document === \"undefined\") return null;\n const el = document.getElementById(BAKED_CATALOG_ELEMENT_ID);\n // A real <script type=\"application/json\"> only. Anything else that happens to carry the id —\n // sanitised CMS HTML rendered on the same page, say — is not ours to read.\n if (\n !el ||\n el.tagName !== \"SCRIPT\" ||\n el.getAttribute(\"type\") !== \"application/json\"\n )\n return null;\n return {\n product: el.getAttribute(\"data-product\") ?? \"\",\n locale: el.getAttribute(\"data-locale\") ?? \"\",\n text: el.textContent ?? \"\",\n };\n}\n\n/**\n * The baked catalog as it stood when this module first evaluated.\n *\n * Reading it from ExplorePage's state initialiser was not early enough on 35 of the 39 host\n * sites. They lazy-load /explore behind a <Suspense> boundary, so React's FIRST commit is the\n * route fallback — and that commit clears the prerendered page, this element with it, ~400ms\n * before ExplorePage renders for the first time. (Reproduced in Chrome by an independent\n * review; the first version's test put the element outside React's container, where no commit\n * could remove it, and so could not see this.) Every host's main.tsx imports the SDK root\n * statically before calling createRoot, and the SDK is one module (tsup splitting: false, no\n * sideEffects field), so this line runs while the static page is still intact.\n *\n * This keeps the catalog; it cannot stop that fallback commit from blanking the page for the\n * ~350ms the route chunk takes. Measured on vibecontrols: lazy, the strip, header and composer\n * all vanished at ~600ms; with ExplorePage imported eagerly, nothing vanished. So the hosts\n * import it eagerly — it only wraps this component, which their entry chunk already carries.\n */\nconst BAKED_AT_LOAD: BakedCatalogElement | null = snapshotBakedElement();\n\n/** Enough of the shape to render without throwing; normalizeCatalog fills the rest. */\nfunction isCatalogShape(value: unknown): value is ExploreCatalog {\n if (!value || typeof value !== \"object\") return false;\n const c = value as Record<string, unknown>;\n const listOrAbsent = (v: unknown) =>\n v === undefined || v === null || Array.isArray(v);\n const textOrAbsent = (v: unknown) =>\n v === undefined || v === null || typeof v === \"string\";\n return (\n (c[\"enabled\"] === undefined || typeof c[\"enabled\"] === \"boolean\") &&\n listOrAbsent(c[\"products\"]) &&\n listOrAbsent(c[\"examplePrompts\"]) &&\n listOrAbsent(c[\"conceptHighlights\"]) &&\n textOrAbsent(c[\"welcomeTitle\"]) &&\n textOrAbsent(c[\"welcomeBody\"])\n );\n}\n\n/**\n * The catalog the static page was prerendered with, if it carries a usable one for this product\n * and this visitor's language. The live element is preferred (a site that imports ExplorePage\n * eagerly still has it at first render); otherwise the load-time snapshot.\n *\n * Every host mounts with createRoot, which REPLACES the prerendered page rather than adopting\n * it; starting from the fallback catalog tore down the concept strip and the product's own\n * intro the static page was showing, and put them back 1–2s later — or never, if the catalog\n * call failed. Any mismatch or parse failure returns null, and the page behaves as it did\n * before this existed.\n */\nfunction readBakedCatalog(\n productSlug: string | undefined,\n): ExploreCatalog | null {\n const baked = snapshotBakedElement() ?? BAKED_AT_LOAD;\n if (!baked) return null;\n if (baked.product !== (productSlug ?? \"\")) return null;\n if (baked.locale && baked.locale !== bakedCatalogLocale()) return null;\n try {\n const parsed: unknown = JSON.parse(baked.text);\n return isCatalogShape(parsed) ? parsed : null;\n } catch {\n return null;\n }\n}\n\n/**\n * JSON that is safe inside a <script> element. `JSON.stringify` leaves `</script>` and `<!--`\n * intact, and the catalog carries free text from the backend (titles, prompts), so the\n * characters that could end the element or open a comment are escaped as \\u sequences —\n * which JSON.parse turns straight back into the same characters.\n */\nexport function serializeBakedCatalog(raw: ExploreCatalog): string {\n return JSON.stringify(raw)\n .replace(/</g, \"\\\\u003c\")\n .replace(/>/g, \"\\\\u003e\")\n .replace(/&/g, \"\\\\u0026\")\n .replace(/\\u2028/g, \"\\\\u2028\")\n .replace(/\\u2029/g, \"\\\\u2029\");\n}\n\n/**\n * Harden a server catalog against partial/degraded responses so a missing\n * field can never blank the page or produce `maxMessageLength: 0` (which would\n * silently disable the composer).\n */\nfunction normalizeCatalog(\n raw: ExploreCatalog,\n fallback: ExploreCatalog,\n): ExploreCatalog {\n return {\n enabled: raw.enabled !== false,\n products: raw.products?.length ? raw.products : fallback.products,\n examplePrompts: raw.examplePrompts?.length\n ? raw.examplePrompts\n : fallback.examplePrompts,\n welcomeTitle: raw.welcomeTitle || fallback.welcomeTitle,\n welcomeBody: raw.welcomeBody || fallback.welcomeBody,\n limits: {\n maxMessageLength:\n raw.limits?.maxMessageLength || fallback.limits.maxMessageLength,\n maxTurnsPerConversation:\n raw.limits?.maxTurnsPerConversation ||\n fallback.limits.maxTurnsPerConversation,\n messagesPerDay:\n raw.limits?.messagesPerDay || fallback.limits.messagesPerDay,\n historyTurns: raw.limits?.historyTurns || fallback.limits.historyTurns,\n },\n captchaRequired: raw.captchaRequired === true,\n recaptchaSiteKey: raw.recaptchaSiteKey ?? null,\n // `[ExploreConceptHighlight!]!` on the wire, so a gateway that KNOWS the field always\n // sends an array. The one response that arrives without it is the retry against an older\n // supergraph (`EXPLORE_CATALOG_QUERY_WITHOUT_HIGHLIGHTS`) — that gateway does not answer\n // the full document at all, it refuses it. Either way\n // `catalog.conceptHighlights.filter(...)` on `undefined` would throw inside render,\n // blanking the welcome screen over a decorative strip.\n conceptHighlights: raw.conceptHighlights?.length\n ? raw.conceptHighlights\n : fallback.conceptHighlights,\n };\n}\n\n/**\n * Codes that mean \"this gateway would not accept the document\", as opposed to\n * \"the request failed\". `HTTP_400` is in the set because a gateway may surface\n * a validation failure as a status instead of a body — production answers 200\n * with `GRAPHQL_VALIDATION_FAILED`, but that is a server choice, not a promise,\n * and `WebSDKClient` collapses any non-2xx into `HTTP_<status>` with the\n * original message discarded.\n */\nconst CATALOG_DOCUMENT_REJECTED_CODES = new Set([\n \"GRAPHQL_VALIDATION_FAILED\",\n \"HTTP_400\",\n]);\n\n/**\n * True when the catalog came back with nothing usable AND the reason looks like\n * the gateway refusing the document rather than failing to serve it.\n *\n * GraphQL validation is all-or-nothing, so against a supergraph that predates\n * `conceptHighlights` there is no partial response to salvage: no `data`, and\n * the loss is the WHOLE catalog — the server-side `enabled` kill switch,\n * `captchaRequired`/`recaptchaSiteKey`, the reviewed welcome copy and `limits`.\n * One retry without the field recovers all of it.\n */\nexport function catalogRejectedTheHighlightField(\n res: GraphQLResponse<ExploreCatalogQueryData>,\n): boolean {\n if (res.data?.exploreCatalog) return false;\n return (res.errors ?? []).some((error) => {\n const code = error.extensions?.code;\n return (\n (typeof code === \"string\" && CATALOG_DOCUMENT_REJECTED_CODES.has(code)) ||\n error.message.includes(\"conceptHighlights\")\n );\n });\n}\n\nconst RATE_LIMIT_CODES = new Set([\n \"RATE_LIMITED\",\n \"HTTP_429\",\n \"EXPLORE_GLOBAL_LIMIT\",\n]);\nconst CAPTCHA_CODES = new Set([\"CAPTCHA_FAILED\", \"CAPTCHA_REQUIRED\"]);\nconst NETWORK_CODES = new Set([\n \"NETWORK_ERROR\",\n \"UPSTREAM_UNAVAILABLE\",\n \"CONVERSATION_BUSY\",\n \"PROVIDER_ERROR\",\n \"QUEUE_FULL\",\n]);\nconst VALIDATION_CODES = new Set([\n \"MESSAGE_TOO_LONG\",\n \"MESSAGE_EMPTY\",\n \"CONVERSATION_LIMIT\",\n \"INVALID_INPUT\",\n]);\n\n/**\n * Map a `extensions.code` (or a client-side transport code) onto the bucket the\n * UI renders. Unknown codes are treated as retryable — a transient backend\n * condition we have not enumerated is far more likely than a permanent one.\n */\n/**\n * Map an `extensions.code` from `escalateExploreConversation` onto the outcome the form\n * renders.\n *\n * Separate from {@link classifyExploreError} on purpose. That one buckets a failed ANSWER\n * and its buckets carry copy about retrying a question — none of which is true here, and\n * `INVALID_EMAIL` has no bucket there at all (it would land in `unknown` and be shown as\n * \"something went wrong\", which is the one message that leaves the visitor unable to fix\n * the one thing they could actually fix).\n *\n * `EXPLORE_GLOBAL_LIMIT` is deliberately NOT treated as rate limiting here: the contract\n * keeps a handover off the fleet-wide LLM budget, so seeing it would mean something\n * unexpected, not \"you are going too fast\".\n */\nexport function escalateOutcomeForCode(\n code: string | null | undefined,\n): ExploreEscalateOutcome {\n switch (code) {\n case \"INVALID_EMAIL\":\n return \"invalid-email\";\n case \"RATE_LIMITED\":\n case \"HTTP_429\":\n return \"rate-limited\";\n case \"CAPTCHA_REQUIRED\":\n case \"CAPTCHA_FAILED\":\n return \"captcha\";\n // The token owns nothing, or owns a different conversation. The server answers NOT_FOUND\n // rather than FORBIDDEN so the endpoint is not an oracle for whether a thread exists;\n // either way there is nothing this visitor can do about it, so it reads as a failure.\n case \"NOT_FOUND\":\n return \"failed\";\n default:\n return \"failed\";\n }\n}\n\nexport function classifyExploreError(\n code: string | null | undefined,\n serverMessage?: string | null,\n): ExploreChatError {\n const c = code ?? \"UNKNOWN\";\n if (RATE_LIMIT_CODES.has(c)) {\n return {\n kind: \"rate-limit\",\n code: c,\n message:\n \"You've reached the message limit for now. Please try again later.\",\n retryable: false,\n };\n }\n if (CAPTCHA_CODES.has(c)) {\n return {\n kind: \"captcha\",\n code: c,\n message:\n \"We couldn't verify that you're human. Please try sending that again.\",\n retryable: true,\n };\n }\n // TOOL_BUDGET is NOT a timeout and must not be described as one. Measured in production:\n // the failing turn ended in 33s against a 75s budget — it did not run out of time, it ran\n // out of searches. Retrying is also the wrong advice: the same question searches the same\n // way and fails identically, so \"try again\" is a button that cannot work. The honest reply\n // names the actual limit and offers the narrowing that does work.\n if (c === \"TOOL_BUDGET\") {\n return {\n kind: \"scope\",\n code: c,\n message:\n \"That question spans more of our products than I can look through in one go. Try asking about two or three specific products.\",\n retryable: false,\n };\n }\n if (c === \"TIMEOUT\") {\n return {\n kind: \"timeout\",\n code: c,\n message: \"That answer took too long to come back. Try asking again.\",\n retryable: true,\n };\n }\n if (NETWORK_CODES.has(c)) {\n return {\n kind: \"network\",\n code: c,\n message:\n \"We couldn't reach the assistant just now. Check your connection and try again.\",\n retryable: true,\n };\n }\n if (c === \"EXPLORE_DISABLED\") {\n return {\n kind: \"disabled\",\n code: c,\n message: \"Explore is unavailable at the moment. Please check back soon.\",\n retryable: false,\n };\n }\n if (VALIDATION_CODES.has(c)) {\n return {\n kind: \"validation\",\n code: c,\n message: serverMessage || \"That message couldn't be sent.\",\n retryable: false,\n };\n }\n if (c === \"EMPTY_ANSWER\") {\n return {\n kind: \"unknown\",\n code: c,\n message:\n \"No answer came back for that one. Try rephrasing, or ask something more specific.\",\n retryable: true,\n };\n }\n return {\n kind: \"unknown\",\n code: c,\n message: serverMessage || \"Something went wrong. Please try again.\",\n retryable: true,\n };\n}\n\ninterface PersistedExploreState {\n conversationToken: string | null;\n messages: ExploreMessage[];\n focusProduct: string | null;\n}\n\n/** One past conversation, as shown in the history list and restored on click. */\nexport interface ArchivedExploreConversation {\n token: string;\n /** First thing the visitor asked — the human-readable label for the thread. */\n title: string;\n /** ISO timestamp of the last message in the thread. */\n updatedAt: string;\n messageCount: number;\n focusProduct: string | null;\n messages: ExploreMessage[];\n}\n\nfunction historyKey(key: string): string {\n return `${key}${HISTORY_SUFFIX}`;\n}\n\nfunction readArchive(key: string): ArchivedExploreConversation[] {\n if (typeof window === \"undefined\") return [];\n try {\n const raw = window.localStorage.getItem(historyKey(key));\n if (!raw) return [];\n const parsed: unknown = JSON.parse(raw);\n if (!Array.isArray(parsed)) return [];\n return parsed.filter(\n (entry): entry is ArchivedExploreConversation =>\n typeof entry === \"object\" &&\n entry !== null &&\n typeof (entry as ArchivedExploreConversation).token === \"string\" &&\n Array.isArray((entry as ArchivedExploreConversation).messages),\n );\n } catch {\n return [];\n }\n}\n\nfunction writeArchive(\n key: string,\n entries: ArchivedExploreConversation[],\n): void {\n if (typeof window === \"undefined\") return;\n try {\n if (entries.length === 0) {\n window.localStorage.removeItem(historyKey(key));\n return;\n }\n window.localStorage.setItem(historyKey(key), JSON.stringify(entries));\n } catch {\n /* private mode or quota — the archive is a convenience, never load-bearing */\n }\n}\n\n/**\n * Fold the given thread into the archive, newest first.\n *\n * Upsert by token rather than append: the same conversation is archived after every completed\n * turn so an abruptly closed tab still leaves a record, and appending would fill the list with\n * duplicates of one thread.\n */\nfunction upsertArchive(\n key: string,\n thread: {\n token: string | null;\n messages: ExploreMessage[];\n focusProduct: string | null;\n },\n): ArchivedExploreConversation[] {\n if (!thread.token) return readArchive(key);\n const real = thread.messages.filter(\n (m) => m.content.trim() !== \"\" || m.role === \"USER\",\n );\n if (real.length === 0) return readArchive(key);\n\n const firstUser = real.find((m) => m.role === \"USER\");\n const entry: ArchivedExploreConversation = {\n token: thread.token,\n title: (firstUser?.content ?? \"Conversation\").trim().slice(0, 80),\n updatedAt: real[real.length - 1]?.createdAt ?? new Date().toISOString(),\n messageCount: real.length,\n focusProduct: thread.focusProduct,\n messages: real.slice(-MAX_ARCHIVED_MESSAGES),\n };\n\n const rest = readArchive(key).filter((e) => e.token !== entry.token);\n const next = [entry, ...rest].slice(0, MAX_ARCHIVED_CONVERSATIONS);\n writeArchive(key, next);\n return next;\n}\n\nfunction readPersisted(key: string): PersistedExploreState | null {\n // SSR / prerender: there is no localStorage, and reading one during the\n // first client render would desync hydration anyway.\n if (typeof window === \"undefined\") return null;\n try {\n const raw = window.localStorage.getItem(key);\n if (!raw) return null;\n const parsed: unknown = JSON.parse(raw);\n if (!parsed || typeof parsed !== \"object\") return null;\n const record = parsed as Partial<PersistedExploreState>;\n return {\n conversationToken:\n typeof record.conversationToken === \"string\"\n ? record.conversationToken\n : null,\n messages: Array.isArray(record.messages)\n ? record.messages.slice(-MAX_PERSISTED_MESSAGES)\n : [],\n focusProduct:\n typeof record.focusProduct === \"string\" ? record.focusProduct : null,\n };\n } catch {\n /* corrupt or unavailable storage — start a fresh conversation */\n return null;\n }\n}\n\n/**\n * A turn that never produced any text is worth dropping rather than rendering\n * as an empty bubble — used by `stop()` and by the failure paths.\n */\n/**\n * The `?c=` share secret, or null.\n *\n * Read from `window.location` rather than from a router: the explore page ships to 39\n * separate marketing sites, and only some of them mount it under a router whose search\n * params this hook could reach.\n */\nfunction readShareToken(): string | null {\n if (typeof window === \"undefined\") return null;\n try {\n return new URL(window.location.href).searchParams.get(\"c\");\n } catch {\n // A malformed href — fall through to the normal path rather than blanking the page.\n return null;\n }\n}\n\nfunction visibleText(message: ExploreMessage): string {\n return message.content || message.partialContent || \"\";\n}\n\nfunction countUserTurns(messages: ExploreMessage[]): number {\n return messages.filter((m) => m.role === \"USER\").length;\n}\n\n// ============================================================================\n// Hook\n// ============================================================================\n\nexport function useExploreChat(\n options: UseExploreChatOptions = {},\n): UseExploreChatResult {\n const {\n productSlug,\n pageUrl,\n locale,\n examplePrompts,\n fallbackWelcome,\n pollIntervalMs = DEFAULT_POLL_INTERVAL_MS,\n pollTimeoutMs = DEFAULT_POLL_TIMEOUT_MS,\n storageKey = DEFAULT_STORAGE_KEY,\n persist = true,\n onSend,\n } = options;\n\n const client = useWebSDK();\n // The site's `PlatformProduct` id, so a handover reaches THAT product's support inbox\n // rather than a generic one. Optional in the contract: a site that has not configured one\n // still gets its conversation to a person.\n const { productId } = useWebSDKConfig();\n\n // The fallback catalog is the initial value AND the merge base for the\n // server response. Keyed on the prompt CONTENT, not the array identity —\n // a consumer passing an inline `examplePrompts={[...]}` literal would\n // otherwise rebuild it every render and re-fire the catalog effect.\n const promptKey = JSON.stringify(examplePrompts ?? []);\n // Same reasoning for the welcome copy: keyed on content so an inline object literal from the\n // caller does not rebuild the catalog every render.\n const welcomeKey = JSON.stringify(fallbackWelcome ?? null);\n const fallbackCatalog = useMemo(\n () =>\n buildFallbackCatalog(\n JSON.parse(promptKey) as string[],\n (JSON.parse(welcomeKey) as { title: string; body: string } | null) ??\n undefined,\n ),\n [promptKey, welcomeKey],\n );\n\n // The raw server catalog the static page was prerendered with, read once, before React's\n // first commit replaces that page (see readBakedCatalog).\n const [bakedRaw] = useState<ExploreCatalog | null>(() =>\n readBakedCatalog(productSlug),\n );\n const [catalog, setCatalog] = useState<ExploreCatalog>(() =>\n bakedRaw ? normalizeCatalog(bakedRaw, fallbackCatalog) : fallbackCatalog,\n );\n // The last catalog that came from the SERVER (baked or fetched), as raw JSON, for\n // ExplorePage to write into the page so the next prerender captures it. Null while only\n // the fallback has been seen — a fallback is never baked, or a page prerendered during a\n // catalog outage would pin every visitor to it.\n const [serverCatalogRaw, setServerCatalogRaw] =\n useState<ExploreCatalog | null>(bakedRaw);\n const [messages, setMessages] = useState<ExploreMessage[]>([]);\n // A baked catalog is a loaded one: start in the state the static page was captured in.\n const [phase, setPhase] = useState<ExploreChatPhase>(() =>\n bakedRaw ? (bakedRaw.enabled === false ? \"disabled\" : \"ready\") : \"loading\",\n );\n const [error, setError] = useState<ExploreChatError | null>(null);\n const [remainingToday, setRemainingToday] = useState<number | null>(null);\n const [focusProduct, setFocusProductState] = useState<string | null>(null);\n const [turnCount, setTurnCount] = useState(0);\n // Mirror of `messages` for callbacks that must not re-create on every message.\n const messagesRef = useRef<ExploreMessage[]>([]);\n const [archive, setArchive] = useState<ArchivedExploreConversation[]>([]);\n\n const [hydrated, setHydrated] = useState(false);\n\n // Refs mirror the state that async callbacks read, so `send`/`poll` never\n // close over a stale value and never need to be re-created mid-flight.\n const mountedRef = useRef(true);\n const pollGenerationRef = useRef(0);\n const pollTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const conversationTokenRef = useRef<string | null>(null);\n /**\n * Render-visible mirror of `conversationTokenRef`.\n *\n * The ref alone cannot gate a control: a ref write does not re-render, so a form offered\n * on `conversationTokenRef.current !== null` would still be absent on the render after the\n * first message landed. Everything that assigns the token goes through\n * {@link rememberConversationToken} so the two cannot drift.\n */\n const [hasConversation, setHasConversation] = useState(false);\n /**\n * The `?c=` share token, read ONCE and synchronously.\n *\n * It cannot be discovered inside the share effect and then relied on by the hydration\n * effect: both run in the same commit, so the hydration effect would still close over\n * `isSharedView === false` and load the visitor's own stored thread underneath the shared\n * one. On a slow link that shows their old chat first and then swaps it; if the share\n * lookup fails, they are left staring at their old conversation next to a \"not found\"\n * error. Reading it here makes both effects agree on the first render.\n */\n const shareTokenRef = useRef<string | null | undefined>(undefined);\n if (shareTokenRef.current === undefined) {\n shareTokenRef.current = readShareToken();\n }\n // Derived, not state: the token comes from the URL, and leaving the shared view is a\n // full navigation (the \"New chat\" button drops `?c=` via `location.href`), so there is no\n // in-place transition for a setter to model.\n const isSharedView = shareTokenRef.current !== null;\n const catalogRef = useRef<ExploreCatalog>(catalog);\n const turnCountRef = useRef(0);\n const focusProductRef = useRef<string | null>(null);\n const lastSentTextRef = useRef<string | null>(null);\n // Retried alongside the text. Without this a retry after a network failure would re-ask the\n // question with the documents silently dropped, and the visitor would get a confidently\n // wrong answer with nothing on screen saying their files were not used.\n const lastSentAttachmentsRef = useRef<readonly ExploreAttachmentPayload[]>(\n [],\n );\n const inFlightRef = useRef(false);\n\n catalogRef.current = catalog;\n\n /**\n * The single writer for the conversation token.\n *\n * Async callbacks read the ref (so they never close over a stale value); render reads the\n * state (so a control can appear the moment a conversation exists). Writing both here is\n * what keeps \"we have a thread\" from being true in one and false in the other.\n */\n const rememberConversationToken = useCallback((token: string | null) => {\n conversationTokenRef.current = token;\n setHasConversation(token !== null);\n }, []);\n\n const clearPollTimer = useCallback(() => {\n if (pollTimerRef.current !== null) {\n clearTimeout(pollTimerRef.current);\n pollTimerRef.current = null;\n }\n }, []);\n\n /** Invalidate any running poll chain. Late responses become no-ops. */\n const cancelPolling = useCallback(() => {\n pollGenerationRef.current += 1;\n clearPollTimer();\n }, [clearPollTimer]);\n\n useEffect(() => {\n mountedRef.current = true;\n return () => {\n mountedRef.current = false;\n pollGenerationRef.current += 1;\n if (pollTimerRef.current !== null) {\n clearTimeout(pollTimerRef.current);\n pollTimerRef.current = null;\n }\n };\n }, []);\n\n // --------------------------------------------------------------------------\n // A shared link (`?c=<shareToken>`).\n //\n // Runs BEFORE the localStorage hydration below and short-circuits it: someone opening a\n // link they were sent must see that conversation, not whatever they happened to ask this\n // site last week. The secret in the URL is a read-only share token, never a conversation\n // token, so this view cannot continue the thread — which is why `isSharedView` is exposed\n // and the composer is disabled on it.\n // --------------------------------------------------------------------------\n useEffect(() => {\n const shareToken = shareTokenRef.current;\n if (!shareToken) return;\n\n let cancelled = false;\n setHydrated(true);\n\n void client\n .query<{ exploreSharedConversation?: ExploreMessage[] | null }>(\n EXPLORE_SHARED_CONVERSATION_QUERY,\n { shareToken },\n { cache: false },\n )\n .then((res) => {\n if (cancelled || !mountedRef.current) return;\n const shared = res?.data?.exploreSharedConversation;\n if (shared && shared.length > 0) {\n setMessages(shared);\n return;\n }\n // A token that resolves to nothing is an expired or wrong link. Say so rather than\n // showing an empty chat that looks like the feature is broken.\n setError({\n kind: \"unknown\",\n code: \"SHARE_NOT_FOUND\",\n message:\n \"That shared conversation could not be found. The link may have expired.\",\n // Nothing to retry: a bad token will stay bad.\n retryable: false,\n });\n })\n .catch(() => {\n if (cancelled || !mountedRef.current) return;\n setError({\n kind: \"unknown\",\n code: \"SHARE_NOT_FOUND\",\n message: \"That shared conversation could not be loaded.\",\n retryable: true,\n });\n });\n\n return () => {\n cancelled = true;\n };\n }, [client]);\n\n // --------------------------------------------------------------------------\n // Hydrate from localStorage (effect, not lazy state, so the FIRST client\n // render matches the prerendered HTML and hydration stays clean).\n // --------------------------------------------------------------------------\n useEffect(() => {\n // A shared link owns the transcript; never let stored history replace it. The ref, not\n // the state: this effect and the share effect run in the same commit.\n if (shareTokenRef.current !== null) return;\n if (!persist) {\n setHydrated(true);\n return;\n }\n setArchive(readArchive(storageKey));\n const stored = readPersisted(storageKey);\n if (stored) {\n rememberConversationToken(stored.conversationToken);\n focusProductRef.current = stored.focusProduct;\n // A turn that was still streaming when the tab was closed can never be\n // resumed from here — the poll chain is gone. Land it as-is.\n const settled = stored.messages.flatMap<ExploreMessage>((m) => {\n if (m.role !== \"ASSISTANT\") return [m];\n if (m.status !== \"PENDING\" && m.status !== \"RUNNING\") return [m];\n const text = visibleText(m);\n if (!text) return [];\n return [{ ...m, status: \"COMPLETED\", content: text }];\n });\n setMessages(settled);\n setFocusProductState(stored.focusProduct);\n const turns = countUserTurns(settled);\n turnCountRef.current = turns;\n setTurnCount(turns);\n }\n setHydrated(true);\n }, [persist, rememberConversationToken, storageKey]);\n\n // --------------------------------------------------------------------------\n // Persist (after hydration, so the initial empty state never wipes storage).\n // --------------------------------------------------------------------------\n useEffect(() => {\n if (!persist || !hydrated || typeof window === \"undefined\") return;\n try {\n const payload: PersistedExploreState = {\n conversationToken: conversationTokenRef.current,\n messages: messages.slice(-MAX_PERSISTED_MESSAGES),\n focusProduct,\n };\n // Nothing worth restoring (fresh page, or just after reset()): drop the\n // key entirely rather than leaving an empty husk behind.\n if (\n !payload.conversationToken &&\n payload.messages.length === 0 &&\n !payload.focusProduct\n ) {\n window.localStorage.removeItem(storageKey);\n return;\n }\n window.localStorage.setItem(storageKey, JSON.stringify(payload));\n } catch {\n /* private mode / quota exceeded — persistence is a nicety, not a feature */\n }\n }, [persist, hydrated, storageKey, messages, focusProduct]);\n\n // Mirror messages into a ref for callbacks, and fold a settled thread into the archive.\n // Archiving on every settled turn (not only on reset) is what makes an abruptly closed tab\n // still leave a record the visitor can come back to.\n useEffect(() => {\n messagesRef.current = messages;\n if (!persist || !hydrated) return;\n const settled = messages.some(\n (m) =>\n m.role === \"ASSISTANT\" &&\n (m.status === \"COMPLETED\" || m.status === \"FAILED\"),\n );\n if (!settled) return;\n setArchive(\n upsertArchive(storageKey, {\n token: conversationTokenRef.current,\n messages,\n focusProduct: focusProductRef.current,\n }),\n );\n }, [messages, persist, hydrated, storageKey]);\n\n // --------------------------------------------------------------------------\n // Catalog. Cached (the default) — it is per-page-load configuration, not a\n // poll. A failure keeps the fallback so the page NEVER renders blank.\n // --------------------------------------------------------------------------\n useEffect(() => {\n let cancelled = false;\n const load = async () => {\n const variables = { productSlug: productSlug ?? null };\n let res = await client.query<ExploreCatalogQueryData>(\n EXPLORE_CATALOG_QUERY,\n variables,\n );\n if (cancelled || !mountedRef.current) return;\n\n // A gateway whose supergraph predates `conceptHighlights` does not answer this\n // document without the field — it refuses the document outright, so `res.data` is\n // undefined and the loss is the entire catalog, not the strip. Retry once with the\n // field dropped rather than spending the whole window on `buildFallbackCatalog()`,\n // which hardcodes `enabled: true` (ignoring the only kill switch that needs no\n // redeploy) and `captchaRequired: false` (every send then rejected server-side while\n // the composer looks fine). The window is unavoidable even with perfect deploy\n // ordering: the prod gateway's CDN edge serves a stale supergraph for ~30-40 min\n // after the subgraph publishes.\n if (catalogRejectedTheHighlightField(res)) {\n res = await client.query<ExploreCatalogQueryData>(\n EXPLORE_CATALOG_QUERY_WITHOUT_HIGHLIGHTS,\n variables,\n );\n if (cancelled || !mountedRef.current) return;\n }\n\n const raw = res.data?.exploreCatalog;\n if (res.errors?.length || !raw) {\n console.warn(\n \"[WebSDK] exploreCatalog unavailable — keeping the catalog the page started with\",\n res.errors?.[0]?.message,\n );\n setPhase((prev) => (prev === \"loading\" ? \"ready\" : prev));\n return;\n }\n\n // Over the baked catalog when there is one: the retry against an older supergraph\n // answers without conceptHighlights, and merged over the fallback that dropped the\n // strip the static page had just shown.\n const next = normalizeCatalog(\n raw,\n bakedRaw\n ? normalizeCatalog(bakedRaw, fallbackCatalog)\n : fallbackCatalog,\n );\n setCatalog(next);\n setServerCatalogRaw(raw);\n catalogRef.current = next;\n setPhase((prev) => {\n if (!next.enabled) return \"disabled\";\n // \"disabled\" is only ever set from catalog.enabled, so a live enabled:true is the\n // authority. Without this a page that started from a baked enabled:false stayed off\n // after the live catalog said otherwise, until the site was rebuilt.\n return prev === \"loading\" || prev === \"disabled\" ? \"ready\" : prev;\n });\n };\n void load();\n return () => {\n cancelled = true;\n };\n }, [client, productSlug, fallbackCatalog, bakedRaw]);\n\n // --------------------------------------------------------------------------\n // Poll chain\n // --------------------------------------------------------------------------\n const startPolling = useCallback(\n (conversationToken: string, messageId: string) => {\n pollGenerationRef.current += 1;\n const generation = pollGenerationRef.current;\n const startedAt = Date.now();\n let consecutiveFailures = 0;\n\n const isStale = () =>\n !mountedRef.current || generation !== pollGenerationRef.current;\n\n const finishWithError = (chatError: ExploreChatError) => {\n inFlightRef.current = false;\n setMessages((prev) =>\n prev.flatMap<ExploreMessage>((m) => {\n if (m.id !== messageId) return [m];\n const text = visibleText(m);\n if (!text) return [];\n return [{ ...m, status: \"FAILED\", content: text }];\n }),\n );\n setError(chatError);\n setPhase(chatError.kind === \"rate-limit\" ? \"limited\" : \"error\");\n };\n\n const tick = async () => {\n if (isStale()) return;\n\n if (Date.now() - startedAt > pollTimeoutMs) {\n finishWithError(classifyExploreError(\"TIMEOUT\"));\n return;\n }\n\n // `cache: false` is load-bearing — see CONTRACT.md §4.\n const res = await client.query<ExploreMessageQueryData>(\n EXPLORE_MESSAGE_QUERY,\n { conversationToken, id: messageId },\n { cache: false, timeoutMs: POLL_REQUEST_TIMEOUT_MS },\n );\n if (isStale()) return;\n\n const first = res.errors?.[0];\n if (first) {\n const code =\n typeof first.extensions?.code === \"string\"\n ? first.extensions.code\n : undefined;\n const classified = classifyExploreError(code, first.message);\n // Hard stops: retrying a rate limit or a disabled feature is pointless.\n if (!classified.retryable) {\n finishWithError(classified);\n return;\n }\n consecutiveFailures += 1;\n if (consecutiveFailures >= MAX_CONSECUTIVE_POLL_FAILURES) {\n finishWithError(classified);\n return;\n }\n pollTimerRef.current = setTimeout(() => void tick(), pollIntervalMs);\n return;\n }\n\n const message = res.data?.exploreMessage;\n if (!message) {\n // Can happen briefly right after submit; tolerate a few misses.\n consecutiveFailures += 1;\n if (consecutiveFailures >= MAX_CONSECUTIVE_POLL_FAILURES) {\n finishWithError(classifyExploreError(\"NOT_FOUND\"));\n return;\n }\n pollTimerRef.current = setTimeout(() => void tick(), pollIntervalMs);\n return;\n }\n\n consecutiveFailures = 0;\n // Replace in place so `partialContent` renders as it streams.\n setMessages((prev) =>\n prev.map((m) => (m.id === messageId ? message : m)),\n );\n\n if (message.status === \"COMPLETED\") {\n inFlightRef.current = false;\n setError(null);\n setPhase(\"ready\");\n return;\n }\n if (message.status === \"FAILED\") {\n finishWithError(\n classifyExploreError(message.errorCode ?? \"UPSTREAM_UNAVAILABLE\"),\n );\n return;\n }\n\n setPhase(\"streaming\");\n pollTimerRef.current = setTimeout(() => void tick(), pollIntervalMs);\n };\n\n // First poll after one interval — the turn cannot possibly be done sooner.\n pollTimerRef.current = setTimeout(() => void tick(), pollIntervalMs);\n },\n [client, pollIntervalMs, pollTimeoutMs],\n );\n\n // --------------------------------------------------------------------------\n // send\n // --------------------------------------------------------------------------\n const submit = useCallback(\n async (\n text: string,\n optimisticId: string,\n captchaAttempt: number,\n attachments: readonly ExploreAttachmentPayload[],\n ): Promise<void> => {\n const activeCatalog = catalogRef.current;\n\n let recaptchaToken: string | undefined;\n if (activeCatalog.captchaRequired && activeCatalog.recaptchaSiteKey) {\n try {\n recaptchaToken = await getRecaptchaV3Token(\n activeCatalog.recaptchaSiteKey,\n );\n } catch (err) {\n console.warn(\"[WebSDK] reCAPTCHA v3 token could not be minted\", err);\n // Send without a token and let the backend decide — it owns the\n // policy, and a blocked third-party script must not silently kill\n // the whole feature.\n }\n }\n\n const input: SendExploreMessageInput = {\n message: text,\n ...(conversationTokenRef.current\n ? { conversationToken: conversationTokenRef.current }\n : {}),\n ...(focusProductRef.current || productSlug\n ? { productSlug: focusProductRef.current ?? productSlug }\n : {}),\n ...(recaptchaToken ? { recaptchaToken } : {}),\n ...(locale ? { locale } : {}),\n ...(pageUrl ? { pageUrl } : {}),\n ...(attachments.length > 0 ? { attachments: [...attachments] } : {}),\n };\n\n const res = await client.mutate<SendExploreMessageMutationData>(\n SEND_EXPLORE_MESSAGE_MUTATION,\n { input },\n { timeoutMs: SEND_REQUEST_TIMEOUT_MS },\n );\n if (!mountedRef.current) return;\n\n const first = res.errors?.[0];\n const result = res.data?.sendExploreMessage;\n\n if (first || !result) {\n const code =\n typeof first?.extensions?.code === \"string\"\n ? first.extensions.code\n : undefined;\n const classified = classifyExploreError(code, first?.message);\n\n // CAPTCHA_FAILED: one silent re-mint + re-send before bothering the\n // visitor. v3 tokens expire in ~2 minutes and are single-use, so a\n // stale one is a routine, self-healing failure.\n if (classified.kind === \"captcha\" && captchaAttempt === 0) {\n await submit(text, optimisticId, 1, attachments);\n return;\n }\n\n inFlightRef.current = false;\n setError(classified);\n setPhase(classified.kind === \"rate-limit\" ? \"limited\" : \"error\");\n return;\n }\n\n rememberConversationToken(result.conversation.token);\n turnCountRef.current = result.conversation.turnCount;\n setTurnCount(result.conversation.turnCount);\n setRemainingToday(result.remainingToday ?? null);\n\n const userMessage = result.userMessage;\n const assistantMessage = result.assistantMessage;\n\n // Swap the optimistic user bubble for the server's, then append the\n // PENDING assistant turn that the poll chain will fill in. The `some`\n // guard keeps the visitor's words on screen even in the impossible case\n // where the optimistic bubble is already gone.\n setMessages((prev) => {\n const replaced = prev.some((m) => m.id === optimisticId)\n ? prev.map((m) => (m.id === optimisticId ? userMessage : m))\n : [...prev, userMessage];\n return [...replaced, assistantMessage];\n });\n\n if (\n assistantMessage.status === \"COMPLETED\" ||\n assistantMessage.status === \"FAILED\"\n ) {\n inFlightRef.current = false;\n if (assistantMessage.status === \"FAILED\") {\n const classified = classifyExploreError(\n assistantMessage.errorCode ?? \"UPSTREAM_UNAVAILABLE\",\n );\n setError(classified);\n setPhase(\"error\");\n } else {\n setPhase(\"ready\");\n }\n return;\n }\n\n setPhase(\"streaming\");\n startPolling(result.conversation.token, assistantMessage.id);\n },\n [\n client,\n locale,\n pageUrl,\n productSlug,\n rememberConversationToken,\n startPolling,\n ],\n );\n\n const send = useCallback(\n async (\n message: string,\n attachments: readonly ExploreAttachmentPayload[] = [],\n ): Promise<void> => {\n const text = message.trim();\n // A question is still required: files are extra context for a question, not a question\n // on their own. The model has no way to guess what to do with a bare document.\n if (!text) return;\n if (inFlightRef.current) return;\n\n const activeCatalog = catalogRef.current;\n if (!activeCatalog.enabled) {\n setError(classifyExploreError(\"EXPLORE_DISABLED\"));\n setPhase(\"disabled\");\n return;\n }\n if (text.length > activeCatalog.limits.maxMessageLength) {\n setError({\n kind: \"validation\",\n code: \"MESSAGE_TOO_LONG\",\n message: `That message is ${text.length} characters — the limit is ${activeCatalog.limits.maxMessageLength}.`,\n retryable: false,\n });\n return;\n }\n if (\n turnCountRef.current >= activeCatalog.limits.maxTurnsPerConversation\n ) {\n setError({\n kind: \"validation\",\n code: \"CONVERSATION_LIMIT\",\n message: `This conversation has reached its ${activeCatalog.limits.maxTurnsPerConversation}-message limit. Start a new chat to keep going.`,\n retryable: false,\n });\n return;\n }\n\n inFlightRef.current = true;\n lastSentTextRef.current = text;\n lastSentAttachmentsRef.current = attachments;\n setError(null);\n setPhase(\"sending\");\n cancelPolling();\n\n // Optimistic user bubble — the composer clears instantly instead of\n // waiting a network round-trip for the visitor's own words.\n const optimistic: ExploreMessage = {\n id: makeOptimisticId(),\n conversationId: conversationTokenRef.current ?? \"pending\",\n role: \"USER\",\n status: \"COMPLETED\",\n content: text,\n partialContent: null,\n activity: [],\n references: [],\n ctas: [],\n errorCode: null,\n createdAt: new Date().toISOString(),\n completedAt: new Date().toISOString(),\n };\n setMessages((prev) => [...prev, optimistic]);\n turnCountRef.current += 1;\n setTurnCount(turnCountRef.current);\n onSend?.(text);\n\n try {\n await submit(text, optimistic.id, 0, attachments);\n } catch (err) {\n if (!mountedRef.current) return;\n console.error(\"[WebSDK] Explore send failed\", err);\n inFlightRef.current = false;\n setError(classifyExploreError(\"NETWORK_ERROR\"));\n setPhase(\"error\");\n }\n },\n [cancelPolling, onSend, submit],\n );\n\n const stop = useCallback(() => {\n cancelPolling();\n inFlightRef.current = false;\n setMessages((prev) =>\n prev.flatMap<ExploreMessage>((m) => {\n if (m.role !== \"ASSISTANT\") return [m];\n if (m.status !== \"PENDING\" && m.status !== \"RUNNING\") return [m];\n const text = visibleText(m);\n // Nothing streamed yet — drop the placeholder instead of leaving an\n // empty bubble behind.\n if (!text) return [];\n return [\n {\n ...m,\n status: \"COMPLETED\",\n content: text,\n completedAt: new Date().toISOString(),\n },\n ];\n }),\n );\n setPhase((prev) =>\n prev === \"sending\" || prev === \"streaming\" ? \"ready\" : prev,\n );\n }, [cancelPolling]);\n\n const retry = useCallback(() => {\n const text = lastSentTextRef.current;\n if (!text) return;\n cancelPolling();\n inFlightRef.current = false;\n // Drop the failed turn (and the user bubble that produced it) so the retry\n // does not stack a duplicate pair into the transcript.\n setMessages((prev) => {\n const next = [...prev];\n const last = next[next.length - 1];\n if (last?.role === \"ASSISTANT\" && last.status === \"FAILED\") next.pop();\n const prior = next[next.length - 1];\n if (prior?.role === \"USER\" && prior.content === text) next.pop();\n return next;\n });\n turnCountRef.current = Math.max(0, turnCountRef.current - 1);\n setTurnCount(turnCountRef.current);\n void send(text, lastSentAttachmentsRef.current);\n }, [cancelPolling, send]);\n\n const reset = useCallback(() => {\n // Archive the thread being left behind, so \"New chat\" never destroys history.\n setArchive(\n upsertArchive(storageKey, {\n token: conversationTokenRef.current,\n messages: messagesRef.current,\n focusProduct: focusProductRef.current,\n }),\n );\n cancelPolling();\n inFlightRef.current = false;\n rememberConversationToken(null);\n lastSentTextRef.current = null;\n lastSentAttachmentsRef.current = [];\n turnCountRef.current = 0;\n setTurnCount(0);\n setMessages([]);\n setError(null);\n setRemainingToday(null);\n setPhase(catalogRef.current.enabled ? \"ready\" : \"disabled\");\n if (typeof window !== \"undefined\") {\n try {\n window.localStorage.removeItem(storageKey);\n } catch {\n /* storage unavailable — nothing to clear */\n }\n }\n }, [cancelPolling, rememberConversationToken, storageKey]);\n\n /**\n * Reopen a past conversation. The messages come from the local archive rather than the server:\n * the backend expires a job's state, and a visitor looking at their own history should see it\n * whether or not the server still holds anything. The token is restored too, so the next\n * message continues the SAME server-side conversation.\n */\n const openConversation = useCallback(\n (token: string) => {\n const entry = readArchive(storageKey).find((e) => e.token === token);\n if (!entry) return;\n // Archive whatever is on screen first, or switching away would lose it.\n upsertArchive(storageKey, {\n token: conversationTokenRef.current,\n messages: messagesRef.current,\n focusProduct: focusProductRef.current,\n });\n cancelPolling();\n inFlightRef.current = false;\n rememberConversationToken(entry.token);\n turnCountRef.current = entry.messages.filter(\n (m) => m.role === \"USER\",\n ).length;\n setTurnCount(turnCountRef.current);\n setMessages(entry.messages);\n focusProductRef.current = entry.focusProduct;\n setFocusProductState(entry.focusProduct);\n setError(null);\n setPhase(catalogRef.current.enabled ? \"ready\" : \"disabled\");\n setArchive(readArchive(storageKey));\n },\n [cancelPolling, rememberConversationToken, storageKey],\n );\n\n /** Forget one past conversation. Local only — see the page's storage notice. */\n const deleteConversation = useCallback(\n (token: string) => {\n const next = readArchive(storageKey).filter((e) => e.token !== token);\n writeArchive(storageKey, next);\n setArchive(next);\n },\n [storageKey],\n );\n\n /** Forget every past conversation held in this browser. */\n const clearHistory = useCallback(() => {\n writeArchive(storageKey, []);\n setArchive([]);\n }, [storageKey]);\n\n const setFocusProduct = useCallback((slug: string | null) => {\n focusProductRef.current = slug;\n setFocusProductState(slug);\n }, []);\n\n const activity = useMemo<string | null>(() => {\n for (let i = messages.length - 1; i >= 0; i -= 1) {\n const m = messages[i];\n if (!m || m.role !== \"ASSISTANT\") continue;\n if (m.status !== \"PENDING\" && m.status !== \"RUNNING\") return null;\n const lines = m.activity ?? [];\n return lines.length ? (lines[lines.length - 1] ?? null) : null;\n }\n return null;\n }, [messages]);\n\n const canSend =\n (phase === \"ready\" || phase === \"error\") &&\n catalog.enabled &&\n turnCount < catalog.limits.maxTurnsPerConversation;\n\n /**\n * Tell the server the visitor followed one of the links this answer offered.\n *\n * Deliberately fire-and-forget and never awaited: the browser is navigating away as this\n * goes out, so anything that blocked on it would delay the click the visitor actually\n * made. Failures are swallowed — analytics must never break navigation.\n */\n const createShareLink = useCallback(async (): Promise<string | null> => {\n const token = conversationTokenRef.current;\n if (!token) return null;\n try {\n const result = await client.mutate<{\n createExploreShareLink?: { shareToken?: string | null } | null;\n }>(CREATE_EXPLORE_SHARE_LINK_MUTATION, { conversationToken: token });\n return result?.data?.createExploreShareLink?.shareToken ?? null;\n } catch {\n // Sharing is a convenience; a failure must not take the conversation down with it.\n return null;\n }\n }, [client]);\n\n const recordClick = useCallback(\n (messageId: string, kind: \"CTA\" | \"REFERENCE\", url: string): void => {\n const token = conversationTokenRef.current;\n if (!token) return;\n try {\n void client\n .mutate(RECORD_EXPLORE_CLICK_MUTATION, {\n input: { conversationToken: token, messageId, kind, url },\n })\n .catch(() => {\n /* the visitor is mid-navigation; there is nothing useful to do here */\n });\n } catch {\n /* client unavailable — same reasoning */\n }\n },\n [client],\n );\n\n /**\n * Rate one answer, and fold the server's record back into the transcript.\n *\n * The fold matters beyond the control that triggered it: `messages` is what the persist\n * effect writes to localStorage, so a rating stored here survives a reload of the same\n * conversation and not just this render. The optimistic state in the UI covers the moment\n * before this resolves; this covers everything after.\n */\n const submitFeedback = useCallback(\n async (\n messageId: string,\n rating: ExploreFeedbackRating,\n detail?: {\n reason?: ExploreFeedbackReason | null;\n comment?: string | null;\n },\n ): Promise<boolean> => {\n const token = conversationTokenRef.current;\n // No conversation of our own (a fresh page, or a read-only shared view, whose `?c=`\n // secret is not a conversation token): the server could only answer `recorded: false`.\n if (!token) return false;\n\n const comment = detail?.comment?.trim();\n // Annotated, not inferred. `client.mutate` types its variables as an open\n // `Record<string, unknown>`, so an input literal passed inline is unchecked — renaming\n // `messageId` to `id`, or adding a key the server does not accept, would compile\n // cleanly and fail only at runtime against the live gateway. Naming the type is what\n // makes CONTRACT.md compile-checked. `sendMessage` above does the same.\n const input: SubmitExploreFeedbackInput = {\n conversationToken: token,\n messageId,\n rating,\n // Only meaningful with NOT_HELPFUL. The server stores null for HELPFUL either\n // way; sending it would just be noise the backend has to defend against.\n reason: rating === \"NOT_HELPFUL\" ? (detail?.reason ?? null) : null,\n comment: comment ? comment : null,\n };\n try {\n const res = await client.mutate<SubmitExploreFeedbackMutationData>(\n SUBMIT_EXPLORE_FEEDBACK_MUTATION,\n { input },\n );\n const result = res?.data?.submitExploreFeedback;\n if (!result?.recorded) return false;\n\n const recorded = result.feedback;\n if (recorded && mountedRef.current) {\n setMessages((current) =>\n current.map((message) =>\n message.id === messageId\n ? { ...message, feedback: recorded }\n : message,\n ),\n );\n }\n return true;\n } catch {\n // `client.mutate` resolves every outcome rather than throwing, so this is belt and\n // braces for a client that is not there at all. Either way: not recorded.\n return false;\n }\n },\n [client],\n );\n\n /**\n * Hand the conversation to a person.\n *\n * The visitor pressed a control and is waiting on it, so this is awaited and reports what\n * actually happened rather than a boolean: `already-handed-over` in particular is a\n * SUCCESS the caller has to be able to tell apart from a failure, and `invalid-email` is\n * the server's verdict on an address the form's own check let through.\n *\n * `email` never leaves this function except in the mutation variables. It is not folded\n * into `messages` (the persist effect writes those to localStorage), not written to\n * storage directly, and not passed to `onSend`. Anyone adding \"just log the address\" here\n * is putting personal data in a place nothing ever deletes it from.\n */\n const escalate = useCallback(\n async (email: string, note?: string): Promise<ExploreEscalateResult> => {\n const token = conversationTokenRef.current;\n // A read-only shared view (its `?c=` secret is not a conversation token) and a page\n // where nothing has been asked yet both land here. The server could only answer\n // NOT_FOUND, so do not spend a request finding that out.\n if (!token) {\n return { outcome: \"no-conversation\", email: null, createdAt: null };\n }\n\n const attempt = async (\n captchaAttempt: number,\n ): Promise<ExploreEscalateResult> => {\n const activeCatalog = catalogRef.current;\n\n // Exactly what `submit` does, including the default action — the contract puts the\n // whole explore surface on ONE v3 action on purpose, because an action mismatch is a\n // silent 100% rejection.\n let recaptchaToken: string | undefined;\n if (activeCatalog.captchaRequired && activeCatalog.recaptchaSiteKey) {\n try {\n recaptchaToken = await getRecaptchaV3Token(\n activeCatalog.recaptchaSiteKey,\n );\n } catch (err) {\n console.warn(\n \"[WebSDK] reCAPTCHA v3 token could not be minted\",\n err,\n );\n // Send without one and let the backend decide: it owns the policy, and a\n // blocked third-party script must not be what stops someone reaching a human.\n }\n }\n\n // Annotated, not inferred — `client.mutate` types its variables as an open\n // `Record<string, unknown>`, so an inline literal is unchecked and a renamed field\n // would compile cleanly and fail against the live gateway. Same rule as\n // `submitFeedback` and `submit`.\n const input: EscalateExploreConversationInput = {\n conversationToken: token,\n email: email.trim(),\n note: note?.trim() ? note.trim() : null,\n ...(recaptchaToken ? { recaptchaToken } : {}),\n ...(productId ? { productId } : {}),\n };\n\n try {\n const res =\n await client.mutate<EscalateExploreConversationMutationData>(\n ESCALATE_EXPLORE_CONVERSATION_MUTATION,\n { input },\n );\n\n const first = res?.errors?.[0];\n const result = res?.data?.escalateExploreConversation;\n\n if (first || !result) {\n const code =\n typeof first?.extensions?.code === \"string\"\n ? first.extensions.code\n : \"ESCALATION_FAILED\";\n\n // One silent re-mint, exactly as `submit` does: a v3 token is single-use and\n // expires in ~2 minutes, so a rejected one is routine and self-healing.\n if (code === \"CAPTCHA_FAILED\" && captchaAttempt === 0) {\n return attempt(1);\n }\n\n return {\n outcome: escalateOutcomeForCode(code),\n email: null,\n createdAt: null,\n };\n }\n\n return {\n // `created: false` is the double-click, the retry, the reload. The handover\n // exists, so this is a success — and the address comes from the SERVER, because\n // on a repeat submit the reply goes to whatever the FIRST handover used.\n outcome: result.created ? \"sent\" : \"already-handed-over\",\n email: result.escalation?.email ?? null,\n createdAt: result.escalation?.createdAt ?? null,\n };\n } catch {\n // `client.mutate` resolves every transport outcome rather than throwing, so this\n // is for a client that is not there at all. Either way: nothing was handed over,\n // and this function's contract is that it never throws at the caller.\n return { outcome: \"failed\", email: null, createdAt: null };\n }\n };\n\n return attempt(0);\n },\n [client, productId],\n );\n\n /**\n * A shared view holds a share secret, not a conversation token, so `hasConversation` is\n * already false there. Both are named anyway: the second is the reason, and a later\n * refactor that starts restoring a token under a shared transcript should fail this\n * condition rather than quietly start offering the form.\n */\n const canEscalate = hasConversation && !isSharedView;\n\n return {\n phase,\n catalog,\n serverCatalogRaw,\n messages,\n activity,\n remainingToday,\n error,\n focusProduct,\n setFocusProduct,\n send,\n stop,\n retry,\n reset,\n canSend,\n history: archive,\n openConversation,\n deleteConversation,\n clearHistory,\n recordClick,\n submitFeedback,\n escalate,\n canEscalate,\n createShareLink,\n isSharedView,\n };\n}\n\n/**\n * Client-only id for the optimistic user bubble, swapped for the server's\n * `userMessage.id` as soon as the mutation resolves. The counter only has to\n * make ids unique within a tab, never across one.\n */\nlet optimisticCounter = 0;\n\nfunction makeOptimisticId(): string {\n optimisticCounter += 1;\n return `boff-explore-local-${Date.now()}-${optimisticCounter}`;\n}\n","\"use client\";\n\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\n/**\n * Dictation for the explore composer, on the browser's own speech recogniser.\n *\n * Uses the Web Speech API directly — no dependency, no network call of our own, no audio ever\n * leaving the browser through us. Support is genuinely partial (Chrome, Edge and Safari expose\n * it, most commonly behind the `webkit` prefix; Firefox does not implement it at all), so the\n * caller MUST hide the microphone entirely when `supported` is false rather than offering a\n * control that cannot work.\n *\n * Interim results are surfaced separately from final ones so the composer can show speech\n * appearing live while only committing settled text to the draft.\n *\n * ## Mobile\n *\n * Mobile took three separate fixes, each of which looked like the whole problem:\n *\n * 1. **The prompt never appeared.** `recognition.start()` raises the permission prompt on\n * desktop Chrome but on Android Chrome and iOS fires `not-allowed` without showing\n * anything, so a visitor who had never been asked was told to change a browser setting.\n * `getUserMedia` is what actually prompts, so it is primed first.\n * 2. **Granting still failed.** Recognition opens its OWN capture, and starting it in the same\n * tick that the primed stream is being torn down races that teardown. It now waits for the\n * release, and retries once — Android frequently refuses the first attempt after a\n * permission is newly granted and accepts the second.\n * 3. **`continuous` is not honoured on mobile.** Android Chrome ends the session after a\n * single utterance regardless, and asking for continuous mode makes it fail sooner. Mobile\n * therefore runs single-shot and is restarted from `onend` for as long as the visitor is\n * still dictating, which behaves like continuous without fighting the platform.\n */\n\ninterface SpeechRecognitionAlternativeLike {\n transcript: string;\n}\ninterface SpeechRecognitionResultLike {\n readonly isFinal: boolean;\n readonly length: number;\n [index: number]: SpeechRecognitionAlternativeLike;\n}\ninterface SpeechRecognitionEventLike {\n readonly resultIndex: number;\n readonly results: {\n readonly length: number;\n [index: number]: SpeechRecognitionResultLike;\n };\n}\ninterface SpeechRecognitionErrorEventLike {\n readonly error: string;\n}\ninterface SpeechRecognitionLike {\n lang: string;\n continuous: boolean;\n interimResults: boolean;\n maxAlternatives: number;\n start: () => void;\n stop: () => void;\n abort: () => void;\n onresult: ((event: SpeechRecognitionEventLike) => void) | null;\n onerror: ((event: SpeechRecognitionErrorEventLike) => void) | null;\n onend: (() => void) | null;\n onstart: (() => void) | null;\n}\ntype SpeechRecognitionCtor = new () => SpeechRecognitionLike;\n\ninterface SpeechWindow {\n SpeechRecognition?: SpeechRecognitionCtor;\n webkitSpeechRecognition?: SpeechRecognitionCtor;\n}\n\nfunction getRecognitionCtor(): SpeechRecognitionCtor | null {\n if (typeof window === \"undefined\") return null;\n const w = window as unknown as SpeechWindow;\n return w.SpeechRecognition ?? w.webkitSpeechRecognition ?? null;\n}\n\n/**\n * Coarse pointer means a touch device, which is what actually predicts the recogniser's\n * behaviour here — not the screen size, and not a UA string that lies.\n */\nfunction isTouchDevice(): boolean {\n if (typeof window === \"undefined\" || !window.matchMedia) return false;\n try {\n return window.matchMedia(\"(pointer: coarse)\").matches;\n } catch {\n return false;\n }\n}\n\nconst MIC_DENIED_MESSAGE =\n \"Microphone access was blocked. Allow it in your browser's site settings to dictate.\";\nconst MIC_UNAVAILABLE_MESSAGE =\n \"Dictation isn't available in this browser. You can type instead.\";\n\n/** Let a released capture settle before recognition opens its own. */\nconst RELEASE_SETTLE_MS = 250;\n/** Android often refuses the first start after a fresh grant and accepts the next. */\nconst RETRY_DELAY_MS = 350;\n\nconst sleep = (ms: number): Promise<void> =>\n new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n\n/**\n * Ask for the microphone the only way that reliably raises a prompt on mobile.\n *\n * `confirmed` reports whether access was actually VERIFIED, which is what separates \"you need\n * to grant this\" from \"your browser cannot do this\" further down.\n *\n * Must be called synchronously from the user gesture that requested dictation — awaiting\n * anything first spends the gesture and the browser then rejects the request outright.\n */\nasync function ensureMicrophoneAccess(): Promise<\n | { ok: true; confirmed: boolean; primed: boolean }\n | { ok: false; message: string }\n> {\n const media =\n typeof navigator === \"undefined\" ? undefined : navigator.mediaDevices;\n // Older/unusual browsers without mediaDevices: fall through and let recognition try. It\n // may well work, and refusing here would remove dictation from browsers that support it.\n if (!media?.getUserMedia)\n return { ok: true, confirmed: false, primed: false };\n\n // `granted` means the prompt would not appear anyway, so skip the extra capture — which\n // also avoids the teardown race entirely on repeat use.\n //\n // A `denied` reading is deliberately NOT trusted to short-circuit. The Permissions API\n // reports `denied` for a prompt the visitor merely dismissed, and its microphone support\n // is uneven across mobile browsers — believing it would recreate the exact bug this\n // function exists to fix, telling someone to change a setting instead of asking them.\n // getUserMedia is the authoritative check, and when access really is denied it rejects\n // immediately without showing anything, so the only cost of asking is a settled promise.\n //\n // `permissions` is absent on Safari and `microphone` is not a valid name everywhere, so\n // every part of this is optional and failure is non-fatal.\n try {\n const status = await navigator.permissions?.query({\n name: \"microphone\" as PermissionName,\n });\n if (status?.state === \"granted\")\n return { ok: true, confirmed: true, primed: false };\n } catch {\n /* permissions API unavailable or the name is unsupported — prompt below instead */\n }\n\n try {\n const stream = await media.getUserMedia({ audio: true });\n // Release immediately: this capture existed only to raise the prompt, and leaving it\n // open would show a recording indicator for the whole session — and on mobile would\n // occupy the very microphone recognition is about to ask for.\n for (const track of stream.getTracks()) track.stop();\n return { ok: true, confirmed: true, primed: true };\n } catch (error) {\n const name =\n typeof error === \"object\" && error !== null && \"name\" in error\n ? String((error as { name: unknown }).name)\n : \"\";\n if (name === \"NotAllowedError\" || name === \"SecurityError\") {\n return { ok: false, message: MIC_DENIED_MESSAGE };\n }\n if (name === \"NotFoundError\" || name === \"DevicesNotFoundError\") {\n return { ok: false, message: \"No microphone was found.\" };\n }\n return {\n ok: false,\n message: \"Dictation could not start. You can type instead.\",\n };\n }\n}\n\n/** Messages a visitor can act on. `not-allowed` is the one that needs real explanation. */\nfunction describeError(code: string): string {\n switch (code) {\n case \"not-allowed\":\n case \"service-not-allowed\":\n return MIC_DENIED_MESSAGE;\n case \"no-speech\":\n return \"I didn't catch anything — try again a little closer to the mic.\";\n case \"audio-capture\":\n return \"No microphone was found.\";\n case \"network\":\n return \"Speech recognition needs a network connection.\";\n case \"aborted\":\n return \"\";\n default:\n return \"Dictation stopped unexpectedly. You can type instead.\";\n }\n}\n\nexport interface UseSpeechInputOptions {\n /** Receives settled text as it is recognised. Append it to the draft. */\n onFinalTranscript: (text: string) => void;\n /** BCP-47 tag. Defaults to the browser's language. */\n lang?: string;\n /** Called when the recogniser reports a problem worth showing. */\n onError?: (message: string) => void;\n}\n\nexport interface UseSpeechInputResult {\n /** False when the browser has no speech recogniser — hide the control entirely. */\n supported: boolean;\n listening: boolean;\n /** Words recognised but not yet settled; render as a live hint, do not store. */\n interim: string;\n error: string | null;\n start: () => void;\n stop: () => void;\n toggle: () => void;\n}\n\nexport function useSpeechInput({\n onFinalTranscript,\n lang,\n onError,\n}: UseSpeechInputOptions): UseSpeechInputResult {\n const [supported] = useState(() => getRecognitionCtor() !== null);\n const [listening, setListening] = useState(false);\n const [interim, setInterim] = useState(\"\");\n const [error, setError] = useState<string | null>(null);\n\n const recognitionRef = useRef<SpeechRecognitionLike | null>(null);\n /**\n * Bumped on every start()/stop(). A permission prompt, a settle delay or a retry resolving\n * after the visitor moved on must not open a microphone behind them.\n */\n const startTokenRef = useRef(0);\n /**\n * True only when the microphone was actually VERIFIED for this attempt — permission\n * reported granted, or getUserMedia succeeded. Stays false on browsers with no\n * mediaDevices, where nothing was checked and a recogniser `not-allowed` may still be a\n * real permission problem.\n */\n const micConfirmedRef = useRef(false);\n /** The visitor's intent, as opposed to whether a session happens to be open right now. */\n const wantsListeningRef = useRef(false);\n /** One retry per start(), for the Android \"refuses the first attempt\" case. */\n const retriedRef = useRef(false);\n\n // Held in refs so the recogniser's long-lived handlers always call the latest closures\n // without tearing down and re-creating the recogniser on every render.\n const finalRef = useRef(onFinalTranscript);\n const errorRef = useRef(onError);\n finalRef.current = onFinalTranscript;\n errorRef.current = onError;\n\n const langRef = useRef(lang);\n langRef.current = lang;\n\n const stop = useCallback(() => {\n startTokenRef.current += 1;\n wantsListeningRef.current = false;\n setListening(false);\n setInterim(\"\");\n const recognition = recognitionRef.current;\n if (!recognition) return;\n try {\n recognition.stop();\n } catch {\n /* already stopped */\n }\n }, []);\n\n /** Open a recogniser session. Separate from start() so a retry can reuse it. */\n const openSession = useCallback((token: number) => {\n const Ctor = getRecognitionCtor();\n if (!Ctor || token !== startTokenRef.current) return;\n\n const recognition = new Ctor();\n recognition.lang =\n langRef.current ??\n (typeof navigator === \"undefined\"\n ? \"en-US\"\n : navigator.language || \"en-US\");\n // Continuous mode is a desktop-only affordance. Android Chrome ends the session after a\n // single utterance whatever this says, and asking for continuous makes it fail sooner —\n // mobile is restarted from `onend` instead, which behaves the same to the visitor.\n recognition.continuous = !isTouchDevice();\n recognition.interimResults = true;\n recognition.maxAlternatives = 1;\n\n recognition.onstart = () => {\n if (token !== startTokenRef.current) return;\n // NOTE: the retry budget is deliberately NOT reset here. It belongs to the visitor's\n // press, not to the session — a browser that fires onstart and then immediately\n // errors would otherwise retry forever.\n setError(null);\n setListening(true);\n };\n\n recognition.onresult = (event) => {\n if (token !== startTokenRef.current) return;\n let settled = \"\";\n let pending = \"\";\n for (let i = event.resultIndex; i < event.results.length; i += 1) {\n const result = event.results[i];\n if (!result) continue;\n const text = result[0]?.transcript ?? \"\";\n if (result.isFinal) settled += text;\n else pending += text;\n }\n setInterim(pending);\n if (settled.trim() !== \"\") finalRef.current(settled);\n };\n\n recognition.onerror = (event) => {\n if (token !== startTokenRef.current) return;\n\n // Android commonly refuses the FIRST start after a permission is newly granted and\n // accepts the next one. Retry once, silently, before telling the visitor anything —\n // an error message they can do nothing about is worse than a short pause.\n if (\n (event.error === \"not-allowed\" ||\n event.error === \"service-not-allowed\") &&\n micConfirmedRef.current &&\n !retriedRef.current\n ) {\n retriedRef.current = true;\n void sleep(RETRY_DELAY_MS).then(() => {\n if (token !== startTokenRef.current || !wantsListeningRef.current)\n return;\n openSessionRef.current?.(token);\n });\n return;\n }\n\n // The microphone was verified moments ago, so a `not-allowed` that survived the retry\n // is the SPEECH SERVICE refusing, not a permission the visitor can grant. Sending them\n // to a site setting would point at something that cannot help.\n const message =\n micConfirmedRef.current &&\n (event.error === \"not-allowed\" || event.error === \"service-not-allowed\")\n ? MIC_UNAVAILABLE_MESSAGE\n : describeError(event.error);\n\n wantsListeningRef.current = false;\n setListening(false);\n setInterim(\"\");\n if (message !== \"\") {\n setError(message);\n errorRef.current?.(message);\n }\n };\n\n recognition.onend = () => {\n if (token !== startTokenRef.current) return;\n setInterim(\"\");\n // Single-shot mobile sessions end after each utterance. While the visitor still has\n // dictation switched on, reopen so it behaves like continuous mode.\n if (wantsListeningRef.current && isTouchDevice()) {\n void sleep(120).then(() => {\n if (token !== startTokenRef.current || !wantsListeningRef.current)\n return;\n openSessionRef.current?.(token);\n });\n return;\n }\n setListening(false);\n };\n\n recognitionRef.current = recognition;\n try {\n recognition.start();\n } catch {\n // Chrome throws InvalidStateError if start() races a session that is still closing.\n // The onend handler above will not fire for a session that never began, so settle here.\n wantsListeningRef.current = false;\n setListening(false);\n }\n }, []);\n\n // openSession refers to itself from its own handlers (retry, mobile restart), which a plain\n // const cannot do inside useCallback.\n const openSessionRef = useRef<((token: number) => void) | null>(null);\n openSessionRef.current = openSession;\n\n const start = useCallback(() => {\n if (!getRecognitionCtor()) return;\n\n startTokenRef.current += 1;\n const token = startTokenRef.current;\n wantsListeningRef.current = true;\n retriedRef.current = false;\n micConfirmedRef.current = false;\n\n // Show the pressed state straight away: the permission prompt can sit on screen for a\n // while on mobile, and a mic button that looks inert invites a second tap.\n setError(null);\n setListening(true);\n\n void ensureMicrophoneAccess().then(async (access) => {\n // A stop() or a newer start() landed while the prompt was open — abandon this attempt.\n if (token !== startTokenRef.current) return;\n\n if (!access.ok) {\n wantsListeningRef.current = false;\n setListening(false);\n setError(access.message);\n errorRef.current?.(access.message);\n return;\n }\n\n micConfirmedRef.current = access.confirmed;\n\n // Recognition opens its own capture. Starting in the same tick that the primed stream\n // is being torn down races that teardown and is refused — this is what made \"I granted\n // it and it still says blocked\" happen on mobile.\n if (access.primed) {\n await sleep(RELEASE_SETTLE_MS);\n if (token !== startTokenRef.current) return;\n }\n\n openSessionRef.current?.(token);\n });\n }, []);\n\n const toggle = useCallback(() => {\n if (listening) stop();\n else start();\n }, [listening, start, stop]);\n\n // Never leave the microphone open behind a closed page.\n useEffect(\n () => () => {\n startTokenRef.current += 1;\n wantsListeningRef.current = false;\n const recognition = recognitionRef.current;\n if (!recognition) return;\n recognition.onresult = null;\n recognition.onerror = null;\n recognition.onend = null;\n recognition.onstart = null;\n try {\n recognition.abort();\n } catch {\n /* nothing to abort */\n }\n },\n [],\n );\n\n return { supported, listening, interim, error, start, stop, toggle };\n}\n","\"use client\";\n\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\n/**\n * Read an answer aloud, on the browser's own speech synthesiser.\n *\n * The other half of {@link useSpeechInput}: that one turns a visitor's voice into a\n * question, this one turns our answer back into speech. Same posture — the Web Speech API\n * directly, no dependency, no request of ours, no cost, and nothing leaves the browser.\n *\n * ## Why `supported` can go false after a press\n *\n * `window.speechSynthesis` existing proves nothing. Several browsers expose the object and\n * then never speak: some Android WebViews with no TTS engine installed, Chrome on a machine\n * with no voices, and any engine where the utterance is refused without an `error` event.\n * There is no capability flag for that, so the only honest test is to try once — a press\n * that neither starts nor errors within {@link SILENT_FAILURE_MS}, while\n * `speechSynthesis.speaking` is still false, means this browser cannot do it and the control\n * removes itself. A button that does nothing, twice, is worse than no button.\n *\n * ## One voice per page\n *\n * `speechSynthesis` is a single queue shared by the whole document, so two answers reading\n * at once is not a thing the platform can do — a second `speak()` would simply queue behind\n * the first, minutes later. Every instance of this hook therefore registers with the\n * module-level owner below, and taking the voice stops whoever had it. {@link stopSpeaking}\n * is the same handle for code that is not a hook: the composer calls it when a new message\n * is sent, because an answer still reading aloud over the next question is the complaint\n * people actually make about pages that talk.\n */\n\ninterface SpeechSynthesisVoiceLike {\n readonly lang: string;\n readonly name: string;\n readonly default: boolean;\n}\n\ninterface SpeechSynthesisUtteranceLike {\n text: string;\n lang: string;\n voice: SpeechSynthesisVoiceLike | null;\n onstart: (() => void) | null;\n onend: (() => void) | null;\n onerror: ((event: { error?: string }) => void) | null;\n}\n\ntype SpeechSynthesisUtteranceCtor = new (\n text: string,\n) => SpeechSynthesisUtteranceLike;\n\ninterface SpeechSynthesisLike {\n readonly speaking: boolean;\n speak: (utterance: SpeechSynthesisUtteranceLike) => void;\n cancel: () => void;\n getVoices: () => SpeechSynthesisVoiceLike[];\n}\n\ninterface SpeechOutputWindow {\n speechSynthesis?: SpeechSynthesisLike;\n SpeechSynthesisUtterance?: SpeechSynthesisUtteranceCtor;\n}\n\n/** How long a press has to produce speech before the browser is taken at its word. */\nconst SILENT_FAILURE_MS = 1500;\n\n/**\n * The two API pieces, or null when this browser has no synthesiser at all (every iOS\n * in-app WebView without the entitlement, and anything server-side).\n */\nfunction speechPlatform(): {\n synth: SpeechSynthesisLike;\n Utterance: SpeechSynthesisUtteranceCtor;\n} | null {\n if (typeof window === \"undefined\") return null;\n const scope = window as unknown as SpeechOutputWindow;\n const synth = scope.speechSynthesis;\n const Utterance = scope.SpeechSynthesisUtterance;\n if (!synth || typeof synth.speak !== \"function\") return null;\n if (typeof Utterance !== \"function\") return null;\n return { synth, Utterance };\n}\n\n/** Lowercased, `-` separated: platforms spell the same tag `en_US`, `en-us` and `en-US`. */\nfunction normaliseTag(tag: string): string {\n return tag.trim().toLowerCase().replace(/_/g, \"-\");\n}\n\nfunction primaryOf(tag: string): string {\n return normaliseTag(tag).split(\"-\")[0] ?? \"\";\n}\n\n/**\n * The best voice for `lang`, or null to let the platform choose.\n *\n * Exact tag first (`ta-IN` over `ta-LK`), then the platform's default for the language, then\n * anything speaking it. Null rather than a wrong-language voice: an utterance with no voice\n * and the right `lang` is read by the platform's own fallback, which is closer than reading\n * Tamil with an English voice.\n */\nexport function pickVoice(\n voices: readonly SpeechSynthesisVoiceLike[],\n lang: string,\n): SpeechSynthesisVoiceLike | null {\n const wanted = normaliseTag(lang);\n if (wanted === \"\") return null;\n const primary = primaryOf(wanted);\n\n const exact = voices.filter((voice) => normaliseTag(voice.lang) === wanted);\n const related = voices.filter((voice) => primaryOf(voice.lang) === primary);\n\n return (\n exact.find((voice) => voice.default) ??\n exact[0] ??\n related.find((voice) => voice.default) ??\n related[0] ??\n null\n );\n}\n\n/** Whoever currently holds the page's one voice, and how to take it off them. */\nlet activeOwner: object | null = null;\nlet activeStop: (() => void) | null = null;\n\n/**\n * Stop whatever this SDK is reading aloud, from outside a component.\n *\n * A no-op when nothing of ours is speaking — deliberately, rather than calling\n * `speechSynthesis.cancel()` unconditionally, which would also silence a host page that is\n * using the same queue for its own reasons.\n */\nexport function stopSpeaking(): void {\n activeStop?.();\n}\n\nexport interface UseSpeechOutputOptions {\n /** BCP-47 tag to read in. Omit to let the platform pick its default voice. */\n lang?: string;\n}\n\nexport interface UseSpeechOutputResult {\n /**\n * False when this browser has no synthesiser, or has one that was asked to speak and did\n * not. Render no control at all when it is false — see the note at the top of the file.\n */\n supported: boolean;\n speaking: boolean;\n speak: (text: string) => void;\n stop: () => void;\n toggle: (text: string) => void;\n}\n\nexport function useSpeechOutput({\n lang,\n}: UseSpeechOutputOptions = {}): UseSpeechOutputResult {\n const [available, setAvailable] = useState(() => speechPlatform() !== null);\n const [speaking, setSpeaking] = useState(false);\n\n /** The utterance this instance is waiting on. Also \"do we own the voice right now\". */\n const utteranceRef = useRef<SpeechSynthesisUtteranceLike | null>(null);\n const watchdogRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n /** A stable identity for this instance, so the module-level owner can be compared. */\n const ownerRef = useRef<object>({});\n /** Lets the owner registration call the latest `stop` without re-registering. */\n const stopRef = useRef<() => void>(() => undefined);\n\n const langRef = useRef(lang);\n langRef.current = lang;\n\n const clearWatchdog = useCallback(() => {\n if (watchdogRef.current === null) return;\n clearTimeout(watchdogRef.current);\n watchdogRef.current = null;\n }, []);\n\n /** Forget this turn without touching the platform — for `end`, `error` and the watchdog. */\n const settle = useCallback(() => {\n clearWatchdog();\n utteranceRef.current = null;\n if (activeOwner === ownerRef.current) {\n activeOwner = null;\n activeStop = null;\n }\n setSpeaking(false);\n }, [clearWatchdog]);\n\n const stop = useCallback(() => {\n // Only cancel a queue we are actually using. Unmounting one answer's controls must not\n // silence a different answer that is mid-sentence.\n const owned = utteranceRef.current !== null;\n settle();\n if (owned) speechPlatform()?.synth.cancel();\n }, [settle]);\n\n stopRef.current = stop;\n\n const speak = useCallback(\n (text: string) => {\n const platform = speechPlatform();\n if (!platform) {\n setAvailable(false);\n return;\n }\n const spoken = text.trim();\n if (spoken === \"\") return;\n\n // Take the page's one voice, and clear anything already queued on it.\n if (activeStop && activeOwner !== ownerRef.current) activeStop();\n platform.synth.cancel();\n\n const utterance = new platform.Utterance(spoken);\n const wanted = langRef.current;\n if (wanted !== undefined && wanted !== \"\") {\n // Voices are fetched at press time rather than subscribed to: `getVoices()` is\n // empty until the platform has loaded them, and by the time a visitor has read an\n // answer and reached for this button, it has.\n const voice = pickVoice(platform.synth.getVoices(), wanted);\n utterance.lang = voice?.lang ?? wanted;\n if (voice) utterance.voice = voice;\n }\n\n const mine = (): boolean => utteranceRef.current === utterance;\n utterance.onstart = () => {\n if (!mine()) return;\n clearWatchdog();\n setSpeaking(true);\n };\n utterance.onend = () => {\n if (!mine()) return;\n settle();\n };\n utterance.onerror = (event) => {\n if (!mine()) return;\n const code = typeof event?.error === \"string\" ? event.error : \"\";\n settle();\n // `interrupted`/`canceled` is our own `cancel()` coming back — the visitor pressed\n // stop, or sent another message. Anything else is the engine refusing, and a\n // control that has now failed once is a control worth removing.\n if (code !== \"interrupted\" && code !== \"canceled\") setAvailable(false);\n };\n\n utteranceRef.current = utterance;\n activeOwner = ownerRef.current;\n activeStop = () => {\n stopRef.current();\n };\n // Optimistic, like the microphone: the engine can take a moment to start and a\n // control that looks inert invites a second press.\n setSpeaking(true);\n platform.synth.speak(utterance);\n\n clearWatchdog();\n watchdogRef.current = setTimeout(() => {\n watchdogRef.current = null;\n if (!mine()) return;\n // It is talking and simply never fired `start` — leave it alone.\n if (platform.synth.speaking) return;\n settle();\n setAvailable(false);\n }, SILENT_FAILURE_MS);\n },\n [clearWatchdog, settle],\n );\n\n const toggle = useCallback(\n (text: string) => {\n if (speaking) stop();\n else speak(text);\n },\n [speak, speaking, stop],\n );\n\n // A page that keeps talking after you leave it is the failure people complain about.\n // `pagehide` covers a real navigation and a bfcache freeze alike; unmount covers an SPA\n // route change and the transcript being reset.\n useEffect(() => {\n if (!speaking || typeof window === \"undefined\") return;\n const onHide = () => {\n stopRef.current();\n };\n window.addEventListener(\"pagehide\", onHide);\n return () => window.removeEventListener(\"pagehide\", onHide);\n }, [speaking]);\n\n useEffect(\n () => () => {\n stopRef.current();\n },\n [],\n );\n\n return { supported: available, speaking, speak, stop, toggle };\n}\n","\"use client\";\n\nimport { useEffect, useRef, useState } from \"react\";\n\n/**\n * Reveal an in-flight answer at a readable pace instead of in poll-sized blocks.\n *\n * ## What this is NOT\n *\n * It is not a transport change. `/explore` answers arrive by submit-and-poll, one\n * `exploreMessage` read every ~1.5s (CONTRACT.md §1), and nothing here touches that: no\n * extra request, no shorter interval, no socket. Every character this reveals had already\n * been fetched — the hook only decides *when it is painted*.\n *\n * ## The guarantees\n *\n * - **Only text that arrived.** What it returns is always a PREFIX of the string it was\n * given, so it can neither invent a word nor reorder one. When the answer is rewritten\n * rather than appended to — the final `content` replacing a `partialContent` that was cut\n * mid-token — it jumps to the end rather than showing a prefix of the old answer inside\n * the new one.\n * - **Never behind a finished answer.** `settled` (the turn is COMPLETED or FAILED) returns\n * the whole string immediately, on the render it flips. Nobody waits on an animation for\n * text that is already in hand, and a restored or shared transcript paints in full on its\n * first frame.\n * - **A table is painted as a table or not at all.** A GFM header row without its `| --- |`\n * row renders as a paragraph of pipes, so one is held back until that row arrives, for as\n * long as the answer is still running (CONTRACT.md §7). It is the one construct whose\n * rendering changes retroactively when a LATER line lands.\n * - **Reduced motion is not \"slower\".** `prefers-reduced-motion: reduce` gets the text at\n * once, with no timer started at all.\n * - **No scrolling, no measuring.** The hook returns a string. It reads no box, writes no\n * scroll position and renders no element, so it cannot fight the transcript's\n * `stickToBottom` logic or the pane geometry — the page was fixed once already for\n * showing nothing but the site footer, and the fix lives in effects that this cannot\n * reach.\n *\n * ## The pace\n *\n * Adaptive rather than a fixed characters-per-second: the cursor moves at whatever speed\n * drains the *backlog* in {@link DRAIN_TARGET_MS}, which is a little under the poll\n * interval. A fast turn therefore reads fast and a trickling one still moves, and the\n * cursor catches up with the arrived text before the next chunk lands instead of falling\n * further behind with every poll.\n */\n\n/** Drain whatever has arrived in about this long — just inside the ~1.5s poll interval. */\nconst DRAIN_TARGET_MS = 1200;\n/** A floor, so the last few characters of a chunk still move rather than crawl. */\nconst MIN_CHARS_PER_SECOND = 90;\n/** A ceiling, so a large backlog is caught up with quickly but not in one frame. */\nconst MAX_CHARS_PER_SECOND = 2200;\n/**\n * The most one step may advance by, however long the gap was. A backgrounded tab stops\n * firing frames entirely, and without this the first frame after it comes back would paint\n * the whole answer at once — the snap this exists to remove.\n */\nconst MAX_STEP_MS = 250;\n/**\n * How often a step is allowed to re-render. Every commit re-parses the answer's markdown,\n * and doing that once a frame is wasted work nobody can see; ~22 commits a second reads as\n * continuous.\n */\nconst COMMIT_INTERVAL_MS = 45;\n/** Only used where `requestAnimationFrame` does not exist. */\nconst FALLBACK_STEP_MS = 50;\n\n/** A ``` or ~~~ fence, which opens or closes a verbatim block. */\nconst FENCE = /^ {0,3}(?:```|~~~)/;\n/** A GFM table row. */\nconst TABLE_ROW = /^ {0,3}\\|/;\n/** A block marker with nothing after it yet — a bullet or heading mid-keystroke. */\nconst EMPTY_BLOCK_MARKER = /^ {0,3}(?:[-+>]|#{1,6}|\\d{1,9}[.)])\\s*$/;\n\n/**\n * A character that makes `_` part of a word rather than emphasis. Deliberately wider than\n * ASCII: `día_uno` is one word in Spanish exactly as `day_one` is in English.\n */\nfunction isWordCharacter(char: string | undefined): boolean {\n return char !== undefined && /[\\p{L}\\p{N}]/u.test(char);\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.min(Math.max(value, min), max);\n}\n\nfunction now(): number {\n return typeof performance !== \"undefined\" &&\n typeof performance.now === \"function\"\n ? performance.now()\n : Date.now();\n}\n\n/** The first half of an astral character — a cut here would paint a replacement glyph. */\nfunction isHighSurrogate(code: number): boolean {\n return code >= 0xd800 && code <= 0xdbff;\n}\n\n/**\n * Where an incomplete LINE may be cut: before the first inline construct it has left open.\n *\n * Markdown is not revealable one character at a time as written. Cutting `**Starter**` after\n * `**Star` paints two literal asterisks, and the same half-written `[label](https://…` paints\n * a bracket and a raw URL, which then rearrange themselves a frame later. Holding the cut at\n * the point the construct OPENED costs a few characters of delay and never paints syntax.\n *\n * Scanned per line on purpose. Emphasis that spans a line break is rare, and carrying an\n * unclosed marker across lines would pin the cut arbitrarily far back.\n *\n * ## What is NOT an open construct\n *\n * A marker character in ordinary prose must not pin the cut, because the pin lasts for the\n * REST OF THE LINE: an answer's paragraph is one unwrapped line, so a `~` that never closes\n * would freeze the reveal mid-sentence and then paint the remaining hundred characters in a\n * single commit — the snap this hook exists to remove. So:\n *\n * - **A lone `*`, `_` or `~` opens nothing.** `answerPlainText` reached the same rule from\n * the other side (\"single-delimiter emphasis only when it wraps a run\"): a `* ` bullet, a\n * `snake_case` identifier, a `~30 seconds`, and a `per seat*` footnote mark are all one\n * literal character, and one literal character on screen for a frame is far cheaper than a\n * frozen paragraph. A run of two or more (`**`, `__`, `~~`) is markup often enough to be\n * worth the wait.\n * - **Intraword `_` is not emphasis at all**, per CommonMark — `MAX__TURNS__PER` neither\n * opens nor closes, whatever the run length.\n * - **A run with whitespace after it opens nothing**, also per CommonMark: it is not a\n * left-flanking delimiter run. Whitespace after the END of the arrived text is unknown,\n * not absent, so a line ending in `**` still holds — that is the `**29` case above.\n */\nfunction safeInlineEnd(line: string): number {\n let codeSpan: number | null = null;\n /**\n * The link and image constructs still open, innermost last: a `label` waiting for its `]`,\n * a `destination` waiting for its `)`, and a `paren` the URL itself opened. A stack, not one\n * position, because they nest — `[![alt](poster)](https://…/video.mp4)` is a link whose\n * label is an image, and the image's `)` does not close the link. The bottom entry is the\n * outermost construct, and the only one that is ever the cut.\n */\n const links: Array<{\n kind: \"label\" | \"destination\" | \"paren\";\n start: number;\n }> = [];\n const emphasis: Array<{ marker: string; start: number }> = [];\n\n let at = 0;\n while (at < line.length) {\n const char = line[at] ?? \"\";\n if (char === \"\\\\\") {\n // An escape and whatever it escapes are one unit and carry no meaning of their own.\n at += 2;\n continue;\n }\n const innermost = links[links.length - 1];\n if (innermost !== undefined && innermost.kind !== \"label\") {\n // Inside a URL only parentheses mean anything: its own `(` and `)` nest, and the one\n // that balances the `](` closes the construct. A `_`, a backtick or a bracket in a URL\n // is a character of the URL, and read as markup it would pin the cut inside it.\n if (char === \"(\") links.push({ kind: \"paren\", start: innermost.start });\n else if (char === \")\") links.pop();\n at += 1;\n continue;\n }\n if (char === \"`\") {\n let run = 1;\n while (line[at + run] === \"`\") run += 1;\n codeSpan = codeSpan === null ? at : null;\n at += run;\n continue;\n }\n if (codeSpan !== null) {\n // Inside a code span nothing else is markup.\n at += 1;\n continue;\n }\n if (char === \"[\") {\n // `![` is an image; either way the bracket is what opens it.\n links.push({ kind: \"label\", start: line[at - 1] === \"!\" ? at - 1 : at });\n at += 1;\n continue;\n }\n if (char === \"]\" && innermost !== undefined) {\n // `[3]` is a citation marker and closes here. `](` carries on into the destination,\n // which is still the same construct.\n links.pop();\n if (line[at + 1] === \"(\") {\n links.push({ kind: \"destination\", start: innermost.start });\n at += 2;\n continue;\n }\n at += 1;\n continue;\n }\n if (char === \"*\" || char === \"_\" || char === \"~\") {\n let run = 1;\n while (line[at + run] === char) run += 1;\n const start = at;\n at += run;\n const before = line[start - 1];\n const after = line[start + run];\n\n // See \"What is NOT an open construct\" above for all three of these.\n if (run < 2) continue;\n if (char === \"_\" && isWordCharacter(before) && isWordCharacter(after)) {\n continue;\n }\n\n const marker = char.repeat(run);\n const open = emphasis.findIndex((entry) => entry.marker === marker);\n // Closing first: a run that ends emphasis is usually followed by a space, and the\n // opener guard below would otherwise leave that run open forever.\n if (open !== -1) {\n emphasis.splice(open, 1);\n continue;\n }\n if (after !== undefined && /\\s/.test(after)) continue;\n emphasis.push({ marker, start });\n continue;\n }\n at += 1;\n }\n\n const open = [\n codeSpan,\n links[0]?.start ?? null,\n emphasis[0]?.start ?? null,\n ].filter((value): value is number => value !== null);\n return open.length === 0 ? line.length : Math.min(...open);\n}\n\n/**\n * The largest index at or below `limit` that `text` may be cut at and still render as the\n * markdown it will eventually be.\n *\n * Three block rules, then the inline scan above:\n *\n * - **A half-written fenced block is held at its fence.** Until the closing fence arrives\n * the opening one is just three backticks and a word, and the code inside it is prose.\n * - **A table is held until its delimiter row is complete.** A GFM table is only a table\n * once the `| --- |` row exists; before that the header renders as a paragraph full of\n * pipes, which then becomes a table — the single most jarring thing an answer can do.\n * Complete means a WHOLE line: half of `| --- | --- |` is not a delimiter row, and one\n * that has not been closed by a newline may still grow another column. Afterwards only\n * the incomplete row is held, so the table grows a row at a time.\n * - **A bare block marker is held.** `-`, `>` or `####` with nothing after it yet paints an\n * empty bullet or an empty heading.\n *\n * Reaching the end of the arrived text (`limit >= text.length`) releases all of it but the\n * table, because the cursor being level with the text is the NORMAL end state of a poll, not\n * an edge case: the pace drains a chunk in {@link DRAIN_TARGET_MS}, inside the ~1.5s\n * interval, so every poll ends caught up. A construct that is only ever held while the\n * cursor is behind is therefore held only for the part of a chunk it happens to land in.\n *\n * The table is the one that cannot be left to that, and it is the only one that NEEDS more\n * than the text in hand to render correctly. A header row whose delimiter row is in the next\n * poll would be painted as a paragraph of pipes and then, because a cut never moves\n * backwards, stay one on screen while the rest of the table grows below it. An unclosed\n * fence, by contrast, already renders as the code block it will be (CommonMark runs it to\n * the end of the document), and a `- ` bullet already renders as a bullet.\n *\n * Nothing here decides what a FINISHED answer paints: {@link useProgressiveReveal} returns\n * `content` whole the moment the turn settles, without asking.\n */\nexport function revealBoundary(text: string, limit: number): number {\n if (limit <= 0) return 0;\n\n /** True when the cursor has reached everything that has arrived. */\n const caughtUp = limit >= text.length;\n const head = caughtUp ? text : text.slice(0, limit);\n const lineStart = head.lastIndexOf(\"\\n\") + 1;\n const line = head.slice(lineStart);\n\n // Block state, from the WHOLE lines before the cut.\n let fenceStart: number | null = null;\n let tableStart: number | null = null;\n let tableRows = 0;\n let at = 0;\n while (at < lineStart) {\n const breakAt = head.indexOf(\"\\n\", at);\n const end = breakAt === -1 ? lineStart : breakAt;\n const row = head.slice(at, end);\n if (fenceStart !== null) {\n if (FENCE.test(row)) fenceStart = null;\n } else if (FENCE.test(row)) {\n fenceStart = at;\n } else if (TABLE_ROW.test(row)) {\n if (tableStart === null) {\n tableStart = at;\n tableRows = 0;\n }\n tableRows += 1;\n } else if (tableStart !== null) {\n tableStart = null;\n tableRows = 0;\n }\n at = end + 1;\n }\n\n // The last line is not a whole line: the rest of it may still be on its way. A `|` on it\n // still OPENS a table, and an empty one (the text ends at the newline after the row above)\n // has not closed the table it follows either.\n if (fenceStart === null) {\n const lastIsRow = TABLE_ROW.test(line);\n if (lastIsRow && tableStart === null) {\n // It opens a table. It does not COUNT as one of the two rows a table needs: half of\n // `| --- |` is not a delimiter row.\n tableStart = lineStart;\n tableRows = 0;\n } else if (!lastIsRow && line !== \"\") {\n tableStart = null;\n tableRows = 0;\n }\n // Held even at the end of the arrived text — see the note above.\n if (tableStart !== null && tableRows < 2) return tableStart;\n }\n\n if (caughtUp) return text.length;\n if (fenceStart !== null) return fenceStart;\n if (TABLE_ROW.test(line)) return lineStart;\n if (EMPTY_BLOCK_MARKER.test(line)) return lineStart;\n\n const cut = lineStart + safeInlineEnd(line);\n return isHighSurrogate(text.charCodeAt(cut - 1)) ? cut - 1 : cut;\n}\n\n/**\n * True when the visitor has asked for less movement. Read once per mount rather than per\n * render: `matchMedia` is absent on some engines (and in jsdom), and a setting that changes\n * mid-answer is not worth a subscription.\n *\n * Server-side this answers true, so a prerender serialises the finished text rather than an\n * animation's first frame.\n */\nfunction prefersReducedMotion(): boolean {\n if (typeof window === \"undefined\") return true;\n try {\n return (\n window.matchMedia?.(\"(prefers-reduced-motion: reduce)\").matches === true\n );\n } catch {\n /* an engine whose matchMedia throws on an unknown feature — treat as no preference */\n return false;\n }\n}\n\n/**\n * The part of `text` that is currently on screen.\n *\n * @param text everything that has arrived so far — `content` once the turn completes,\n * `partialContent` while it runs.\n * @param settled true once the turn is COMPLETED or FAILED and `text` will not grow again.\n */\nexport function useProgressiveReveal(text: string, settled: boolean): string {\n const [, setFrame] = useState(0);\n const [reduced] = useState(prefersReducedMotion);\n const immediate = settled || reduced;\n\n /** The text the cursor below points into. */\n const textRef = useRef(text);\n /** How far the cursor has travelled, fractionally — a step is usually part of a character. */\n const cursorRef = useRef(immediate ? text.length : 0);\n /** The last cut actually rendered, so a cut can never move backwards. */\n const cutRef = useRef(immediate ? text.length : 0);\n const committedRef = useRef(0);\n\n // Reconciled during render, not in an effect: what this function returns has to describe\n // the text it was handed THIS render, never the previous answer for a frame.\n if (text !== textRef.current) {\n const revealed = textRef.current.slice(0, Math.floor(cursorRef.current));\n if (revealed !== \"\" && !text.startsWith(revealed)) {\n // Rewritten rather than appended to. Everything on screen must be text that arrived,\n // so finish rather than show the old prefix inside the new answer.\n cursorRef.current = text.length;\n }\n cursorRef.current = Math.min(cursorRef.current, text.length);\n cutRef.current = Math.min(cutRef.current, text.length);\n textRef.current = text;\n }\n if (immediate) cursorRef.current = text.length;\n\n // A settled turn paints whole rather than asking the boundary. `content` is the entire\n // answer, so a table it stops in the middle of is all the table there is ever going to\n // be, and holding it back would hold it back for good.\n const cut = immediate\n ? text.length\n : Math.max(\n cutRef.current,\n revealBoundary(text, Math.floor(cursorRef.current)),\n );\n cutRef.current = cut;\n\n useEffect(() => {\n if (immediate) return;\n if (cursorRef.current >= text.length) return;\n\n let cancelled = false;\n let cancelStep: (() => void) | null = null;\n let previous = now();\n\n const commit = (at: number, force: boolean): void => {\n if (!force && at - committedRef.current < COMMIT_INTERVAL_MS) return;\n committedRef.current = at;\n setFrame((value) => value + 1);\n };\n\n const step = (): void => {\n cancelStep = null;\n if (cancelled) return;\n\n const at = now();\n const elapsed = clamp(at - previous, 0, MAX_STEP_MS);\n previous = at;\n\n const length = textRef.current.length;\n const pending = length - cursorRef.current;\n if (pending <= 0) {\n commit(at, true);\n return;\n }\n\n const perSecond = clamp(\n pending / (DRAIN_TARGET_MS / 1000),\n MIN_CHARS_PER_SECOND,\n MAX_CHARS_PER_SECOND,\n );\n cursorRef.current = Math.min(\n length,\n cursorRef.current + (perSecond * elapsed) / 1000,\n );\n\n const done = cursorRef.current >= length;\n commit(at, done);\n if (!done) schedule();\n };\n\n const schedule = (): void => {\n if (typeof window.requestAnimationFrame === \"function\") {\n const id = window.requestAnimationFrame(step);\n cancelStep = () => window.cancelAnimationFrame(id);\n return;\n }\n const id = setTimeout(step, FALLBACK_STEP_MS);\n cancelStep = () => clearTimeout(id);\n };\n\n schedule();\n return () => {\n cancelled = true;\n cancelStep?.();\n };\n }, [text, immediate]);\n\n return text.slice(0, cut);\n}\n"]}