@liforma/client 0.4.4 → 0.4.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -0
- package/dist/next/index.js +467 -379
- package/dist/next/index.js.map +1 -1
- package/dist/next/next/createLiformaSessionRouteHandler.d.ts +8 -0
- package/dist/next/next/createLiformaSessionRouteHandler.d.ts.map +1 -1
- package/dist/next/next/index.d.ts +8 -1
- package/dist/next/next/index.d.ts.map +1 -1
- package/dist/next/v2/api/captureAttribution.d.ts +10 -0
- package/dist/next/v2/api/captureAttribution.d.ts.map +1 -0
- package/dist/next/v2/api/mintPublicSession.d.ts +1 -0
- package/dist/next/v2/api/mintPublicSession.d.ts.map +1 -1
- package/dist/next/v2/api/sessionEndpointRequest.d.ts +1 -0
- package/dist/next/v2/api/sessionEndpointRequest.d.ts.map +1 -1
- package/dist/next/v2/embed/fullWindowHost.d.ts +20 -0
- package/dist/next/v2/embed/fullWindowHost.d.ts.map +1 -0
- package/dist/next/v2/embed/iframeHost.d.ts +4 -1
- package/dist/next/v2/embed/iframeHost.d.ts.map +1 -1
- package/dist/next/v2/embed/protocol.d.ts +18 -1
- package/dist/next/v2/embed/protocol.d.ts.map +1 -1
- package/dist/next/v2/experience.d.ts +14 -2
- package/dist/next/v2/experience.d.ts.map +1 -1
- package/dist/next/v2/types/sessionInteraction.d.ts +49 -0
- package/dist/next/v2/types/sessionInteraction.d.ts.map +1 -1
- package/dist/npm/index.js +559 -477
- package/dist/npm/index.js.map +1 -1
- package/dist/npm/v2/api/captureAttribution.d.ts +10 -0
- package/dist/npm/v2/api/captureAttribution.d.ts.map +1 -0
- package/dist/npm/v2/api/mintPublicSession.d.ts +1 -0
- package/dist/npm/v2/api/mintPublicSession.d.ts.map +1 -1
- package/dist/npm/v2/api/sessionEndpointRequest.d.ts +1 -0
- package/dist/npm/v2/api/sessionEndpointRequest.d.ts.map +1 -1
- package/dist/npm/v2/embed/fullWindowHost.d.ts +20 -0
- package/dist/npm/v2/embed/fullWindowHost.d.ts.map +1 -0
- package/dist/npm/v2/embed/iframeHost.d.ts +4 -1
- package/dist/npm/v2/embed/iframeHost.d.ts.map +1 -1
- package/dist/npm/v2/embed/protocol.d.ts +18 -1
- package/dist/npm/v2/embed/protocol.d.ts.map +1 -1
- package/dist/npm/v2/experience.d.ts +14 -2
- package/dist/npm/v2/experience.d.ts.map +1 -1
- package/dist/npm/v2/index.d.ts +1 -1
- package/dist/npm/v2/index.d.ts.map +1 -1
- package/dist/npm/v2/npm.d.ts +2 -1
- package/dist/npm/v2/npm.d.ts.map +1 -1
- package/dist/npm/v2/types/sessionInteraction.d.ts +49 -0
- package/dist/npm/v2/types/sessionInteraction.d.ts.map +1 -1
- package/dist/npm/v2/types/sessionManifestSchema.d.ts +9 -0
- package/dist/npm/v2/types/sessionManifestSchema.d.ts.map +1 -1
- package/dist/react/component/experienceComponentLaunch.d.ts.map +1 -1
- package/dist/react/component/experienceComponentRuntime.d.ts +1 -0
- package/dist/react/component/experienceComponentRuntime.d.ts.map +1 -1
- package/dist/react/component/experienceComponentTypes.d.ts +7 -0
- package/dist/react/component/experienceComponentTypes.d.ts.map +1 -1
- package/dist/react/index.js +710 -621
- package/dist/react/index.js.map +1 -1
- package/dist/react/react/Experience.d.ts.map +1 -1
- package/dist/react/v2/api/captureAttribution.d.ts +10 -0
- package/dist/react/v2/api/captureAttribution.d.ts.map +1 -0
- package/dist/react/v2/api/mintPublicSession.d.ts +1 -0
- package/dist/react/v2/api/mintPublicSession.d.ts.map +1 -1
- package/dist/react/v2/api/sessionEndpointRequest.d.ts +1 -0
- package/dist/react/v2/api/sessionEndpointRequest.d.ts.map +1 -1
- package/dist/react/v2/embed/fullWindowHost.d.ts +20 -0
- package/dist/react/v2/embed/fullWindowHost.d.ts.map +1 -0
- package/dist/react/v2/embed/iframeHost.d.ts +4 -1
- package/dist/react/v2/embed/iframeHost.d.ts.map +1 -1
- package/dist/react/v2/embed/protocol.d.ts +18 -1
- package/dist/react/v2/embed/protocol.d.ts.map +1 -1
- package/dist/react/v2/experience.d.ts +14 -2
- package/dist/react/v2/experience.d.ts.map +1 -1
- package/dist/react/v2/types/sessionInteraction.d.ts +49 -0
- package/dist/react/v2/types/sessionInteraction.d.ts.map +1 -1
- package/dist/sdk/v2/client.js +6 -6
- package/dist/sdk/v2/client.js.map +1 -1
- package/dist/svelte/Experience.svelte +10 -0
- package/dist/svelte/Experience.svelte.d.ts +1 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","names":["#render","#teardown","#cleanup","#root","#boolAttr","#shouldLoad","#hovering","#scrollParallax","#avatarReady","#backgroundReady","#foregroundReady","#parallaxX","#parallaxY","#avatarLoadFailed","#pointerDown","#applyHostBox","#mount","#controller","#host"],"sources":["../../../src/shims/localStack.ts","../../../src/shims/apiBaseUrl.ts","../../../src/v2/errors/LiformaError.ts","../../../src/v2/api/sessionEndpointRequest.ts","../../../src/v2/types/sessionLaunch.ts","../../../src/v2/api/mintPublicSession.ts","../../../src/v2/conversationProcessor.ts","../../../src/v2/embed/playerReachability.ts","../../../src/v2/embed/protocol.ts","../../../src/v2/embed/iframeHost.ts","../../../src/v2/embed/readyGate.ts","../../../src/v2/shims/embedBaseUrl.ts","../../../src/v2/experienceEvents.ts","../../../src/v2/speech/types.ts","../../../src/byo/pcm.ts","../../../src/byo/resample.ts","../../../src/byo/audioCapture.ts","../../../src/v2/speech/captureMediaStreamTrack.ts","../../../src/v2/speech/speechController.ts","../../../src/v2/types/sessionInteraction.ts","../../../src/v2/experience.ts","../../../src/v2/embed/returnNavigation.ts","../../../src/svelte/experienceLoadError.ts","../../../src/svelte/experienceLoadErrorStyles.ts","../../../src/svelte/experienceLoadErrorOverlay.ts","../../../src/v2/liforma-experience.ts","../../../src/thumbnail/types.ts","../../../src/thumbnail/toGalleryThumbUrl.ts","../../../src/thumbnail/hostedLaunchUrl.ts","../../../src/thumbnail/thumbLoadScheduler.ts","../../../src/thumbnail/lazyLoad.ts","../../../src/thumbnail/scrollParallax.ts","../../../src/thumbnail/attachThumbnailParallax.ts","../../../src/thumbnail/styles.ts","../../../src/thumbnail/ensureStyles.ts","../../../src/v2/liforma-experience-thumbnail.ts","../../../src/v2/api/fetchExperiencePreview.ts","../../../src/widget/dockGeometry.ts","../../../src/widget/styles.ts","../../../src/widget/mountExperienceWidget.ts","../../../src/v2/liforma-experience-widget.ts","../../../src/v2/index.ts"],"sourcesContent":["/**\n * Opt-in local monorepo stack (api :3001, player :3002).\n * Never inferred from hostname alone — third-party demos run on localhost against production.\n */\nexport function useExplicitLocalStack(): boolean {\n\tif ((globalThis as { __LIFORMA_STACK?: string }).__LIFORMA_STACK === 'local') {\n\t\treturn true;\n\t}\n\n\tif (typeof location === 'undefined') {\n\t\treturn false;\n\t}\n\n\ttry {\n\t\treturn new URLSearchParams(location.search).get('stack') === 'local';\n\t} catch {\n\t\treturn false;\n\t}\n}\n","import { useExplicitLocalStack } from './localStack';\n\nconst PRODUCTION_API_BASE_URL = 'https://api.liforma.ai';\nconst LOCAL_API_BASE_URL = 'http://localhost:3001';\n\nfunction readConfiguredApiBaseUrl(): string | null {\n\tconst globalValue = (globalThis as { __LIFORMA_API_BASE_URL?: string }).__LIFORMA_API_BASE_URL;\n\tif (globalValue?.trim()) {\n\t\treturn globalValue.trim().replace(/\\/$/, '');\n\t}\n\n\tif (typeof document === 'undefined') {\n\t\treturn null;\n\t}\n\n\tconst script =\n\t\tdocument.querySelector('script[data-liforma-sdk][src*=\"client.js\"]') ??\n\t\tdocument.querySelector('script[src*=\"/sdk/v2/client.js\"]');\n\tconst fromScript = script?.getAttribute('data-api-base-url')?.trim();\n\tif (fromScript) {\n\t\treturn fromScript.replace(/\\/$/, '');\n\t}\n\n\treturn null;\n}\n\n/**\n * Browser-visible API origin for Liforma HTTP calls from the CDN SDK.\n *\n * Defaults to production. Local stack is opt-in only — never inferred from\n * `localhost` hostname (third-party demos run on localhost against production).\n *\n * Override order:\n * 1. `window.__LIFORMA_API_BASE_URL`\n * 2. `data-api-base-url` on the SDK script tag\n * 3. `window.__LIFORMA_STACK === 'local'` or host `?stack=local` → http://localhost:3001\n * 4. production default → https://api.liforma.ai\n */\nexport function resolvePublicApiBaseUrl(): string {\n\tconst configured = readConfiguredApiBaseUrl();\n\tif (configured) {\n\t\treturn configured;\n\t}\n\n\tif (useExplicitLocalStack()) {\n\t\treturn LOCAL_API_BASE_URL;\n\t}\n\n\treturn PRODUCTION_API_BASE_URL;\n}\n","/**\n * Structured API / mint error from Liforma.\n * Prefer `.code` and `.requestId` over parsing `.message`.\n */\nexport class LiformaError extends Error {\n\treadonly code: string;\n\treadonly requestId: string | undefined;\n\treadonly status: number | undefined;\n\treadonly details: Record<string, unknown> | undefined;\n\n\tconstructor(\n\t\tmessage: string,\n\t\toptions?: {\n\t\t\treadonly code?: string;\n\t\t\treadonly requestId?: string;\n\t\t\treadonly status?: number;\n\t\t\treadonly details?: Record<string, unknown>;\n\t\t\treadonly cause?: unknown;\n\t\t}\n\t) {\n\t\tsuper(message, options?.cause !== undefined ? { cause: options.cause } : undefined);\n\t\tthis.name = 'LiformaError';\n\t\tthis.code = options?.code?.trim() || 'UNKNOWN';\n\t\tthis.requestId = options?.requestId?.trim() || undefined;\n\t\tthis.status = options?.status;\n\t\tthis.details = options?.details;\n\t}\n}\n\ntype NestedErrorShape = {\n\treadonly error?: {\n\t\treadonly code?: unknown;\n\t\treadonly message?: unknown;\n\t\treadonly requestId?: unknown;\n\t\treadonly [key: string]: unknown;\n\t};\n\treadonly code?: unknown;\n\treadonly message?: unknown;\n\treadonly requestId?: unknown;\n};\n\nfunction asRecord(value: unknown): Record<string, unknown> | undefined {\n\tif (!value || typeof value !== 'object' || Array.isArray(value)) return undefined;\n\treturn value as Record<string, unknown>;\n}\n\n/**\n * Parse a mint/API error response body + optional `Liforma-Request-Id` header\n * into a {@link LiformaError}. Falls back to flat `{ code, message }` shapes.\n */\nexport function liformaErrorFromResponse(\n\tstatus: number,\n\ttext: string,\n\theaders?: Headers\n): LiformaError {\n\tconst headerRequestId = headers?.get('Liforma-Request-Id')?.trim() || undefined;\n\tlet parsed: NestedErrorShape | undefined;\n\ttry {\n\t\tparsed = JSON.parse(text) as NestedErrorShape;\n\t} catch {\n\t\t/* keep */\n\t}\n\n\tconst nested = parsed?.error && typeof parsed.error === 'object' ? parsed.error : undefined;\n\tconst codeRaw = nested?.code ?? parsed?.code;\n\tconst messageRaw = nested?.message ?? parsed?.message;\n\tconst requestIdRaw = nested?.requestId ?? parsed?.requestId ?? headerRequestId;\n\n\tconst code = typeof codeRaw === 'string' && codeRaw.trim() ? codeRaw.trim() : 'HTTP_ERROR';\n\tconst message =\n\t\ttypeof messageRaw === 'string' && messageRaw.trim()\n\t\t\t? messageRaw.trim()\n\t\t\t: text.trim().slice(0, 500) || `Request failed (${status})`;\n\tconst requestId =\n\t\ttypeof requestIdRaw === 'string' && requestIdRaw.trim() ? requestIdRaw.trim() : headerRequestId;\n\n\tconst details = asRecord(nested) ?? asRecord(parsed);\n\tconst { code: _c, message: _m, requestId: _r, ...rest } = details ?? {};\n\tvoid _c;\n\tvoid _m;\n\tvoid _r;\n\n\treturn new LiformaError(message, {\n\t\tcode,\n\t\trequestId,\n\t\tstatus,\n\t\tdetails: Object.keys(rest).length > 0 ? rest : undefined\n\t});\n}\n","export type SessionEndpointRequestOptions = {\n\treadonly experienceId: string;\n\t/** BCP 47 locale (preferred). When omitted in the browser, defaults to `navigator.language`. */\n\treadonly locale?: string;\n\t/** Paired / learning language for dual experiences (BCP 47). */\n\treadonly secondaryLocale?: string;\n\t/** Alias of `secondaryLocale` — language-learning apps (tutors + roleplay). */\n\treadonly learningLocale?: string;\n\t/** @deprecated Prefer `locale`. */\n\treadonly language?: 'en' | 'es' | string;\n\treadonly mode?: 'conversation' | 'presenter';\n\treadonly speechInputMode?: 'auto' | 'manual' | 'off';\n\treadonly speechOnly?: boolean;\n\t/** Optional Idempotency-Key header value (max 256 chars). */\n\treadonly idempotencyKey?: string;\n};\n\nfunction resolveLocale(options: SessionEndpointRequestOptions): string | undefined {\n\tconst locale = options.locale?.trim();\n\tif (locale) return locale;\n\tconst language = options.language?.trim();\n\tif (language) return language;\n\tif (typeof navigator !== 'undefined' && navigator.language?.trim()) {\n\t\treturn navigator.language.trim();\n\t}\n\treturn undefined;\n}\n\nfunction resolveSecondaryLocale(options: SessionEndpointRequestOptions): string | undefined {\n\tconst secondary = options.secondaryLocale?.trim();\n\tconst learning = options.learningLocale?.trim();\n\tif (secondary && learning && secondary !== learning) {\n\t\tthrow new Error(\n\t\t\t'secondaryLocale and learningLocale disagree; pass only one or make them equal.'\n\t\t);\n\t}\n\treturn secondary || learning || undefined;\n}\n\nexport function createSessionEndpointRequest(\n\tsessionEndpoint: string,\n\toptions: SessionEndpointRequestOptions\n): { readonly url: string; readonly init: RequestInit } {\n\tconst endpoint = sessionEndpoint.trim();\n\tif (!endpoint) {\n\t\tthrow new Error('sessionEndpoint must not be empty.');\n\t}\n\tif (typeof location === 'undefined') {\n\t\tthrow new Error('sessionEndpoint is only available in a browser.');\n\t}\n\n\tconst url = new URL(endpoint, location.href);\n\tif (url.origin !== location.origin) {\n\t\tthrow new Error('sessionEndpoint must be same-origin.');\n\t}\n\n\tconst experienceId = options.experienceId.trim();\n\tif (!experienceId) {\n\t\tthrow new Error('sessionEndpoint requires experienceId.');\n\t}\n\n\tconst locale = resolveLocale(options);\n\tconst secondaryLocale = resolveSecondaryLocale(options);\n\tconst idempotencyKey = options.idempotencyKey?.trim();\n\n\treturn {\n\t\turl: url.toString(),\n\t\tinit: {\n\t\t\tmethod: 'POST',\n\t\t\tcredentials: 'same-origin',\n\t\t\theaders: {\n\t\t\t\tAccept: 'application/json',\n\t\t\t\t'Content-Type': 'application/json',\n\t\t\t\t...(idempotencyKey ? { 'Idempotency-Key': idempotencyKey } : {})\n\t\t\t},\n\t\t\tbody: JSON.stringify({\n\t\t\t\texperienceId,\n\t\t\t\t...(locale ? { locale } : {}),\n\t\t\t\t...(secondaryLocale ? { secondaryLocale } : {}),\n\t\t\t\t...(options.mode ? { mode: options.mode } : {}),\n\t\t\t\t...(options.speechInputMode ? { speechInputMode: options.speechInputMode } : {}),\n\t\t\t\t...(options.speechOnly !== undefined ? { speechOnly: options.speechOnly } : {})\n\t\t\t})\n\t\t}\n\t};\n}\n","/**\n * Public Session Launch contract (integrator-facing).\n * Runtime internals live inside the opaque `launch` string — do not parse it.\n */\n\nexport type SessionCapabilities = {\n\treadonly speech: { readonly input: boolean; readonly output: boolean; readonly externalAudio: boolean };\n\treadonly conversation: { readonly managed: boolean };\n\treadonly avatar: { readonly enabled: boolean };\n\treadonly tools: { readonly enabled: boolean };\n};\n\nexport type SessionMode = 'conversation' | 'presenter';\n\nexport type SessionStatus = 'active' | 'ended';\n\n/** Mint collapsed a non-English locale onto English because TTS cannot synthesize it. */\nexport type LocaleVoiceFallback = {\n\treadonly axis: 'locale' | 'secondaryLocale';\n\treadonly requested: string;\n\treadonly resolved: string;\n\treadonly reason: 'voice_unsupported';\n};\n\nexport type Session = {\n\treadonly id: string;\n\treadonly experienceId: string;\n\treadonly experienceRevisionId: string;\n\treadonly expiresAt: string;\n\treadonly locale: string;\n\t/** Paired / learning language when dual; omitted for plain sessions. */\n\treadonly secondaryLocale?: string;\n\treadonly mode: SessionMode;\n\treadonly capabilities: SessionCapabilities;\n\treadonly localeVoiceFallbacks?: readonly LocaleVoiceFallback[];\n\t/** Present on mint when RuntimeSession was persisted. */\n\treadonly status?: SessionStatus;\n\treadonly createdAt?: string;\n};\n\nexport type SessionLaunchResponse = {\n\treadonly session: Session;\n\t/**\n\t * Asymmetrically signed opaque runtime bootstrap for the player.\n\t * Contract-opaque (unsupported to parse), not confidentiality-opaque.\n\t */\n\treadonly launch: string;\n};\n\nexport type PlayerStatus = 'loading' | 'scene' | 'ready' | 'error';\n\n/** Listening / speaking activity (replaces overloaded modeChange). */\nexport type SessionActivity = 'idle' | 'listening' | 'thinking' | 'speaking';\n\nexport type CloseButtonOptions = {\n\treadonly visible?: boolean;\n\treadonly style?: 'icon' | 'text';\n\treadonly label?: string | null;\n\treadonly position?: string;\n};\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn Boolean(value) && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction isBoolean(value: unknown): value is boolean {\n\treturn typeof value === 'boolean';\n}\n\nfunction parseNestedCapabilities(raw: unknown): SessionCapabilities | null {\n\tif (!isRecord(raw)) return null;\n\tconst speech = raw.speech;\n\tconst conversation = raw.conversation;\n\tconst avatar = raw.avatar;\n\tconst tools = raw.tools;\n\tif (\n\t\t!isRecord(speech) ||\n\t\t!isRecord(conversation) ||\n\t\t!isRecord(avatar) ||\n\t\t!isRecord(tools) ||\n\t\t!isBoolean(speech.input) ||\n\t\t!isBoolean(speech.output) ||\n\t\t!isBoolean(speech.externalAudio) ||\n\t\t!isBoolean(conversation.managed) ||\n\t\t!isBoolean(avatar.enabled) ||\n\t\t!isBoolean(tools.enabled)\n\t) {\n\t\treturn null;\n\t}\n\treturn {\n\t\tspeech: {\n\t\t\tinput: speech.input,\n\t\t\toutput: speech.output,\n\t\t\texternalAudio: speech.externalAudio\n\t\t},\n\t\tconversation: { managed: conversation.managed },\n\t\tavatar: { enabled: avatar.enabled },\n\t\ttools: { enabled: tools.enabled }\n\t};\n}\n\n/** @deprecated Flat capabilities from pre-v1 mint responses. */\nfunction parseLegacyFlatCapabilities(raw: unknown): SessionCapabilities | null {\n\tif (!isRecord(raw)) return null;\n\tif (\n\t\t!isBoolean(raw.avatar) ||\n\t\t!isBoolean(raw.speechInput) ||\n\t\t!isBoolean(raw.speechOutput) ||\n\t\t!isBoolean(raw.tools) ||\n\t\t!isBoolean(raw.externalAudio)\n\t) {\n\t\treturn null;\n\t}\n\treturn {\n\t\tspeech: {\n\t\t\tinput: raw.speechInput,\n\t\t\toutput: raw.speechOutput,\n\t\t\texternalAudio: raw.externalAudio\n\t\t},\n\t\tconversation: { managed: true },\n\t\tavatar: { enabled: raw.avatar },\n\t\ttools: { enabled: raw.tools }\n\t};\n}\n\nfunction parseCapabilities(raw: unknown): SessionCapabilities | null {\n\treturn parseNestedCapabilities(raw) ?? parseLegacyFlatCapabilities(raw);\n}\n\nexport function parseSession(raw: unknown): Session {\n\tif (!isRecord(raw)) {\n\t\tthrow new Error('Session Launch response is missing session.');\n\t}\n\tconst id = typeof raw.id === 'string' ? raw.id.trim() : '';\n\tconst experienceId = typeof raw.experienceId === 'string' ? raw.experienceId.trim() : '';\n\tconst experienceRevisionId =\n\t\ttypeof raw.experienceRevisionId === 'string' ? raw.experienceRevisionId.trim() : '';\n\tconst expiresAt = typeof raw.expiresAt === 'string' ? raw.expiresAt.trim() : '';\n\tconst locale = typeof raw.locale === 'string' ? raw.locale.trim() : '';\n\tconst secondaryLocale =\n\t\ttypeof raw.secondaryLocale === 'string' && raw.secondaryLocale.trim()\n\t\t\t? raw.secondaryLocale.trim()\n\t\t\t: undefined;\n\tconst mode = raw.mode === 'conversation' || raw.mode === 'presenter' ? raw.mode : null;\n\tconst capabilities = parseCapabilities(raw.capabilities);\n\tconst localeVoiceFallbacks = parseLocaleVoiceFallbacks(raw.localeVoiceFallbacks);\n\tconst status =\n\t\traw.status === 'active' || raw.status === 'ended' ? raw.status : undefined;\n\tconst createdAt =\n\t\ttypeof raw.createdAt === 'string' && raw.createdAt.trim() ? raw.createdAt.trim() : undefined;\n\n\tif (!id || !experienceId || !experienceRevisionId || !expiresAt || !locale || !mode || !capabilities) {\n\t\tthrow new Error('Session Launch response has an invalid session object.');\n\t}\n\n\treturn {\n\t\tid,\n\t\texperienceId,\n\t\texperienceRevisionId,\n\t\texpiresAt,\n\t\tlocale,\n\t\t...(secondaryLocale ? { secondaryLocale } : {}),\n\t\tmode,\n\t\tcapabilities,\n\t\t...(localeVoiceFallbacks ? { localeVoiceFallbacks } : {}),\n\t\t...(status ? { status } : {}),\n\t\t...(createdAt ? { createdAt } : {})\n\t};\n}\n\nfunction parseLocaleVoiceFallbacks(raw: unknown): readonly LocaleVoiceFallback[] | undefined {\n\tif (!Array.isArray(raw) || raw.length === 0) return undefined;\n\tconst out: LocaleVoiceFallback[] = [];\n\tfor (const item of raw) {\n\t\tif (!isRecord(item)) continue;\n\t\tconst axis = item.axis === 'locale' || item.axis === 'secondaryLocale' ? item.axis : null;\n\t\tconst requested = typeof item.requested === 'string' ? item.requested.trim() : '';\n\t\tconst resolved = typeof item.resolved === 'string' ? item.resolved.trim() : '';\n\t\tif (!axis || !requested || !resolved || item.reason !== 'voice_unsupported') continue;\n\t\tout.push({ axis, requested, resolved, reason: 'voice_unsupported' });\n\t}\n\treturn out.length > 0 ? out : undefined;\n}\n\n/** Browser console warnings when mint fell back to English for voice support. */\nexport function warnLocaleVoiceFallbacks(session: Session): void {\n\tconst fallbacks = session.localeVoiceFallbacks;\n\tif (!fallbacks?.length) return;\n\tfor (const fb of fallbacks) {\n\t\tconst label = fb.axis === 'secondaryLocale' ? 'secondaryLocale' : 'locale';\n\t\tconsole.warn(\n\t\t\t`[liforma] Session ${label} fell back to ${fb.resolved} because the voice does not support ${fb.requested}.`\n\t\t);\n\t}\n}\n\nexport function parseSessionLaunchResponse(raw: unknown): SessionLaunchResponse {\n\tif (!isRecord(raw)) {\n\t\tthrow new Error('Session Launch response must be a JSON object.');\n\t}\n\tconst launch = typeof raw.launch === 'string' ? raw.launch.trim() : '';\n\tif (!launch) {\n\t\tthrow new Error('Session Launch response is missing launch.');\n\t}\n\tconst session = parseSession(raw.session);\n\twarnLocaleVoiceFallbacks(session);\n\treturn {\n\t\tsession,\n\t\tlaunch\n\t};\n}\n","import { resolvePublicApiBaseUrl } from '../../shims/apiBaseUrl';\nimport { LiformaError, liformaErrorFromResponse } from '../errors/LiformaError';\nimport { createSessionEndpointRequest } from './sessionEndpointRequest';\nimport {\n\tparseSessionLaunchResponse,\n\ttype SessionLaunchResponse\n} from '../types/sessionLaunch';\n\nexport type MintBrowserSessionOptions = {\n\t/** BCP 47 locale (preferred). When omitted in the browser, defaults to `navigator.language`. */\n\treadonly locale?: string;\n\t/** Paired / learning language for dual experiences (BCP 47). */\n\treadonly secondaryLocale?: string;\n\t/** Alias of `secondaryLocale` — language-learning apps (tutors + roleplay). */\n\treadonly learningLocale?: string;\n\t/** @deprecated Prefer `locale`. */\n\treadonly language?: 'en' | 'es' | string;\n\treadonly mode?: 'conversation' | 'presenter';\n\treadonly speechInputMode?: 'auto' | 'manual' | 'off';\n\treadonly speechOnly?: boolean;\n\t/** Optional Idempotency-Key header value (max 256 chars). */\n\treadonly idempotencyKey?: string;\n};\n\nfunction currentPageOrigin(): string | undefined {\n\ttry {\n\t\tif (typeof globalThis.location?.origin === 'string' && globalThis.location.origin) {\n\t\t\treturn globalThis.location.origin;\n\t\t}\n\t} catch {\n\t\t/* ignore */\n\t}\n\treturn undefined;\n}\n\nfunction normalizeMintErrorCode(code: string | undefined): string {\n\tif (!code) return '';\n\treturn code.trim().toUpperCase().replace(/-/g, '_');\n}\n\nfunction enrichPublicMintError(experienceId: string, err: LiformaError): LiformaError {\n\tconst pageOrigin = currentPageOrigin();\n\tconst code = normalizeMintErrorCode(err.code);\n\tconst apiMessage = err.message.trim();\n\tconst details = err.details ?? {};\n\n\tif (\n\t\tcode === 'ORIGIN_NOT_ALLOWED' ||\n\t\t(/^Origin not allowed/i.test(apiMessage) && pageOrigin)\n\t) {\n\t\tif (apiMessage.includes(experienceId) && apiMessage.includes('allowed origin')) {\n\t\t\treturn err;\n\t\t}\n\t\tconst origin =\n\t\t\t(typeof details.origin === 'string' && details.origin) ||\n\t\t\tpageOrigin ||\n\t\t\t'(unknown origin)';\n\t\treturn new LiformaError(\n\t\t\t`Your origin ${origin} is not an allowed origin for experience ${experienceId}. ` +\n\t\t\t\t`If you own this experience, sign in to the Liforma developer portal (app.liforma.ai), ` +\n\t\t\t\t`open Workspace → Settings → Origins, add ${origin} to the list of allowed origins, ` +\n\t\t\t\t`and ensure “Allow public client-only embeds” is enabled.`,\n\t\t\t{ code: 'ORIGIN_NOT_ALLOWED', requestId: err.requestId, status: err.status, details }\n\t\t);\n\t}\n\n\tif (code === 'BROWSER_EMBEDS_DISABLED' || /^Browser embeds are not enabled/i.test(apiMessage)) {\n\t\tif (apiMessage.includes('Allow public client-only embeds')) {\n\t\t\treturn err;\n\t\t}\n\t\treturn new LiformaError(\n\t\t\t`Browser embeds are not enabled for experience ${experienceId}. ` +\n\t\t\t\t`If you own this experience, sign in to the Liforma developer portal (app.liforma.ai), ` +\n\t\t\t\t`open Workspace → Settings → Origins, and enable “Allow public client-only embeds”.`,\n\t\t\t{\n\t\t\t\tcode: 'BROWSER_EMBEDS_DISABLED',\n\t\t\t\trequestId: err.requestId,\n\t\t\t\tstatus: err.status,\n\t\t\t\tdetails\n\t\t\t}\n\t\t);\n\t}\n\n\tif (\n\t\tcode === 'EXPERIENCE_UNAVAILABLE' ||\n\t\t/^Experience is not available\\.?$/i.test(apiMessage) ||\n\t\t/^Unknown experience\\.?$/i.test(apiMessage)\n\t) {\n\t\tif (apiMessage.includes('No playable experience found')) {\n\t\t\treturn err;\n\t\t}\n\t\treturn new LiformaError(\n\t\t\t`No playable experience found for “${experienceId}”. ` +\n\t\t\t\t`Check that the experience id is correct (it should look like exp_…), ` +\n\t\t\t\t`and that the experience exists and is published in the Liforma developer portal (app.liforma.ai). ` +\n\t\t\t\t`Deleted, archived, and unpublished experiences cannot be started from a browser embed.`,\n\t\t\t{\n\t\t\t\tcode: 'EXPERIENCE_UNAVAILABLE',\n\t\t\t\trequestId: err.requestId,\n\t\t\t\tstatus: err.status,\n\t\t\t\tdetails\n\t\t\t}\n\t\t);\n\t}\n\n\treturn err;\n}\n\nfunction resolveLocale(options?: MintBrowserSessionOptions): string | undefined {\n\tconst locale = options?.locale?.trim();\n\tif (locale) return locale;\n\tconst language = options?.language?.trim();\n\tif (language) return language;\n\tif (typeof navigator !== 'undefined' && navigator.language?.trim()) {\n\t\treturn navigator.language.trim();\n\t}\n\treturn undefined;\n}\n\nfunction resolveSecondaryLocale(options?: MintBrowserSessionOptions): string | undefined {\n\tconst secondary = options?.secondaryLocale?.trim();\n\tconst learning = options?.learningLocale?.trim();\n\tif (secondary && learning && secondary !== learning) {\n\t\tthrow new LiformaError(\n\t\t\t'secondaryLocale and learningLocale disagree; pass only one or make them equal.',\n\t\t\t{ code: 'INVALID_BODY' }\n\t\t);\n\t}\n\treturn secondary || learning || undefined;\n}\n\nfunction buildMintBody(\n\texperienceId: string,\n\toptions?: MintBrowserSessionOptions\n): Record<string, unknown> {\n\tconst body: Record<string, unknown> = { experienceId };\n\tconst locale = resolveLocale(options);\n\tif (locale) body.locale = locale;\n\tconst secondaryLocale = resolveSecondaryLocale(options);\n\tif (secondaryLocale) body.secondaryLocale = secondaryLocale;\n\tif (options?.mode) body.mode = options.mode;\n\tif (options?.speechInputMode) body.speechInputMode = options.speechInputMode;\n\tif (options?.speechOnly !== undefined) body.speechOnly = options.speechOnly;\n\treturn body;\n}\n\n/** Mint a browser session via `POST /v1/browser-sessions`. */\nexport async function mintBrowserSession(\n\texperienceId: string,\n\toptions?: MintBrowserSessionOptions\n): Promise<SessionLaunchResponse> {\n\tconst apiBaseUrl = resolvePublicApiBaseUrl();\n\tconst idempotencyKey = options?.idempotencyKey?.trim();\n\tconst res = await fetch(`${apiBaseUrl}/v1/browser-sessions`, {\n\t\tmethod: 'POST',\n\t\theaders: {\n\t\t\t'Content-Type': 'application/json',\n\t\t\t...(idempotencyKey ? { 'Idempotency-Key': idempotencyKey } : {})\n\t\t},\n\t\tbody: JSON.stringify(buildMintBody(experienceId, options))\n\t});\n\n\tif (!res.ok) {\n\t\tconst text = await res.text();\n\t\tthrow enrichPublicMintError(experienceId, liformaErrorFromResponse(res.status, text, res.headers));\n\t}\n\tlet raw: unknown;\n\ttry {\n\t\traw = await res.json();\n\t} catch {\n\t\tthrow new LiformaError('Browser session mint returned invalid JSON.', {\n\t\t\tcode: 'INVALID_RESPONSE',\n\t\t\tstatus: res.status,\n\t\t\trequestId: res.headers.get('Liforma-Request-Id') ?? undefined\n\t\t});\n\t}\n\n\treturn parseSessionLaunchResponse(raw);\n}\n\n/** @deprecated Prefer {@link mintBrowserSession}. */\nexport const mintPublicSession = mintBrowserSession;\n\nexport type FetchSessionLaunchOptions = MintBrowserSessionOptions & {\n\treadonly experienceId: string;\n\treadonly signal?: AbortSignal;\n\treadonly timeoutMs?: number;\n};\n\n/** @deprecated Prefer {@link FetchSessionLaunchOptions}. */\nexport type FetchSessionManifestOptions = FetchSessionLaunchOptions;\n\nexport async function fetchSessionLaunch(\n\tsessionEndpoint: string,\n\toptions: FetchSessionLaunchOptions\n): Promise<SessionLaunchResponse> {\n\tconst request = createSessionEndpointRequest(sessionEndpoint, options);\n\n\tconst controller = new AbortController();\n\tconst abortFromCaller = () => controller.abort(options.signal?.reason);\n\tif (options.signal?.aborted) {\n\t\tabortFromCaller();\n\t} else {\n\t\toptions.signal?.addEventListener('abort', abortFromCaller, { once: true });\n\t}\n\tconst timeoutId = globalThis.setTimeout(\n\t\t() => controller.abort(new DOMException('Session endpoint timed out.', 'TimeoutError')),\n\t\toptions.timeoutMs ?? 30_000\n\t);\n\n\tlet res: Response;\n\ttry {\n\t\tres = await fetch(request.url, {\n\t\t\t...request.init,\n\t\t\tsignal: controller.signal\n\t\t});\n\t} finally {\n\t\tglobalThis.clearTimeout(timeoutId);\n\t\toptions.signal?.removeEventListener('abort', abortFromCaller);\n\t}\n\n\tif (!res.ok) {\n\t\tconst text = await res.text();\n\t\tthrow liformaErrorFromResponse(res.status, text, res.headers);\n\t}\n\tif (!res.headers.get('content-type')?.toLowerCase().includes('application/json')) {\n\t\tthrow new LiformaError('Session endpoint must return a JSON Session Launch response.', {\n\t\t\tcode: 'INVALID_RESPONSE',\n\t\t\tstatus: res.status,\n\t\t\trequestId: res.headers.get('Liforma-Request-Id') ?? undefined\n\t\t});\n\t}\n\n\tlet raw: unknown;\n\ttry {\n\t\traw = await res.json();\n\t} catch {\n\t\tthrow new LiformaError('Session endpoint returned invalid JSON.', {\n\t\t\tcode: 'INVALID_RESPONSE',\n\t\t\tstatus: res.status,\n\t\t\trequestId: res.headers.get('Liforma-Request-Id') ?? undefined\n\t\t});\n\t}\n\n\treturn parseSessionLaunchResponse(raw);\n}\n\n/** @deprecated Prefer {@link fetchSessionLaunch}. */\nexport const fetchSessionManifest = fetchSessionLaunch;\n","import type {\n\tConversationProcessorContext,\n\tConversationProcessorFn,\n\tConversationProcessorResult,\n\tNormalizedProcessorSpeech,\n\tProcessorDispatchResult,\n\tSpeakBehavior\n} from './types/sessionInteraction';\n\nexport function isAsyncIterableString(value: unknown): value is AsyncIterable<string> {\n\tif (value == null || typeof value !== 'object') return false;\n\treturn typeof (value as AsyncIterable<string>)[Symbol.asyncIterator] === 'function';\n}\n\nexport function normalizeConversationProcessorResult(\n\tresult: ConversationProcessorResult\n): NormalizedProcessorSpeech {\n\tif (typeof result === 'string') {\n\t\tconst text = result.trim();\n\t\tif (!text) {\n\t\t\tthrow new Error('conversationProcessor returned empty text.');\n\t\t}\n\t\treturn { text };\n\t}\n\n\tconst text = result.text.trim();\n\tif (!text) {\n\t\tthrow new Error('conversationProcessor returned empty text.');\n\t}\n\n\treturn {\n\t\ttext,\n\t\t...(result.characterId ? { characterId: result.characterId } : {}),\n\t\t...(result.behavior ? { behavior: result.behavior } : {})\n\t};\n}\n\nexport async function runConversationProcessor(\n\tprocessor: ConversationProcessorFn,\n\tcontext: ConversationProcessorContext\n): Promise<NormalizedProcessorSpeech> {\n\tconst dispatch = await dispatchConversationProcessor(processor, context);\n\tif (dispatch.kind !== 'complete') {\n\t\tthrow new Error('conversationProcessor returned a stream; use dispatchConversationProcessor.');\n\t}\n\treturn dispatch.speech;\n}\n\nexport async function dispatchConversationProcessor(\n\tprocessor: ConversationProcessorFn,\n\tcontext: ConversationProcessorContext\n): Promise<ProcessorDispatchResult> {\n\tconst result = await processor(context);\n\tif (isAsyncIterableString(result)) {\n\t\treturn { kind: 'stream', stream: result };\n\t}\n\treturn {\n\t\tkind: 'complete',\n\t\tspeech: normalizeConversationProcessorResult(result)\n\t};\n}\n","const LOCAL_PLAYER_DEV_PORT = '3002';\n\nexport function formatPlayerUnreachableMessage(playerOrigin: string): string {\n\tconst url = new URL(playerOrigin);\n\tconst isLocalHost = url.hostname === 'localhost' || url.hostname === '127.0.0.1';\n\n\tif (isLocalHost) {\n\t\tconst devPort = url.port || LOCAL_PLAYER_DEV_PORT;\n\t\treturn (\n\t\t\t`Cannot connect to the Liforma player at ${playerOrigin} — nothing is listening on port ${devPort}. ` +\n\t\t\t`Start the player dev server in player.liforma.ai (npm run dev).`\n\t\t);\n\t}\n\n\treturn (\n\t\t`Cannot connect to the Liforma player at ${playerOrigin}. ` +\n\t\t`The embed did not load; check your network connection and player URL configuration.`\n\t);\n}\n\nexport async function assertPlayerReachable(\n\tembedUrl: string,\n\tplayerOrigin: string\n): Promise<void> {\n\ttry {\n\t\tawait fetch(embedUrl, {\n\t\t\tmethod: 'GET',\n\t\t\tmode: 'no-cors',\n\t\t\tsignal: AbortSignal.timeout(8_000)\n\t\t});\n\t} catch {\n\t\tthrow new Error(formatPlayerUnreachableMessage(playerOrigin));\n\t}\n}\n\nexport function postMessageToPlayer(\n\ttarget: Window,\n\tmessage: unknown,\n\tplayerOrigin: string,\n\ttransfer?: Transferable[]\n): void {\n\ttry {\n\t\tif (transfer && transfer.length > 0) {\n\t\t\ttarget.postMessage(message, playerOrigin, transfer);\n\t\t} else {\n\t\t\ttarget.postMessage(message, playerOrigin);\n\t\t}\n\t} catch (error) {\n\t\tif (error instanceof DOMException && error.name === 'SecurityError') {\n\t\t\tthrow new Error(formatPlayerUnreachableMessage(playerOrigin), { cause: error });\n\t\t}\n\t\tthrow error;\n\t}\n}\n","import type {\n\tCharacterSpeechEvent,\n\tConversationMessage,\n\tSpeakBehavior,\n\tSessionModeChange,\n\tUserSpeechBoundaryEvent,\n\tUserTranscriptUpdate,\n\tUtteranceResult\n} from '../types/sessionInteraction';\nimport type { Session, SessionActivity } from '../types/sessionLaunch';\nimport type { StartButtonOptions } from '../types/sessionInteraction';\nimport type {\n\tCompleteSpeechQueuePolicy,\n\tSpeechAudioFormat,\n\tSpeechInterruptOptions,\n\tSpeechPlayEmbedSource,\n\tStreamingSpeechQueuePolicy\n} from '../speech/types';\n\nexport const EMBED_MESSAGE_SCHEMA_VERSION = 2 as const;\n\n/** Outbound postMessage versions accepted from the player embed. */\nexport const SUPPORTED_EMBED_MESSAGE_SCHEMA_VERSIONS: ReadonlySet<number> = new Set([1, 2]);\n\nexport function isSupportedEmbedMessageSchemaVersion(version: unknown): boolean {\n\treturn typeof version === 'number' && SUPPORTED_EMBED_MESSAGE_SCHEMA_VERSIONS.has(version);\n}\n\nexport type CloseEvent = {\n\treadonly sessionId?: string;\n\treadonly reason: 'user' | 'destroy';\n\treadonly returnUrl?: string;\n};\n\nexport type EmbedOutboundMessage =\n\t| {\n\t\t\treadonly type: 'close';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: CloseEvent;\n\t }\n\t| {\n\t\t\treadonly type: 'stateUpdate';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: { readonly state: 'loading' | 'scene' | 'ready' | 'error' };\n\t }\n\t| {\n\t\t\treadonly type: 'commandResult';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: {\n\t\t\t\treadonly correlationId: string;\n\t\t\t\treadonly ok: boolean;\n\t\t\t\treadonly error?: string;\n\t\t\t\treadonly result?: unknown;\n\t\t\t};\n\t }\n\t| {\n\t\t\treadonly type: 'userTranscript';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: UserTranscriptUpdate;\n\t }\n\t| {\n\t\t\treadonly type: 'characterSpeechStarted';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: CharacterSpeechEvent;\n\t }\n\t| {\n\t\t\treadonly type: 'characterSpeechEnded';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: CharacterSpeechEvent;\n\t }\n\t| {\n\t\t\treadonly type: 'characterFocusChanged';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: {\n\t\t\t\treadonly characterId: string;\n\t\t\t\treadonly reason: 'speech' | 'api' | 'node_enter';\n\t\t\t};\n\t }\n\t| {\n\t\t\treadonly type: 'conversationUpdate';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: { readonly conversation: readonly ConversationMessage[] };\n\t }\n\t| {\n\t\t\treadonly type: 'listeningState';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: { readonly listening: boolean };\n\t }\n\t| {\n\t\t\treadonly type: 'userSpeechStarted';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: UserSpeechBoundaryEvent;\n\t }\n\t| {\n\t\t\treadonly type: 'userSpeechEnded';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: UserSpeechBoundaryEvent;\n\t }\n\t| {\n\t\t\treadonly type: 'modeChange';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: { readonly mode: SessionModeChange };\n\t }\n\t| {\n\t\t\treadonly type: 'activityChange';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: { readonly activity: SessionActivity };\n\t }\n\t| {\n\t\t\treadonly type: 'feedback';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: {\n\t\t\t\treadonly contractVersion: 'feedback.v1';\n\t\t\t\treadonly types: readonly {\n\t\t\t\t\treadonly feedbackId: string;\n\t\t\t\t\treadonly name: string;\n\t\t\t\t\treadonly criteria: readonly {\n\t\t\t\t\t\treadonly criterionId: string;\n\t\t\t\t\t\treadonly label: string;\n\t\t\t\t\t\treadonly score: number | string;\n\t\t\t\t\t\treadonly rationale: string;\n\t\t\t\t\t}[];\n\t\t\t\t}[];\n\t\t\t};\n\t }\n\t| {\n\t\t\treadonly type: 'feedbackFailed';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: { readonly error: string; readonly model?: string };\n\t }\n\t| {\n\t\t\treadonly type: 'message';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: ConversationMessage;\n\t }\n\t| {\n\t\t\treadonly type: 'started';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: { readonly mode: 'conversation' | 'presenter' };\n\t }\n\t| {\n\t\t\t/**\n\t\t\t * Player document is ready to receive `launch` (iframe HMR / Vite reload).\n\t\t\t * Parent re-posts the last launch token so bootstrap does not hang.\n\t\t\t */\n\t\t\treadonly type: 'embedReady';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t }\n\t| {\n\t\t\treadonly type: 'widgetReady';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: Record<string, never>;\n\t }\n\t| {\n\t\t\treadonly type: 'widgetExpand';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: Record<string, never>;\n\t }\n\t| {\n\t\t\treadonly type: 'widgetCollapse';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: Record<string, never>;\n\t }\n\t| {\n\t\t\treadonly type: 'widgetAudioSettings';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: { readonly open: boolean };\n\t }\n\t| {\n\t\t\treadonly type: 'widgetDragStart';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: {\n\t\t\t\treadonly pointerId: number;\n\t\t\t};\n\t }\n\t| {\n\t\t\treadonly type: 'widgetDragMove';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: {\n\t\t\t\treadonly pointerId: number;\n\t\t\t\t/** Pointer deltas in CSS pixels (not iframe-local coordinates). */\n\t\t\t\treadonly dx: number;\n\t\t\t\treadonly dy: number;\n\t\t\t};\n\t }\n\t| {\n\t\t\treadonly type: 'widgetDragEnd';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: {\n\t\t\t\treadonly pointerId: number;\n\t\t\t};\n\t };\n\nexport type WidgetChromeMode = 'collapsed' | 'loading' | 'expanded' | 'docked';\n\nexport type WidgetDragStartEvent = {\n\treadonly pointerId: number;\n};\n\nexport type WidgetDragMoveEvent = {\n\treadonly pointerId: number;\n\treadonly dx: number;\n\treadonly dy: number;\n};\n\n/** @deprecated Prefer WidgetDragStartEvent / WidgetDragMoveEvent */\nexport type WidgetDragPointerEvent = WidgetDragMoveEvent;\n\nexport type EmbedInboundMessage =\n\t| {\n\t\t\treadonly type: 'launch';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: {\n\t\t\t\treadonly launch: string;\n\t\t\t\treadonly session?: Session | object;\n\t\t\t\t/** Player chrome from attach — not part of the signed launch string. */\n\t\t\t\treadonly startButton?: StartButtonOptions;\n\t\t\t\t/**\n\t\t\t\t * When false, player skips Feedback step-through UI (host renders scores).\n\t\t\t\t * Default when omitted: true (player shows Feedback).\n\t\t\t\t */\n\t\t\t\treadonly showFeedback?: boolean;\n\t\t\t};\n\t }\n\t| {\n\t\t\t/** @deprecated Prefer `launch`. Kept briefly for older players. */\n\t\t\treadonly type: 'manifest';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: { readonly manifest: unknown };\n\t }\n\t| {\n\t\t\treadonly type: 'parentHello';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t }\n\t| {\n\t\t\treadonly type: 'speak';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: {\n\t\t\t\treadonly correlationId: string;\n\t\t\t\treadonly text: string;\n\t\t\t\treadonly characterId?: string;\n\t\t\t\treadonly behavior?: SpeakBehavior;\n\t\t\t};\n\t }\n\t| {\n\t\t\treadonly type: 'startListening';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: { readonly correlationId: string };\n\t }\n\t| {\n\t\t\treadonly type: 'stopListening';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: { readonly correlationId: string };\n\t }\n\t| {\n\t\t\treadonly type: 'getConversation';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: { readonly correlationId: string };\n\t }\n\t| {\n\t\t\treadonly type: 'listenOnce';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: {\n\t\t\t\treadonly correlationId: string;\n\t\t\t\treadonly timeoutMs?: number;\n\t\t\t};\n\t }\n\t| {\n\t\t\treadonly type: 'speakStreamStart';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: {\n\t\t\t\treadonly correlationId: string;\n\t\t\t\treadonly characterId?: string;\n\t\t\t\treadonly behavior?: SpeakBehavior;\n\t\t\t};\n\t }\n\t| {\n\t\t\treadonly type: 'speakStreamChunk';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: {\n\t\t\t\treadonly streamId: string;\n\t\t\t\treadonly text: string;\n\t\t\t};\n\t }\n\t| {\n\t\t\treadonly type: 'speakStreamEnd';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: {\n\t\t\t\treadonly correlationId: string;\n\t\t\t\treadonly streamId: string;\n\t\t\t};\n\t }\n\t| {\n\t\t\treadonly type: 'widgetSetChrome';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: { readonly mode: WidgetChromeMode };\n\t }\n\t| {\n\t\t\treadonly type: 'widgetToggleAudioSettings';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: Record<string, never>;\n\t }\n\t| {\n\t\t\treadonly type: 'speechPlay';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: {\n\t\t\t\treadonly correlationId: string;\n\t\t\t\treadonly source: SpeechPlayEmbedSource;\n\t\t\t\treadonly characterId?: string;\n\t\t\t\treadonly transcript?: string;\n\t\t\t\treadonly history?: 'none' | 'commit';\n\t\t\t\treadonly queue?: CompleteSpeechQueuePolicy;\n\t\t\t};\n\t }\n\t| {\n\t\t\treadonly type: 'speechUtteranceBegin';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: {\n\t\t\t\treadonly correlationId: string;\n\t\t\t\treadonly utteranceId: string;\n\t\t\t\treadonly format: SpeechAudioFormat;\n\t\t\t\treadonly characterId?: string;\n\t\t\t\treadonly queue: StreamingSpeechQueuePolicy;\n\t\t\t\treadonly transcript?: string;\n\t\t\t};\n\t }\n\t| {\n\t\t\treadonly type: 'speechUtteranceWrite';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: {\n\t\t\t\treadonly correlationId: string;\n\t\t\t\treadonly utteranceId: string;\n\t\t\t\treadonly audio: ArrayBuffer;\n\t\t\t\treadonly byteLength: number;\n\t\t\t};\n\t }\n\t| {\n\t\t\treadonly type: 'speechUtteranceSetTranscript';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: {\n\t\t\t\treadonly correlationId: string;\n\t\t\t\treadonly utteranceId: string;\n\t\t\t\treadonly transcript: string;\n\t\t\t};\n\t }\n\t| {\n\t\t\treadonly type: 'speechUtteranceClose';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: {\n\t\t\t\treadonly correlationId: string;\n\t\t\t\treadonly utteranceId: string;\n\t\t\t\treadonly transcript?: string;\n\t\t\t\treadonly history?: 'none' | 'commit';\n\t\t\t};\n\t }\n\t| {\n\t\t\treadonly type: 'speechUtteranceCancel';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: {\n\t\t\t\treadonly correlationId: string;\n\t\t\t\treadonly utteranceId: string;\n\t\t\t};\n\t }\n\t| {\n\t\t\treadonly type: 'speechInterrupt';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: {\n\t\t\t\treadonly correlationId: string;\n\t\t\t} & SpeechInterruptOptions;\n\t }\n\t| {\n\t\t\treadonly type: 'focusCharacter';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: {\n\t\t\t\treadonly correlationId: string;\n\t\t\t\treadonly characterId: string;\n\t\t\t};\n\t }\n\t| {\n\t\t\treadonly type: 'setFit';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: {\n\t\t\t\treadonly correlationId: string;\n\t\t\t\treadonly fit: 'full' | 'face' | 'medium';\n\t\t\t};\n\t };\n\nexport type EmbedState = 'loading' | 'scene' | 'ready' | 'error';\n\nconst OUTBOUND_TYPES = new Set([\n\t'close',\n\t'stateUpdate',\n\t'commandResult',\n\t'userTranscript',\n\t'characterSpeechStarted',\n\t'characterSpeechEnded',\n\t'characterFocusChanged',\n\t'conversationUpdate',\n\t'listeningState',\n\t'userSpeechStarted',\n\t'userSpeechEnded',\n\t'modeChange',\n\t'activityChange',\n\t'feedback',\n\t'feedbackFailed',\n\t'message',\n\t'started',\n\t'embedReady',\n\t'widgetReady',\n\t'widgetExpand',\n\t'widgetCollapse',\n\t'widgetAudioSettings',\n\t'widgetDragStart',\n\t'widgetDragMove',\n\t'widgetDragEnd'\n]);\n\nfunction isWidgetChromeMode(value: unknown): value is WidgetChromeMode {\n\treturn (\n\t\tvalue === 'collapsed' ||\n\t\tvalue === 'loading' ||\n\t\tvalue === 'expanded' ||\n\t\tvalue === 'docked'\n\t);\n}\n\nexport function parseOutboundMessage(data: unknown): EmbedOutboundMessage | null {\n\tif (!data || typeof data !== 'object') return null;\n\tconst msg = data as Partial<EmbedOutboundMessage>;\n\tif (!isSupportedEmbedMessageSchemaVersion(msg.schemaVersion)) return null;\n\tif (!msg.type || !OUTBOUND_TYPES.has(msg.type)) return null;\n\tif (\n\t\tmsg.type === 'embedReady' ||\n\t\tmsg.type === 'widgetReady' ||\n\t\tmsg.type === 'widgetExpand' ||\n\t\tmsg.type === 'widgetCollapse'\n\t) {\n\t\treturn msg as EmbedOutboundMessage;\n\t}\n\tif (msg.type === 'widgetAudioSettings') {\n\t\tconst payload = (msg as { payload?: unknown }).payload;\n\t\tif (!payload || typeof payload !== 'object') return null;\n\t\tif (typeof (payload as { open?: unknown }).open !== 'boolean') return null;\n\t\treturn msg as EmbedOutboundMessage;\n\t}\n\tif (msg.type === 'widgetDragStart' || msg.type === 'widgetDragEnd') {\n\t\tconst payload = (msg as { payload?: unknown }).payload;\n\t\tif (!payload || typeof payload !== 'object') return null;\n\t\tif (typeof (payload as { pointerId?: unknown }).pointerId !== 'number') return null;\n\t\tif (!Number.isFinite((payload as { pointerId: number }).pointerId)) return null;\n\t\treturn msg as EmbedOutboundMessage;\n\t}\n\tif (msg.type === 'widgetDragMove') {\n\t\tconst payload = (msg as { payload?: unknown }).payload;\n\t\tif (!payload || typeof payload !== 'object') return null;\n\t\tconst { pointerId, dx, dy } = payload as {\n\t\t\tpointerId?: unknown;\n\t\t\tdx?: unknown;\n\t\t\tdy?: unknown;\n\t\t};\n\t\tif (typeof pointerId !== 'number' || !Number.isFinite(pointerId)) return null;\n\t\tif (typeof dx !== 'number' || !Number.isFinite(dx)) return null;\n\t\tif (typeof dy !== 'number' || !Number.isFinite(dy)) return null;\n\t\treturn msg as EmbedOutboundMessage;\n\t}\n\tconst payload = (msg as { payload?: unknown }).payload;\n\tif (!payload || typeof payload !== 'object') return null;\n\treturn msg as EmbedOutboundMessage;\n}\n\nconst INBOUND_TYPES = new Set([\n\t'launch',\n\t'manifest',\n\t'parentHello',\n\t'speak',\n\t'startListening',\n\t'stopListening',\n\t'getConversation',\n\t'listenOnce',\n\t'speakStreamStart',\n\t'speakStreamChunk',\n\t'speakStreamEnd',\n\t'widgetSetChrome',\n\t'widgetToggleAudioSettings',\n\t'speechPlay',\n\t'speechUtteranceBegin',\n\t'speechUtteranceWrite',\n\t'speechUtteranceSetTranscript',\n\t'speechUtteranceClose',\n\t'speechUtteranceCancel',\n\t'speechInterrupt',\n\t'focusCharacter',\n\t'setFit'\n]);\n\nexport function parseInboundMessage(data: unknown): EmbedInboundMessage | null {\n\tif (!data || typeof data !== 'object') return null;\n\tconst msg = data as Partial<EmbedInboundMessage>;\n\tif (!isSupportedEmbedMessageSchemaVersion(msg.schemaVersion)) return null;\n\tif (!msg.type || !INBOUND_TYPES.has(msg.type)) return null;\n\tif (msg.type === 'parentHello') return msg as EmbedInboundMessage;\n\tif (msg.type === 'widgetToggleAudioSettings') return msg as EmbedInboundMessage;\n\tif (msg.type === 'widgetSetChrome') {\n\t\tconst payload = (msg as { payload?: unknown }).payload;\n\t\tif (!payload || typeof payload !== 'object') return null;\n\t\tconst mode = (payload as { mode?: unknown }).mode;\n\t\tif (!isWidgetChromeMode(mode)) return null;\n\t\treturn msg as EmbedInboundMessage;\n\t}\n\tif (msg.type === 'setFit') {\n\t\tconst payload = (msg as { payload?: unknown }).payload;\n\t\tif (!payload || typeof payload !== 'object') return null;\n\t\tconst fit = (payload as { fit?: unknown }).fit;\n\t\tconst correlationId = (payload as { correlationId?: unknown }).correlationId;\n\t\tif (fit !== 'full' && fit !== 'medium' && fit !== 'face') return null;\n\t\tif (typeof correlationId !== 'string' || !correlationId.trim()) return null;\n\t\treturn msg as EmbedInboundMessage;\n\t}\n\tif (msg.type === 'speakStreamChunk') {\n\t\tconst payload = (msg as { payload?: unknown }).payload;\n\t\tif (!payload || typeof payload !== 'object') return null;\n\t\tconst streamId = (payload as { streamId?: unknown }).streamId;\n\t\tconst text = (payload as { text?: unknown }).text;\n\t\tif (typeof streamId !== 'string' || !streamId.trim()) return null;\n\t\tif (typeof text !== 'string') return null;\n\t\treturn msg as EmbedInboundMessage;\n\t}\n\tif (msg.type === 'launch') {\n\t\tconst payload = (msg as { payload?: unknown }).payload;\n\t\tif (!payload || typeof payload !== 'object') return null;\n\t\tconst launch = (payload as { launch?: unknown }).launch;\n\t\tif (typeof launch !== 'string' || !launch.trim()) return null;\n\t\treturn msg as EmbedInboundMessage;\n\t}\n\tconst payload = (msg as { payload?: unknown }).payload;\n\tif (!payload || typeof payload !== 'object') return null;\n\treturn msg as EmbedInboundMessage;\n}\n\nexport function createLaunchMessage(\n\tlaunch: string,\n\tsession?: Session | object,\n\tchrome?: {\n\t\treadonly startButton?: StartButtonOptions;\n\t\treadonly showFeedback?: boolean;\n\t}\n): EmbedInboundMessage {\n\tconst trimmed = launch.trim();\n\tif (!trimmed) {\n\t\tthrow new Error('createLaunchMessage requires a non-empty launch string.');\n\t}\n\tconst startButton = chrome?.startButton;\n\tconst showFeedback = chrome?.showFeedback;\n\treturn {\n\t\ttype: 'launch',\n\t\tschemaVersion: EMBED_MESSAGE_SCHEMA_VERSION,\n\t\tpayload: {\n\t\t\tlaunch: trimmed,\n\t\t\t...(session !== undefined ? { session } : {}),\n\t\t\t...(startButton ? { startButton } : {}),\n\t\t\t...(showFeedback !== undefined ? { showFeedback } : {})\n\t\t}\n\t};\n}\n\n/** @deprecated Prefer {@link createLaunchMessage}. */\nexport function createManifestMessage(manifest: unknown): EmbedInboundMessage {\n\treturn {\n\t\ttype: 'manifest',\n\t\tschemaVersion: EMBED_MESSAGE_SCHEMA_VERSION,\n\t\tpayload: { manifest }\n\t};\n}\n\nexport function createParentHelloMessage(): EmbedInboundMessage {\n\treturn {\n\t\ttype: 'parentHello',\n\t\tschemaVersion: EMBED_MESSAGE_SCHEMA_VERSION\n\t};\n}\n\nexport function createEmbedReadyMessage(): EmbedOutboundMessage {\n\treturn {\n\t\ttype: 'embedReady',\n\t\tschemaVersion: EMBED_MESSAGE_SCHEMA_VERSION\n\t};\n}\n\nexport function createWidgetSetChromeMessage(mode: WidgetChromeMode): EmbedInboundMessage {\n\treturn {\n\t\ttype: 'widgetSetChrome',\n\t\tschemaVersion: EMBED_MESSAGE_SCHEMA_VERSION,\n\t\tpayload: { mode }\n\t};\n}\n\nexport function createWidgetToggleAudioSettingsMessage(): EmbedInboundMessage {\n\treturn {\n\t\ttype: 'widgetToggleAudioSettings',\n\t\tschemaVersion: EMBED_MESSAGE_SCHEMA_VERSION,\n\t\tpayload: {}\n\t};\n}\n","import {\n\tassertPlayerReachable,\n\tformatPlayerUnreachableMessage,\n\tpostMessageToPlayer\n} from './playerReachability';\nimport type { CloseEvent, EmbedInboundMessage, EmbedOutboundMessage, EmbedState } from './protocol';\nimport {\n\tcreateLaunchMessage,\n\tcreateManifestMessage,\n\tcreateParentHelloMessage,\n\tcreateWidgetSetChromeMessage,\n\tcreateWidgetToggleAudioSettingsMessage,\n\tEMBED_MESSAGE_SCHEMA_VERSION,\n\tparseOutboundMessage,\n\ttype WidgetChromeMode,\n\ttype WidgetDragMoveEvent,\n\ttype WidgetDragStartEvent\n} from './protocol';\nimport type {\n\tCharacterSpeechEvent,\n\tConversationMessage,\n\tSpeakBehavior,\n\tSpeechResult as LegacySpeakResult,\n\tUserSpeechBoundaryEvent,\n\tUserTranscriptUpdate,\n\tUtteranceResult\n} from '../types/sessionInteraction';\nimport type { PlayerStatus, Session, SessionActivity } from '../types/sessionLaunch';\nimport type { StartButtonOptions } from '../types/sessionInteraction';\nimport type {\n\tCompleteSpeechQueuePolicy,\n\tSpeechAudioFormat,\n\tSpeechInterruptOptions,\n\tSpeechPlayEmbedSource,\n\tSpeechResult,\n\tStreamingSpeechQueuePolicy\n} from '../speech/types';\n\nexport type IframeHostEventMap = {\n\tclose: CloseEvent;\n\tstateUpdate: EmbedState;\n\tuserTranscript: UserTranscriptUpdate;\n\tcharacterSpeechStarted: CharacterSpeechEvent;\n\tcharacterSpeechEnded: CharacterSpeechEvent;\n\tconversationUpdate: readonly ConversationMessage[];\n\tlisteningState: boolean;\n\tstarted: { readonly mode: 'conversation' | 'presenter' };\n\twidgetReady: void;\n\twidgetExpand: void;\n\twidgetCollapse: void;\n\twidgetAudioSettings: boolean;\n\twidgetDragStart: WidgetDragStartEvent;\n\twidgetDragMove: WidgetDragMoveEvent;\n\twidgetDragEnd: { readonly pointerId: number };\n};\n\nexport type IframeHostOptions = {\n\treadonly container: HTMLElement;\n\treadonly src: string;\n\treadonly playerOrigin: string;\n\t/** Transparent iframe for ExperienceWidget warm overlay over a thumb. */\n\treadonly transparent?: boolean;\n\treadonly onClose?: (event: CloseEvent) => void;\n\t/** @deprecated Prefer {@link IframeHostOptions.onPlayerStatusChange}. */\n\treadonly onStateUpdate?: (state: EmbedState) => void;\n\treadonly onPlayerStatusChange?: (status: PlayerStatus) => void;\n\treadonly onUserTranscript?: (update: UserTranscriptUpdate) => void;\n\treadonly onUserSpeechStarted?: (event: UserSpeechBoundaryEvent) => void;\n\treadonly onUserSpeechEnded?: (event: UserSpeechBoundaryEvent) => void;\n\treadonly onCharacterSpeechStarted?: (event: CharacterSpeechEvent) => void;\n\treadonly onCharacterSpeechEnded?: (event: CharacterSpeechEvent) => void;\n\treadonly onCharacterFocusChanged?: (event: {\n\t\treadonly characterId: string;\n\t\treadonly reason: 'speech' | 'api' | 'node_enter';\n\t}) => void;\n\treadonly onConversationUpdate?: (conversation: readonly ConversationMessage[]) => void;\n\treadonly onMessage?: (message: ConversationMessage) => void;\n\treadonly onListeningState?: (listening: boolean) => void;\n\treadonly onActivityChange?: (activity: SessionActivity) => void;\n\treadonly onFeedback?: (result: {\n\t\treadonly contractVersion: 'feedback.v1';\n\t\treadonly types: readonly {\n\t\t\treadonly feedbackId: string;\n\t\t\treadonly name: string;\n\t\t\treadonly criteria: readonly {\n\t\t\t\treadonly criterionId: string;\n\t\t\t\treadonly label: string;\n\t\t\t\treadonly score: number | string;\n\t\t\t\treadonly rationale: string;\n\t\t\t}[];\n\t\t}[];\n\t}) => void;\n\treadonly onFeedbackFailed?: (event: {\n\t\treadonly error: string;\n\t\treadonly model?: string;\n\t}) => void;\n\treadonly onStarted?: (event: { readonly mode: 'conversation' | 'presenter' }) => void;\n\treadonly onWidgetReady?: () => void;\n\treadonly onWidgetExpand?: () => void;\n\treadonly onWidgetCollapse?: () => void;\n\treadonly onWidgetAudioSettings?: (open: boolean) => void;\n\treadonly onWidgetDragStart?: (event: WidgetDragStartEvent) => void;\n\treadonly onWidgetDragMove?: (event: WidgetDragMoveEvent) => void;\n\treadonly onWidgetDragEnd?: (event: { readonly pointerId: number }) => void;\n};\n\nfunction legacyModeToActivity(mode: 'speaking' | 'listening' | 'thinking'): SessionActivity {\n\treturn mode;\n}\n\nexport type IframeHost = {\n\twaitForLoad(): Promise<void>;\n\tpostParentHello(): void;\n\tpostLaunch(\n\t\tlaunch: string,\n\t\tsession?: Session | object,\n\t\tchrome?: {\n\t\t\treadonly startButton?: StartButtonOptions;\n\t\t\treadonly showFeedback?: boolean;\n\t\t}\n\t): void;\n\t/** @deprecated Prefer {@link IframeHost.postLaunch}. */\n\tpostManifest(manifest: unknown): void;\n\tpostWidgetSetChrome(mode: WidgetChromeMode): void;\n\tpostWidgetToggleAudioSettings(): void;\n\tspeak(options: {\n\t\ttext: string;\n\t\tcharacterId?: string;\n\t\tbehavior?: SpeakBehavior;\n\t}): Promise<LegacySpeakResult>;\n\tstartListening(): Promise<void>;\n\tstopListening(): Promise<UtteranceResult>;\n\tlistenOnce(options?: { readonly timeoutMs?: number }): Promise<UtteranceResult>;\n\tspeakProcessorStream(\n\t\tstream: AsyncIterable<string>,\n\t\toptions?: {\n\t\t\treadonly characterId?: string;\n\t\t\treadonly behavior?: SpeakBehavior;\n\t\t\treadonly signal?: AbortSignal;\n\t\t}\n\t): Promise<LegacySpeakResult>;\n\tspeechPlay(options: {\n\t\tsource: SpeechPlayEmbedSource;\n\t\tcharacterId?: string;\n\t\ttranscript?: string;\n\t\thistory?: 'none' | 'commit';\n\t\tqueue?: CompleteSpeechQueuePolicy;\n\t\tsignal?: AbortSignal;\n\t}): Promise<SpeechResult>;\n\tspeechUtteranceBegin(options: {\n\t\tutteranceId: string;\n\t\tformat: SpeechAudioFormat;\n\t\tcharacterId?: string;\n\t\tqueue: StreamingSpeechQueuePolicy;\n\t\ttranscript?: string;\n\t}): Promise<void>;\n\tspeechUtteranceWrite(options: {\n\t\tutteranceId: string;\n\t\taudio: ArrayBuffer;\n\t\tbyteLength: number;\n\t}): Promise<{ readonly bufferedMs: number }>;\n\tspeechUtteranceSetTranscript(options: {\n\t\tutteranceId: string;\n\t\ttranscript: string;\n\t}): Promise<void>;\n\tspeechUtteranceClose(options: {\n\t\tutteranceId: string;\n\t\ttranscript?: string;\n\t\thistory?: 'none' | 'commit';\n\t}): Promise<SpeechResult>;\n\tspeechUtteranceCancel(options: { utteranceId: string }): Promise<SpeechResult>;\n\tspeechInterrupt(options: SpeechInterruptOptions): Promise<readonly SpeechResult[]>;\n\tgetConversation(): Promise<readonly ConversationMessage[]>;\n\tfocusCharacter(characterId: string): Promise<void>;\n\tsetFit(fit: 'full' | 'face' | 'medium'): Promise<void>;\n\tdestroy(): void;\n};\n\nfunction newCorrelationId(): string {\n\tif (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n\t\treturn `cmd_${crypto.randomUUID().replace(/-/g, '').slice(0, 12)}`;\n\t}\n\treturn `cmd_${Date.now().toString(36)}${Math.random().toString(36).slice(2, 8)}`;\n}\n\nconst SPEECH_WRITE_COMMAND_TIMEOUT_MS = 30_000;\n\nexport function createIframeHost(options: IframeHostOptions): IframeHost {\n\tconst container = options.container;\n\tconst containerPosition = getComputedStyle(container).position;\n\tif (containerPosition === 'static') {\n\t\tcontainer.style.position = 'relative';\n\t}\n\n\tconst iframe = document.createElement('iframe');\n\tiframe.src = options.src;\n\tiframe.title = 'Liforma experience';\n\tiframe.setAttribute(\n\t\t'allow',\n\t\t'microphone; autoplay; clipboard-read; clipboard-write'\n\t);\n\t// Absolute fill so the player viewport matches the host box even when the host\n\t// height comes from flex or min-height (percentage height:100% often collapses).\n\tiframe.style.cssText = options.transparent\n\t\t? 'display:block;position:absolute;inset:0;width:100%;height:100%;border:0;background:transparent;'\n\t\t: 'display:block;position:absolute;inset:0;width:100%;height:100%;border:0;background:#030508;';\n\tif (options.transparent) {\n\t\tiframe.setAttribute('allowtransparency', 'true');\n\t}\n\n\tlet destroyed = false;\n\tconst pendingCommands = new Map<\n\t\tstring,\n\t\t{\n\t\t\tresolve: (value: unknown) => void;\n\t\t\treject: (error: Error) => void;\n\t\t}\n\t>();\n\n\ttype StoredLaunch = {\n\t\treadonly launch: string;\n\t\treadonly session?: Session | object;\n\t\treadonly chrome?: {\n\t\t\treadonly startButton?: StartButtonOptions;\n\t\t\treadonly showFeedback?: boolean;\n\t\t};\n\t};\n\n\tlet storedLaunch: StoredLaunch | null = null;\n\tlet loadGeneration = 0;\n\tlet lastLaunchBurstAt = 0;\n\tconst LAUNCH_BURST_COOLDOWN_MS = 250;\n\tconst launchBurstTimers: number[] = [];\n\n\tlet firstLoadResolve: (() => void) | null = null;\n\tlet firstLoadReject: ((error: Error) => void) | null = null;\n\tconst loadPromise = new Promise<void>((resolve, reject) => {\n\t\tfirstLoadResolve = resolve;\n\t\tfirstLoadReject = reject;\n\t});\n\n\tconst clearLaunchBurstTimers = () => {\n\t\tfor (const id of launchBurstTimers) window.clearTimeout(id);\n\t\tlaunchBurstTimers.length = 0;\n\t};\n\n\tconst postLaunchBurst = (stored: StoredLaunch): void => {\n\t\tif (destroyed) return;\n\t\tconst now = Date.now();\n\t\tif (lastLaunchBurstAt > 0 && now - lastLaunchBurstAt < LAUNCH_BURST_COOLDOWN_MS) {\n\t\t\treturn;\n\t\t}\n\t\tlastLaunchBurstAt = now;\n\t\tclearLaunchBurstTimers();\n\t\tconst message = createLaunchMessage(stored.launch, stored.session, stored.chrome);\n\t\tconst postOnce = () => {\n\t\t\tif (destroyed) return;\n\t\t\tconst target = iframe.contentWindow;\n\t\t\tif (!target) return;\n\t\t\tpostMessageToPlayer(target, message, options.playerOrigin);\n\t\t};\n\t\tpostOnce();\n\t\tfor (let attempt = 1; attempt <= 12; attempt += 1) {\n\t\t\tlaunchBurstTimers.push(window.setTimeout(postOnce, attempt * 75));\n\t\t}\n\t};\n\n\tconst replayStoredLaunch = (): void => {\n\t\tif (destroyed || !storedLaunch) return;\n\t\tpostLaunchBurst(storedLaunch);\n\t};\n\n\tconst onIframeLoad = () => {\n\t\tloadGeneration += 1;\n\t\tif (loadGeneration === 1) {\n\t\t\tvoid assertPlayerReachable(options.src, options.playerOrigin).then(\n\t\t\t\t() => firstLoadResolve?.(),\n\t\t\t\t(error) =>\n\t\t\t\t\tfirstLoadReject?.(error instanceof Error ? error : new Error(String(error)))\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\t// Vite / HMR reloads the player document in-place. The first-load handshake\n\t\t// already completed, so re-post launch to the new contentWindow.\n\t\tlastLaunchBurstAt = 0;\n\t\treplayStoredLaunch();\n\t};\n\tconst onIframeError = () => {\n\t\tif (loadGeneration > 0) return;\n\t\tfirstLoadReject?.(new Error(formatPlayerUnreachableMessage(options.playerOrigin)));\n\t};\n\tiframe.addEventListener('load', onIframeLoad);\n\tiframe.addEventListener('error', onIframeError);\n\n\tconst onMessage = (event: MessageEvent) => {\n\t\tif (destroyed) return;\n\t\tif (event.source !== iframe.contentWindow) return;\n\t\tif (event.origin !== options.playerOrigin) return;\n\n\t\tconst message = parseOutboundMessage(event.data);\n\t\tif (!message) return;\n\n\t\tswitch (message.type) {\n\t\t\tcase 'close':\n\t\t\t\toptions.onClose?.(message.payload);\n\t\t\t\tbreak;\n\t\t\tcase 'stateUpdate':\n\t\t\t\toptions.onPlayerStatusChange?.(message.payload.state);\n\t\t\t\toptions.onStateUpdate?.(message.payload.state);\n\t\t\t\tbreak;\n\t\t\tcase 'commandResult': {\n\t\t\t\tconst pending = pendingCommands.get(message.payload.correlationId);\n\t\t\t\tif (!pending) return;\n\t\t\t\tpendingCommands.delete(message.payload.correlationId);\n\t\t\t\tif (message.payload.ok) {\n\t\t\t\t\tpending.resolve(message.payload.result);\n\t\t\t\t} else {\n\t\t\t\t\tpending.reject(new Error(message.payload.error ?? 'Command failed.'));\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'userTranscript':\n\t\t\t\toptions.onUserTranscript?.(message.payload);\n\t\t\t\tbreak;\n\t\t\tcase 'characterSpeechStarted':\n\t\t\t\toptions.onCharacterSpeechStarted?.(message.payload);\n\t\t\t\tbreak;\n\t\t\tcase 'characterSpeechEnded':\n\t\t\t\toptions.onCharacterSpeechEnded?.(message.payload);\n\t\t\t\tbreak;\n\t\t\tcase 'characterFocusChanged':\n\t\t\t\toptions.onCharacterFocusChanged?.(message.payload);\n\t\t\t\tbreak;\n\t\t\tcase 'conversationUpdate':\n\t\t\t\toptions.onConversationUpdate?.(message.payload.conversation);\n\t\t\t\tbreak;\n\t\t\tcase 'listeningState':\n\t\t\t\toptions.onListeningState?.(message.payload.listening);\n\t\t\t\tbreak;\n\t\t\tcase 'userSpeechStarted':\n\t\t\t\toptions.onUserSpeechStarted?.(message.payload);\n\t\t\t\tbreak;\n\t\t\tcase 'userSpeechEnded':\n\t\t\t\toptions.onUserSpeechEnded?.(message.payload);\n\t\t\t\tbreak;\n\t\t\tcase 'modeChange':\n\t\t\t\toptions.onActivityChange?.(legacyModeToActivity(message.payload.mode));\n\t\t\t\tbreak;\n\t\t\tcase 'activityChange':\n\t\t\t\toptions.onActivityChange?.(message.payload.activity);\n\t\t\t\tbreak;\n\t\t\tcase 'feedback':\n\t\t\t\toptions.onFeedback?.(message.payload);\n\t\t\t\tbreak;\n\t\t\tcase 'feedbackFailed':\n\t\t\t\toptions.onFeedbackFailed?.(message.payload);\n\t\t\t\tbreak;\n\t\t\tcase 'message':\n\t\t\t\toptions.onMessage?.(message.payload);\n\t\t\t\tbreak;\n\t\t\tcase 'started':\n\t\t\t\toptions.onStarted?.(message.payload);\n\t\t\t\tbreak;\n\t\t\tcase 'embedReady':\n\t\t\t\t// Soft HMR remounts the player without an iframe `load` event.\n\t\t\t\treplayStoredLaunch();\n\t\t\t\tbreak;\n\t\t\tcase 'widgetReady':\n\t\t\t\toptions.onWidgetReady?.();\n\t\t\t\tbreak;\n\t\t\tcase 'widgetExpand':\n\t\t\t\toptions.onWidgetExpand?.();\n\t\t\t\tbreak;\n\t\t\tcase 'widgetCollapse':\n\t\t\t\toptions.onWidgetCollapse?.();\n\t\t\t\tbreak;\n\t\t\tcase 'widgetAudioSettings':\n\t\t\t\toptions.onWidgetAudioSettings?.(message.payload.open);\n\t\t\t\tbreak;\n\t\t\tcase 'widgetDragStart':\n\t\t\t\toptions.onWidgetDragStart?.(message.payload);\n\t\t\t\tbreak;\n\t\t\tcase 'widgetDragMove':\n\t\t\t\toptions.onWidgetDragMove?.(message.payload);\n\t\t\t\tbreak;\n\t\t\tcase 'widgetDragEnd':\n\t\t\t\toptions.onWidgetDragEnd?.(message.payload);\n\t\t\t\tbreak;\n\t\t}\n\t};\n\n\twindow.addEventListener('message', onMessage);\n\tcontainer.replaceChildren(iframe);\n\t// Absolute iframe does not contribute to parent height — keep a usable floor when\n\t// the host only sized itself from previous in-flow content.\n\tif (container.clientHeight < 2 && !container.style.minHeight) {\n\t\tconst computedMin = getComputedStyle(container).minHeight;\n\t\tif (computedMin === '0px' || computedMin === 'auto') {\n\t\t\tcontainer.style.minHeight = '480px';\n\t\t}\n\t}\n\n\tconst postToIframe = (message: EmbedInboundMessage, transfer?: Transferable[]) => {\n\t\tif (destroyed) return;\n\t\tconst target = iframe.contentWindow;\n\t\tif (!target) {\n\t\t\tthrow new Error(formatPlayerUnreachableMessage(options.playerOrigin));\n\t\t}\n\t\tpostMessageToPlayer(target, message, options.playerOrigin, transfer);\n\t};\n\n\tconst postCommand = <T>(\n\t\tmessage: EmbedInboundMessage,\n\t\ttimeoutMs = 120_000,\n\t\ttransfer?: Transferable[]\n\t): Promise<T> => {\n\t\tconst correlationId =\n\t\t\tmessage.type !== 'parentHello' && message.payload && 'correlationId' in message.payload\n\t\t\t\t? (message.payload.correlationId as string)\n\t\t\t\t: newCorrelationId();\n\n\t\tif (message.type !== 'parentHello' && message.payload && 'correlationId' in message.payload) {\n\t\t\t// correlationId already set\n\t\t} else if (message.type !== 'parentHello' && message.payload) {\n\t\t\t(message.payload as { correlationId: string }).correlationId = correlationId;\n\t\t}\n\n\t\treturn new Promise<T>((resolve, reject) => {\n\t\t\tpendingCommands.set(correlationId, {\n\t\t\t\tresolve: (value) => resolve(value as T),\n\t\t\t\treject\n\t\t\t});\n\t\t\tpostToIframe(message, transfer);\n\t\t\tsetTimeout(() => {\n\t\t\t\tif (!pendingCommands.has(correlationId)) return;\n\t\t\t\tpendingCommands.delete(correlationId);\n\t\t\t\treject(new Error('Timed out waiting for player command result.'));\n\t\t\t}, timeoutMs);\n\t\t});\n\t};\n\n\treturn {\n\t\twaitForLoad() {\n\t\t\treturn loadPromise;\n\t\t},\n\t\tpostParentHello() {\n\t\t\tpostToIframe(createParentHelloMessage());\n\t\t},\n\t\tpostLaunch(launch, session, chrome) {\n\t\t\tif (destroyed) return;\n\t\t\tstoredLaunch = { launch, session, chrome };\n\t\t\tlastLaunchBurstAt = 0;\n\t\t\tpostLaunchBurst(storedLaunch);\n\t\t},\n\t\tpostManifest(manifest) {\n\t\t\tif (typeof manifest === 'string') {\n\t\t\t\tthis.postLaunch(manifest);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (\n\t\t\t\tmanifest &&\n\t\t\t\ttypeof manifest === 'object' &&\n\t\t\t\ttypeof (manifest as { launch?: unknown }).launch === 'string'\n\t\t\t) {\n\t\t\t\tconst launch = String((manifest as { launch: string }).launch);\n\t\t\t\tconst session = (manifest as { session?: object }).session;\n\t\t\t\tthis.postLaunch(launch, session);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthrow new Error(\n\t\t\t\t'postManifest is deprecated; use postLaunch with an opaque launch string from SessionLaunchResponse.'\n\t\t\t);\n\t\t},\n\t\tpostWidgetSetChrome(mode) {\n\t\t\tpostToIframe(createWidgetSetChromeMessage(mode));\n\t\t},\n\t\tpostWidgetToggleAudioSettings() {\n\t\t\tpostToIframe(createWidgetToggleAudioSettingsMessage());\n\t\t},\n\t\tspeak(options) {\n\t\t\tconst correlationId = newCorrelationId();\n\t\t\treturn postCommand<LegacySpeakResult>({\n\t\t\t\ttype: 'speak',\n\t\t\t\tschemaVersion: EMBED_MESSAGE_SCHEMA_VERSION,\n\t\t\t\tpayload: {\n\t\t\t\t\tcorrelationId,\n\t\t\t\t\ttext: options.text,\n\t\t\t\t\t...(options.characterId ? { characterId: options.characterId } : {}),\n\t\t\t\t\t...(options.behavior ? { behavior: options.behavior } : {})\n\t\t\t\t}\n\t\t\t});\n\t\t},\n\t\tstartListening() {\n\t\t\tconst correlationId = newCorrelationId();\n\t\t\treturn postCommand<void>({\n\t\t\t\ttype: 'startListening',\n\t\t\t\tschemaVersion: EMBED_MESSAGE_SCHEMA_VERSION,\n\t\t\t\tpayload: { correlationId }\n\t\t\t});\n\t\t},\n\t\tstopListening() {\n\t\t\tconst correlationId = newCorrelationId();\n\t\t\treturn postCommand<UtteranceResult>({\n\t\t\t\ttype: 'stopListening',\n\t\t\t\tschemaVersion: EMBED_MESSAGE_SCHEMA_VERSION,\n\t\t\t\tpayload: { correlationId }\n\t\t\t});\n\t\t},\n\t\tlistenOnce(options) {\n\t\t\tconst correlationId = newCorrelationId();\n\t\t\treturn postCommand<UtteranceResult>({\n\t\t\t\ttype: 'listenOnce',\n\t\t\t\tschemaVersion: EMBED_MESSAGE_SCHEMA_VERSION,\n\t\t\t\tpayload: {\n\t\t\t\t\tcorrelationId,\n\t\t\t\t\t...(options?.timeoutMs !== undefined ? { timeoutMs: options.timeoutMs } : {})\n\t\t\t\t}\n\t\t\t});\n\t\t},\n\t\tasync speakProcessorStream(stream, options) {\n\t\t\tconst startCorrelationId = newCorrelationId();\n\t\t\tconst endCorrelationId = newCorrelationId();\n\t\t\tconst started = await postCommand<{ streamId: string }>({\n\t\t\t\ttype: 'speakStreamStart',\n\t\t\t\tschemaVersion: EMBED_MESSAGE_SCHEMA_VERSION,\n\t\t\t\tpayload: {\n\t\t\t\t\tcorrelationId: startCorrelationId,\n\t\t\t\t\t...(options?.characterId ? { characterId: options.characterId } : {}),\n\t\t\t\t\t...(options?.behavior ? { behavior: options.behavior } : {})\n\t\t\t\t}\n\t\t\t});\n\t\t\tconst streamId = started.streamId;\n\t\t\ttry {\n\t\t\t\tfor await (const chunk of stream) {\n\t\t\t\t\tif (options?.signal?.aborted) {\n\t\t\t\t\t\tthrow new DOMException('Processor stream aborted.', 'AbortError');\n\t\t\t\t\t}\n\t\t\t\t\tif (!chunk) continue;\n\t\t\t\t\tpostToIframe({\n\t\t\t\t\t\ttype: 'speakStreamChunk',\n\t\t\t\t\t\tschemaVersion: EMBED_MESSAGE_SCHEMA_VERSION,\n\t\t\t\t\t\tpayload: { streamId, text: chunk }\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tif (options?.signal?.aborted) {\n\t\t\t\t\tthrow new DOMException('Processor stream aborted.', 'AbortError');\n\t\t\t\t}\n\t\t\t\treturn await postCommand<LegacySpeakResult>({\n\t\t\t\t\ttype: 'speakStreamEnd',\n\t\t\t\t\tschemaVersion: EMBED_MESSAGE_SCHEMA_VERSION,\n\t\t\t\t\tpayload: { correlationId: endCorrelationId, streamId }\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\ttry {\n\t\t\t\t\tawait postCommand<LegacySpeakResult>({\n\t\t\t\t\t\ttype: 'speakStreamEnd',\n\t\t\t\t\t\tschemaVersion: EMBED_MESSAGE_SCHEMA_VERSION,\n\t\t\t\t\t\tpayload: { correlationId: endCorrelationId, streamId }\n\t\t\t\t\t});\n\t\t\t\t} catch {\n\t\t\t\t\t/* stream may already be torn down */\n\t\t\t\t}\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t},\n\t\tspeechPlay(options) {\n\t\t\tconst correlationId = newCorrelationId();\n\t\t\tconst transfer: Transferable[] = [];\n\t\t\tif (options.source.kind === 'pcm' || options.source.kind === 'encoded') {\n\t\t\t\ttransfer.push(options.source.data);\n\t\t\t}\n\t\t\treturn postCommand<SpeechResult>(\n\t\t\t\t{\n\t\t\t\t\ttype: 'speechPlay',\n\t\t\t\t\tschemaVersion: EMBED_MESSAGE_SCHEMA_VERSION,\n\t\t\t\t\tpayload: {\n\t\t\t\t\t\tcorrelationId,\n\t\t\t\t\t\tsource: options.source,\n\t\t\t\t\t\t...(options.characterId ? { characterId: options.characterId } : {}),\n\t\t\t\t\t\t...(options.transcript !== undefined ? { transcript: options.transcript } : {}),\n\t\t\t\t\t\t...(options.history ? { history: options.history } : {}),\n\t\t\t\t\t\t...(options.queue ? { queue: options.queue } : {})\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t120_000,\n\t\t\t\ttransfer\n\t\t\t);\n\t\t},\n\t\tspeechUtteranceBegin(options) {\n\t\t\tconst correlationId = newCorrelationId();\n\t\t\treturn postCommand<void>({\n\t\t\t\ttype: 'speechUtteranceBegin',\n\t\t\t\tschemaVersion: EMBED_MESSAGE_SCHEMA_VERSION,\n\t\t\t\tpayload: {\n\t\t\t\t\tcorrelationId,\n\t\t\t\t\tutteranceId: options.utteranceId,\n\t\t\t\t\tformat: options.format,\n\t\t\t\t\tqueue: options.queue,\n\t\t\t\t\t...(options.characterId ? { characterId: options.characterId } : {}),\n\t\t\t\t\t...(options.transcript !== undefined ? { transcript: options.transcript } : {})\n\t\t\t\t}\n\t\t\t});\n\t\t},\n\t\tspeechUtteranceWrite(options) {\n\t\t\tconst correlationId = newCorrelationId();\n\t\t\treturn postCommand<{ bufferedMs: number }>(\n\t\t\t\t{\n\t\t\t\t\ttype: 'speechUtteranceWrite',\n\t\t\t\t\tschemaVersion: EMBED_MESSAGE_SCHEMA_VERSION,\n\t\t\t\t\tpayload: {\n\t\t\t\t\t\tcorrelationId,\n\t\t\t\t\t\tutteranceId: options.utteranceId,\n\t\t\t\t\t\taudio: options.audio,\n\t\t\t\t\t\tbyteLength: options.byteLength\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tSPEECH_WRITE_COMMAND_TIMEOUT_MS,\n\t\t\t\t[options.audio]\n\t\t\t);\n\t\t},\n\t\tspeechUtteranceSetTranscript(options) {\n\t\t\tconst correlationId = newCorrelationId();\n\t\t\treturn postCommand<void>({\n\t\t\t\ttype: 'speechUtteranceSetTranscript',\n\t\t\t\tschemaVersion: EMBED_MESSAGE_SCHEMA_VERSION,\n\t\t\t\tpayload: {\n\t\t\t\t\tcorrelationId,\n\t\t\t\t\tutteranceId: options.utteranceId,\n\t\t\t\t\ttranscript: options.transcript\n\t\t\t\t}\n\t\t\t});\n\t\t},\n\t\tspeechUtteranceClose(options) {\n\t\t\tconst correlationId = newCorrelationId();\n\t\t\treturn postCommand<SpeechResult>({\n\t\t\t\ttype: 'speechUtteranceClose',\n\t\t\t\tschemaVersion: EMBED_MESSAGE_SCHEMA_VERSION,\n\t\t\t\tpayload: {\n\t\t\t\t\tcorrelationId,\n\t\t\t\t\tutteranceId: options.utteranceId,\n\t\t\t\t\t...(options.transcript !== undefined ? { transcript: options.transcript } : {}),\n\t\t\t\t\t...(options.history ? { history: options.history } : {})\n\t\t\t\t}\n\t\t\t});\n\t\t},\n\t\tspeechUtteranceCancel(options) {\n\t\t\tconst correlationId = newCorrelationId();\n\t\t\treturn postCommand<SpeechResult>({\n\t\t\t\ttype: 'speechUtteranceCancel',\n\t\t\t\tschemaVersion: EMBED_MESSAGE_SCHEMA_VERSION,\n\t\t\t\tpayload: {\n\t\t\t\t\tcorrelationId,\n\t\t\t\t\tutteranceId: options.utteranceId\n\t\t\t\t}\n\t\t\t});\n\t\t},\n\t\tspeechInterrupt(options) {\n\t\t\tconst correlationId = newCorrelationId();\n\t\t\treturn postCommand<readonly SpeechResult[]>({\n\t\t\t\ttype: 'speechInterrupt',\n\t\t\t\tschemaVersion: EMBED_MESSAGE_SCHEMA_VERSION,\n\t\t\t\tpayload: {\n\t\t\t\t\tcorrelationId,\n\t\t\t\t\t...options\n\t\t\t\t}\n\t\t\t});\n\t\t},\n\t\tgetConversation() {\n\t\t\tconst correlationId = newCorrelationId();\n\t\t\treturn postCommand<readonly ConversationMessage[]>({\n\t\t\t\ttype: 'getConversation',\n\t\t\t\tschemaVersion: EMBED_MESSAGE_SCHEMA_VERSION,\n\t\t\t\tpayload: { correlationId }\n\t\t\t});\n\t\t},\n\t\tfocusCharacter(characterId) {\n\t\t\tconst correlationId = newCorrelationId();\n\t\t\treturn postCommand<void>({\n\t\t\t\ttype: 'focusCharacter',\n\t\t\t\tschemaVersion: EMBED_MESSAGE_SCHEMA_VERSION,\n\t\t\t\tpayload: { correlationId, characterId }\n\t\t\t});\n\t\t},\n\t\tsetFit(fit) {\n\t\t\tconst correlationId = newCorrelationId();\n\t\t\treturn postCommand<void>({\n\t\t\t\ttype: 'setFit',\n\t\t\t\tschemaVersion: EMBED_MESSAGE_SCHEMA_VERSION,\n\t\t\t\tpayload: { correlationId, fit }\n\t\t\t});\n\t\t},\n\t\tdestroy() {\n\t\t\tif (destroyed) return;\n\t\t\tdestroyed = true;\n\t\t\tfor (const pending of pendingCommands.values()) {\n\t\t\t\tpending.reject(new Error('Iframe host destroyed.'));\n\t\t\t}\n\t\t\tpendingCommands.clear();\n\t\t\tclearLaunchBurstTimers();\n\t\t\tstoredLaunch = null;\n\t\t\tiframe.removeEventListener('load', onIframeLoad);\n\t\t\tiframe.removeEventListener('error', onIframeError);\n\t\t\twindow.removeEventListener('message', onMessage);\n\t\t\tiframe.remove();\n\t\t}\n\t};\n}\n\n","export type ReadyGate<T> = {\n\tsetValue(value: T): void;\n\tmarkReady(): void;\n};\n\n/** Emits once after both an asynchronous value and readiness signal are available. */\nexport function createReadyGate<T>(onReady: (value: T) => void): ReadyGate<T> {\n\tlet value: T | undefined;\n\tlet ready = false;\n\tlet emitted = false;\n\n\tconst emitIfPossible = (): void => {\n\t\tif (emitted || !ready || value === undefined) return;\n\t\temitted = true;\n\t\tonReady(value);\n\t};\n\n\treturn {\n\t\tsetValue(nextValue) {\n\t\t\tvalue = nextValue;\n\t\t\temitIfPossible();\n\t\t},\n\t\tmarkReady() {\n\t\t\tready = true;\n\t\t\temitIfPossible();\n\t\t}\n\t};\n}\n","import { useExplicitLocalStack } from '../../shims/localStack';\n\nconst PRODUCTION_PLAYER_EMBED_URL = 'https://player.liforma.ai/embed';\nconst LOCAL_PLAYER_EMBED_URL = 'http://localhost:3002/embed';\n\nfunction readConfiguredEmbedUrl(): string | null {\n\tconst globalValue = (globalThis as { __LIFORMA_PLAYER_EMBED_URL?: string })\n\t\t.__LIFORMA_PLAYER_EMBED_URL;\n\tif (globalValue?.trim()) {\n\t\treturn globalValue.trim().replace(/\\/$/, '');\n\t}\n\n\tif (typeof document === 'undefined') {\n\t\treturn null;\n\t}\n\n\tconst script =\n\t\tdocument.querySelector('script[data-liforma-sdk][src*=\"client.js\"]') ??\n\t\tdocument.querySelector('script[src*=\"/sdk/v2/client.js\"]');\n\tconst fromScript = script?.getAttribute('data-player-embed-url')?.trim();\n\tif (fromScript) {\n\t\treturn fromScript.replace(/\\/$/, '');\n\t}\n\n\treturn null;\n}\n\n/**\n * Hosted player embed document URL (iframe `src` base).\n *\n * Defaults to production. Local stack is opt-in only — never inferred from\n * `localhost` hostname (third-party demos run on localhost against production).\n *\n * Override order:\n * 1. `window.__LIFORMA_PLAYER_EMBED_URL`\n * 2. `data-player-embed-url` on the SDK script tag\n * 3. `window.__LIFORMA_STACK === 'local'` or host `?stack=local` → http://localhost:3002/embed\n * 4. production → https://player.liforma.ai/embed\n */\nexport function resolvePublicPlayerEmbedUrl(): string {\n\tconst configured = readConfiguredEmbedUrl();\n\tif (configured) {\n\t\treturn configured.endsWith('/embed') ? configured : `${configured}/embed`;\n\t}\n\n\tif (useExplicitLocalStack()) {\n\t\treturn LOCAL_PLAYER_EMBED_URL;\n\t}\n\n\treturn PRODUCTION_PLAYER_EMBED_URL;\n}\n\nexport function resolveSceneHintId(backdropId?: string, locationId?: string): string | undefined {\n\tconst id = backdropId?.trim() || locationId?.trim();\n\treturn id || undefined;\n}\n\nexport function buildManifestEmbedUrl(\n\tembedBaseUrl?: string,\n\tdebug?: boolean,\n\tbackdropId?: string,\n\tavatarId?: string,\n\tfit?: 'full' | 'face' | 'medium',\n\tui?: 'widget',\n\tshowClose?: boolean,\n\tcastSize?: 2 | 3 | 4\n): string {\n\tconst base = (embedBaseUrl ?? resolvePublicPlayerEmbedUrl()).replace(/\\/$/, '');\n\tconst embedRoot = base.endsWith('/embed') ? base : `${base}/embed`;\n\tconst url = new URL(embedRoot);\n\tconst loc = backdropId?.trim();\n\tif (loc) {\n\t\turl.searchParams.set('backdropId', loc);\n\t\turl.searchParams.set('locationId', loc);\n\t}\n\tconst avatar = avatarId?.trim();\n\tif (avatar) {\n\t\turl.searchParams.set('avatarId', avatar);\n\t}\n\tif (fit === 'face' || fit === 'medium') {\n\t\turl.searchParams.set('fit', fit);\n\t}\n\tif (ui === 'widget') {\n\t\turl.searchParams.set('ui', 'widget');\n\t}\n\tif (showClose) {\n\t\turl.searchParams.set('close', '1');\n\t}\n\tif (debug) {\n\t\turl.searchParams.set('debug', 'true');\n\t}\n\tif (castSize === 2 || castSize === 3 || castSize === 4) {\n\t\turl.searchParams.set('cast', String(castSize));\n\t}\n\treturn withEmbedParentOrigin(url.toString());\n}\n\nexport function buildPublicEmbedUrl(\n\texperienceId: string,\n\tembedBaseUrl?: string,\n\tlanguage?: 'en' | 'es',\n\tbackdropId?: string,\n\tavatarId?: string,\n\tdebug?: boolean\n): string {\n\tconst base = (embedBaseUrl ?? resolvePublicPlayerEmbedUrl()).replace(/\\/$/, '');\n\tconst embedRoot = base.endsWith('/embed') ? base : `${base}/embed`;\n\tconst url = new URL(embedRoot);\n\turl.searchParams.set('experienceId', experienceId);\n\tif (language && language !== 'en') {\n\t\turl.searchParams.set('language', language);\n\t}\n\tconst loc = backdropId?.trim();\n\tif (loc) {\n\t\turl.searchParams.set('backdropId', loc);\n\t\turl.searchParams.set('locationId', loc);\n\t}\n\tconst avatar = avatarId?.trim();\n\tif (avatar) {\n\t\turl.searchParams.set('avatarId', avatar);\n\t}\n\tif (debug) {\n\t\turl.searchParams.set('debug', 'true');\n\t}\n\treturn withEmbedParentOrigin(url.toString());\n}\n\n/** Tell the player embed which origin may receive postMessage events (SDK parent page). */\nexport function withEmbedParentOrigin(embedUrl: string): string {\n\tif (typeof location === 'undefined') return embedUrl;\n\tconst url = new URL(embedUrl);\n\turl.searchParams.set('parentOrigin', location.origin);\n\treturn url.toString();\n}\n\nexport function resolvePlayerOrigin(embedUrl: string): string {\n\treturn new URL(embedUrl).origin;\n}\n","import type { ReadyEvent } from './experience';\nimport type {\n\tCharacterSpeechEvent,\n\tConversationMessage,\n\tConversationProcessorErrorEvent,\n\tStartedEvent,\n\tUserSpeechBoundaryEvent,\n\tUserTranscriptUpdate\n} from './types/sessionInteraction';\nimport type { SessionActivity } from './types/sessionLaunch';\n\nexport type FeedbackResult = {\n\treadonly contractVersion: 'feedback.v1';\n\treadonly types: readonly {\n\t\treadonly feedbackId: string;\n\t\treadonly name: string;\n\t\treadonly criteria: readonly {\n\t\t\treadonly criterionId: string;\n\t\t\treadonly label: string;\n\t\t\treadonly score: number | string;\n\t\t\treadonly rationale: string;\n\t\t}[];\n\t}[];\n};\n\nexport type FeedbackFailedEvent = {\n\treadonly error: string;\n\treadonly model?: string;\n};\n\nexport type ExperienceEventEnvelope<T = unknown> = {\n\treadonly id: string;\n\treadonly type: string;\n\treadonly sessionId: string;\n\treadonly timestamp: string;\n\treadonly data: T;\n};\n\nfunction newEventId(): string {\n\tif (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n\t\treturn `evt_${crypto.randomUUID().replace(/-/g, '').slice(0, 12)}`;\n\t}\n\treturn `evt_${Date.now().toString(36)}${Math.random().toString(36).slice(2, 8)}`;\n}\n\nexport function createExperienceEventEnvelope<T>(\n\ttype: string,\n\tsessionId: string,\n\tdata: T\n): ExperienceEventEnvelope<T> {\n\treturn {\n\t\tid: newEventId(),\n\t\ttype,\n\t\tsessionId,\n\t\ttimestamp: new Date().toISOString(),\n\t\tdata\n\t};\n}\n\nexport type ExperienceEvents = {\n\treadonly ready: ExperienceEventEnvelope<ReadyEvent>;\n\treadonly started: ExperienceEventEnvelope<StartedEvent>;\n\treadonly userTranscript: ExperienceEventEnvelope<UserTranscriptUpdate>;\n\treadonly userSpeechStarted: ExperienceEventEnvelope<UserSpeechBoundaryEvent>;\n\treadonly userSpeechEnded: ExperienceEventEnvelope<UserSpeechBoundaryEvent>;\n\treadonly characterSpeechStarted: ExperienceEventEnvelope<CharacterSpeechEvent>;\n\treadonly characterSpeechEnded: ExperienceEventEnvelope<CharacterSpeechEvent>;\n\treadonly characterFocusChanged: ExperienceEventEnvelope<{\n\t\treadonly characterId: string;\n\t\treadonly reason: 'speech' | 'api' | 'node_enter';\n\t}>;\n\treadonly conversationUpdate: ExperienceEventEnvelope<readonly ConversationMessage[]>;\n\treadonly message: ExperienceEventEnvelope<ConversationMessage>;\n\treadonly listeningState: ExperienceEventEnvelope<boolean>;\n\treadonly activityChange: ExperienceEventEnvelope<SessionActivity>;\n\t/** End-of-session Feedback scores (`feedback.v1`). Emitted when scoring succeeds. */\n\treadonly feedback: ExperienceEventEnvelope<FeedbackResult>;\n\t/** End-of-session Feedback scoring failed. */\n\treadonly feedbackFailed: ExperienceEventEnvelope<FeedbackFailedEvent>;\n\treadonly conversationProcessorError: ExperienceEventEnvelope<ConversationProcessorErrorEvent>;\n};\n\n/**\n * Reserved integrator event name: `backdropChange`.\n * The hosted player does not emit `locationChange` or `backdropChange` today.\n * Do not invent a client-side fake until the runtime actually changes scene mid-session.\n */\n\nexport type ExperienceEventName = keyof ExperienceEvents;\n\ntype HandlerSet<K extends ExperienceEventName> = Set<(event: ExperienceEvents[K]) => void>;\n\nexport type ExperienceEventRegistry = {\n\treadonly [K in ExperienceEventName]: HandlerSet<K>;\n};\n\nexport function createExperienceEventRegistry(): ExperienceEventRegistry {\n\treturn {\n\t\tready: new Set(),\n\t\tstarted: new Set(),\n\t\tuserTranscript: new Set(),\n\t\tuserSpeechStarted: new Set(),\n\t\tuserSpeechEnded: new Set(),\n\t\tcharacterSpeechStarted: new Set(),\n\t\tcharacterSpeechEnded: new Set(),\n\t\tcharacterFocusChanged: new Set(),\n\t\tconversationUpdate: new Set(),\n\t\tmessage: new Set(),\n\t\tlisteningState: new Set(),\n\t\tactivityChange: new Set(),\n\t\tfeedback: new Set(),\n\t\tfeedbackFailed: new Set(),\n\t\tconversationProcessorError: new Set()\n\t};\n}\n\nexport function subscribeExperienceEvent<K extends ExperienceEventName>(\n\tregistry: ExperienceEventRegistry,\n\tevent: K,\n\thandler: (payload: ExperienceEvents[K]) => void\n): () => void {\n\tregistry[event].add(handler);\n\treturn () => registry[event].delete(handler);\n}\n\nexport function emitExperienceEvent<K extends ExperienceEventName>(\n\tregistry: ExperienceEventRegistry,\n\tevent: K,\n\tpayload: ExperienceEvents[K]\n): void {\n\tfor (const handler of registry[event]) {\n\t\thandler(payload);\n\t}\n}\n\nexport function clearExperienceEventRegistry(registry: ExperienceEventRegistry): void {\n\tfor (const key of Object.keys(registry) as ExperienceEventName[]) {\n\t\tregistry[key].clear();\n\t}\n}\n","/**\n * Phase A public speech contract for Experience.speech\n * (docs/plans/2026/08/experience-play-audio-api.plan.md).\n */\n\nexport type SpeechAudioFormat = {\n\treadonly encoding: 'pcm_s16le';\n\treadonly sampleRate: number;\n\treadonly channels: 1;\n};\n\n/** Complete one-shot ops only (`speak` / `play`). */\nexport type CompleteSpeechQueuePolicy = 'append' | 'replace-active' | 'replace-all';\n\n/** Live streams only — `append` is forbidden. */\nexport type StreamingSpeechQueuePolicy = 'replace-active' | 'replace-all';\n\nexport type SpeechTerminalStatus = 'completed' | 'interrupted' | 'cancelled' | 'destroyed';\n\nexport type SpeechAnimationMode = 'tracked' | 'partially-tracked' | 'energy-fallback' | 'none';\n\nexport type SpeechResult = {\n\treadonly utteranceId: string;\n\treadonly characterId: string;\n\treadonly status: SpeechTerminalStatus;\n\treadonly durationMs: number;\n\treadonly transcript?: string;\n\treadonly animation: {\n\t\treadonly mode: SpeechAnimationMode;\n\t\treadonly trackedDurationMs: number;\n\t\treadonly fallbackDurationMs: number;\n\t\treadonly warningCodes?: readonly string[];\n\t};\n};\n\nexport type SpeechInterruptOptions =\n\t| { readonly utteranceId: string }\n\t| { readonly characterId: string }\n\t| { readonly scope?: 'active' | 'all' };\n\nexport type SpeechSpeakOptions = {\n\treadonly text: string;\n\treadonly characterId?: string;\n\treadonly queue?: CompleteSpeechQueuePolicy;\n\treadonly signal?: AbortSignal;\n};\n\n/** Raw PCM one-shot (Phase A). */\nexport type SpeechPlayPcmAudio = {\n\treadonly data: ArrayBuffer | Uint8Array;\n\treadonly format: SpeechAudioFormat;\n};\n\n/**\n * Encoded media bytes (wav/mp3/…). Player decodes via Web Audio — not api/models.\n * `encoding` is a MIME type such as `audio/mpeg` or `audio/wav`.\n */\nexport type SpeechPlayEncodedAudio = {\n\treadonly data: ArrayBuffer | Uint8Array | Blob;\n\treadonly encoding: string;\n\t/** Preferred pcm sample rate after decode (default 24000 when decoded rate is unsupported). */\n\treadonly sampleRate?: number;\n};\n\n/** Fetch + decode in the player (URL must allow CORS from player.liforma.ai). */\nexport type SpeechPlayUrlAudio = {\n\treadonly url: string;\n\treadonly sampleRate?: number;\n};\n\n/**\n * Live audio track. Captured to PCM in the **host** SDK (not transferable across the embed).\n * Prefer `createUtterance({ track })` for long streams; `play({ audio: { track } })` runs until\n * the track ends then closes the utterance.\n */\nexport type SpeechPlayMediaStreamAudio = {\n\treadonly track: MediaStreamTrack;\n\t/** Target pcm sample rate after capture (default 24000). */\n\treadonly sampleRate?: number;\n};\n\nexport type SpeechPlayAudio =\n\t| SpeechPlayPcmAudio\n\t| SpeechPlayEncodedAudio\n\t| SpeechPlayUrlAudio\n\t| SpeechPlayMediaStreamAudio;\n\nexport type SpeechPlayOptions = {\n\treadonly audio: SpeechPlayAudio;\n\treadonly characterId?: string;\n\treadonly transcript?: string;\n\treadonly history?: 'none' | 'commit';\n\treadonly queue?: CompleteSpeechQueuePolicy;\n\treadonly signal?: AbortSignal;\n};\n\n/** Embed / player wire format for {@link SpeechPlayOptions.audio}. */\nexport type SpeechPlayEmbedSource =\n\t| {\n\t\t\treadonly kind: 'pcm';\n\t\t\treadonly data: ArrayBuffer;\n\t\t\treadonly format: SpeechAudioFormat;\n\t }\n\t| {\n\t\t\treadonly kind: 'encoded';\n\t\t\treadonly data: ArrayBuffer;\n\t\t\treadonly encoding: string;\n\t\t\treadonly sampleRate?: number;\n\t }\n\t| {\n\t\t\treadonly kind: 'url';\n\t\t\treadonly url: string;\n\t\t\treadonly sampleRate?: number;\n\t };\n\nexport function isSpeechPlayPcmAudio(audio: SpeechPlayAudio): audio is SpeechPlayPcmAudio {\n\treturn (\n\t\t'data' in audio &&\n\t\t'format' in audio &&\n\t\t(audio as SpeechPlayPcmAudio).format != null &&\n\t\t!('track' in audio)\n\t);\n}\n\nexport function isSpeechPlayUrlAudio(audio: SpeechPlayAudio): audio is SpeechPlayUrlAudio {\n\treturn 'url' in audio && typeof (audio as SpeechPlayUrlAudio).url === 'string';\n}\n\nexport function isSpeechPlayMediaStreamAudio(\n\taudio: SpeechPlayAudio\n): audio is SpeechPlayMediaStreamAudio {\n\tif (!('track' in audio)) return false;\n\tconst track = (audio as SpeechPlayMediaStreamAudio).track as unknown;\n\tif (track == null || typeof track !== 'object') return false;\n\tif (typeof MediaStreamTrack !== 'undefined' && track instanceof MediaStreamTrack) {\n\t\treturn true;\n\t}\n\t// Duck-type for non-DOM / test environments.\n\treturn 'kind' in track && 'readyState' in track;\n}\n\nexport function isSpeechPlayEncodedAudio(audio: SpeechPlayAudio): audio is SpeechPlayEncodedAudio {\n\treturn (\n\t\t!isSpeechPlayPcmAudio(audio) &&\n\t\t!isSpeechPlayUrlAudio(audio) &&\n\t\t!isSpeechPlayMediaStreamAudio(audio) &&\n\t\t'encoding' in audio &&\n\t\ttypeof (audio as SpeechPlayEncodedAudio).encoding === 'string'\n\t);\n}\n\nexport type SpeechCreateUtteranceOptions = {\n\t/**\n\t * Required unless `track` is set. When `track` is provided without `format`, defaults to\n\t * pcm_s16le mono at `sampleRate` (or 24000).\n\t */\n\treadonly format?: SpeechAudioFormat;\n\t/** Optional live track — host captures and writes until the track ends. */\n\treadonly track?: MediaStreamTrack;\n\t/** Used with `track` when `format` is omitted (default 24000). */\n\treadonly sampleRate?: number;\n\treadonly characterId?: string;\n\treadonly queue?: StreamingSpeechQueuePolicy;\n\t/**\n\t * Optional spoken text seed for force-aligned STA (lip sync).\n\t * Non-empty transcript enables force-align for this utterance; omit / whitespace → free CTC.\n\t * May be updated mid-turn via {@link SpeechUtterance.setTranscript} or finalized on\n\t * {@link SpeechUtterance.close} `{ transcript }`.\n\t */\n\treadonly transcript?: string;\n\treadonly signal?: AbortSignal;\n};\n\nexport type SpeechUtteranceCloseOptions = {\n\treadonly transcript?: string;\n\treadonly history?: 'none' | 'commit';\n};\n\nexport type SpeechWriteOptions = {\n\treadonly timeoutMs?: number;\n};\n\nexport type SpeechUtterance = {\n\treadonly id: string;\n\treadonly characterId: string;\n\treadonly result: Promise<SpeechResult>;\n\twrite(audio: ArrayBuffer | Uint8Array, options?: SpeechWriteOptions): Promise<void>;\n\t/**\n\t * Replace / set spoken text mid-turn for force-aligned STA.\n\t * Non-empty text upgrades free decode → force-align (full-utterance on close).\n\t * Empty / whitespace is a no-op.\n\t */\n\tsetTranscript(text: string): Promise<void>;\n\tclose(options?: SpeechUtteranceCloseOptions): Promise<SpeechResult>;\n\tcancel(): Promise<SpeechResult>;\n};\n\nexport type SpeechController = {\n\tspeak(options: SpeechSpeakOptions): Promise<SpeechResult>;\n\tplay(options: SpeechPlayOptions): Promise<SpeechResult>;\n\tcreateUtterance(options: SpeechCreateUtteranceOptions): SpeechUtterance;\n\tinterrupt(options?: SpeechInterruptOptions): Promise<readonly SpeechResult[]>;\n};\n\n/** Accepted PCM sample rates (Phase A). */\nexport const SPEECH_ACCEPTED_SAMPLE_RATES = [\n\t8000, 16000, 22050, 24000, 44100, 48000\n] as const;\n\nexport const SPEECH_LIMITS = {\n\tMAX_CHUNK_BYTES: 64 * 1024,\n\tRECOMMENDED_CHUNK_MS_MIN: 20,\n\tRECOMMENDED_CHUNK_MS_MAX: 200,\n\tMAX_BUFFERED_AUDIO_MS: 8_000,\n\tPLAYBACK_JITTER_BUFFER_MS: 150,\n\tPLAYBACK_JITTER_BUFFER_MS_MIN: 80,\n\tPLAYBACK_JITTER_BUFFER_MS_MAX: 250,\n\tMAX_UTTERANCE_DURATION_MS: 90_000,\n\tMAX_PENDING_WRITES: 8,\n\tDEFAULT_WRITE_TIMEOUT_MS: 5_000\n} as const;\n\nexport class UnsupportedSpeechOperationError extends Error {\n\treadonly name = 'UnsupportedSpeechOperationError';\n\tconstructor(\n\t\treadonly operation: string,\n\t\treadonly requiredCapability: string\n\t) {\n\t\tsuper(`Speech operation \"${operation}\" requires capability \"${requiredCapability}\".`);\n\t}\n}\n\nexport class SpeechUtteranceClosedError extends Error {\n\treadonly name = 'SpeechUtteranceClosedError';\n\tconstructor(message = 'Cannot write to a closed or cancelled speech utterance.') {\n\t\tsuper(message);\n\t}\n}\n\nexport class SpeechRuntimeError extends Error {\n\treadonly name = 'SpeechRuntimeError';\n\tconstructor(message: string, options?: { cause?: unknown }) {\n\t\tsuper(message, options);\n\t}\n}\n\nexport class SpeechBufferTimeoutError extends Error {\n\treadonly name = 'SpeechBufferTimeoutError';\n\tconstructor(message = 'Timed out waiting for speech buffer capacity.') {\n\t\tsuper(message);\n\t}\n}\n\nexport function emptyAnimationResult(\n\tmode: SpeechAnimationMode = 'none'\n): SpeechResult['animation'] {\n\treturn {\n\t\tmode,\n\t\ttrackedDurationMs: 0,\n\t\tfallbackDurationMs: 0\n\t};\n}\n\nexport function isAcceptedSampleRate(rate: number): boolean {\n\treturn (SPEECH_ACCEPTED_SAMPLE_RATES as readonly number[]).includes(rate);\n}\n\nexport function assertPcmFormat(format: SpeechAudioFormat): void {\n\tif (format.encoding !== 'pcm_s16le') {\n\t\tthrow new SpeechRuntimeError(`Unsupported audio encoding: ${format.encoding}`);\n\t}\n\tif (format.channels !== 1) {\n\t\tthrow new SpeechRuntimeError(`Unsupported channel count: ${format.channels}`);\n\t}\n\tif (!isAcceptedSampleRate(format.sampleRate)) {\n\t\tthrow new SpeechRuntimeError(`Unsupported sample rate: ${format.sampleRate}`);\n\t}\n}\n\nexport function assertSpeechPlayAudio(audio: SpeechPlayAudio): void {\n\tif (isSpeechPlayPcmAudio(audio)) {\n\t\tassertPcmFormat(audio.format);\n\t\treturn;\n\t}\n\tif (isSpeechPlayUrlAudio(audio)) {\n\t\tconst url = audio.url.trim();\n\t\tif (!url) throw new SpeechRuntimeError('Audio URL must be non-empty.');\n\t\tif (audio.sampleRate !== undefined && !isAcceptedSampleRate(audio.sampleRate)) {\n\t\t\tthrow new SpeechRuntimeError(`Unsupported sample rate: ${audio.sampleRate}`);\n\t\t}\n\t\treturn;\n\t}\n\tif (isSpeechPlayMediaStreamAudio(audio)) {\n\t\tif (audio.track.kind !== 'audio') {\n\t\t\tthrow new SpeechRuntimeError('speech.play track must be an audio MediaStreamTrack.');\n\t\t}\n\t\tif (audio.sampleRate !== undefined && !isAcceptedSampleRate(audio.sampleRate)) {\n\t\t\tthrow new SpeechRuntimeError(`Unsupported sample rate: ${audio.sampleRate}`);\n\t\t}\n\t\treturn;\n\t}\n\tif (isSpeechPlayEncodedAudio(audio)) {\n\t\tconst encoding = audio.encoding.trim().toLowerCase();\n\t\tif (!encoding) throw new SpeechRuntimeError('Encoded audio MIME type is required.');\n\t\tif (encoding === 'pcm_s16le') {\n\t\t\tthrow new SpeechRuntimeError('Use format: { encoding: \"pcm_s16le\", … } for raw PCM.');\n\t\t}\n\t\tif (audio.sampleRate !== undefined && !isAcceptedSampleRate(audio.sampleRate)) {\n\t\t\tthrow new SpeechRuntimeError(`Unsupported sample rate: ${audio.sampleRate}`);\n\t\t}\n\t\treturn;\n\t}\n\tthrow new SpeechRuntimeError('Unsupported speech.play audio shape.');\n}\n\nexport function resolveCreateUtteranceFormat(\n\toptions: SpeechCreateUtteranceOptions\n): SpeechAudioFormat {\n\tif (options.format) {\n\t\tassertPcmFormat(options.format);\n\t\treturn options.format;\n\t}\n\tif (options.track) {\n\t\tconst sampleRate = options.sampleRate ?? 24_000;\n\t\tconst format: SpeechAudioFormat = {\n\t\t\tencoding: 'pcm_s16le',\n\t\t\tsampleRate,\n\t\t\tchannels: 1\n\t\t};\n\t\tassertPcmFormat(format);\n\t\treturn format;\n\t}\n\tthrow new SpeechRuntimeError('createUtterance requires format or track.');\n}\n\nexport function toUint8Array(data: ArrayBuffer | Uint8Array): Uint8Array {\n\treturn data instanceof Uint8Array ? data : new Uint8Array(data);\n}\n\nexport function queuePolicyToEmbedBehavior(\n\tqueue: CompleteSpeechQueuePolicy | StreamingSpeechQueuePolicy | undefined\n): 'enqueue' | 'interrupt' {\n\tif (queue === 'replace-active' || queue === 'replace-all') return 'interrupt';\n\treturn 'enqueue';\n}\n","/** Liforma `speech.write` rejects chunks larger than 64 KiB. */\nexport const MAX_PCM_CHUNK_BYTES = 64 * 1024;\n\n/**\n * Playback / chunk guidance for custom BYO bridges (mirrors speech controller limits).\n * Prefer ~20–200 ms PCM frames; the player starts after an adaptive ~80–250 ms jitter\n * buffer (default 150 ms) based on chunk arrival variance.\n */\nexport const BYO_PLAYBACK_GUIDANCE = {\n\tRECOMMENDED_CHUNK_MS_MIN: 20,\n\tRECOMMENDED_CHUNK_MS_MAX: 200,\n\tPLAYBACK_JITTER_BUFFER_MS: 150,\n\tPLAYBACK_JITTER_BUFFER_MS_MIN: 80,\n\tPLAYBACK_JITTER_BUFFER_MS_MAX: 250\n} as const;\n\nexport function base64ToPcmBytes(b64: string): Uint8Array {\n\tconst bin = atob(b64);\n\tconst out = new Uint8Array(bin.length);\n\tfor (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);\n\treturn out;\n}\n\nexport function pcmBytesToBase64(bytes: Uint8Array): string {\n\tlet bin = '';\n\tconst chunk = 0x8000;\n\tfor (let i = 0; i < bytes.length; i += chunk) {\n\t\tbin += String.fromCharCode(...bytes.subarray(i, i + chunk));\n\t}\n\treturn btoa(bin);\n}\n\nexport function floatToPcm16Le(input: Float32Array): Uint8Array {\n\tconst out = new Uint8Array(input.length * 2);\n\tconst view = new DataView(out.buffer);\n\tfor (let i = 0; i < input.length; i++) {\n\t\tconst s = Math.max(-1, Math.min(1, input[i]!));\n\t\tview.setInt16(i * 2, s < 0 ? s * 0x8000 : s * 0x7fff, true);\n\t}\n\treturn out;\n}\n\n/** Naive linear resample Float32 → target rate. */\nexport function resampleFloat32(input: Float32Array, fromRate: number, toRate: number): Float32Array {\n\tif (fromRate === toRate || input.length === 0) return input;\n\tconst ratio = fromRate / toRate;\n\tconst outLen = Math.max(1, Math.round(input.length / ratio));\n\tconst out = new Float32Array(outLen);\n\tfor (let i = 0; i < outLen; i++) {\n\t\tconst src = i * ratio;\n\t\tconst i0 = Math.floor(src);\n\t\tconst i1 = Math.min(i0 + 1, input.length - 1);\n\t\tconst t = src - i0;\n\t\tout[i] = input[i0]! * (1 - t) + input[i1]! * t;\n\t}\n\treturn out;\n}\n\n/**\n * Parse vendor format strings like `pcm_16000` / `pcm_24000`.\n * Returns null when the format is missing or not PCM.\n */\nexport function parsePcmSampleRate(format: string | null | undefined): number | null {\n\tconst m = /^pcm_(\\d+)$/.exec(format ?? '');\n\tif (!m) return null;\n\tconst rate = Number(m[1]);\n\treturn Number.isFinite(rate) && rate > 0 ? rate : null;\n}\n\n/**\n * Write even-length PCM, splitting at MAX_PCM_CHUNK_BYTES on even boundaries.\n * Callers that may receive odd-length frames should run bytes through\n * {@link takeEvenPcmBytes} first so samples are not dropped across chunks.\n */\nexport async function writePcmInChunks(\n\tutterance: { write: (chunk: ArrayBuffer | ArrayBufferView) => Promise<unknown> },\n\tbytes: Uint8Array\n): Promise<void> {\n\tif (bytes.byteLength === 0) return;\n\tif ((bytes.byteLength & 1) !== 0) {\n\t\t// Defensive: drop trailing unpaired byte within a single call.\n\t\tbytes = bytes.subarray(0, bytes.byteLength - 1);\n\t\tif (bytes.byteLength === 0) return;\n\t}\n\tif (bytes.byteLength <= MAX_PCM_CHUNK_BYTES) {\n\t\tawait utterance.write(bytes);\n\t\treturn;\n\t}\n\tlet offset = 0;\n\twhile (offset < bytes.byteLength) {\n\t\tlet end = Math.min(offset + MAX_PCM_CHUNK_BYTES, bytes.byteLength);\n\t\tif ((end - offset) % 2 === 1) end -= 1;\n\t\tif (end <= offset) break;\n\t\tawait utterance.write(bytes.subarray(offset, end));\n\t\toffset = end;\n\t}\n}\n","/**\n * Stateful cubic (Catmull-Rom) resampler for continuous PCM capture streams.\n * Better than naive linear for uplink mic / track capture.\n */\n\nexport type FloatResampler = {\n\t/** Push a mono float buffer; returns samples at the target rate (may be empty). */\n\tpush(input: Float32Array): Float32Array;\n\t/** Flush any remaining fractional sample (usually silence). */\n\tflush(): Float32Array;\n\treadonly fromRate: number;\n\treadonly toRate: number;\n};\n\nfunction catmullRom(y0: number, y1: number, y2: number, y3: number, t: number): number {\n\tconst a0 = -0.5 * y0 + 1.5 * y1 - 1.5 * y2 + 0.5 * y3;\n\tconst a1 = y0 - 2.5 * y1 + 2 * y2 - 0.5 * y3;\n\tconst a2 = -0.5 * y0 + 0.5 * y2;\n\tconst a3 = y1;\n\treturn ((a0 * t + a1) * t + a2) * t + a3;\n}\n\n/**\n * Create a continuous resampler. When rates match, push() returns the input as-is\n * (no copy) when possible.\n */\nexport function createCubicResampler(fromRate: number, toRate: number): FloatResampler {\n\tif (!(fromRate > 0) || !(toRate > 0)) {\n\t\tthrow new Error('Resampler rates must be positive');\n\t}\n\n\tif (fromRate === toRate) {\n\t\treturn {\n\t\t\tfromRate,\n\t\t\ttoRate,\n\t\t\tpush(input) {\n\t\t\t\treturn input;\n\t\t\t},\n\t\t\tflush() {\n\t\t\t\treturn new Float32Array(0);\n\t\t\t}\n\t\t};\n\t}\n\n\tconst ratio = fromRate / toRate;\n\t/** History + pending input (keeps 3 samples of lookbehind for cubic). */\n\tlet pending = new Float32Array(0);\n\tlet pos = 0; // position in pending sample space for next output\n\n\treturn {\n\t\tfromRate,\n\t\ttoRate,\n\t\tpush(input) {\n\t\t\tif (input.length === 0) return new Float32Array(0);\n\t\t\tconst merged = new Float32Array(pending.length + input.length);\n\t\t\tmerged.set(pending);\n\t\t\tmerged.set(input, pending.length);\n\t\t\tpending = merged;\n\n\t\t\tif (pending.length < 4) return new Float32Array(0);\n\n\t\t\tconst out: number[] = [];\n\t\t\t// Need i+2 available for cubic; stop while pos+2 < length-1 equivalently floor(pos)+2 < len-?\n\t\t\twhile (pos + 2 < pending.length - 1) {\n\t\t\t\tconst i1 = Math.floor(pos);\n\t\t\t\tconst t = pos - i1;\n\t\t\t\tconst i0 = Math.max(0, i1 - 1);\n\t\t\t\tconst i2 = Math.min(pending.length - 1, i1 + 1);\n\t\t\t\tconst i3 = Math.min(pending.length - 1, i1 + 2);\n\t\t\t\tout.push(\n\t\t\t\t\tcatmullRom(pending[i0]!, pending[i1]!, pending[i2]!, pending[i3]!, t)\n\t\t\t\t);\n\t\t\t\tpos += ratio;\n\t\t\t}\n\n\t\t\t// Drop consumed samples but keep lookbehind (≥1) and unread tail.\n\t\t\tconst drop = Math.max(0, Math.floor(pos) - 1);\n\t\t\tif (drop > 0) {\n\t\t\t\tpending = pending.subarray(drop);\n\t\t\t\tpos -= drop;\n\t\t\t}\n\n\t\t\treturn out.length ? Float32Array.from(out) : new Float32Array(0);\n\t\t},\n\t\tflush() {\n\t\t\tif (pending.length === 0) return new Float32Array(0);\n\t\t\t// Emit one last sample if we have enough history; otherwise drop.\n\t\t\tpending = new Float32Array(0);\n\t\t\tpos = 0;\n\t\t\treturn new Float32Array(0);\n\t\t}\n\t};\n}\n","/**\n * Shared Web Audio capture: AudioWorklet when available, ScriptProcessor fallback.\n * Used by BYO mic uplink and MediaStreamTrack → PCM piping.\n */\n\nimport { floatToPcm16Le } from './pcm';\nimport { createCubicResampler, type FloatResampler } from './resample';\n\nexport type AudioCaptureSession = {\n\tstop(): void;\n\treadonly fromSampleRate: number;\n\treadonly usingWorklet: boolean;\n};\n\nexport type StartAudioCaptureOptions = {\n\treadonly mediaStream: MediaStream;\n\treadonly targetSampleRate: number;\n\t/** Called with pcm_s16le mono at targetSampleRate. */\n\treadonly onPcm: (bytes: Uint8Array) => void;\n\t/** When false, frames are dropped. */\n\treadonly shouldSend?: () => boolean;\n\treadonly onLog?: (line: string, kind?: 'info' | 'warn') => void;\n};\n\nconst WORKLET_NAME = 'liforma-pcm-capture';\n\nconst WORKLET_SOURCE = `\nclass LiformaPcmCaptureProcessor extends AudioWorkletProcessor {\n process(inputs) {\n const ch = inputs[0] && inputs[0][0];\n if (ch && ch.length) {\n const copy = new Float32Array(ch.length);\n copy.set(ch);\n this.port.postMessage(copy, [copy.buffer]);\n }\n return true;\n }\n}\nregisterProcessor('${WORKLET_NAME}', LiformaPcmCaptureProcessor);\n`;\n\nlet workletUrl: string | null = null;\n\nfunction getWorkletUrl(): string {\n\tif (!workletUrl) {\n\t\tworkletUrl = URL.createObjectURL(\n\t\t\tnew Blob([WORKLET_SOURCE], { type: 'application/javascript' })\n\t\t);\n\t}\n\treturn workletUrl;\n}\n\nfunction getAudioContextCtor(): typeof AudioContext | null {\n\treturn (\n\t\twindow.AudioContext ||\n\t\t(window as unknown as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext ||\n\t\tnull\n\t);\n}\n\n/**\n * Capture a MediaStream as pcm_s16le at targetSampleRate.\n * Prefers AudioWorklet; falls back to ScriptProcessorNode.\n */\nexport async function startAudioCapture(\n\toptions: StartAudioCaptureOptions\n): Promise<AudioCaptureSession> {\n\tconst { mediaStream, targetSampleRate, onPcm, shouldSend, onLog } = options;\n\tconst AudioContextCtor = getAudioContextCtor();\n\tif (!AudioContextCtor) throw new Error('Web Audio is required for audio capture');\n\n\tconst audioCtx = new AudioContextCtor();\n\tconst source = audioCtx.createMediaStreamSource(mediaStream);\n\tconst silent = audioCtx.createGain();\n\tsilent.gain.value = 0;\n\tconst fromSampleRate = audioCtx.sampleRate;\n\tconst resampler: FloatResampler = createCubicResampler(fromSampleRate, targetSampleRate);\n\tlet stopped = false;\n\tlet usingWorklet = false;\n\n\tconst emitFloat = (input: Float32Array) => {\n\t\tif (stopped) return;\n\t\tif (shouldSend && !shouldSend()) return;\n\t\tconst resampled = resampler.push(input);\n\t\tif (resampled.length === 0) return;\n\t\tonPcm(floatToPcm16Le(resampled));\n\t};\n\n\tlet disconnectGraph: () => void = () => undefined;\n\n\tconst tryWorklet = async (): Promise<boolean> => {\n\t\tif (typeof audioCtx.audioWorklet?.addModule !== 'function') return false;\n\t\ttry {\n\t\t\tawait audioCtx.audioWorklet.addModule(getWorkletUrl());\n\t\t\tconst node = new AudioWorkletNode(audioCtx, WORKLET_NAME, {\n\t\t\t\tnumberOfInputs: 1,\n\t\t\t\tnumberOfOutputs: 1,\n\t\t\t\toutputChannelCount: [1]\n\t\t\t});\n\t\t\tnode.port.onmessage = (ev) => {\n\t\t\t\tif (ev.data instanceof Float32Array) emitFloat(ev.data);\n\t\t\t};\n\t\t\tsource.connect(node);\n\t\t\tnode.connect(silent);\n\t\t\tsilent.connect(audioCtx.destination);\n\t\t\tdisconnectGraph = () => {\n\t\t\t\tnode.port.onmessage = null;\n\t\t\t\ttry {\n\t\t\t\t\tnode.disconnect();\n\t\t\t\t} catch {\n\t\t\t\t\t/* ignore */\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tsource.disconnect();\n\t\t\t\t} catch {\n\t\t\t\t\t/* ignore */\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tsilent.disconnect();\n\t\t\t\t} catch {\n\t\t\t\t\t/* ignore */\n\t\t\t\t}\n\t\t\t};\n\t\t\tusingWorklet = true;\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t};\n\n\tconst useScriptProcessor = (): void => {\n\t\tconst createScriptProcessor = (\n\t\t\taudioCtx as AudioContext & {\n\t\t\t\tcreateScriptProcessor?: (\n\t\t\t\t\tbufferSize: number,\n\t\t\t\t\tinputChannels: number,\n\t\t\t\t\toutputChannels: number\n\t\t\t\t) => ScriptProcessorNode;\n\t\t\t}\n\t\t).createScriptProcessor?.bind(audioCtx);\n\n\t\tif (!createScriptProcessor) {\n\t\t\tthrow new Error('Neither AudioWorklet nor ScriptProcessor is available');\n\t\t}\n\n\t\tconst processor = createScriptProcessor(4096, 1, 1);\n\t\tprocessor.onaudioprocess = (ev) => {\n\t\t\temitFloat(ev.inputBuffer.getChannelData(0));\n\t\t};\n\t\tsource.connect(processor);\n\t\tprocessor.connect(silent);\n\t\tsilent.connect(audioCtx.destination);\n\t\tdisconnectGraph = () => {\n\t\t\tprocessor.onaudioprocess = null;\n\t\t\ttry {\n\t\t\t\tprocessor.disconnect();\n\t\t\t} catch {\n\t\t\t\t/* ignore */\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tsource.disconnect();\n\t\t\t} catch {\n\t\t\t\t/* ignore */\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tsilent.disconnect();\n\t\t\t} catch {\n\t\t\t\t/* ignore */\n\t\t\t}\n\t\t};\n\t\tusingWorklet = false;\n\t};\n\n\tif (!(await tryWorklet())) {\n\t\tuseScriptProcessor();\n\t\tonLog?.(\n\t\t\t`Mic/track capture via ScriptProcessor at ${fromSampleRate} Hz → ${targetSampleRate} Hz`,\n\t\t\t'warn'\n\t\t);\n\t} else {\n\t\tonLog?.(`Mic/track capture via AudioWorklet at ${fromSampleRate} Hz → ${targetSampleRate} Hz`);\n\t}\n\n\tif (audioCtx.state === 'suspended') {\n\t\tawait audioCtx.resume().catch(() => undefined);\n\t}\n\n\treturn {\n\t\tfromSampleRate,\n\t\tusingWorklet,\n\t\tstop() {\n\t\t\tif (stopped) return;\n\t\t\tstopped = true;\n\t\t\tresampler.flush();\n\t\t\tdisconnectGraph();\n\t\t\tvoid audioCtx.close().catch(() => undefined);\n\t\t}\n\t};\n}\n","/**\n * Host-side MediaStreamTrack → mono pcm_s16le capture for experience.speech.\n * Capture stays in the SDK (host page); player only receives PCM chunks.\n */\n\nimport { startAudioCapture } from '../../byo/audioCapture';\nimport { SPEECH_LIMITS, SpeechRuntimeError, toUint8Array } from './types';\nimport type { SpeechUtterance } from './types';\n\nexport type CaptureMediaStreamTrackOptions = {\n\treadonly track: MediaStreamTrack;\n\treadonly utterance: SpeechUtterance;\n\treadonly sampleRate: number;\n\treadonly signal?: AbortSignal;\n};\n\n/**\n * Capture a live audio track into an open utterance until the track ends or abort.\n * Prefers AudioWorklet + cubic resample (ScriptProcessor fallback).\n */\nexport async function pipeMediaStreamTrackToUtterance(\n\toptions: CaptureMediaStreamTrackOptions\n): Promise<void> {\n\tconst { track, utterance, sampleRate, signal } = options;\n\tif (typeof MediaStreamTrack === 'undefined' || !(track instanceof MediaStreamTrack)) {\n\t\tthrow new SpeechRuntimeError('MediaStreamTrack is not available in this environment.');\n\t}\n\tif (track.kind !== 'audio') {\n\t\tthrow new SpeechRuntimeError('speech.play track must be an audio MediaStreamTrack.');\n\t}\n\tif (track.readyState === 'ended') {\n\t\treturn;\n\t}\n\tif (signal?.aborted) {\n\t\treturn;\n\t}\n\n\tconst stream = new MediaStream([track]);\n\tlet writeChain: Promise<void> = Promise.resolve();\n\tlet stopped = false;\n\tlet capture: Awaited<ReturnType<typeof startAudioCapture>> | null = null;\n\n\tconst teardown = () => {\n\t\tif (stopped) return;\n\t\tstopped = true;\n\t\tcapture?.stop();\n\t\tcapture = null;\n\t};\n\n\ttry {\n\t\tcapture = await startAudioCapture({\n\t\t\tmediaStream: stream,\n\t\t\ttargetSampleRate: sampleRate,\n\t\t\tshouldSend: () => !stopped && !signal?.aborted,\n\t\t\tonPcm: (pcm) => {\n\t\t\t\tif (stopped || signal?.aborted) return;\n\t\t\t\tconst bytes = toUint8Array(pcm);\n\t\t\t\twriteChain = writeChain\n\t\t\t\t\t.then(async () => {\n\t\t\t\t\t\tif (stopped || signal?.aborted) return;\n\t\t\t\t\t\tlet offset = 0;\n\t\t\t\t\t\twhile (offset < bytes.byteLength) {\n\t\t\t\t\t\t\tlet end = Math.min(offset + SPEECH_LIMITS.MAX_CHUNK_BYTES, bytes.byteLength);\n\t\t\t\t\t\t\tif ((end - offset) % 2 === 1) end -= 1;\n\t\t\t\t\t\t\tif (end <= offset) break;\n\t\t\t\t\t\t\tconst slice = bytes.subarray(offset, end);\n\t\t\t\t\t\t\toffset = end;\n\t\t\t\t\t\t\tif (slice.byteLength === 0) continue;\n\t\t\t\t\t\t\tawait utterance.write(slice);\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\t.catch(() => {\n\t\t\t\t\t\tteardown();\n\t\t\t\t\t});\n\t\t\t}\n\t\t});\n\t} catch (err) {\n\t\tteardown();\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tthrow new SpeechRuntimeError(`MediaStreamTrack capture failed: ${message}`);\n\t}\n\n\tawait new Promise<void>((resolve) => {\n\t\tconst finish = () => {\n\t\t\tteardown();\n\t\t\tvoid writeChain.finally(resolve);\n\t\t};\n\t\ttrack.addEventListener('ended', finish, { once: true });\n\t\tsignal?.addEventListener(\n\t\t\t'abort',\n\t\t\t() => {\n\t\t\t\tvoid utterance.cancel().catch(() => undefined);\n\t\t\t\tfinish();\n\t\t\t},\n\t\t\t{ once: true }\n\t\t);\n\t\tif (track.readyState === 'ended' || signal?.aborted) {\n\t\t\tfinish();\n\t\t}\n\t});\n}\n","import type { IframeHost } from '../embed/iframeHost';\nimport type { SpeakBehavior } from '../types/sessionInteraction';\nimport type { SpeechResult as EmbedSpeakResult } from '../types/sessionInteraction';\nimport {\n\tSPEECH_LIMITS,\n\tSpeechBufferTimeoutError,\n\tSpeechRuntimeError,\n\tSpeechUtteranceClosedError,\n\tUnsupportedSpeechOperationError,\n\tassertSpeechPlayAudio,\n\temptyAnimationResult,\n\tisSpeechPlayEncodedAudio,\n\tisSpeechPlayMediaStreamAudio,\n\tisSpeechPlayPcmAudio,\n\tisSpeechPlayUrlAudio,\n\tqueuePolicyToEmbedBehavior,\n\tresolveCreateUtteranceFormat,\n\ttoUint8Array,\n\ttype SpeechAudioFormat,\n\ttype SpeechController,\n\ttype SpeechCreateUtteranceOptions,\n\ttype SpeechInterruptOptions,\n\ttype SpeechPlayAudio,\n\ttype SpeechPlayEmbedSource,\n\ttype SpeechPlayOptions,\n\ttype SpeechResult,\n\ttype SpeechSpeakOptions,\n\ttype SpeechTerminalStatus,\n\ttype SpeechUtterance,\n\ttype SpeechUtteranceCloseOptions,\n\ttype SpeechWriteOptions,\n\ttype StreamingSpeechQueuePolicy\n} from './types';\nimport { pipeMediaStreamTrackToUtterance } from './captureMediaStreamTrack';\n\nasync function toSpeechPlayEmbedSource(\n\taudio: SpeechPlayAudio,\n\tsignal?: AbortSignal\n): Promise<SpeechPlayEmbedSource> {\n\tif (isSpeechPlayMediaStreamAudio(audio)) {\n\t\tthrow new SpeechRuntimeError('MediaStreamTrack play is handled on the host before embed.');\n\t}\n\tif (isSpeechPlayPcmAudio(audio)) {\n\t\tconst data = toUint8Array(audio.data);\n\t\tif ((data.byteLength & 1) !== 0) {\n\t\t\tthrow new SpeechRuntimeError('PCM byte length must be even.');\n\t\t}\n\t\treturn {\n\t\t\tkind: 'pcm',\n\t\t\tdata: data.slice().buffer,\n\t\t\tformat: audio.format\n\t\t};\n\t}\n\tif (isSpeechPlayUrlAudio(audio)) {\n\t\treturn {\n\t\t\tkind: 'url',\n\t\t\turl: audio.url.trim(),\n\t\t\t...(audio.sampleRate !== undefined ? { sampleRate: audio.sampleRate } : {})\n\t\t};\n\t}\n\tif (!isSpeechPlayEncodedAudio(audio)) {\n\t\tthrow new SpeechRuntimeError('Unsupported speech.play audio shape.');\n\t}\n\tlet bytes: Uint8Array;\n\tif (typeof Blob !== 'undefined' && audio.data instanceof Blob) {\n\t\tif (signal?.aborted) throw new DOMException('Play aborted.', 'AbortError');\n\t\tbytes = new Uint8Array(await audio.data.arrayBuffer());\n\t} else {\n\t\tbytes = toUint8Array(audio.data as ArrayBuffer | Uint8Array);\n\t}\n\treturn {\n\t\tkind: 'encoded',\n\t\tdata: bytes.slice().buffer,\n\t\tencoding: audio.encoding.trim(),\n\t\t...(audio.sampleRate !== undefined ? { sampleRate: audio.sampleRate } : {})\n\t};\n}\n\nexport type {\n\tSpeechAudioFormat,\n\tSpeechController,\n\tSpeechCreateUtteranceOptions,\n\tSpeechInterruptOptions,\n\tSpeechPlayOptions,\n\tSpeechResult,\n\tSpeechSpeakOptions,\n\tSpeechUtterance,\n\tSpeechUtteranceCloseOptions,\n\tSpeechWriteOptions\n} from './types';\n\nexport type SpeechCapabilities = {\n\treadonly textSpeech: boolean;\n\treadonly externalSpeechAudio: boolean;\n};\n\nexport type SpeechControllerHost = {\n\treadonly getIframeHost: () => IframeHost | null;\n\treadonly assertSessionStarted: () => void;\n\treadonly getCapabilities: () => SpeechCapabilities;\n\treadonly getActiveCharacterId: () => string;\n\treadonly onProcessorInterrupt?: () => void;\n};\n\ntype PendingWrite = {\n\treadonly resolve: () => void;\n\treadonly reject: (error: Error) => void;\n};\n\nfunction newUtteranceId(): string {\n\tif (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n\t\treturn `utt_${crypto.randomUUID().replace(/-/g, '').slice(0, 12)}`;\n\t}\n\treturn `utt_${Date.now().toString(36)}${Math.random().toString(36).slice(2, 8)}`;\n}\n\nfunction mapEmbedSpeakResult(\n\tresult: EmbedSpeakResult,\n\tstatus: SpeechTerminalStatus = 'completed'\n): SpeechResult {\n\treturn {\n\t\tutteranceId: result.speechId,\n\t\tcharacterId: result.characterId,\n\t\tstatus,\n\t\tdurationMs: result.durationMs,\n\t\ttranscript: result.text,\n\t\tanimation: {\n\t\t\tmode: 'tracked',\n\t\t\ttrackedDurationMs: result.durationMs,\n\t\t\tfallbackDurationMs: 0\n\t\t}\n\t};\n}\n\nfunction settleError(error: unknown): Error {\n\tif (error instanceof Error) return error;\n\treturn new SpeechRuntimeError(String(error));\n}\n\nfunction isAbortError(error: unknown): boolean {\n\treturn (\n\t\t(error instanceof DOMException && error.name === 'AbortError') ||\n\t\t(error instanceof Error && error.name === 'AbortError')\n\t);\n}\n\nclass HostSpeechUtterance implements SpeechUtterance {\n\treadonly id: string;\n\treadonly characterId: string;\n\treadonly result: Promise<SpeechResult>;\n\n\tprivate readonly format: SpeechAudioFormat;\n\tprivate readonly host: SpeechControllerHost;\n\tprivate readonly signal?: AbortSignal;\n\tprivate settleResolve!: (value: SpeechResult) => void;\n\tprivate settleReject!: (error: Error) => void;\n\tprivate settled = false;\n\tprivate closed = false;\n\tprivate writeChain: Promise<void> = Promise.resolve();\n\tprivate pendingWriteCount = 0;\n\tprivate readonly pendingWriteWaiters: PendingWrite[] = [];\n\tprivate bufferedMs = 0;\n\tprivate readonly sampleRate: number;\n\tprivate readonly beginPromise: Promise<void>;\n\n\tconstructor(options: {\n\t\treadonly id: string;\n\t\treadonly characterId: string;\n\t\treadonly format: SpeechAudioFormat;\n\t\treadonly queue: StreamingSpeechQueuePolicy;\n\t\treadonly host: SpeechControllerHost;\n\t\treadonly transcript?: string;\n\t\treadonly signal?: AbortSignal;\n\t}) {\n\t\tthis.id = options.id;\n\t\tthis.characterId = options.characterId;\n\t\tthis.format = options.format;\n\t\tthis.host = options.host;\n\t\tthis.signal = options.signal;\n\t\tthis.sampleRate = options.format.sampleRate;\n\t\tthis.result = new Promise<SpeechResult>((resolve, reject) => {\n\t\t\tthis.settleResolve = resolve;\n\t\t\tthis.settleReject = reject;\n\t\t});\n\t\t// Prevent unhandled rejection if caller only uses close()/cancel().\n\t\tvoid this.result.catch(() => undefined);\n\n\t\tconst iframe = this.requireHost();\n\t\tthis.beginPromise = iframe\n\t\t\t.speechUtteranceBegin({\n\t\t\t\tutteranceId: this.id,\n\t\t\t\tformat: this.format,\n\t\t\t\tcharacterId: this.characterId,\n\t\t\t\tqueue: options.queue,\n\t\t\t\t...(options.transcript !== undefined ? { transcript: options.transcript } : {})\n\t\t\t})\n\t\t\t.catch((error: unknown) => {\n\t\t\t\tthis.fail(settleError(error));\n\t\t\t\tthrow settleError(error);\n\t\t\t});\n\n\t\tthis.signal?.addEventListener(\n\t\t\t'abort',\n\t\t\t() => {\n\t\t\t\tvoid this.cancel();\n\t\t\t},\n\t\t\t{ once: true }\n\t\t);\n\t}\n\n\tasync write(audio: ArrayBuffer | Uint8Array, options?: SpeechWriteOptions): Promise<void> {\n\t\tif (this.closed || this.settled) {\n\t\t\tthrow new SpeechUtteranceClosedError();\n\t\t}\n\t\tawait this.beginPromise.catch(() => undefined);\n\t\tif (this.closed || this.settled) {\n\t\t\tthrow new SpeechUtteranceClosedError();\n\t\t}\n\t\tconst bytes = toUint8Array(audio);\n\t\tif ((bytes.byteLength & 1) !== 0) {\n\t\t\tthrow new SpeechRuntimeError('PCM chunk byte length must be even (complete s16 samples).');\n\t\t}\n\t\tif (bytes.byteLength > SPEECH_LIMITS.MAX_CHUNK_BYTES) {\n\t\t\tthrow new SpeechRuntimeError(\n\t\t\t\t`PCM chunk exceeds MAX_CHUNK_BYTES (${SPEECH_LIMITS.MAX_CHUNK_BYTES}).`\n\t\t\t);\n\t\t}\n\t\tif (bytes.byteLength === 0) return;\n\n\t\tconst timeoutMs = options?.timeoutMs ?? SPEECH_LIMITS.DEFAULT_WRITE_TIMEOUT_MS;\n\t\tawait this.acquireWriteSlot(timeoutMs);\n\n\t\tconst chunkMs = (bytes.byteLength / 2 / this.sampleRate) * 1000;\n\t\tif (this.bufferedMs + chunkMs > SPEECH_LIMITS.MAX_UTTERANCE_DURATION_MS) {\n\t\t\tthis.releaseWriteSlot();\n\t\t\tthrow new SpeechRuntimeError('Utterance exceeds MAX_UTTERANCE_DURATION_MS.');\n\t\t}\n\n\t\tconst run = async () => {\n\t\t\tif (this.closed || this.settled) {\n\t\t\t\tthrow new SpeechUtteranceClosedError();\n\t\t\t}\n\t\t\tconst iframe = this.requireHost();\n\t\t\tconst copy = bytes.slice();\n\t\t\tconst writePromise = iframe.speechUtteranceWrite({\n\t\t\t\tutteranceId: this.id,\n\t\t\t\taudio: copy.buffer,\n\t\t\t\tbyteLength: copy.byteLength\n\t\t\t});\n\t\t\tconst ack = await Promise.race([\n\t\t\t\twritePromise,\n\t\t\t\tnew Promise<never>((_, reject) => {\n\t\t\t\t\tsetTimeout(() => reject(new SpeechBufferTimeoutError()), timeoutMs);\n\t\t\t\t})\n\t\t\t]);\n\t\t\tthis.bufferedMs = Math.max(0, ack.bufferedMs);\n\t\t};\n\n\t\tconst tracked = this.writeChain.then(run, run);\n\t\tthis.writeChain = tracked.then(\n\t\t\t() => undefined,\n\t\t\t() => undefined\n\t\t);\n\t\ttry {\n\t\t\tawait tracked;\n\t\t} finally {\n\t\t\tthis.releaseWriteSlot();\n\t\t}\n\t}\n\n\tasync setTranscript(text: string): Promise<void> {\n\t\tif (this.closed || this.settled) {\n\t\t\tthrow new SpeechUtteranceClosedError();\n\t\t}\n\t\tawait this.beginPromise.catch(() => undefined);\n\t\tif (this.closed || this.settled) {\n\t\t\tthrow new SpeechUtteranceClosedError();\n\t\t}\n\t\tconst iframe = this.requireHost();\n\t\tawait iframe.speechUtteranceSetTranscript({\n\t\t\tutteranceId: this.id,\n\t\t\ttranscript: text\n\t\t});\n\t}\n\n\tasync close(options?: SpeechUtteranceCloseOptions): Promise<SpeechResult> {\n\t\tif (this.settled) return this.result;\n\t\tthis.closed = true;\n\t\tawait this.beginPromise.catch(() => undefined);\n\t\tawait this.writeChain.catch(() => undefined);\n\t\ttry {\n\t\t\tconst iframe = this.requireHost();\n\t\t\tconst result = await iframe.speechUtteranceClose({\n\t\t\t\tutteranceId: this.id,\n\t\t\t\ttranscript: options?.transcript,\n\t\t\t\thistory: options?.history ?? 'none'\n\t\t\t});\n\t\t\tthis.resolve(result);\n\t\t\treturn result;\n\t\t} catch (error) {\n\t\t\tconst err = settleError(error);\n\t\t\tthis.fail(err);\n\t\t\tthrow err;\n\t\t}\n\t}\n\n\tasync cancel(): Promise<SpeechResult> {\n\t\tif (this.settled) return this.result;\n\t\tthis.closed = true;\n\t\ttry {\n\t\t\tconst iframe = this.host.getIframeHost();\n\t\t\tif (iframe) {\n\t\t\t\tconst result = await iframe.speechUtteranceCancel({ utteranceId: this.id });\n\t\t\t\tthis.resolve(result);\n\t\t\t\treturn result;\n\t\t\t}\n\t\t\tconst cancelled: SpeechResult = {\n\t\t\t\tutteranceId: this.id,\n\t\t\t\tcharacterId: this.characterId,\n\t\t\t\tstatus: 'cancelled',\n\t\t\t\tdurationMs: 0,\n\t\t\t\tanimation: emptyAnimationResult('none')\n\t\t\t};\n\t\t\tthis.resolve(cancelled);\n\t\t\treturn cancelled;\n\t\t} catch (error) {\n\t\t\tconst err = settleError(error);\n\t\t\tthis.fail(err);\n\t\t\tthrow err;\n\t\t}\n\t}\n\n\t/** Adopt an interrupt settlement from the player without a second cancel RPC. */\n\tadoptExternalResult(result: SpeechResult): void {\n\t\tthis.resolve(result);\n\t}\n\n\tprivate requireHost(): IframeHost {\n\t\tconst iframe = this.host.getIframeHost();\n\t\tif (!iframe) {\n\t\t\tthrow new SpeechRuntimeError('Speech session is not attached.');\n\t\t}\n\t\treturn iframe;\n\t}\n\n\tprivate resolve(result: SpeechResult): void {\n\t\tif (this.settled) return;\n\t\tthis.settled = true;\n\t\tthis.settleResolve(Object.freeze(result));\n\t\tthis.flushWriteWaiters(new SpeechUtteranceClosedError());\n\t}\n\n\tprivate fail(error: Error): void {\n\t\tif (this.settled) return;\n\t\tthis.settled = true;\n\t\tthis.settleReject(error);\n\t\tthis.flushWriteWaiters(error);\n\t}\n\n\tprivate async acquireWriteSlot(timeoutMs: number): Promise<void> {\n\t\tif (this.pendingWriteCount < SPEECH_LIMITS.MAX_PENDING_WRITES) {\n\t\t\tthis.pendingWriteCount += 1;\n\t\t\treturn;\n\t\t}\n\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\tconst timer = setTimeout(() => {\n\t\t\t\treject(new SpeechBufferTimeoutError());\n\t\t\t}, timeoutMs);\n\t\t\tthis.pendingWriteWaiters.push({\n\t\t\t\tresolve: () => {\n\t\t\t\t\tclearTimeout(timer);\n\t\t\t\t\tthis.pendingWriteCount += 1;\n\t\t\t\t\tresolve();\n\t\t\t\t},\n\t\t\t\treject: (error) => {\n\t\t\t\t\tclearTimeout(timer);\n\t\t\t\t\treject(error);\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\t}\n\n\tprivate releaseWriteSlot(): void {\n\t\tthis.pendingWriteCount = Math.max(0, this.pendingWriteCount - 1);\n\t\tconst next = this.pendingWriteWaiters.shift();\n\t\tnext?.resolve();\n\t}\n\n\tprivate flushWriteWaiters(error: Error): void {\n\t\twhile (this.pendingWriteWaiters.length > 0) {\n\t\t\tthis.pendingWriteWaiters.shift()?.reject(error);\n\t\t}\n\t}\n}\n\nexport function createSpeechController(host: SpeechControllerHost): SpeechController {\n\tlet openUtterance: HostSpeechUtterance | null = null;\n\n\tconst requireCapability = (operation: string, capability: keyof SpeechCapabilities) => {\n\t\tconst caps = host.getCapabilities();\n\t\tif (!caps[capability]) {\n\t\t\tthrow new UnsupportedSpeechOperationError(operation, capability);\n\t\t}\n\t};\n\n\tconst controller: SpeechController = {\n\t\tasync speak(options: SpeechSpeakOptions): Promise<SpeechResult> {\n\t\t\thost.assertSessionStarted();\n\t\t\trequireCapability('speak', 'textSpeech');\n\t\t\tif (options.signal?.aborted) {\n\t\t\t\tthrow new DOMException('Speak aborted.', 'AbortError');\n\t\t\t}\n\t\t\tconst queue = options.queue ?? 'append';\n\t\t\tconst behavior: SpeakBehavior = queuePolicyToEmbedBehavior(queue);\n\t\t\tif (behavior === 'interrupt') {\n\t\t\t\thost.onProcessorInterrupt?.();\n\t\t\t\tif (openUtterance) {\n\t\t\t\t\tvoid openUtterance.cancel();\n\t\t\t\t\topenUtterance = null;\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst iframe = host.getIframeHost();\n\t\t\tif (!iframe) throw new SpeechRuntimeError('Speech session is not attached.');\n\n\t\t\tconst speakPromise = iframe.speak({\n\t\t\t\ttext: options.text,\n\t\t\t\tcharacterId: options.characterId,\n\t\t\t\tbehavior\n\t\t\t});\n\n\t\t\tif (options.signal) {\n\t\t\t\tconst signal = options.signal;\n\t\t\t\treturn await new Promise<SpeechResult>((resolve, reject) => {\n\t\t\t\t\tconst onAbort = () => {\n\t\t\t\t\t\tvoid iframe.speechInterrupt({ scope: 'active' }).catch(() => undefined);\n\t\t\t\t\t\treject(new DOMException('Speak aborted.', 'AbortError'));\n\t\t\t\t\t};\n\t\t\t\t\tif (signal.aborted) {\n\t\t\t\t\t\tonAbort();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tsignal.addEventListener('abort', onAbort, { once: true });\n\t\t\t\t\tspeakPromise.then(\n\t\t\t\t\t\t(legacy) => {\n\t\t\t\t\t\t\tsignal.removeEventListener('abort', onAbort);\n\t\t\t\t\t\t\tresolve(mapEmbedSpeakResult(legacy, 'completed'));\n\t\t\t\t\t\t},\n\t\t\t\t\t\t(error: unknown) => {\n\t\t\t\t\t\t\tsignal.removeEventListener('abort', onAbort);\n\t\t\t\t\t\t\tif (isAbortError(error)) {\n\t\t\t\t\t\t\t\tresolve({\n\t\t\t\t\t\t\t\t\tutteranceId: `utt_interrupted`,\n\t\t\t\t\t\t\t\t\tcharacterId: options.characterId ?? host.getActiveCharacterId(),\n\t\t\t\t\t\t\t\t\tstatus: 'interrupted',\n\t\t\t\t\t\t\t\t\tdurationMs: 0,\n\t\t\t\t\t\t\t\t\ttranscript: options.text,\n\t\t\t\t\t\t\t\t\tanimation: emptyAnimationResult('none')\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treject(settleError(error));\n\t\t\t\t\t\t}\n\t\t\t\t\t);\n\t\t\t\t});\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst legacy = await speakPromise;\n\t\t\t\treturn mapEmbedSpeakResult(legacy, 'completed');\n\t\t\t} catch (error) {\n\t\t\t\tif (isAbortError(error)) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tutteranceId: `utt_interrupted`,\n\t\t\t\t\t\tcharacterId: options.characterId ?? host.getActiveCharacterId(),\n\t\t\t\t\t\tstatus: 'interrupted',\n\t\t\t\t\t\tdurationMs: 0,\n\t\t\t\t\t\ttranscript: options.text,\n\t\t\t\t\t\tanimation: emptyAnimationResult('none')\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\tthrow settleError(error);\n\t\t\t}\n\t\t},\n\n\t\tasync play(options: SpeechPlayOptions): Promise<SpeechResult> {\n\t\t\thost.assertSessionStarted();\n\t\t\trequireCapability('play', 'externalSpeechAudio');\n\t\t\tassertSpeechPlayAudio(options.audio);\n\t\t\tif (options.signal?.aborted) {\n\t\t\t\tthrow new DOMException('Play aborted.', 'AbortError');\n\t\t\t}\n\t\t\tconst queue = options.queue ?? 'append';\n\t\t\tif (queue === 'replace-active' || queue === 'replace-all') {\n\t\t\t\thost.onProcessorInterrupt?.();\n\t\t\t\tif (openUtterance) {\n\t\t\t\t\tvoid openUtterance.cancel();\n\t\t\t\t\topenUtterance = null;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (isSpeechPlayMediaStreamAudio(options.audio)) {\n\t\t\t\tconst sampleRate = options.audio.sampleRate ?? 24_000;\n\t\t\t\tconst utterance = controller.createUtterance({\n\t\t\t\t\tformat: { encoding: 'pcm_s16le', sampleRate, channels: 1 },\n\t\t\t\t\ttrack: options.audio.track,\n\t\t\t\t\tcharacterId: options.characterId,\n\t\t\t\t\tqueue: queue === 'append' ? 'replace-active' : queue,\n\t\t\t\t\tsignal: options.signal\n\t\t\t\t});\n\t\t\t\treturn utterance.result;\n\t\t\t}\n\n\t\t\tconst iframe = host.getIframeHost();\n\t\t\tif (!iframe) throw new SpeechRuntimeError('Speech session is not attached.');\n\t\t\tconst source = await toSpeechPlayEmbedSource(options.audio, options.signal);\n\t\t\treturn iframe.speechPlay({\n\t\t\t\tsource,\n\t\t\t\tcharacterId: options.characterId,\n\t\t\t\ttranscript: options.transcript,\n\t\t\t\thistory: options.history ?? 'none',\n\t\t\t\tqueue,\n\t\t\t\tsignal: options.signal\n\t\t\t});\n\t\t},\n\n\t\tcreateUtterance(options: SpeechCreateUtteranceOptions): SpeechUtterance {\n\t\t\thost.assertSessionStarted();\n\t\t\trequireCapability('createUtterance', 'externalSpeechAudio');\n\t\t\tconst format = resolveCreateUtteranceFormat(options);\n\t\t\tif (options.track && options.track.kind !== 'audio') {\n\t\t\t\tthrow new SpeechRuntimeError('createUtterance track must be an audio MediaStreamTrack.');\n\t\t\t}\n\t\t\tconst queue: StreamingSpeechQueuePolicy = options.queue ?? 'replace-active';\n\t\t\tif (queue !== 'replace-active' && queue !== 'replace-all') {\n\t\t\t\tthrow new SpeechRuntimeError('createUtterance queue must be replace-active or replace-all.');\n\t\t\t}\n\t\t\thost.onProcessorInterrupt?.();\n\t\t\tif (openUtterance) {\n\t\t\t\tvoid openUtterance.cancel();\n\t\t\t\topenUtterance = null;\n\t\t\t}\n\t\t\tconst utterance = new HostSpeechUtterance({\n\t\t\t\tid: newUtteranceId(),\n\t\t\t\tcharacterId: options.characterId ?? host.getActiveCharacterId(),\n\t\t\t\tformat,\n\t\t\t\tqueue,\n\t\t\t\thost,\n\t\t\t\t...(options.transcript !== undefined ? { transcript: options.transcript } : {}),\n\t\t\t\tsignal: options.signal\n\t\t\t});\n\t\t\topenUtterance = utterance;\n\t\t\tvoid utterance.result.finally(() => {\n\t\t\t\tif (openUtterance === utterance) openUtterance = null;\n\t\t\t});\n\t\t\tif (options.track) {\n\t\t\t\tvoid (async () => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait pipeMediaStreamTrackToUtterance({\n\t\t\t\t\t\t\ttrack: options.track!,\n\t\t\t\t\t\t\tutterance,\n\t\t\t\t\t\t\tsampleRate: format.sampleRate,\n\t\t\t\t\t\t\tsignal: options.signal\n\t\t\t\t\t\t});\n\t\t\t\t\t\tif (options.signal?.aborted) return;\n\t\t\t\t\t\tawait utterance.close({ history: 'none' });\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tawait utterance.cancel().catch(() => undefined);\n\t\t\t\t\t}\n\t\t\t\t})();\n\t\t\t}\n\t\t\treturn utterance;\n\t\t},\n\n\t\tasync interrupt(options?: SpeechInterruptOptions): Promise<readonly SpeechResult[]> {\n\t\t\thost.assertSessionStarted();\n\t\t\thost.onProcessorInterrupt?.();\n\t\t\tconst iframe = host.getIframeHost();\n\t\t\tif (!iframe) return [];\n\t\t\tconst results = await iframe.speechInterrupt(options ?? { scope: 'active' });\n\t\t\tif (openUtterance) {\n\t\t\t\tconst match = results.find((r) => r.utteranceId === openUtterance!.id);\n\t\t\t\tif (match) openUtterance.adoptExternalResult(match);\n\t\t\t\topenUtterance = null;\n\t\t\t}\n\t\t\treturn results;\n\t\t}\n\t};\n\n\treturn controller;\n}\n","export type ExperienceMode = 'conversation' | 'presenter';\nexport type ResponseMode = 'managed' | 'custom' | 'manual';\nexport type SpeechInputMode = 'auto' | 'manual' | 'off';\n/**\n * Viewport framing for the composed location + avatar scene.\n * - `full` (default): height-fit the full avatar into the host div.\n * - `medium`: height-fit a bust window — 0.75H above and 2H below the mesh face oval (3.75H total).\n * - `face`: cover-zoom the scene onto the mesh face oval (inset / PIP use cases).\n */\nexport type ExperienceFit = 'full' | 'face' | 'medium';\n\nexport function parseExperienceFit(value: string | null | undefined): ExperienceFit | undefined {\n\tconst trimmed = value?.trim();\n\treturn trimmed === 'full' || trimmed === 'face' || trimmed === 'medium' ? trimmed : undefined;\n}\n\nexport type SessionInteractionModes = {\n\treadonly mode: ExperienceMode;\n\treadonly responseMode: ResponseMode;\n\treadonly speechInputMode: SpeechInputMode;\n\treadonly processorId?: string;\n};\n\nexport type StartedEvent = {\n\treadonly mode: ExperienceMode;\n};\n\nexport type StartButtonPlacement = 'center' | 'bottom-center' | 'bottom-left' | 'bottom-right';\nexport type StartButtonVariant = 'primary' | 'secondary' | 'minimal';\nexport type StartButtonSize = 'small' | 'medium' | 'large';\nexport type StartButtonShadow = 'none' | 'soft' | 'strong';\n\nexport type StartButtonAppearance = {\n\treadonly backgroundColor?: string;\n\treadonly textColor?: string;\n\treadonly borderColor?: string;\n\treadonly borderRadiusPx?: number;\n\treadonly size?: StartButtonSize;\n\treadonly shadow?: StartButtonShadow;\n};\n\nexport type StartButtonOptions = {\n\treadonly label?: string;\n\treadonly ariaLabel?: string;\n\treadonly placement?: StartButtonPlacement;\n\treadonly variant?: StartButtonVariant;\n\treadonly appearance?: StartButtonAppearance;\n};\n\nexport type SpeakBehavior = 'enqueue' | 'interrupt';\n\nexport type SpeakOptions = {\n\treadonly text: string;\n\treadonly characterId?: string;\n\treadonly behavior?: SpeakBehavior;\n};\n\n/** @deprecated Prefer {@link import('../speech').SpeechResult} from Experience.speech. */\nexport type TextSpeakResult = {\n\treadonly speechId: string;\n\treadonly turnId: string;\n\treadonly characterId: string;\n\treadonly text: string;\n\treadonly durationMs: number;\n};\n\n/** @deprecated Alias for TextSpeakResult — use Experience.speech SpeechResult for new code. */\nexport type SpeechResult = TextSpeakResult;\n\nexport type UtteranceResult = {\n\treadonly utteranceId: string;\n\treadonly text: string;\n};\n\nexport type UserTranscriptUpdate = {\n\treadonly utteranceId: string;\n\treadonly text: string;\n\treadonly revision: number;\n\treadonly isFinal: boolean;\n\treadonly delta?: string;\n};\n\nexport type ConversationMessageSource = 'user' | 'llm' | 'speak' | 'opening' | 'speechAudio';\n\nexport type ConversationMessage = {\n\treadonly messageId: string;\n\treadonly turnId: string;\n\treadonly role: 'user' | 'assistant';\n\treadonly text: string;\n\treadonly characterId?: string;\n\treadonly source: ConversationMessageSource;\n\treadonly status: 'final';\n\treadonly timestamp: number;\n};\n\nexport type CharacterSpeechSource = 'llm' | 'speak' | 'opening' | 'speechAudio';\n\nexport type CharacterSpeechTerminalReason =\n\t| 'completed'\n\t| 'interrupted'\n\t| 'error'\n\t| 'destroyed';\n\nexport type CharacterSpeechEvent = {\n\treadonly speechId: string;\n\treadonly turnId: string;\n\treadonly characterId: string;\n\treadonly text: string;\n\treadonly source: CharacterSpeechSource;\n\treadonly durationMs?: number;\n\treadonly reason?: CharacterSpeechTerminalReason;\n};\n\nexport type SessionModeChange = 'speaking' | 'listening' | 'thinking';\n\nexport type UserSpeechBoundaryEvent = {\n\treadonly utteranceId?: string;\n};\n\nexport type ConversationProcessorResult =\n\t| string\n\t| {\n\t\t\treadonly text: string;\n\t\t\treadonly characterId?: string;\n\t\t\treadonly behavior?: SpeakBehavior;\n\t };\n\nexport type ConversationProcessorContext = {\n\treadonly utteranceId: string;\n\treadonly text: string;\n\treadonly conversation: readonly ConversationMessage[];\n\treadonly signal: AbortSignal;\n};\n\nexport type ConversationProcessorFn = (\n\tcontext: ConversationProcessorContext\n) =>\n\t| ConversationProcessorResult\n\t| Promise<ConversationProcessorResult>\n\t| AsyncIterable<string>;\n\nexport type NormalizedProcessorSpeech = {\n\treadonly text: string;\n\treadonly characterId?: string;\n\treadonly behavior?: SpeakBehavior;\n};\n\nexport type ProcessorDispatchResult =\n\t| { readonly kind: 'complete'; readonly speech: NormalizedProcessorSpeech }\n\t| { readonly kind: 'stream'; readonly stream: AsyncIterable<string> };\n\nexport type ConversationProcessorErrorEvent = {\n\treadonly utteranceId: string;\n\treadonly message: string;\n};\n\nexport type ListenOnceOptions = {\n\treadonly signal?: AbortSignal;\n\treadonly timeoutMs?: number;\n};\n","import { fetchSessionLaunch, mintBrowserSession } from './api/mintPublicSession';\nimport { dispatchConversationProcessor } from './conversationProcessor';\nimport { createIframeHost, type IframeHost } from './embed/iframeHost';\nimport { createReadyGate } from './embed/readyGate';\nimport type { CloseEvent, EmbedState } from './embed/protocol';\nimport {\n\tbuildManifestEmbedUrl,\n\tresolvePlayerOrigin,\n\tresolvePublicPlayerEmbedUrl,\n\tresolveSceneHintId\n} from './shims/embedBaseUrl';\nimport type {\n\tCloseButtonOptions,\n\tPlayerStatus,\n\tSession,\n\tSessionActivity,\n\tSessionLaunchResponse\n} from './types/sessionLaunch';\nimport { warnLocaleVoiceFallbacks } from './types/sessionLaunch';\nimport {\n\tclearExperienceEventRegistry,\n\tcreateExperienceEventEnvelope,\n\tcreateExperienceEventRegistry,\n\temitExperienceEvent,\n\tsubscribeExperienceEvent,\n\ttype ExperienceEventName,\n\ttype ExperienceEvents\n} from './experienceEvents';\nimport {\n\tcreateSpeechController,\n\ttype SpeechCapabilities,\n\ttype SpeechController\n} from './speech/speechController';\nimport {\n\tparseExperienceFit,\n\ttype CharacterSpeechEvent,\n\ttype ConversationMessage,\n\ttype ConversationProcessorFn,\n\ttype ExperienceFit,\n\ttype ExperienceMode,\n\ttype ListenOnceOptions,\n\ttype SpeechInputMode,\n\ttype StartButtonOptions,\n\ttype StartedEvent,\n\ttype UserTranscriptUpdate,\n\ttype UtteranceResult\n} from './types/sessionInteraction';\nimport type { SessionManifest } from './types/sessionManifest';\n\nexport type { CloseEvent };\nexport type {\n\tCharacterSpeechEvent,\n\tCharacterSpeechSource,\n\tCharacterSpeechTerminalReason,\n\tConversationMessage,\n\tConversationProcessorContext,\n\tConversationProcessorErrorEvent,\n\tConversationProcessorFn,\n\tConversationProcessorResult,\n\tExperienceFit,\n\tListenOnceOptions,\n\tSpeakOptions,\n\tTextSpeakResult,\n\tSpeechResult as LegacySpeakResult,\n\tStartButtonAppearance,\n\tStartButtonOptions,\n\tStartButtonPlacement,\n\tStartButtonShadow,\n\tStartButtonSize,\n\tStartButtonVariant,\n\tUserSpeechBoundaryEvent,\n\tUserTranscriptUpdate,\n\tUtteranceResult\n} from './types/sessionInteraction';\nexport type {\n\tCloseButtonOptions,\n\tPlayerStatus,\n\tSession,\n\tSessionActivity,\n\tSessionCapabilities,\n\tSessionLaunchResponse,\n\tSessionMode\n} from './types/sessionLaunch';\nexport type {\n\tSpeechAudioFormat,\n\tSpeechController,\n\tSpeechCreateUtteranceOptions,\n\tSpeechInterruptOptions,\n\tSpeechPlayAudio,\n\tSpeechPlayEncodedAudio,\n\tSpeechPlayOptions,\n\tSpeechPlayPcmAudio,\n\tSpeechPlayUrlAudio,\n\tSpeechResult,\n\tSpeechSpeakOptions,\n\tSpeechUtterance\n} from './speech';\nexport {\n\tSPEECH_LIMITS,\n\tSpeechBufferTimeoutError,\n\tSpeechRuntimeError,\n\tSpeechUtteranceClosedError,\n\tUnsupportedSpeechOperationError\n} from './speech';\n\nexport type {\n\tExperienceEventEnvelope,\n\tExperienceEventName,\n\tExperienceEvents,\n\tFeedbackFailedEvent,\n\tFeedbackResult\n} from './experienceEvents';\n\ntype StartSessionShared = {\n\treadonly embedBaseUrl?: string;\n\treadonly debug?: boolean;\n\t/** ExperienceWidget warm embed chrome (`?ui=widget`). */\n\treadonly ui?: 'widget';\n\treadonly onStart?: (event: StartedEvent) => void | Promise<void>;\n\treadonly onUserTranscript?: (update: UserTranscriptUpdate) => void | Promise<void>;\n\treadonly conversationProcessor?: ConversationProcessorFn;\n};\n\nexport type StartSessionOptions =\n\t| (StartSessionShared & {\n\t\t\treadonly experienceId: string;\n\t\t\t/** BCP 47 locale (preferred). When omitted in the browser, defaults to `navigator.language`. */\n\t\t\treadonly locale?: string;\n\t\t\treadonly secondaryLocale?: string;\n\t\t\treadonly learningLocale?: string;\n\t\t\t/** @deprecated Prefer `locale`. */\n\t\t\treadonly language?: 'en' | 'es' | string;\n\t\t\treadonly backdropId?: string;\n\t\t\t/** @deprecated Prefer `backdropId`. */\n\t\t\treadonly locationId?: string;\n\t\t\treadonly avatarId?: string;\n\t\t\treadonly castSize?: 2 | 3 | 4;\n\t\t\treadonly mode?: ExperienceMode;\n\t\t\treadonly speechInputMode?: SpeechInputMode;\n\t\t\t/** Voice-only session: skip avatar renderer and location scene asset loading. */\n\t\t\treadonly speechOnly?: boolean;\n\t })\n\t| (StartSessionShared & {\n\t\t\treadonly experienceId: string;\n\t\t\treadonly sessionEndpoint: string;\n\t\t\t/** BCP 47 locale (preferred). When omitted in the browser, defaults to `navigator.language`. */\n\t\t\treadonly locale?: string;\n\t\t\treadonly secondaryLocale?: string;\n\t\t\treadonly learningLocale?: string;\n\t\t\t/** @deprecated Prefer `locale`. */\n\t\t\treadonly language?: 'en' | 'es' | string;\n\t\t\treadonly backdropId?: string;\n\t\t\t/** @deprecated Prefer `backdropId`. */\n\t\t\treadonly locationId?: string;\n\t\t\treadonly avatarId?: string;\n\t\t\treadonly castSize?: 2 | 3 | 4;\n\t\t\treadonly mode?: ExperienceMode;\n\t\t\treadonly speechInputMode?: SpeechInputMode;\n\t\t\t/** Voice-only session: skip avatar renderer and location scene asset loading. */\n\t\t\treadonly speechOnly?: boolean;\n\t })\n\t| (StartSessionShared & {\n\t\t\t/**\n\t\t\t * Opaque signed launch string from a prior Session Launch response.\n\t\t\t * Prefer `sessionEndpoint` so page JS does not hold launch when avoidable.\n\t\t\t */\n\t\t\treadonly launch: string;\n\t\t\treadonly session?: Session;\n\t\t\treadonly backdropId?: string;\n\t\t\t/** @deprecated Prefer `backdropId`. */\n\t\t\treadonly locationId?: string;\n\t\t\treadonly avatarId?: string;\n\t\t\t/** Voice-only session: skip avatar renderer and location scene asset loading. */\n\t\t\treadonly speechOnly?: boolean;\n\t });\n\nexport type AttachOptions = {\n\treadonly container: HTMLElement;\n\t/** Initial scene framing. Change later with {@link Experience.setFit}. */\n\treadonly fit?: ExperienceFit;\n\treadonly startButton?: StartButtonOptions;\n\t/**\n\t * When true (default), the Liforma player shows end-of-session Feedback UI.\n\t * When false, hosts receive scores via `feedback` / `feedbackFailed` and render their own UX.\n\t * Presentation only — does not remint.\n\t */\n\treadonly showFeedback?: boolean;\n\treadonly closeButton?: CloseButtonOptions | boolean;\n\treadonly returnUrl?: string;\n\treadonly onPlayerStatusChange?: (status: PlayerStatus) => void;\n\t/** @deprecated Prefer {@link AttachOptions.onPlayerStatusChange}. */\n\treadonly onStateUpdate?: (state: EmbedState) => void;\n\treadonly onWidgetReady?: () => void;\n\treadonly onWidgetExpand?: () => void;\n\treadonly onWidgetCollapse?: () => void;\n\treadonly onWidgetAudioSettings?: (open: boolean) => void;\n\t/** Docked shell: player surface drag → parent repositions the dock. */\n\treadonly onWidgetDragStart?: (event: { readonly pointerId: number }) => void;\n\treadonly onWidgetDragMove?: (event: {\n\t\treadonly pointerId: number;\n\t\treadonly dx: number;\n\t\treadonly dy: number;\n\t}) => void;\n\treadonly onWidgetDragEnd?: (event: { readonly pointerId: number }) => void;\n\t/**\n\t * Show the in-player close control. Set when the host provides `onClose`\n\t * and/or `returnUrl` / `closeButton`. Widget chrome keeps its own toolbar close separately.\n\t * @deprecated Prefer {@link AttachOptions.closeButton}.\n\t */\n\treadonly showClose?: boolean;\n};\n\nexport type { EmbedState };\n\nexport type ReadyEvent = {\n\treadonly session: Session;\n};\n\nexport type LiformaPlayer = {\n\tclose(): void;\n\tdestroy(): void;\n\ton(event: 'close', handler: (event: CloseEvent) => void): void;\n};\n\ntype SessionBootstrap =\n\t| {\n\t\t\treadonly kind: 'public';\n\t\t\treadonly experienceId: string;\n\t\t\treadonly locale?: string;\n\t\t\treadonly secondaryLocale?: string;\n\t\t\treadonly learningLocale?: string;\n\t\t\treadonly language?: string;\n\t\t\treadonly backdropId?: string;\n\t\t\treadonly locationId?: string;\n\t\t\treadonly avatarId?: string;\n\t\t\treadonly castSize?: 2 | 3 | 4;\n\t\t\treadonly embedBaseUrl?: string;\n\t\t\treadonly debug?: boolean;\n\t\t\treadonly mode?: ExperienceMode;\n\t\t\treadonly speechInputMode?: SpeechInputMode;\n\t\t\treadonly speechOnly?: boolean;\n\t\t\treadonly ui?: 'widget';\n\t\t\treadonly onStart?: (event: StartedEvent) => void | Promise<void>;\n\t\t\treadonly onUserTranscript?: (update: UserTranscriptUpdate) => void | Promise<void>;\n\t\t\treadonly conversationProcessor?: ConversationProcessorFn;\n\t }\n\t| {\n\t\t\treadonly kind: 'sessionEndpoint';\n\t\t\treadonly experienceId: string;\n\t\t\treadonly sessionEndpoint: string;\n\t\t\treadonly locale?: string;\n\t\t\treadonly secondaryLocale?: string;\n\t\t\treadonly learningLocale?: string;\n\t\t\treadonly language?: string;\n\t\t\treadonly backdropId?: string;\n\t\t\treadonly locationId?: string;\n\t\t\treadonly avatarId?: string;\n\t\t\treadonly castSize?: 2 | 3 | 4;\n\t\t\treadonly embedBaseUrl?: string;\n\t\t\treadonly debug?: boolean;\n\t\t\treadonly mode?: ExperienceMode;\n\t\t\treadonly speechInputMode?: SpeechInputMode;\n\t\t\treadonly speechOnly?: boolean;\n\t\t\treadonly ui?: 'widget';\n\t\t\treadonly onStart?: (event: StartedEvent) => void | Promise<void>;\n\t\t\treadonly onUserTranscript?: (update: UserTranscriptUpdate) => void | Promise<void>;\n\t\t\treadonly conversationProcessor?: ConversationProcessorFn;\n\t }\n\t| {\n\t\t\treadonly kind: 'launch';\n\t\t\treadonly launch: string;\n\t\t\treadonly session?: Session;\n\t\t\treadonly backdropId?: string;\n\t\t\treadonly locationId?: string;\n\t\t\treadonly avatarId?: string;\n\t\t\treadonly embedBaseUrl?: string;\n\t\t\treadonly debug?: boolean;\n\t\t\treadonly speechOnly?: boolean;\n\t\t\treadonly ui?: 'widget';\n\t\t\treadonly onStart?: (event: StartedEvent) => void | Promise<void>;\n\t\t\treadonly onUserTranscript?: (update: UserTranscriptUpdate) => void | Promise<void>;\n\t\t\treadonly conversationProcessor?: ConversationProcessorFn;\n\t };\n\nfunction resolveShowClose(options: AttachOptions): boolean {\n\tif (options.showClose === true) return true;\n\tif (options.returnUrl?.trim()) return true;\n\tif (options.closeButton === true) return true;\n\tif (\n\t\toptions.closeButton &&\n\t\ttypeof options.closeButton === 'object' &&\n\t\toptions.closeButton.visible !== false\n\t) {\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nexport class Experience {\n\tprivate readonly bootstrap: SessionBootstrap;\n\tprivate readonly conversationProcessor: ConversationProcessorFn | null;\n\tprivate iframeHost: IframeHost | null = null;\n\tprivate attachInFlight: Promise<LiformaPlayer> | null = null;\n\tprivate endpointAbortController: AbortController | null = null;\n\tprivate processorAbortController: AbortController | null = null;\n\tprivate processorChain: Promise<void> = Promise.resolve();\n\tprivate resolvedSession: Session | null;\n\tprivate launchToken: string | null;\n\tprivate attachReturnUrl: string | null = null;\n\tprivate closed = false;\n\tprivate attached = false;\n\tprivate pendingFit: ExperienceFit | null = null;\n\tprivate readonly closeHandlers: ((event: CloseEvent) => void)[] = [];\n\tprivate readonly eventRegistry = createExperienceEventRegistry();\n\tprivate readyEnvelope: ExperienceEvents['ready'] | null = null;\n\tprivate startedEnvelope: ExperienceEvents['started'] | null = null;\n\tprivate conversationCache: readonly ConversationMessage[] = [];\n\t/** Public speech API — text TTS and BYO PCM (`speak` / `play` / `createUtterance` / `interrupt`). */\n\treadonly speech: SpeechController;\n\n\tprivate constructor(bootstrap: SessionBootstrap) {\n\t\tthis.bootstrap = bootstrap;\n\t\tthis.conversationProcessor = bootstrap.conversationProcessor ?? null;\n\t\tthis.resolvedSession = bootstrap.kind === 'launch' ? (bootstrap.session ?? null) : null;\n\t\tthis.launchToken = bootstrap.kind === 'launch' ? bootstrap.launch : null;\n\t\tthis.speech = createSpeechController({\n\t\t\tgetIframeHost: () => this.iframeHost,\n\t\t\tassertSessionStarted: () => this.assertSessionStarted(),\n\t\t\tgetCapabilities: () => resolveSpeechCapabilities(this.resolvedSession),\n\t\t\tgetActiveCharacterId: () => 'character_unknown',\n\t\t\tonProcessorInterrupt: () => {\n\t\t\t\tthis.processorAbortController?.abort();\n\t\t\t\tthis.processorAbortController = null;\n\t\t\t}\n\t\t});\n\t}\n\n\tstatic async startSession(options: StartSessionOptions): Promise<Experience> {\n\t\tif ('launch' in options && typeof options.launch === 'string') {\n\t\t\tconst launch = options.launch.trim();\n\t\t\tif (!launch) {\n\t\t\t\tthrow new Error('StartSessionOptions.launch must be a non-empty string.');\n\t\t\t}\n\t\t\treturn new Experience({\n\t\t\t\tkind: 'launch',\n\t\t\t\tlaunch,\n\t\t\t\tsession: options.session,\n\t\t\t\tbackdropId: options.backdropId,\n\t\t\t\tlocationId: options.locationId,\n\t\t\t\tavatarId: options.avatarId,\n\t\t\t\tembedBaseUrl: options.embedBaseUrl,\n\t\t\t\tdebug: options.debug,\n\t\t\t\tspeechOnly: options.speechOnly,\n\t\t\t\tui: options.ui,\n\t\t\t\tonStart: options.onStart,\n\t\t\t\tonUserTranscript: options.onUserTranscript,\n\t\t\t\tconversationProcessor: options.conversationProcessor\n\t\t\t});\n\t\t}\n\n\t\tif ('sessionEndpoint' in options) {\n\t\t\treturn new Experience({\n\t\t\t\tkind: 'sessionEndpoint',\n\t\t\t\texperienceId: options.experienceId,\n\t\t\t\tsessionEndpoint: options.sessionEndpoint,\n\t\t\t\tlocale: options.locale,\n\t\t\t\tsecondaryLocale: options.secondaryLocale,\n\t\t\t\tlearningLocale: options.learningLocale,\n\t\t\t\tlanguage: options.language,\n\t\t\t\tbackdropId: options.backdropId,\n\t\t\t\tlocationId: options.locationId,\n\t\t\t\tavatarId: options.avatarId,\n\t\t\t\tcastSize: options.castSize,\n\t\t\t\tembedBaseUrl: options.embedBaseUrl,\n\t\t\t\tdebug: options.debug,\n\t\t\t\tmode: options.mode,\n\t\t\t\tspeechInputMode: options.speechInputMode,\n\t\t\t\tspeechOnly: options.speechOnly,\n\t\t\t\tui: options.ui,\n\t\t\t\tonStart: options.onStart,\n\t\t\t\tonUserTranscript: options.onUserTranscript,\n\t\t\t\tconversationProcessor: options.conversationProcessor\n\t\t\t});\n\t\t}\n\n\t\tif ('experienceId' in options) {\n\t\t\treturn new Experience({\n\t\t\t\tkind: 'public',\n\t\t\t\texperienceId: options.experienceId,\n\t\t\t\tlocale: options.locale,\n\t\t\t\tsecondaryLocale: options.secondaryLocale,\n\t\t\t\tlearningLocale: options.learningLocale,\n\t\t\t\tlanguage: options.language,\n\t\t\t\tbackdropId: options.backdropId,\n\t\t\t\tlocationId: options.locationId,\n\t\t\t\tavatarId: options.avatarId,\n\t\t\t\tcastSize: options.castSize,\n\t\t\t\tembedBaseUrl: options.embedBaseUrl,\n\t\t\t\tdebug: options.debug,\n\t\t\t\tmode: options.mode,\n\t\t\t\tspeechInputMode: options.speechInputMode,\n\t\t\t\tspeechOnly: options.speechOnly,\n\t\t\t\tui: options.ui,\n\t\t\t\tonStart: options.onStart,\n\t\t\t\tonUserTranscript: options.onUserTranscript,\n\t\t\t\tconversationProcessor: options.conversationProcessor\n\t\t\t});\n\t\t}\n\n\t\tthrow new Error(\n\t\t\t'Experience.startSession requires experienceId, sessionEndpoint, or launch.'\n\t\t);\n\t}\n\n\tgetSession(): Session | null {\n\t\treturn this.resolvedSession;\n\t}\n\n\t/**\n\t * @deprecated Session Manifest is no longer a public SDK contract.\n\t * Use {@link Experience.getSession} for the public Session summary.\n\t */\n\tgetManifest(): SessionManifest | null {\n\t\treturn null;\n\t}\n\n\ton<K extends ExperienceEventName>(\n\t\tevent: K,\n\t\thandler: (event: ExperienceEvents[K]) => void\n\t): () => void {\n\t\tconst unsubscribe = subscribeExperienceEvent(this.eventRegistry, event, handler);\n\t\tif (event === 'ready' && this.readyEnvelope) {\n\t\t\tqueueMicrotask(() => handler(this.readyEnvelope as ExperienceEvents[K]));\n\t\t}\n\t\tif (event === 'started' && this.startedEnvelope) {\n\t\t\tqueueMicrotask(() => handler(this.startedEnvelope as ExperienceEvents[K]));\n\t\t}\n\t\treturn unsubscribe;\n\t}\n\n\tasync attach(options: AttachOptions): Promise<LiformaPlayer> {\n\t\tif (this.closed) {\n\t\t\tthrow new Error('Session has ended.');\n\t\t}\n\t\tif (!options.container) {\n\t\t\tthrow new Error('Experience.attach requires a DOM container element.');\n\t\t}\n\t\tif (this.attached && this.iframeHost) {\n\t\t\treturn this.createPlayerFacade();\n\t\t}\n\t\tif (this.attachInFlight) {\n\t\t\treturn this.attachInFlight;\n\t\t}\n\n\t\tthis.attachInFlight = this.attachInternal(options).finally(() => {\n\t\t\tthis.attachInFlight = null;\n\t\t});\n\t\treturn this.attachInFlight;\n\t}\n\n\tprivate async attachInternal(options: AttachOptions): Promise<LiformaPlayer> {\n\t\tif (this.closed) {\n\t\t\tthrow new Error('Session has ended.');\n\t\t}\n\t\tif (this.attached && this.iframeHost) {\n\t\t\treturn this.createPlayerFacade();\n\t\t}\n\t\tif (this.iframeHost) {\n\t\t\tthis.iframeHost.destroy();\n\t\t\tthis.iframeHost = null;\n\t\t}\n\n\t\tthis.attachReturnUrl = options.returnUrl?.trim() || null;\n\n\t\tconst embedBaseUrl = this.bootstrap.embedBaseUrl ?? resolvePublicPlayerEmbedUrl();\n\t\tconst debug = this.bootstrap.debug === true;\n\t\tconst speechOnly = this.bootstrap.speechOnly === true;\n\t\tconst locationId = speechOnly\n\t\t\t? undefined\n\t\t\t: resolveSceneHintId(this.bootstrap.backdropId, this.bootstrap.locationId);\n\t\tconst avatarId = speechOnly ? undefined : this.bootstrap.avatarId;\n\t\t// The SDK owns minting and posts the opaque launch. Do not put experienceId in the\n\t\t// iframe URL or the player will mint a second session before the parent launch arrives.\n\t\tconst iframeSrc = buildManifestEmbedUrl(\n\t\t\tembedBaseUrl,\n\t\t\tdebug,\n\t\t\tlocationId,\n\t\t\tavatarId,\n\t\t\toptions.fit,\n\t\t\tthis.bootstrap.ui,\n\t\t\tresolveShowClose(options),\n\t\t\tthis.bootstrap.kind === 'launch' ? undefined : this.bootstrap.castSize\n\t\t);\n\t\tconst playerOrigin = resolvePlayerOrigin(iframeSrc);\n\n\t\tlet resolveLoadingUi: (() => void) | null = null;\n\t\tconst loadingUiPromise = new Promise<void>((resolve) => {\n\t\t\tresolveLoadingUi = resolve;\n\t\t});\n\t\tlet resolveReadyUi: (() => void) | null = null;\n\t\tconst readyUiPromise = new Promise<void>((resolve) => {\n\t\t\tresolveReadyUi = resolve;\n\t\t});\n\t\tconst readyGate = createReadyGate<Session>((readySession) => {\n\t\t\tif (this.readyEnvelope) return;\n\t\t\tthis.resolvedSession = readySession;\n\t\t\tthis.readyEnvelope = this.emitWrapped('ready', { session: readySession });\n\t\t});\n\n\t\tconst onUserTranscript = this.bootstrap.onUserTranscript;\n\n\t\tconst onPlayerStatus = (status: PlayerStatus) => {\n\t\t\toptions.onPlayerStatusChange?.(status);\n\t\t\toptions.onStateUpdate?.(status);\n\t\t\tif (status === 'loading' && resolveLoadingUi) {\n\t\t\t\tresolveLoadingUi();\n\t\t\t\tresolveLoadingUi = null;\n\t\t\t}\n\t\t\tif (status === 'ready' && resolveReadyUi) {\n\t\t\t\tresolveReadyUi();\n\t\t\t\tresolveReadyUi = null;\n\t\t\t}\n\t\t\tif (status === 'ready') {\n\t\t\t\treadyGate.markReady();\n\t\t\t}\n\t\t};\n\n\t\tconst iframeHost = createIframeHost({\n\t\t\tcontainer: options.container,\n\t\t\tsrc: iframeSrc,\n\t\t\tplayerOrigin,\n\t\t\ttransparent: this.bootstrap.ui === 'widget',\n\t\t\tonClose: (event) => {\n\t\t\t\tconst enriched: CloseEvent =\n\t\t\t\t\tevent.returnUrl || !this.attachReturnUrl\n\t\t\t\t\t\t? event\n\t\t\t\t\t\t: { ...event, returnUrl: this.attachReturnUrl };\n\t\t\t\tfor (const handler of this.closeHandlers) {\n\t\t\t\t\thandler(enriched);\n\t\t\t\t}\n\t\t\t},\n\t\t\tonPlayerStatusChange: onPlayerStatus,\n\t\t\tonUserTranscript: (update) => {\n\t\t\t\tthis.emitWrapped('userTranscript', update);\n\t\t\t\tvoid onUserTranscript?.(update);\n\t\t\t\tthis.queueProcessorTurn(update);\n\t\t\t},\n\t\t\tonUserSpeechStarted: (event) => {\n\t\t\t\tthis.emitWrapped('userSpeechStarted', event);\n\t\t\t},\n\t\t\tonUserSpeechEnded: (event) => {\n\t\t\t\tthis.emitWrapped('userSpeechEnded', event);\n\t\t\t},\n\t\t\tonCharacterSpeechStarted: (event: CharacterSpeechEvent) => {\n\t\t\t\tthis.emitWrapped('characterSpeechStarted', event);\n\t\t\t},\n\t\t\tonCharacterSpeechEnded: (event: CharacterSpeechEvent) => {\n\t\t\t\tthis.emitWrapped('characterSpeechEnded', event);\n\t\t\t},\n\t\t\tonCharacterFocusChanged: (event) => {\n\t\t\t\tthis.emitWrapped('characterFocusChanged', event);\n\t\t\t},\n\t\t\tonListeningState: (listening) => {\n\t\t\t\tthis.emitWrapped('listeningState', listening);\n\t\t\t},\n\t\t\tonActivityChange: (activity: SessionActivity) => {\n\t\t\t\tthis.emitWrapped('activityChange', activity);\n\t\t\t},\n\t\t\tonFeedback: (result) => {\n\t\t\t\tthis.emitWrapped('feedback', result);\n\t\t\t},\n\t\t\tonFeedbackFailed: (event) => {\n\t\t\t\tthis.emitWrapped('feedbackFailed', event);\n\t\t\t},\n\t\t\tonMessage: (message) => {\n\t\t\t\tthis.emitWrapped('message', message);\n\t\t\t},\n\t\t\tonStarted: (event) => {\n\t\t\t\tif (!this.startedEnvelope) {\n\t\t\t\t\tthis.startedEnvelope = this.emitWrapped('started', event);\n\t\t\t\t\tvoid this.bootstrap.onStart?.(event);\n\t\t\t\t}\n\t\t\t},\n\t\t\tonConversationUpdate: (conversation) => {\n\t\t\t\tthis.conversationCache = conversation;\n\t\t\t\tthis.emitWrapped('conversationUpdate', conversation);\n\t\t\t},\n\t\t\tonWidgetReady: () => options.onWidgetReady?.(),\n\t\t\tonWidgetExpand: () => options.onWidgetExpand?.(),\n\t\t\tonWidgetCollapse: () => options.onWidgetCollapse?.(),\n\t\t\tonWidgetAudioSettings: (open) => options.onWidgetAudioSettings?.(open),\n\t\t\tonWidgetDragStart: (event) => options.onWidgetDragStart?.(event),\n\t\t\tonWidgetDragMove: (event) => options.onWidgetDragMove?.(event),\n\t\t\tonWidgetDragEnd: (event) => options.onWidgetDragEnd?.(event)\n\t\t});\n\t\tthis.iframeHost = iframeHost;\n\n\t\tconst mintInteraction = this.resolveMintInteraction();\n\n\t\tconst launchPromise: Promise<SessionLaunchResponse | null> =\n\t\t\tthis.bootstrap.kind === 'sessionEndpoint'\n\t\t\t\t? (() => {\n\t\t\t\t\t\tthis.endpointAbortController = new AbortController();\n\t\t\t\t\t\treturn fetchSessionLaunch(this.bootstrap.sessionEndpoint, {\n\t\t\t\t\t\t\texperienceId: this.bootstrap.experienceId,\n\t\t\t\t\t\t\tlocale: this.bootstrap.locale,\n\t\t\t\t\t\t\tsecondaryLocale: this.bootstrap.secondaryLocale,\n\t\t\t\t\t\t\tlearningLocale: this.bootstrap.learningLocale,\n\t\t\t\t\t\t\tlanguage: this.bootstrap.language,\n\t\t\t\t\t\t\tmode: mintInteraction.mode,\n\t\t\t\t\t\t\tspeechInputMode: mintInteraction.speechInputMode,\n\t\t\t\t\t\t\tspeechOnly: this.bootstrap.speechOnly,\n\t\t\t\t\t\t\tsignal: this.endpointAbortController.signal\n\t\t\t\t\t\t});\n\t\t\t\t\t})()\n\t\t\t\t: this.bootstrap.kind === 'public'\n\t\t\t\t\t? mintBrowserSession(this.bootstrap.experienceId, {\n\t\t\t\t\t\t\tlocale: this.bootstrap.locale,\n\t\t\t\t\t\t\tsecondaryLocale: this.bootstrap.secondaryLocale,\n\t\t\t\t\t\t\tlearningLocale: this.bootstrap.learningLocale,\n\t\t\t\t\t\t\tlanguage: this.bootstrap.language,\n\t\t\t\t\t\t\tmode: mintInteraction.mode,\n\t\t\t\t\t\t\tspeechInputMode: mintInteraction.speechInputMode,\n\t\t\t\t\t\t\tspeechOnly: this.bootstrap.speechOnly\n\t\t\t\t\t\t})\n\t\t\t\t\t: Promise.resolve(\n\t\t\t\t\t\t\tthis.bootstrap.session\n\t\t\t\t\t\t\t\t? { session: this.bootstrap.session, launch: this.bootstrap.launch }\n\t\t\t\t\t\t\t\t: null\n\t\t\t\t\t\t);\n\n\t\tlet launchResponse: SessionLaunchResponse | null;\n\t\ttry {\n\t\t\t[, launchResponse] = await Promise.all([iframeHost.waitForLoad(), launchPromise]);\n\t\t} catch (error) {\n\t\t\tiframeHost.destroy();\n\t\t\tif (this.iframeHost === iframeHost) {\n\t\t\t\tthis.iframeHost = null;\n\t\t\t}\n\t\t\tthrow error;\n\t\t} finally {\n\t\t\tthis.endpointAbortController = null;\n\t\t}\n\t\tiframeHost.postParentHello();\n\n\t\tconst launchToken =\n\t\t\tlaunchResponse?.launch ??\n\t\t\t(this.bootstrap.kind === 'launch' ? this.bootstrap.launch : null);\n\t\tconst session =\n\t\t\tlaunchResponse?.session ??\n\t\t\t(this.bootstrap.kind === 'launch' ? this.bootstrap.session : undefined);\n\n\t\tif (!launchToken) {\n\t\t\tiframeHost.destroy();\n\t\t\tif (this.iframeHost === iframeHost) {\n\t\t\t\tthis.iframeHost = null;\n\t\t\t}\n\t\t\tthrow new Error('Session Launch response did not include a launch token.');\n\t\t}\n\n\t\tthis.launchToken = launchToken;\n\t\tif (session) {\n\t\t\tthis.resolvedSession = session;\n\t\t\t// Mint/endpoint paths already warn inside parseSessionLaunchResponse.\n\t\t\tif (this.bootstrap.kind === 'launch') {\n\t\t\t\twarnLocaleVoiceFallbacks(session);\n\t\t\t}\n\t\t\tiframeHost.postLaunch(launchToken, session, {\n\t\t\t\tstartButton: options.startButton,\n\t\t\t\tshowFeedback: options.showFeedback\n\t\t\t});\n\t\t\treadyGate.setValue(session);\n\t\t} else {\n\t\t\tiframeHost.postLaunch(launchToken, undefined, {\n\t\t\t\tstartButton: options.startButton,\n\t\t\t\tshowFeedback: options.showFeedback\n\t\t\t});\n\t\t}\n\n\t\tawait Promise.race([\n\t\t\tloadingUiPromise,\n\t\t\tnew Promise<void>((_, reject) => {\n\t\t\t\tsetTimeout(\n\t\t\t\t\t() => reject(new Error('Timed out waiting for player loading UI.')),\n\t\t\t\t\t120_000\n\t\t\t\t);\n\t\t\t})\n\t\t]);\n\n\t\tawait Promise.race([\n\t\t\treadyUiPromise,\n\t\t\tnew Promise<void>((_, reject) => {\n\t\t\t\tsetTimeout(\n\t\t\t\t\t() => reject(new Error('Timed out waiting for player ready state.')),\n\t\t\t\t\t120_000\n\t\t\t\t);\n\t\t\t})\n\t\t]);\n\n\t\tthis.attached = true;\n\t\tif (this.pendingFit) {\n\t\t\tawait iframeHost.setFit(this.pendingFit);\n\t\t}\n\t\treturn this.createPlayerFacade();\n\t}\n\n\tasync startListening(): Promise<void> {\n\t\tthis.assertSessionStarted();\n\t\tawait this.iframeHost!.startListening();\n\t}\n\n\tasync stopListening(): Promise<UtteranceResult> {\n\t\tthis.assertSessionStarted();\n\t\treturn this.iframeHost!.stopListening();\n\t}\n\n\tasync listenOnce(options?: ListenOnceOptions): Promise<UtteranceResult> {\n\t\tthis.assertSessionStarted();\n\t\treturn this.iframeHost!.listenOnce({\n\t\t\ttimeoutMs: options?.timeoutMs\n\t\t});\n\t}\n\n\tgetConversation(): readonly ConversationMessage[] {\n\t\treturn this.conversationCache;\n\t}\n\n\t/** Parent → player chrome mode for ExperienceWidget warm/cold expand / dock. */\n\tsetWidgetChrome(mode: 'collapsed' | 'loading' | 'expanded' | 'docked'): void {\n\t\tthis.iframeHost?.postWidgetSetChrome(mode);\n\t}\n\n\t/** Parent → player toggle for mic/speaker settings (ExperienceWidget toolbar). */\n\ttoggleAudioSettings(): void {\n\t\tthis.iframeHost?.postWidgetToggleAudioSettings();\n\t}\n\n\t/** Presentation-only. No-op on single-Character nodes. */\n\tasync focusCharacter(characterId: string): Promise<void> {\n\t\tthis.assertSessionStarted();\n\t\tawait this.iframeHost!.focusCharacter(characterId);\n\t}\n\n\t/** Live scene framing. Does not remint or restart the session. Queues until `attach()` if needed. */\n\tasync setFit(fit: ExperienceFit): Promise<void> {\n\t\tconst parsed = parseExperienceFit(fit);\n\t\tif (!parsed) {\n\t\t\tthrow new Error('setFit requires \"full\", \"medium\", or \"face\".');\n\t\t}\n\t\tif (this.closed) {\n\t\t\tthrow new Error('Session has ended.');\n\t\t}\n\t\tthis.pendingFit = parsed;\n\t\tif (!this.attached || !this.iframeHost) return;\n\t\tawait this.iframeHost.setFit(parsed);\n\t}\n\n\tgetLastTurn(): readonly ConversationMessage[] {\n\t\tif (this.conversationCache.length === 0) return [];\n\t\tconst lastTurnId = this.conversationCache[this.conversationCache.length - 1]!.turnId;\n\t\treturn this.conversationCache.filter((m) => m.turnId === lastTurnId);\n\t}\n\n\tasync end(): Promise<void> {\n\t\tthis.close();\n\t}\n\n\tdestroy(): void {\n\t\tif (this.closed) return;\n\t\tthis.closed = true;\n\t\tthis.processorAbortController?.abort();\n\t\tthis.processorAbortController = null;\n\t\tthis.endpointAbortController?.abort();\n\t\tthis.endpointAbortController = null;\n\t\tthis.attachInFlight = null;\n\t\tthis.iframeHost?.destroy();\n\t\tthis.iframeHost = null;\n\t\tclearExperienceEventRegistry(this.eventRegistry);\n\t\tthis.readyEnvelope = null;\n\t\tthis.startedEnvelope = null;\n\t\tthis.closeHandlers.length = 0;\n\t\tthis.attached = false;\n\t}\n\n\tprivate resolveMintInteraction(): {\n\t\treadonly mode?: ExperienceMode;\n\t\treadonly speechInputMode?: SpeechInputMode;\n\t} {\n\t\tif (this.bootstrap.kind === 'launch') {\n\t\t\treturn {};\n\t\t}\n\t\treturn {\n\t\t\tmode: this.bootstrap.mode,\n\t\t\tspeechInputMode: this.bootstrap.speechInputMode\n\t\t};\n\t}\n\n\tprivate getSessionId(): string {\n\t\treturn this.resolvedSession?.id ?? '';\n\t}\n\n\tprivate emitWrapped<K extends ExperienceEventName>(\n\t\tevent: K,\n\t\tdata: ExperienceEvents[K]['data']\n\t): ExperienceEvents[K] {\n\t\tconst envelope = createExperienceEventEnvelope(event, this.getSessionId(), data) as unknown as ExperienceEvents[K];\n\t\temitExperienceEvent(this.eventRegistry, event, envelope);\n\t\treturn envelope;\n\t}\n\n\tprivate queueProcessorTurn(update: UserTranscriptUpdate): void {\n\t\tif (!update.isFinal || !update.text.trim() || !this.conversationProcessor) return;\n\n\t\tconst processor = this.conversationProcessor;\n\t\tthis.processorAbortController?.abort();\n\t\tthis.processorAbortController = new AbortController();\n\t\tconst signal = this.processorAbortController.signal;\n\n\t\tthis.processorChain = this.processorChain.then(async () => {\n\t\t\tif (signal.aborted) return;\n\t\t\ttry {\n\t\t\t\tconst dispatch = await dispatchConversationProcessor(processor, {\n\t\t\t\t\tutteranceId: update.utteranceId,\n\t\t\t\t\ttext: update.text,\n\t\t\t\t\tconversation: this.conversationCache,\n\t\t\t\t\tsignal\n\t\t\t\t});\n\t\t\t\tif (signal.aborted) return;\n\t\t\t\tif (dispatch.kind === 'complete') {\n\t\t\t\t\tconst speech = dispatch.speech;\n\t\t\t\t\tawait this.speech.speak({\n\t\t\t\t\t\ttext: speech.text,\n\t\t\t\t\t\t...(speech.characterId ? { characterId: speech.characterId } : {}),\n\t\t\t\t\t\tqueue: speech.behavior === 'interrupt' ? 'replace-active' : 'append'\n\t\t\t\t\t});\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tawait this.iframeHost!.speakProcessorStream(dispatch.stream, { signal });\n\t\t\t} catch (error) {\n\t\t\t\tif (signal.aborted) return;\n\t\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\t\tthis.emitWrapped('conversationProcessorError', {\n\t\t\t\t\tutteranceId: update.utteranceId,\n\t\t\t\t\tmessage\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate assertAttached(): void {\n\t\tif (!this.attached || !this.iframeHost) {\n\t\t\tthrow new Error('Call Experience.attach() before using speech APIs.');\n\t\t}\n\t}\n\n\tprivate assertSessionStarted(): void {\n\t\tthis.assertAttached();\n\t\tif (!this.startedEnvelope) {\n\t\t\tthrow new Error(\n\t\t\t\t'Session has not started. Wait for the player start button and the started event before using speak() or listening APIs.'\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate close(): void {\n\t\tif (this.closed) return;\n\t\tthis.closed = true;\n\t\tthis.endpointAbortController?.abort();\n\t\tthis.endpointAbortController = null;\n\t\tthis.attachInFlight = null;\n\t\tthis.iframeHost?.destroy();\n\t\tthis.iframeHost = null;\n\t\tthis.attached = false;\n\t}\n\n\tprivate createPlayerFacade(): LiformaPlayer {\n\t\treturn {\n\t\t\tclose: () => {\n\t\t\t\tthis.close();\n\t\t\t},\n\t\t\tdestroy: () => {\n\t\t\t\tthis.destroy();\n\t\t\t},\n\t\t\ton: (event, handler) => {\n\t\t\t\tif (event === 'close') {\n\t\t\t\t\tthis.closeHandlers.push(handler);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t}\n}\n\n/** Live session handle returned by {@link Experience.startSession}. */\nexport type ExperienceSession = Experience;\n\nfunction resolveSpeechCapabilities(session: Session | null): SpeechCapabilities {\n\tif (!session) {\n\t\treturn { textSpeech: true, externalSpeechAudio: true };\n\t}\n\treturn {\n\t\ttextSpeech: session.capabilities.speech.output,\n\t\texternalSpeechAudio: session.capabilities.speech.externalAudio\n\t};\n}\n","import type { CloseEvent } from './protocol';\n\nfunction resolveIntegrationReturnUrl(returnUrl: string, baseOrigin: string): URL | null {\n\tconst raw = returnUrl.trim();\n\tif (!raw) return null;\n\n\tif (/^https?:\\/\\//i.test(raw)) {\n\t\ttry {\n\t\t\treturn new URL(raw);\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tconst base = baseOrigin.replace(/\\/$/, '');\n\tconst path = raw.startsWith('/') ? raw : `/${raw}`;\n\ttry {\n\t\treturn new URL(path, `${base}/`);\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction resolveFallbackReturnUrl(fallbackReturnUrl: string): string {\n\ttry {\n\t\tif (document.referrer) {\n\t\t\tconst referrer = new URL(document.referrer);\n\t\t\tif (\n\t\t\t\treferrer.origin === window.location.origin &&\n\t\t\t\treferrer.pathname !== window.location.pathname\n\t\t\t) {\n\t\t\t\treturn `${referrer.pathname}${referrer.search}${referrer.hash}`;\n\t\t\t}\n\t\t}\n\t} catch {\n\t\t/* ignore invalid referrer */\n\t}\n\treturn fallbackReturnUrl;\n}\n\n/** Navigate the host page when an iframe session closes. */\nexport function navigateOnExperienceClose(\n\tdetail: CloseEvent,\n\tfallbackReturnUrl = '/meet'\n): void {\n\tif (detail.reason !== 'user') return;\n\n\tconst target =\n\t\tdetail.returnUrl?.trim() || resolveFallbackReturnUrl(fallbackReturnUrl);\n\tconst url = resolveIntegrationReturnUrl(target, window.location.origin);\n\tif (!url?.protocol.startsWith('http')) return;\n\n\twindow.location.assign(url.toString());\n}\n","export type ExperienceLoadErrorKind =\n\t| 'not_found'\n\t| 'origin_not_allowed'\n\t| 'browser_embeds_disabled'\n\t| 'generic';\n\nexport type ExperienceLoadErrorPresentation = {\n\treadonly kind: ExperienceLoadErrorKind;\n\treadonly title: string;\n\treadonly detail: string;\n\treadonly technicalMessage: string;\n\treadonly experienceId?: string;\n\treadonly origin?: string;\n};\n\nconst EXPERIENCE_ID_IN_MESSAGE =\n\t/\\b(exp_[A-Za-z0-9][A-Za-z0-9_-]{2,127})\\b/;\n\nconst ORIGIN_IN_MESSAGE =\n\t/(?:Your origin|origin)\\s+(\\S+)\\s+is not an allowed origin/i;\n\nfunction toErrorMessage(error: unknown): string {\n\tif (error instanceof Error) return error.message;\n\treturn String(error);\n}\n\nfunction currentPageOrigin(): string | undefined {\n\ttry {\n\t\tif (typeof globalThis.location?.origin === 'string' && globalThis.location.origin) {\n\t\t\treturn globalThis.location.origin;\n\t\t}\n\t} catch {\n\t\t/* ignore */\n\t}\n\treturn undefined;\n}\n\nexport function extractExperienceIdFromMessage(message: string): string | undefined {\n\treturn EXPERIENCE_ID_IN_MESSAGE.exec(message)?.[1];\n}\n\nexport function extractOriginFromMessage(message: string): string | undefined {\n\treturn ORIGIN_IN_MESSAGE.exec(message)?.[1];\n}\n\nexport function presentExperienceLoadError(\n\terror: unknown,\n\toptions?: { readonly experienceId?: string | null; readonly origin?: string | null }\n): ExperienceLoadErrorPresentation {\n\tconst technicalMessage = toErrorMessage(error).trim() || 'Unknown error';\n\tconst experienceId =\n\t\toptions?.experienceId?.trim() ||\n\t\textractExperienceIdFromMessage(technicalMessage) ||\n\t\tundefined;\n\tconst origin =\n\t\toptions?.origin?.trim() ||\n\t\textractOriginFromMessage(technicalMessage) ||\n\t\tcurrentPageOrigin() ||\n\t\tundefined;\n\n\tif (\n\t\t/No playable experience found/i.test(technicalMessage) ||\n\t\t/^Experience is not available\\.?$/i.test(technicalMessage) ||\n\t\t/^Unknown experience\\.?$/i.test(technicalMessage) ||\n\t\t/experience_unavailable/i.test(technicalMessage)\n\t) {\n\t\tconst idLabel = experienceId ?? 'the requested experience';\n\t\treturn {\n\t\t\tkind: 'not_found',\n\t\t\ttitle: 'The application tried to load an avatar experience that doesn’t exist.',\n\t\t\tdetail: `Experience with id ${idLabel} does not exist`,\n\t\t\ttechnicalMessage,\n\t\t\texperienceId,\n\t\t\torigin\n\t\t};\n\t}\n\n\tif (\n\t\t/origin_not_allowed/i.test(technicalMessage) ||\n\t\t/is not an allowed origin/i.test(technicalMessage) ||\n\t\t/^Origin not allowed/i.test(technicalMessage)\n\t) {\n\t\tconst originLabel = origin ?? 'this page';\n\t\tconst idLabel = experienceId ?? 'the requested experience';\n\t\treturn {\n\t\t\tkind: 'origin_not_allowed',\n\t\t\ttitle: 'The application tried to load an avatar experience from a website that isn’t allowed.',\n\t\t\tdetail: `Origin ${originLabel} is not allowed for experience ${idLabel}`,\n\t\t\ttechnicalMessage,\n\t\t\texperienceId,\n\t\t\torigin\n\t\t};\n\t}\n\n\tif (\n\t\t/browser_embeds_disabled/i.test(technicalMessage) ||\n\t\t/^Browser embeds are not enabled/i.test(technicalMessage)\n\t) {\n\t\tconst idLabel = experienceId ?? 'the requested experience';\n\t\treturn {\n\t\t\tkind: 'browser_embeds_disabled',\n\t\t\ttitle: 'The application tried to load an avatar experience that isn’t enabled for browser embeds.',\n\t\t\tdetail: `Browser embeds are not enabled for experience ${idLabel}`,\n\t\t\ttechnicalMessage,\n\t\t\texperienceId,\n\t\t\torigin\n\t\t};\n\t}\n\n\treturn {\n\t\tkind: 'generic',\n\t\ttitle: 'This avatar experience couldn’t be started.',\n\t\tdetail: technicalMessage,\n\t\ttechnicalMessage,\n\t\texperienceId,\n\t\torigin\n\t};\n}\n","/** Subset of session loading overlay styles for pre-attach Experience load errors. */\n\nexport const EXPERIENCE_LOAD_ERROR_STYLE_ID = 'liforma-experience-load-error-styles';\n\nexport function injectExperienceLoadErrorStyles(): void {\n\tif (typeof document === 'undefined') return;\n\tif (document.getElementById(EXPERIENCE_LOAD_ERROR_STYLE_ID)) return;\n\tconst style = document.createElement('style');\n\tstyle.id = EXPERIENCE_LOAD_ERROR_STYLE_ID;\n\tstyle.textContent = EXPERIENCE_LOAD_ERROR_CSS;\n\tdocument.head.append(style);\n}\n\nexport const EXPERIENCE_LOAD_ERROR_CSS = `\n\t.liforma-loading-screen {\n\t\tinset: 0;\n\t\tz-index: 20;\n\t\tbox-sizing: border-box;\n\t\toverflow-x: clip;\n\t\twidth: 100%;\n\t\theight: 100%;\n\t\tmax-width: 100vw;\n\t\tbackground:\n\t\t\tradial-gradient(circle at 50% 60%, rgba(79, 126, 196, 0.18), transparent 38%),\n\t\t\t#030508;\n\t\tfont-family: system-ui, -apple-system, sans-serif;\n\t\tcolor: #e8f0ff;\n\t\ttext-align: center;\n\t}\n\t.liforma-loading-screen-absolute {\n\t\tposition: absolute;\n\t}\n\t.liforma-loading-card {\n\t\tposition: absolute;\n\t\tleft: 50%;\n\t\ttop: 60%;\n\t\ttransform: translate(-50%, -50%);\n\t\twidth: min(22rem, calc(100% - 2.5rem));\n\t\tmax-width: calc(100% - max(1.25rem, env(safe-area-inset-left, 0px)) - max(1.25rem, env(safe-area-inset-right, 0px)));\n\t\tbox-sizing: border-box;\n\t\tmargin: 0;\n\t\tpadding: 0;\n\t\tborder: 0;\n\t\tbackground: transparent;\n\t\tbox-shadow: none;\n\t}\n\t.liforma-loading-card-error {\n\t\tdisplay: block;\n\t\ttext-align: center;\n\t\tpadding: 1rem 1.05rem;\n\t\tborder-radius: 14px;\n\t\tborder: 1px solid rgba(255, 154, 154, 0.28);\n\t\tbackground:\n\t\t\tlinear-gradient(180deg, rgba(21, 29, 42, 0.92), rgba(9, 13, 20, 0.92));\n\t}\n\t.liforma-loading-kicker {\n\t\tmargin: 0 0 0.18rem;\n\t\tcolor: #8ba4c5;\n\t\tfont-size: 0.68rem;\n\t\tfont-weight: 700;\n\t\ttext-transform: uppercase;\n\t\tletter-spacing: 0.08em;\n\t}\n\t.liforma-loading-error {\n\t\tmargin: 0;\n\t\tmax-width: 28rem;\n\t\tcolor: #ffb4b4;\n\t\tfont-size: 0.92rem;\n\t\tline-height: 1.4;\n\t}\n\t.liforma-loading-error-title {\n\t\tmargin: 0;\n\t\tcolor: #f6ebe8;\n\t\tfont-size: 0.98rem;\n\t\tfont-weight: 650;\n\t\tline-height: 1.35;\n\t}\n\t.liforma-loading-error-advanced {\n\t\tmargin: 0.85rem 0 0;\n\t\tpadding: 0;\n\t\tborder: 0;\n\t\tbackground: transparent;\n\t\tcolor: #9eb7d8;\n\t\tfont: inherit;\n\t\tfont-size: 0.78rem;\n\t\tfont-weight: 650;\n\t\tletter-spacing: 0.02em;\n\t\ttext-decoration: underline;\n\t\ttext-underline-offset: 0.18em;\n\t\tcursor: pointer;\n\t}\n\t.liforma-loading-error-advanced:hover,\n\t.liforma-loading-error-advanced:focus-visible {\n\t\tcolor: #d7e6ff;\n\t\toutline: none;\n\t}\n\t.liforma-loading-error-detail {\n\t\tmargin: 0.7rem 0 0;\n\t\tpadding: 0.65rem 0.7rem;\n\t\tborder-radius: 10px;\n\t\tborder: 1px solid rgba(158, 191, 255, 0.16);\n\t\tbackground: rgba(3, 5, 8, 0.45);\n\t\tcolor: #c6d4e8;\n\t\tfont-size: 0.78rem;\n\t\tfont-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;\n\t\tline-height: 1.4;\n\t\ttext-align: left;\n\t\tword-break: break-word;\n\t}\n\t.liforma-loading-error-detail[hidden] {\n\t\tdisplay: none;\n\t}\n`;\n","import type { ExperienceLoadErrorPresentation } from './experienceLoadError';\nimport { injectExperienceLoadErrorStyles } from './experienceLoadErrorStyles';\n\nexport type ExperienceLoadErrorOverlay = {\n\treadonly root: HTMLDivElement;\n\tsetPresentation(presentation: ExperienceLoadErrorPresentation): void;\n\tremove(): void;\n};\n\nexport function createExperienceLoadErrorOverlay(\n\tcontainer: HTMLElement,\n\tinitial: ExperienceLoadErrorPresentation\n): ExperienceLoadErrorOverlay {\n\tinjectExperienceLoadErrorStyles();\n\n\tlet presentation = initial;\n\tlet advancedOpen = false;\n\n\tconst root = document.createElement('div');\n\troot.className = 'liforma-loading-screen liforma-loading-screen-absolute';\n\troot.setAttribute('role', 'alert');\n\troot.setAttribute('aria-live', 'assertive');\n\n\tconst card = document.createElement('div');\n\tcard.className = 'liforma-loading-card liforma-loading-card-error';\n\n\tconst kicker = document.createElement('p');\n\tkicker.className = 'liforma-loading-kicker';\n\tkicker.textContent = 'Avatar experience';\n\n\tconst title = document.createElement('p');\n\ttitle.className = 'liforma-loading-error-title';\n\n\tconst advancedBtn = document.createElement('button');\n\tadvancedBtn.type = 'button';\n\tadvancedBtn.className = 'liforma-loading-error-advanced';\n\n\tconst detail = document.createElement('p');\n\tdetail.className = 'liforma-loading-error-detail';\n\n\tadvancedBtn.addEventListener('click', () => {\n\t\tadvancedOpen = !advancedOpen;\n\t\trender();\n\t});\n\n\tcard.append(kicker, title, advancedBtn, detail);\n\troot.append(card);\n\tcontainer.append(root);\n\n\tfunction render(): void {\n\t\ttitle.textContent = presentation.title;\n\t\tdetail.textContent = presentation.detail;\n\t\tdetail.hidden = !advancedOpen;\n\t\tadvancedBtn.textContent = advancedOpen ? 'Hide details' : 'Advanced';\n\t\tadvancedBtn.setAttribute('aria-expanded', advancedOpen ? 'true' : 'false');\n\t}\n\n\trender();\n\n\treturn {\n\t\troot,\n\t\tsetPresentation(next) {\n\t\t\tpresentation = next;\n\t\t\tadvancedOpen = false;\n\t\t\trender();\n\t\t},\n\t\tremove() {\n\t\t\troot.remove();\n\t\t}\n\t};\n}\n","import { Experience } from './experience';\nimport type { CloseEvent } from './embed/protocol';\nimport { navigateOnExperienceClose } from './embed/returnNavigation';\nimport {\n\tparseExperienceFit,\n\ttype ExperienceFit,\n\ttype ExperienceMode,\n\ttype SpeechInputMode\n} from './types/sessionInteraction';\nimport { presentExperienceLoadError } from '../svelte/experienceLoadError';\nimport {\n\tcreateExperienceLoadErrorOverlay,\n\ttype ExperienceLoadErrorOverlay\n} from '../svelte/experienceLoadErrorOverlay';\n\nconst ELEMENT_NAME = 'liforma-experience';\n\nfunction parseMode(value: string | null): ExperienceMode | undefined {\n\tconst trimmed = value?.trim();\n\treturn trimmed === 'conversation' || trimmed === 'presenter' ? trimmed : undefined;\n}\n\nfunction parseSpeechInputMode(value: string | null): SpeechInputMode | undefined {\n\tconst trimmed = value?.trim();\n\treturn trimmed === 'auto' || trimmed === 'manual' || trimmed === 'off' ? trimmed : undefined;\n}\n\nfunction parseFit(value: string | null): ExperienceFit | undefined {\n\treturn parseExperienceFit(value);\n}\n\nexport class LiformaExperienceElement extends HTMLElement {\n\tprivate host: HTMLDivElement | null = null;\n\tprivate errorHost: HTMLDivElement | null = null;\n\tprivate errorOverlay: ExperienceLoadErrorOverlay | null = null;\n\tprivate experience: Experience | null = null;\n\tprivate bootstrapGeneration = 0;\n\n\tstatic get observedAttributes(): string[] {\n\t\treturn [\n\t\t\t'experience-id',\n\t\t\t'locale',\n\t\t\t'language',\n\t\t\t'secondary-locale',\n\t\t\t'learning-locale',\n\t\t\t'session-endpoint',\n\t\t\t'speech-only',\n\t\t\t'mode',\n\t\t\t'speech-input-mode',\n\t\t\t'fit'\n\t\t];\n\t}\n\n\tconnectedCallback(): void {\n\t\tthis.style.display = 'block';\n\t\tthis.style.position = 'relative';\n\t\tthis.style.overflow = 'hidden';\n\t\tif (!this.style.width) this.style.width = '100%';\n\t\t// Fill the host box when it has a definite height (flex/grid/explicit).\n\t\t// Do not set minHeight:inherit — that overrides host CSS such as\n\t\t// `.liforma-embed { min-height: 420px }` and collapses absolute children.\n\t\tif (!this.style.height) this.style.height = '100%';\n\t\tconst computedMinHeight = getComputedStyle(this).minHeight;\n\t\tif (\n\t\t\t!this.style.minHeight &&\n\t\t\t(computedMinHeight === '0px' || computedMinHeight === 'auto')\n\t\t) {\n\t\t\tthis.style.minHeight = '480px';\n\t\t}\n\n\t\tif (!this.errorHost) {\n\t\t\tthis.errorHost = document.createElement('div');\n\t\t\tthis.errorHost.style.cssText =\n\t\t\t\t'position:absolute;inset:0;z-index:20;pointer-events:none;';\n\t\t\tthis.append(this.errorHost);\n\t\t}\n\n\t\tif (!this.host) {\n\t\t\tthis.host = document.createElement('div');\n\t\t\t// Absolute fill avoids height:100% collapsing when size comes from flex/min-height.\n\t\t\tthis.host.style.cssText = 'position:absolute;inset:0;overflow:hidden;';\n\t\t\tthis.append(this.host);\n\t\t}\n\n\t\tvoid this.bootstrap();\n\t}\n\n\tdisconnectedCallback(): void {\n\t\tthis.bootstrapGeneration += 1;\n\t\tthis.experience?.destroy();\n\t\tthis.experience = null;\n\t\tthis.clearError();\n\t}\n\n\tattributeChangedCallback(\n\t\tname: string,\n\t\toldValue: string | null,\n\t\tnewValue: string | null\n\t): void {\n\t\tif (!LiformaExperienceElement.observedAttributes.includes(name)) {\n\t\t\treturn;\n\t\t}\n\t\tif (oldValue === newValue || !this.isConnected) {\n\t\t\treturn;\n\t\t}\n\t\tif (name === 'fit') {\n\t\t\tif (this.experience) {\n\t\t\t\tvoid this.experience.setFit(parseFit(newValue) ?? 'full');\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tthis.bootstrapGeneration += 1;\n\t\tthis.experience?.destroy();\n\t\tthis.experience = null;\n\t\tthis.clearError();\n\t\tif (this.host) {\n\t\t\tthis.host.replaceChildren();\n\t\t}\n\t\tvoid this.bootstrap();\n\t}\n\n\tprivate async bootstrap(): Promise<void> {\n\t\tconst experienceId = this.getAttribute('experience-id')?.trim();\n\t\tconst sessionEndpoint = this.getAttribute('session-endpoint')?.trim();\n\t\tconst locale = this.getAttribute('locale')?.trim() || undefined;\n\t\tconst languageAttr = this.getAttribute('language')?.trim();\n\t\tconst language = languageAttr || undefined;\n\t\tconst secondaryLocale = this.getAttribute('secondary-locale')?.trim() || undefined;\n\t\tconst learningLocale = this.getAttribute('learning-locale')?.trim() || undefined;\n\t\tconst speechOnly = this.getAttribute('speech-only') === 'true';\n\t\tconst mode = parseMode(this.getAttribute('mode'));\n\t\tconst speechInputMode = parseSpeechInputMode(this.getAttribute('speech-input-mode'));\n\t\tconst fit = parseFit(this.getAttribute('fit'));\n\t\tif (!experienceId || !this.host) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst generation = ++this.bootstrapGeneration;\n\t\tthis.clearError();\n\n\t\ttry {\n\t\t\tconst mint = {\n\t\t\t\texperienceId,\n\t\t\t\tlocale,\n\t\t\t\tlanguage,\n\t\t\t\tsecondaryLocale,\n\t\t\t\tlearningLocale,\n\t\t\t\tspeechOnly,\n\t\t\t\tmode,\n\t\t\t\tspeechInputMode\n\t\t\t};\n\t\t\tconst experience = await Experience.startSession(\n\t\t\t\tsessionEndpoint\n\t\t\t\t\t? {\n\t\t\t\t\t\t\t...mint,\n\t\t\t\t\t\t\tsessionEndpoint\n\t\t\t\t\t\t}\n\t\t\t\t\t: mint\n\t\t\t);\n\t\t\tif (generation !== this.bootstrapGeneration) {\n\t\t\t\texperience.destroy();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.experience = experience;\n\t\t\tconst player = await experience.attach({\n\t\t\t\tcontainer: this.host,\n\t\t\t\t...(fit ? { fit } : {})\n\t\t\t});\n\t\t\tif (generation !== this.bootstrapGeneration) {\n\t\t\t\texperience.destroy();\n\t\t\t\tthis.experience = null;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tplayer.on('close', (detail: CloseEvent) => {\n\t\t\t\tthis.dispatchEvent(new CustomEvent<CloseEvent>('close', { detail, bubbles: true }));\n\t\t\t\tnavigateOnExperienceClose(detail);\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tif (generation !== this.bootstrapGeneration) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.showError(error, experienceId);\n\t\t\tconsole.error('[liforma-experience]', error);\n\t\t\tthis.experience?.destroy();\n\t\t\tthis.experience = null;\n\t\t}\n\t}\n\n\tprivate showError(error: unknown, experienceId?: string): void {\n\t\tthis.clearError();\n\t\tif (!this.errorHost) return;\n\t\tthis.errorHost.style.pointerEvents = 'auto';\n\t\tthis.errorOverlay = createExperienceLoadErrorOverlay(\n\t\t\tthis.errorHost,\n\t\t\tpresentExperienceLoadError(error, { experienceId })\n\t\t);\n\t}\n\n\tprivate clearError(): void {\n\t\tthis.errorOverlay?.remove();\n\t\tthis.errorOverlay = null;\n\t\tif (this.errorHost) {\n\t\t\tthis.errorHost.style.pointerEvents = 'none';\n\t\t\tthis.errorHost.replaceChildren();\n\t\t}\n\t}\n}\n\nif (typeof customElements !== 'undefined' && !customElements.get(ELEMENT_NAME)) {\n\tcustomElements.define(ELEMENT_NAME, LiformaExperienceElement);\n}\n","/** Catalog-shaped layered gallery preview (CDN URLs). */\nexport type GalleryThumb = {\n\treadonly avatarImage: string;\n\treadonly backgroundImage?: string;\n\treadonly foregroundImage?: string;\n};\n\nexport type ThumbnailInteractionKind = 'anchor' | 'button' | 'presentational';\n\nexport type ResolveThumbnailInteractionInput = {\n\treadonly href?: string | null;\n\treadonly hasClickHandler?: boolean;\n\treadonly experienceId?: string | null;\n};\n\nexport type ThumbnailLayers = {\n\treadonly avatarImage: string;\n\treadonly backgroundImage?: string;\n\treadonly foregroundImage?: string;\n\treadonly fallbackImage?: string;\n};\n\n/** Shared props across Svelte / React / Next (React uses `onClick`). */\nexport type ExperienceThumbnailBaseProps = {\n\treadonly galleryThumb?: GalleryThumb;\n\treadonly avatarImage?: string;\n\treadonly backgroundImage?: string;\n\treadonly foregroundImage?: string;\n\treadonly alt: string;\n\treadonly experienceId?: string;\n\treadonly href?: string;\n\treadonly target?: string;\n\treadonly rel?: string;\n\treadonly returnUrl?: string;\n\treadonly playerBaseUrl?: string;\n\treadonly parallax?: boolean;\n\t/** Same as `<Experience fit>`: `full` (default), `medium`, or `face`. */\n\treadonly fit?: 'full' | 'face' | 'medium';\n\treadonly lazy?: boolean;\n\treadonly className?: string;\n\treadonly fallbackImage?: string;\n};\n\nexport function thumbnailFitClass(fit?: 'full' | 'face' | 'medium'): string {\n\tif (fit === 'face') return 'liforma-et--fit-face';\n\tif (fit === 'medium') return 'liforma-et--fit-medium';\n\treturn '';\n}\n","const AVATAR_TIER_RE = /(\\/avatars\\/[^/]+\\/)(\\d+)(\\/)/i;\nconst AVATAR_CH_PLATE_RE = /_CH(\\.webp)/i;\nconst LOCATION_SIZE_RE = /(-)(\\d+x\\d+)(\\.webp)(\\?|$)/i;\n\n/**\n * Rewrite known Liforma CDN URLs to gallery thumb tiers.\n * Avatars: /{tier}/ → /256/; locations: -{WxH}.webp → -256x256.webp.\n * Unrecognized URLs are returned unchanged.\n */\nexport function toGalleryThumbUrl(url: string): string {\n\tconst raw = url.trim();\n\tif (!raw) return raw;\n\n\tlet next = raw.replace(AVATAR_TIER_RE, (_match, prefix: string, tier: string, slash: string) => {\n\t\tif (tier === '256') return `${prefix}${tier}${slash}`;\n\t\treturn `${prefix}256${slash}`;\n\t});\n\tnext = next.replace(AVATAR_CH_PLATE_RE, '_neutral$1');\n\n\tnext = next.replace(\n\t\tLOCATION_SIZE_RE,\n\t\t(_match, dash: string, size: string, ext: string, end: string) => {\n\t\t\tif (size === '256x256') return `${dash}${size}${ext}${end}`;\n\t\t\treturn `${dash}256x256${ext}${end}`;\n\t\t}\n\t);\n\n\treturn next;\n}\n\nexport function resolveThumbnailLayers(input: {\n\treadonly galleryThumb?: {\n\t\treadonly avatarImage?: string;\n\t\treadonly backgroundImage?: string;\n\t\treadonly foregroundImage?: string;\n\t};\n\treadonly avatarImage?: string;\n\treadonly backgroundImage?: string;\n\treadonly foregroundImage?: string;\n\treadonly fallbackImage?: string;\n}): {\n\treadonly avatarImage: string;\n\treadonly backgroundImage?: string;\n\treadonly foregroundImage?: string;\n\treadonly fallbackImage?: string;\n} | null {\n\tconst avatarSource =\n\t\tinput.avatarImage?.trim() ||\n\t\tinput.galleryThumb?.avatarImage?.trim() ||\n\t\t'';\n\tif (!avatarSource) return null;\n\n\tconst backgroundSource =\n\t\tinput.backgroundImage?.trim() || input.galleryThumb?.backgroundImage?.trim() || '';\n\tconst foregroundSource =\n\t\tinput.foregroundImage?.trim() || input.galleryThumb?.foregroundImage?.trim() || '';\n\tconst fallbackSource = input.fallbackImage?.trim() || '';\n\n\treturn {\n\t\tavatarImage: toGalleryThumbUrl(avatarSource),\n\t\t...(backgroundSource ? { backgroundImage: toGalleryThumbUrl(backgroundSource) } : {}),\n\t\t...(foregroundSource ? { foregroundImage: toGalleryThumbUrl(foregroundSource) } : {}),\n\t\t...(fallbackSource ? { fallbackImage: toGalleryThumbUrl(fallbackSource) } : {})\n\t};\n}\n","import { useExplicitLocalStack } from '../shims/localStack';\nimport type { ResolveThumbnailInteractionInput, ThumbnailInteractionKind } from './types';\n\nconst PRODUCTION_PLAYER_ORIGIN = 'https://player.liforma.ai';\nconst LOCAL_PLAYER_ORIGIN = 'http://localhost:3002';\n\n/** Priority: href → click-only → hosted (experienceId) → presentational. */\nexport function resolveThumbnailInteraction(\n\tinput: ResolveThumbnailInteractionInput\n): ThumbnailInteractionKind {\n\tif (input.href?.trim()) return 'anchor';\n\tif (input.hasClickHandler) return 'button';\n\tif (input.experienceId?.trim()) return 'anchor';\n\treturn 'presentational';\n}\n\nexport function resolvePlayerBaseUrl(playerBaseUrl?: string): string {\n\tconst configured = playerBaseUrl?.trim();\n\tif (configured) return configured.replace(/\\/$/, '');\n\n\tif (useExplicitLocalStack()) {\n\t\treturn LOCAL_PLAYER_ORIGIN;\n\t}\n\n\treturn PRODUCTION_PLAYER_ORIGIN;\n}\n\n/**\n * Build hosted player launch URL: `{player}/{experienceId}?returnUrl=…`\n * When `returnUrl` is omitted, callers should assign on click (SSR-safe) or pass an explicit URL.\n */\nexport function buildHostedLaunchUrl(options: {\n\treadonly experienceId: string;\n\treadonly returnUrl?: string;\n\treadonly playerBaseUrl?: string;\n}): string {\n\tconst experienceId = options.experienceId.trim();\n\tconst base = resolvePlayerBaseUrl(options.playerBaseUrl);\n\tconst url = new URL(`${base}/${encodeURIComponent(experienceId)}`);\n\tconst returnUrl = options.returnUrl?.trim();\n\tif (returnUrl) {\n\t\turl.searchParams.set('returnUrl', returnUrl);\n\t}\n\treturn url.toString();\n}\n\n/** Resolve href for anchor mode (app link or hosted launch). */\nexport function resolveThumbnailHref(options: {\n\treadonly href?: string;\n\treadonly experienceId?: string;\n\treadonly returnUrl?: string;\n\treadonly playerBaseUrl?: string;\n\t/** When true and returnUrl omitted, use window.location.href (browser only). */\n\treadonly useCurrentPageAsReturnUrl?: boolean;\n}): string | undefined {\n\tconst href = options.href?.trim();\n\tif (href) return href;\n\n\tconst experienceId = options.experienceId?.trim();\n\tif (!experienceId) return undefined;\n\n\tlet returnUrl = options.returnUrl?.trim();\n\tif (!returnUrl && options.useCurrentPageAsReturnUrl && typeof window !== 'undefined') {\n\t\treturnUrl = window.location.href;\n\t}\n\n\treturn buildHostedLaunchUrl({\n\t\texperienceId,\n\t\t...(returnUrl ? { returnUrl } : {}),\n\t\tplayerBaseUrl: options.playerBaseUrl\n\t});\n}\n","/** Touch / coarse pointers — limit how many thumbs start loading at once. */\nexport const COARSE_THUMB_LOAD_CONCURRENCY = 4;\n/** Delay before a started thumb frees its queue slot for the next card. */\nexport const COARSE_THUMB_LOAD_STAGGER_MS = 160;\n\nexport function prefersCoarsePointer(): boolean {\n\tif (typeof window === 'undefined') return false;\n\treturn window.matchMedia('(hover: none), (pointer: coarse)').matches;\n}\n\ntype QueuedLoad = {\n\treadonly start: () => void;\n\tcancelled: boolean;\n};\n\nlet inflight = 0;\nconst waiting: QueuedLoad[] = [];\n\nfunction maxConcurrent(): number {\n\treturn prefersCoarsePointer() ? COARSE_THUMB_LOAD_CONCURRENCY : Number.POSITIVE_INFINITY;\n}\n\nfunction staggerMs(): number {\n\treturn prefersCoarsePointer() ? COARSE_THUMB_LOAD_STAGGER_MS : 0;\n}\n\nfunction pump(): void {\n\tconst limit = maxConcurrent();\n\twhile (inflight < limit && waiting.length > 0) {\n\t\tconst next = waiting.shift();\n\t\tif (!next || next.cancelled) continue;\n\t\tinflight += 1;\n\t\tnext.start();\n\t\tconst delay = staggerMs();\n\t\tif (delay <= 0) {\n\t\t\tinflight = Math.max(0, inflight - 1);\n\t\t\tcontinue;\n\t\t}\n\t\twindow.setTimeout(() => {\n\t\t\tinflight = Math.max(0, inflight - 1);\n\t\t\tpump();\n\t\t}, delay);\n\t}\n}\n\n/**\n * On coarse pointers, start thumb image loads in small waves so old devices\n * are not hit with dozens of concurrent decodes. Fine pointers start immediately.\n * Returns a cancel function (no-op after start has run).\n */\nexport function scheduleThumbLoad(start: () => void): () => void {\n\tif (!prefersCoarsePointer()) {\n\t\tstart();\n\t\treturn () => {};\n\t}\n\n\tconst item: QueuedLoad = { start, cancelled: false };\n\twaiting.push(item);\n\tpump();\n\n\treturn () => {\n\t\titem.cancelled = true;\n\t};\n}\n\n/** Test helper — clear queue state between vitest cases. */\nexport function resetThumbLoadSchedulerForTests(): void {\n\tinflight = 0;\n\twaiting.length = 0;\n}\n","import { prefersCoarsePointer, scheduleThumbLoad } from './thumbLoadScheduler';\n\nexport type LazyThumbOptions = {\n\treadonly rootMargin?: string;\n\treadonly onVisibleChange: (visible: boolean) => void;\n};\n\n/** Desktop: prefetch earlier. Coarse/touch: tighter margin to cut burst loads. */\nexport function getDefaultLazyRootMargin(): string {\n\treturn prefersCoarsePointer() ? '96px 0px' : '280px 0px';\n}\n\nfunction parseVerticalRootMarginPx(rootMargin: string): number {\n\tconst first = rootMargin.trim().split(/\\s+/)[0] ?? '0px';\n\tconst value = Number.parseFloat(first);\n\treturn Number.isFinite(value) ? Math.abs(value) : 280;\n}\n\nfunction isNearViewport(element: HTMLElement, marginPx: number): boolean {\n\tif (typeof window === 'undefined') return false;\n\tconst rect = element.getBoundingClientRect();\n\treturn rect.bottom >= -marginPx && rect.top <= window.innerHeight + marginPx;\n}\n\n/** Observe the thumb and enable image loads when near the viewport. */\nexport function attachLazyThumb(element: HTMLElement, options: LazyThumbOptions): () => void {\n\tconst { onVisibleChange } = options;\n\tconst rootMargin = options.rootMargin ?? getDefaultLazyRootMargin();\n\tconst marginPx = parseVerticalRootMarginPx(rootMargin);\n\tlet visible = false;\n\tlet loadStarted = false;\n\tlet cancelScheduled: (() => void) | null = null;\n\n\tconst requestLoad = () => {\n\t\tif (loadStarted) return;\n\t\tcancelScheduled?.();\n\t\tcancelScheduled = scheduleThumbLoad(() => {\n\t\t\tcancelScheduled = null;\n\t\t\tloadStarted = true;\n\t\t\tonVisibleChange(true);\n\t\t});\n\t};\n\n\tconst setVisible = (next: boolean) => {\n\t\tif (next === visible) return;\n\t\tvisible = next;\n\t\tif (next) {\n\t\t\trequestLoad();\n\t\t\treturn;\n\t\t}\n\t\t// Keep images once started; only cancel a queued (not-yet-started) load.\n\t\tcancelScheduled?.();\n\t\tcancelScheduled = null;\n\t\tonVisibleChange(false);\n\t};\n\n\tif (typeof IntersectionObserver === 'undefined') {\n\t\trequestLoad();\n\t\treturn () => {\n\t\t\tcancelScheduled?.();\n\t\t\tcancelScheduled = null;\n\t\t};\n\t}\n\n\tconst observer = new IntersectionObserver(\n\t\t(entries) => {\n\t\t\tsetVisible(entries.some((entry) => entry.isIntersecting));\n\t\t},\n\t\t{ root: null, rootMargin, threshold: 0 }\n\t);\n\n\tobserver.observe(element);\n\n\tif (isNearViewport(element, marginPx)) {\n\t\tsetVisible(true);\n\t}\n\n\treturn () => {\n\t\tcancelScheduled?.();\n\t\tcancelScheduled = null;\n\t\tobserver.disconnect();\n\t};\n}\n\nexport function attachImageReady(element: HTMLImageElement, onReady: () => void): () => void {\n\tconst notify = () => onReady();\n\tif (element.complete) {\n\t\tqueueMicrotask(notify);\n\t} else {\n\t\telement.addEventListener('load', notify, { once: true });\n\t\telement.addEventListener('error', notify, { once: true });\n\t}\n\treturn () => {\n\t\telement.removeEventListener('load', notify);\n\t\telement.removeEventListener('error', notify);\n\t};\n}\n","/** Touch devices without hover — drive thumbnail parallax from scroll position. */\nexport function prefersScrollLinkedParallax(): boolean {\n\tif (typeof window === 'undefined') return false;\n\tif (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return false;\n\treturn window.matchMedia('(hover: none), (pointer: coarse)').matches;\n}\n\n/**\n * Map an element's vertical centre in the viewport to horizontal parallax in [-1, 1].\n */\nexport function horizontalParallaxFromViewportY(rect: DOMRect, viewportHeight: number): number {\n\tconst centerY = rect.top + rect.height / 2;\n\tconst t = centerY / Math.max(viewportHeight, 1);\n\treturn (t - 0.5) * 2;\n}\n\nexport type ScrollLinkedParallaxOptions = {\n\treadonly onParallax: (x: number) => void;\n\treadonly pointerActive?: () => boolean;\n};\n\ntype ScrollParallaxSubscriber = {\n\treadonly element: HTMLElement;\n\treadonly onParallax: (x: number) => void;\n\treadonly pointerActive?: () => boolean;\n};\n\nconst subscribers = new Set<ScrollParallaxSubscriber>();\nlet listening = false;\nlet frame = 0;\n\nfunction syncAll(): void {\n\tframe = 0;\n\tif (subscribers.size === 0 || typeof window === 'undefined') return;\n\tconst viewportHeight = window.innerHeight;\n\tfor (const entry of subscribers) {\n\t\tif (entry.pointerActive?.()) continue;\n\t\tconst rect = entry.element.getBoundingClientRect();\n\t\t// Skip off-screen thumbs — avoids N layout reads + framework updates per scroll.\n\t\tif (rect.bottom < -64 || rect.top > viewportHeight + 64) continue;\n\t\tentry.onParallax(horizontalParallaxFromViewportY(rect, viewportHeight));\n\t}\n}\n\nfunction scheduleSync(): void {\n\tif (frame !== 0) return;\n\tframe = requestAnimationFrame(syncAll);\n}\n\nfunction ensureListening(): void {\n\tif (listening || typeof window === 'undefined') return;\n\tlistening = true;\n\twindow.addEventListener('scroll', scheduleSync, { passive: true });\n\twindow.addEventListener('resize', scheduleSync, { passive: true });\n}\n\nfunction stopListeningIfIdle(): void {\n\tif (subscribers.size > 0 || !listening || typeof window === 'undefined') return;\n\tlistening = false;\n\tif (frame !== 0) {\n\t\tcancelAnimationFrame(frame);\n\t\tframe = 0;\n\t}\n\twindow.removeEventListener('scroll', scheduleSync);\n\twindow.removeEventListener('resize', scheduleSync);\n}\n\n/**\n * Subscribe to scroll/resize and update horizontal parallax from viewport Y.\n * Uses one shared window listener for all thumbs (old iPads with large galleries).\n */\nexport function attachScrollLinkedParallax(\n\telement: HTMLElement,\n\toptions: ScrollLinkedParallaxOptions\n): () => void {\n\tif (!prefersScrollLinkedParallax()) return () => {};\n\n\tconst entry: ScrollParallaxSubscriber = {\n\t\telement,\n\t\tonParallax: options.onParallax,\n\t\tpointerActive: options.pointerActive\n\t};\n\tsubscribers.add(entry);\n\tensureListening();\n\n\tif (!entry.pointerActive?.()) {\n\t\toptions.onParallax(\n\t\t\thorizontalParallaxFromViewportY(element.getBoundingClientRect(), window.innerHeight)\n\t\t);\n\t}\n\n\treturn () => {\n\t\tsubscribers.delete(entry);\n\t\tstopListeningIfIdle();\n\t};\n}\n\n/** Test helper — clear shared scroll state between vitest cases. */\nexport function resetScrollLinkedParallaxForTests(): void {\n\tsubscribers.clear();\n\tif (typeof window !== 'undefined' && listening) {\n\t\twindow.removeEventListener('scroll', scheduleSync);\n\t\twindow.removeEventListener('resize', scheduleSync);\n\t}\n\tlistening = false;\n\tif (frame !== 0) {\n\t\tcancelAnimationFrame(frame);\n\t\tframe = 0;\n\t}\n}\n","import {\n\tattachScrollLinkedParallax,\n\thorizontalParallaxFromViewportY\n} from './scrollParallax';\n\n/**\n * Drive `--liforma-et-parallax-*` CSS vars and `liforma-et--parallax` /\n * `liforma-et--scroll-parallax` classes from pointer (desktop) or scroll (touch).\n * Matches ExperienceThumbnail / `<liforma-experience-thumbnail>` behaviour.\n */\nexport function attachThumbnailParallax(root: HTMLElement): () => void {\n\tlet parallaxX = 0;\n\tlet parallaxY = 0;\n\tlet hovering = false;\n\tlet scrollParallax = false;\n\tlet pointerDown = false;\n\n\tconst sync = () => {\n\t\tconst parallaxActive = hovering || scrollParallax;\n\t\troot.classList.toggle('liforma-et--parallax', parallaxActive);\n\t\troot.classList.toggle('liforma-et--scroll-parallax', scrollParallax);\n\t\troot.style.setProperty('--liforma-et-parallax-x', String(parallaxX));\n\t\troot.style.setProperty('--liforma-et-parallax-y', String(parallaxY));\n\t};\n\n\tconst detachScroll = attachScrollLinkedParallax(root, {\n\t\tonParallax: (x) => {\n\t\t\tscrollParallax = true;\n\t\t\tif (!pointerDown) {\n\t\t\t\tparallaxX = x;\n\t\t\t\tparallaxY = 0;\n\t\t\t\tsync();\n\t\t\t}\n\t\t},\n\t\tpointerActive: () => pointerDown\n\t});\n\n\tconst onPointerMove = (event: PointerEvent) => {\n\t\tif (scrollParallax) return;\n\t\tconst rect = root.getBoundingClientRect();\n\t\tif (rect.width <= 0 || rect.height <= 0) return;\n\t\tparallaxX = ((event.clientX - rect.left) / rect.width - 0.5) * 2;\n\t\tparallaxY = ((event.clientY - rect.top) / rect.height - 0.5) * 2;\n\t\tsync();\n\t};\n\n\tconst onPointerDown = (event: PointerEvent) => {\n\t\tpointerDown = true;\n\t\tif (event.pointerType === 'mouse') hovering = true;\n\t\tsync();\n\t};\n\n\tconst onPointerUp = () => {\n\t\tconst wasDown = pointerDown;\n\t\tpointerDown = false;\n\t\thovering = false;\n\t\tif (scrollParallax && wasDown) {\n\t\t\tparallaxX = horizontalParallaxFromViewportY(\n\t\t\t\troot.getBoundingClientRect(),\n\t\t\t\twindow.innerHeight\n\t\t\t);\n\t\t\tparallaxY = 0;\n\t\t} else if (!scrollParallax) {\n\t\t\tparallaxX = 0;\n\t\t\tparallaxY = 0;\n\t\t}\n\t\tsync();\n\t};\n\n\tconst onPointerEnter = (event: PointerEvent) => {\n\t\tif (event.pointerType === 'mouse') {\n\t\t\thovering = true;\n\t\t\tsync();\n\t\t}\n\t};\n\n\tconst onPointerLeave = () => {\n\t\tif (!pointerDown) {\n\t\t\thovering = false;\n\t\t\tif (!scrollParallax) {\n\t\t\t\tparallaxX = 0;\n\t\t\t\tparallaxY = 0;\n\t\t\t}\n\t\t\tsync();\n\t\t}\n\t};\n\n\troot.addEventListener('pointermove', onPointerMove);\n\troot.addEventListener('pointerdown', onPointerDown);\n\troot.addEventListener('pointerup', onPointerUp);\n\troot.addEventListener('pointercancel', onPointerUp);\n\troot.addEventListener('pointerenter', onPointerEnter);\n\troot.addEventListener('pointerleave', onPointerLeave);\n\n\treturn () => {\n\t\tdetachScroll();\n\t\troot.removeEventListener('pointermove', onPointerMove);\n\t\troot.removeEventListener('pointerdown', onPointerDown);\n\t\troot.removeEventListener('pointerup', onPointerUp);\n\t\troot.removeEventListener('pointercancel', onPointerUp);\n\t\troot.removeEventListener('pointerenter', onPointerEnter);\n\t\troot.removeEventListener('pointerleave', onPointerLeave);\n\t\troot.classList.remove('liforma-et--parallax', 'liforma-et--scroll-parallax');\n\t\troot.style.removeProperty('--liforma-et-parallax-x');\n\t\troot.style.removeProperty('--liforma-et-parallax-y');\n\t};\n}\n","/** Shared styles for ExperienceThumbnail (class prefix `liforma-et`). */\nexport const EXPERIENCE_THUMBNAIL_CSS = `\n.liforma-et {\n\t--liforma-et-parallax-x: 0;\n\t--liforma-et-parallax-y: 0;\n\t--liforma-et-bg-scale: 1;\n\t--liforma-et-fg-scale: 1;\n\t--liforma-et-avatar-scale: 1;\n\t--liforma-et-bg-shift: 0px;\n\t--liforma-et-fg-shift: 0px;\n\t--liforma-et-avatar-shift: 0px;\n\t--liforma-et-reveal-duration: 420ms;\n\n\tbox-sizing: border-box;\n\tposition: relative;\n\tdisplay: block;\n\twidth: 100%;\n\theight: 100%;\n\tmin-height: 0;\n\tmargin: 0;\n\tpadding: 0;\n\tborder: 0;\n\tborder-radius: 0.65rem;\n\toverflow: hidden;\n\tbackground: #111;\n\tcolor: inherit;\n\ttext-decoration: none;\n\tcursor: inherit;\n\t-webkit-tap-highlight-color: transparent;\n}\n\na.liforma-et,\nbutton.liforma-et {\n\tcursor: pointer;\n}\n\nbutton.liforma-et {\n\tfont: inherit;\n\tappearance: none;\n}\n\n.liforma-et__placeholder {\n\tposition: absolute;\n\tinset: 0;\n\tz-index: 0;\n\tbackground:\n\t\tradial-gradient(120% 90% at 50% 0%, rgba(255, 255, 255, 0.06), transparent 58%),\n\t\tlinear-gradient(180deg, #1a1a1a 0%, #101010 100%);\n\topacity: 1;\n\ttransition: opacity var(--liforma-et-reveal-duration) ease;\n}\n\n.liforma-et--revealed .liforma-et__placeholder {\n\topacity: 0;\n}\n\n.liforma-et__media {\n\topacity: 0;\n\ttransition: opacity var(--liforma-et-reveal-duration) ease;\n}\n\n.liforma-et__media--ready {\n\topacity: 1;\n}\n\n.liforma-et__background {\n\tposition: absolute;\n\tinset: 0;\n\tz-index: 1;\n\twidth: 100%;\n\theight: 100%;\n\tobject-fit: cover;\n\tobject-position: center center;\n\ttransform: translate(\n\t\t\tcalc(var(--liforma-et-parallax-x) * var(--liforma-et-bg-shift)),\n\t\t\tcalc(var(--liforma-et-parallax-y) * var(--liforma-et-bg-shift))\n\t\t)\n\t\tscale(var(--liforma-et-bg-scale));\n\ttransform-origin: center center;\n\ttransition:\n\t\topacity var(--liforma-et-reveal-duration) ease,\n\t\ttransform 160ms ease;\n}\n\n.liforma-et__avatar {\n\tposition: relative;\n\tz-index: 2;\n\tdisplay: block;\n\twidth: 100%;\n\theight: 100%;\n\tobject-fit: contain;\n\tobject-position: center bottom;\n\ttransform: scale(var(--liforma-et-avatar-scale))\n\t\ttranslate(\n\t\t\tcalc(var(--liforma-et-parallax-x) * var(--liforma-et-avatar-shift)),\n\t\t\tcalc(var(--liforma-et-parallax-y) * var(--liforma-et-avatar-shift))\n\t\t);\n\ttransform-origin: center center;\n\ttransition:\n\t\topacity var(--liforma-et-reveal-duration) ease,\n\t\ttransform 160ms ease;\n}\n\n/* Gallery plates are typically square in a square tile — contain vs cover is a no-op\n without a zoom. Scale the whole scene from the upper third so Face matches\n player fit (location + avatar crop together). */\n.liforma-et--fit-face {\n\t--liforma-et-avatar-scale: 1.85;\n\t--liforma-et-bg-scale: 1.85;\n\t--liforma-et-fg-scale: 1.85;\n}\n\n.liforma-et--fit-face .liforma-et__avatar,\n.liforma-et--fit-face .liforma-et__background,\n.liforma-et--fit-face .liforma-et__foreground {\n\ttransform-origin: 50% 18%;\n}\n\n.liforma-et--fit-face .liforma-et__avatar {\n\tobject-fit: cover;\n\tobject-position: 50% 18%;\n}\n\n/* Bust / medium: less zoom than face so upper chest stays in frame (player 3.75H window). */\n.liforma-et--fit-medium {\n\t--liforma-et-avatar-scale: 1.35;\n\t--liforma-et-bg-scale: 1.35;\n\t--liforma-et-fg-scale: 1.35;\n}\n\n.liforma-et--fit-medium .liforma-et__avatar,\n.liforma-et--fit-medium .liforma-et__background,\n.liforma-et--fit-medium .liforma-et__foreground {\n\ttransform-origin: 50% 28%;\n}\n\n.liforma-et--fit-medium .liforma-et__avatar {\n\tobject-fit: cover;\n\tobject-position: 50% 28%;\n}\n\n.liforma-et__foreground {\n\tposition: absolute;\n\tinset: 0;\n\tz-index: 3;\n\twidth: 100%;\n\theight: 100%;\n\tobject-fit: cover;\n\tobject-position: center center;\n\tpointer-events: none;\n\ttransform: translate(\n\t\t\tcalc(var(--liforma-et-parallax-x) * var(--liforma-et-fg-shift) * -1),\n\t\t\tcalc(var(--liforma-et-parallax-y) * var(--liforma-et-fg-shift) * -1)\n\t\t)\n\t\tscale(var(--liforma-et-fg-scale));\n\ttransform-origin: center center;\n\ttransition:\n\t\topacity var(--liforma-et-reveal-duration) ease,\n\t\ttransform 160ms ease;\n}\n\n.liforma-et--parallax {\n\t--liforma-et-bg-scale: 1.08;\n\t--liforma-et-fg-scale: 1.05;\n\t--liforma-et-avatar-scale: 1.08;\n\t--liforma-et-bg-shift: 10px;\n\t--liforma-et-fg-shift: 4px;\n\t--liforma-et-avatar-shift: 4px;\n}\n\n/* Promote layers only while parallax is active — avoids N compositor surfaces on galleries. */\n.liforma-et--parallax .liforma-et__background,\n.liforma-et--parallax .liforma-et__foreground,\n.liforma-et--parallax .liforma-et__avatar {\n\twill-change: transform;\n}\n\n.liforma-et--parallax.liforma-et--fit-face {\n\t--liforma-et-avatar-scale: 1.95;\n\t--liforma-et-bg-scale: 1.95;\n\t--liforma-et-fg-scale: 1.95;\n\t--liforma-et-avatar-shift: 0px;\n}\n\n.liforma-et--parallax.liforma-et--fit-medium {\n\t--liforma-et-avatar-scale: 1.42;\n\t--liforma-et-bg-scale: 1.42;\n\t--liforma-et-fg-scale: 1.42;\n\t--liforma-et-avatar-shift: 0px;\n}\n\n.liforma-et--scroll-parallax .liforma-et__background,\n.liforma-et--scroll-parallax .liforma-et__foreground,\n.liforma-et--scroll-parallax .liforma-et__avatar {\n\ttransition: opacity var(--liforma-et-reveal-duration) ease;\n}\n\n@media (prefers-reduced-motion: reduce) {\n\t.liforma-et {\n\t\t--liforma-et-reveal-duration: 0ms;\n\t}\n\n\t.liforma-et--parallax {\n\t\t--liforma-et-bg-scale: 1;\n\t\t--liforma-et-fg-scale: 1;\n\t\t--liforma-et-avatar-scale: 1;\n\t\t--liforma-et-bg-shift: 0px;\n\t\t--liforma-et-fg-shift: 0px;\n\t\t--liforma-et-avatar-shift: 0px;\n\t}\n\n\t.liforma-et--parallax.liforma-et--fit-face {\n\t\t--liforma-et-avatar-scale: 1.85;\n\t\t--liforma-et-bg-scale: 1.85;\n\t\t--liforma-et-fg-scale: 1.85;\n\t}\n\n\t.liforma-et__background,\n\t.liforma-et__foreground,\n\t.liforma-et__avatar {\n\t\ttransition: none;\n\t}\n}\n`.trim();\n","let styleMounted = false;\n\n/** Inject shared thumbnail CSS once per document. */\nexport function ensureExperienceThumbnailStyles(css: string): void {\n\tif (typeof document === 'undefined' || styleMounted) return;\n\tif (document.querySelector('style[data-liforma-et]')) {\n\t\tstyleMounted = true;\n\t\treturn;\n\t}\n\tconst style = document.createElement('style');\n\tstyle.setAttribute('data-liforma-et', '');\n\tstyle.textContent = css;\n\tdocument.head.appendChild(style);\n\tstyleMounted = true;\n}\n","import {\n\tattachImageReady,\n\tattachLazyThumb,\n\tattachScrollLinkedParallax,\n\tEXPERIENCE_THUMBNAIL_CSS,\n\thorizontalParallaxFromViewportY,\n\tresolveThumbnailHref,\n\tresolveThumbnailInteraction,\n\tresolveThumbnailLayers,\n\tthumbnailFitClass,\n\ttype GalleryThumb\n} from '../thumbnail/index';\n\nconst ELEMENT_NAME = 'liforma-experience-thumbnail';\n\nfunction parseGalleryThumb(raw: string | null): GalleryThumb | undefined {\n\tif (!raw?.trim()) return undefined;\n\ttry {\n\t\tconst parsed = JSON.parse(raw) as GalleryThumb;\n\t\tif (parsed && typeof parsed.avatarImage === 'string') return parsed;\n\t} catch {\n\t\t/* ignore */\n\t}\n\treturn undefined;\n}\n\nclass LiformaExperienceThumbnailElement extends HTMLElement {\n\tstatic get observedAttributes(): string[] {\n\t\treturn [\n\t\t\t'experience-id',\n\t\t\t'gallery-thumb',\n\t\t\t'avatar-image',\n\t\t\t'background-image',\n\t\t\t'foreground-image',\n\t\t\t'fallback-image',\n\t\t\t'alt',\n\t\t\t'href',\n\t\t\t'target',\n\t\t\t'rel',\n\t\t\t'return-url',\n\t\t\t'player-base-url',\n\t\t\t'parallax',\n\t\t\t'fit',\n\t\t\t'lazy'\n\t\t];\n\t}\n\n\t#root: HTMLElement | null = null;\n\t#cleanup: (() => void)[] = [];\n\t#shouldLoad = false;\n\t#hovering = false;\n\t#pointerDown = false;\n\t#scrollParallax = false;\n\t#parallaxX = 0;\n\t#parallaxY = 0;\n\t#avatarLoadFailed = false;\n\t#backgroundReady = false;\n\t#avatarReady = false;\n\t#foregroundReady = false;\n\n\tconnectedCallback(): void {\n\t\tthis.#render();\n\t}\n\n\tdisconnectedCallback(): void {\n\t\tthis.#teardown();\n\t}\n\n\tattributeChangedCallback(): void {\n\t\tif (this.isConnected) this.#render();\n\t}\n\n\t#teardown(): void {\n\t\tfor (const fn of this.#cleanup) fn();\n\t\tthis.#cleanup = [];\n\t\tthis.#root = null;\n\t}\n\n\t#boolAttr(name: string, defaultValue: boolean): boolean {\n\t\tif (!this.hasAttribute(name)) return defaultValue;\n\t\tconst value = this.getAttribute(name);\n\t\tif (value === null || value === '' || value === 'true') return true;\n\t\tif (value === 'false') return false;\n\t\treturn defaultValue;\n\t}\n\n\t#render(): void {\n\t\tthis.#teardown();\n\n\t\tconst shadow = this.shadowRoot ?? this.attachShadow({ mode: 'open' });\n\t\tconst style = document.createElement('style');\n\t\tstyle.textContent = EXPERIENCE_THUMBNAIL_CSS;\n\t\tshadow.replaceChildren(style);\n\n\t\tconst alt = this.getAttribute('alt')?.trim() || 'Experience';\n\t\tconst hrefAttr = this.getAttribute('href');\n\t\tconst experienceId = this.getAttribute('experience-id');\n\t\tconst hasClickHandler = this.hasAttribute('onclick') || typeof this.onclick === 'function';\n\t\tconst interaction = resolveThumbnailInteraction({\n\t\t\thref: hrefAttr,\n\t\t\thasClickHandler,\n\t\t\texperienceId\n\t\t});\n\n\t\tconst layers = resolveThumbnailLayers({\n\t\t\tgalleryThumb: parseGalleryThumb(this.getAttribute('gallery-thumb')),\n\t\t\tavatarImage: this.getAttribute('avatar-image') ?? undefined,\n\t\t\tbackgroundImage: this.getAttribute('background-image') ?? undefined,\n\t\t\tforegroundImage: this.getAttribute('foreground-image') ?? undefined,\n\t\t\tfallbackImage: this.getAttribute('fallback-image') ?? undefined\n\t\t});\n\t\tif (!layers) return;\n\n\t\tconst parallaxEnabled = this.#boolAttr('parallax', true);\n\t\tconst fitAttr = this.getAttribute('fit')?.trim();\n\t\tconst fit = fitAttr === 'face' || fitAttr === 'medium' ? fitAttr : 'full';\n\t\tconst lazy = this.#boolAttr('lazy', true);\n\t\tthis.#shouldLoad = !lazy;\n\n\t\tconst isHostedLaunch =\n\t\t\tinteraction === 'anchor' && !hrefAttr?.trim() && Boolean(experienceId?.trim());\n\t\tconst resolvedHref = resolveThumbnailHref({\n\t\t\thref: hrefAttr ?? undefined,\n\t\t\texperienceId: experienceId ?? undefined,\n\t\t\treturnUrl: this.getAttribute('return-url') ?? undefined,\n\t\t\tplayerBaseUrl: this.getAttribute('player-base-url') ?? undefined,\n\t\t\tuseCurrentPageAsReturnUrl:\n\t\t\t\tisHostedLaunch && !this.getAttribute('return-url')?.trim()\n\t\t});\n\n\t\tlet root: HTMLElement;\n\t\tif (interaction === 'anchor' && resolvedHref) {\n\t\t\tconst a = document.createElement('a');\n\t\t\ta.href = resolvedHref;\n\t\t\tconst target = this.getAttribute('target');\n\t\t\tconst rel = this.getAttribute('rel');\n\t\t\tif (target) a.target = target;\n\t\t\tif (rel) a.rel = rel;\n\t\t\troot = a;\n\t\t} else if (interaction === 'button') {\n\t\t\tconst button = document.createElement('button');\n\t\t\tbutton.type = 'button';\n\t\t\troot = button;\n\t\t} else {\n\t\t\troot = document.createElement('div');\n\t\t\troot.setAttribute('role', 'img');\n\t\t}\n\n\t\troot.className = 'liforma-et';\n\t\troot.setAttribute('aria-label', alt);\n\t\tthis.#root = root;\n\t\tshadow.appendChild(root);\n\n\t\tconst syncClass = () => {\n\t\t\tconst hasScene = Boolean(layers.backgroundImage);\n\t\t\tconst parallaxActive = parallaxEnabled && (this.#hovering || this.#scrollParallax);\n\t\t\t// Fade placeholder as soon as any layer can paint — don't wait for the slowest plate.\n\t\t\tconst reveal =\n\t\t\t\tthis.#shouldLoad &&\n\t\t\t\t(this.#avatarReady || this.#backgroundReady || this.#foregroundReady);\n\t\t\troot.className = [\n\t\t\t\t'liforma-et',\n\t\t\t\thasScene ? 'liforma-et--scene' : '',\n\t\t\t\tthumbnailFitClass(fit),\n\t\t\t\tparallaxActive ? 'liforma-et--parallax' : '',\n\t\t\t\tthis.#scrollParallax ? 'liforma-et--scroll-parallax' : '',\n\t\t\t\tthis.#shouldLoad && !reveal ? 'liforma-et--loading' : '',\n\t\t\t\treveal ? 'liforma-et--revealed' : ''\n\t\t\t]\n\t\t\t\t.filter(Boolean)\n\t\t\t\t.join(' ');\n\t\t\troot.style.setProperty('--liforma-et-parallax-x', String(this.#parallaxX));\n\t\t\troot.style.setProperty('--liforma-et-parallax-y', String(this.#parallaxY));\n\t\t};\n\n\t\tconst placeholder = document.createElement('span');\n\t\tplaceholder.className = 'liforma-et__placeholder';\n\t\tplaceholder.setAttribute('aria-hidden', 'true');\n\t\troot.appendChild(placeholder);\n\n\t\tconst paintImages = () => {\n\t\t\tif (!this.#shouldLoad) return;\n\t\t\tconst avatarSrc =\n\t\t\t\tthis.#avatarLoadFailed && layers.fallbackImage\n\t\t\t\t\t? layers.fallbackImage\n\t\t\t\t\t: layers.avatarImage;\n\n\t\t\tif (layers.backgroundImage && !root.querySelector('.liforma-et__background')) {\n\t\t\t\tconst img = document.createElement('img');\n\t\t\t\timg.className = 'liforma-et__background liforma-et__media';\n\t\t\t\timg.alt = '';\n\t\t\t\timg.decoding = 'async';\n\t\t\t\timg.src = layers.backgroundImage;\n\t\t\t\tthis.#cleanup.push(\n\t\t\t\t\tattachImageReady(img, () => {\n\t\t\t\t\t\tthis.#backgroundReady = true;\n\t\t\t\t\t\timg.classList.add('liforma-et__media--ready');\n\t\t\t\t\t\tsyncClass();\n\t\t\t\t\t})\n\t\t\t\t);\n\t\t\t\troot.appendChild(img);\n\t\t\t}\n\n\t\t\tlet avatar = root.querySelector('.liforma-et__avatar') as HTMLImageElement | null;\n\t\t\tif (!avatar) {\n\t\t\t\tavatar = document.createElement('img');\n\t\t\t\tavatar.className = 'liforma-et__avatar liforma-et__media';\n\t\t\t\tavatar.alt = '';\n\t\t\t\tavatar.decoding = 'async';\n\t\t\t\tavatar.addEventListener('error', () => {\n\t\t\t\t\tif (layers.fallbackImage && !this.#avatarLoadFailed) {\n\t\t\t\t\t\tthis.#avatarLoadFailed = true;\n\t\t\t\t\t\tavatar!.src = layers.fallbackImage;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.#avatarReady = true;\n\t\t\t\t\t\tsyncClass();\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tthis.#cleanup.push(\n\t\t\t\t\tattachImageReady(avatar, () => {\n\t\t\t\t\t\tthis.#avatarReady = true;\n\t\t\t\t\t\tavatar!.classList.add('liforma-et__media--ready');\n\t\t\t\t\t\tsyncClass();\n\t\t\t\t\t})\n\t\t\t\t);\n\t\t\t\troot.appendChild(avatar);\n\t\t\t}\n\t\t\tif (avatar.src !== avatarSrc) avatar.src = avatarSrc;\n\n\t\t\tif (layers.foregroundImage && !root.querySelector('.liforma-et__foreground')) {\n\t\t\t\tconst img = document.createElement('img');\n\t\t\t\timg.className = 'liforma-et__foreground liforma-et__media';\n\t\t\t\timg.alt = '';\n\t\t\t\timg.decoding = 'async';\n\t\t\t\timg.src = layers.foregroundImage;\n\t\t\t\tthis.#cleanup.push(\n\t\t\t\t\tattachImageReady(img, () => {\n\t\t\t\t\t\tthis.#foregroundReady = true;\n\t\t\t\t\t\timg.classList.add('liforma-et__media--ready');\n\t\t\t\t\t\tsyncClass();\n\t\t\t\t\t})\n\t\t\t\t);\n\t\t\t\troot.appendChild(img);\n\t\t\t}\n\t\t\tsyncClass();\n\t\t};\n\n\t\tif (lazy) {\n\t\t\tthis.#cleanup.push(\n\t\t\t\tattachLazyThumb(root, {\n\t\t\t\t\tonVisibleChange: (visible) => {\n\t\t\t\t\t\tif (!visible) return;\n\t\t\t\t\t\tthis.#shouldLoad = true;\n\t\t\t\t\t\tpaintImages();\n\t\t\t\t\t\tsyncClass();\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t);\n\t\t} else {\n\t\t\tpaintImages();\n\t\t}\n\n\t\tif (parallaxEnabled) {\n\t\t\tthis.#cleanup.push(\n\t\t\t\tattachScrollLinkedParallax(root, {\n\t\t\t\t\tonParallax: (x) => {\n\t\t\t\t\t\tthis.#scrollParallax = true;\n\t\t\t\t\t\tif (!this.#pointerDown) {\n\t\t\t\t\t\t\tthis.#parallaxX = x;\n\t\t\t\t\t\t\tthis.#parallaxY = 0;\n\t\t\t\t\t\t\tsyncClass();\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\tpointerActive: () => this.#pointerDown\n\t\t\t\t})\n\t\t\t);\n\n\t\t\tconst onPointerMove = (event: PointerEvent) => {\n\t\t\t\tif (this.#scrollParallax) return;\n\t\t\t\tconst rect = root.getBoundingClientRect();\n\t\t\t\tif (rect.width <= 0 || rect.height <= 0) return;\n\t\t\t\tthis.#parallaxX = ((event.clientX - rect.left) / rect.width - 0.5) * 2;\n\t\t\t\tthis.#parallaxY = ((event.clientY - rect.top) / rect.height - 0.5) * 2;\n\t\t\t\tsyncClass();\n\t\t\t};\n\t\t\tconst onPointerDown = (event: PointerEvent) => {\n\t\t\t\tthis.#pointerDown = true;\n\t\t\t\tif (event.pointerType === 'mouse') this.#hovering = true;\n\t\t\t\tsyncClass();\n\t\t\t};\n\t\t\tconst onPointerUp = () => {\n\t\t\t\tconst wasDown = this.#pointerDown;\n\t\t\t\tthis.#pointerDown = false;\n\t\t\t\tthis.#hovering = false;\n\t\t\t\tif (this.#scrollParallax && wasDown) {\n\t\t\t\t\tthis.#parallaxX = horizontalParallaxFromViewportY(\n\t\t\t\t\t\troot.getBoundingClientRect(),\n\t\t\t\t\t\twindow.innerHeight\n\t\t\t\t\t);\n\t\t\t\t\tthis.#parallaxY = 0;\n\t\t\t\t} else if (!this.#scrollParallax) {\n\t\t\t\t\tthis.#parallaxX = 0;\n\t\t\t\t\tthis.#parallaxY = 0;\n\t\t\t\t}\n\t\t\t\tsyncClass();\n\t\t\t};\n\t\t\troot.addEventListener('pointermove', onPointerMove);\n\t\t\troot.addEventListener('pointerdown', onPointerDown);\n\t\t\troot.addEventListener('pointerup', onPointerUp);\n\t\t\troot.addEventListener('pointercancel', onPointerUp);\n\t\t\troot.addEventListener('pointerenter', (event: PointerEvent) => {\n\t\t\t\tif (event.pointerType === 'mouse') {\n\t\t\t\t\tthis.#hovering = true;\n\t\t\t\t\tsyncClass();\n\t\t\t\t}\n\t\t\t});\n\t\t\troot.addEventListener('pointerleave', () => {\n\t\t\t\tif (!this.#pointerDown) {\n\t\t\t\t\tthis.#hovering = false;\n\t\t\t\t\tif (!this.#scrollParallax) {\n\t\t\t\t\t\tthis.#parallaxX = 0;\n\t\t\t\t\t\tthis.#parallaxY = 0;\n\t\t\t\t\t}\n\t\t\t\t\tsyncClass();\n\t\t\t\t}\n\t\t\t});\n\t\t\tthis.#cleanup.push(() => {\n\t\t\t\troot.removeEventListener('pointermove', onPointerMove);\n\t\t\t\troot.removeEventListener('pointerdown', onPointerDown);\n\t\t\t\troot.removeEventListener('pointerup', onPointerUp);\n\t\t\t\troot.removeEventListener('pointercancel', onPointerUp);\n\t\t\t});\n\t\t}\n\n\t\troot.addEventListener('click', (event: MouseEvent) => {\n\t\t\tif (isHostedLaunch && !this.getAttribute('return-url')?.trim()) {\n\t\t\t\tconst next = resolveThumbnailHref({\n\t\t\t\t\texperienceId: experienceId ?? undefined,\n\t\t\t\t\tplayerBaseUrl: this.getAttribute('player-base-url') ?? undefined,\n\t\t\t\t\tuseCurrentPageAsReturnUrl: true\n\t\t\t\t});\n\t\t\t\tif (\n\t\t\t\t\tnext &&\n\t\t\t\t\tevent.button === 0 &&\n\t\t\t\t\t!event.metaKey &&\n\t\t\t\t\t!event.ctrlKey &&\n\t\t\t\t\t!event.shiftKey\n\t\t\t\t) {\n\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\twindow.location.assign(next);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tsyncClass();\n\t}\n}\n\nif (!customElements.get(ELEMENT_NAME)) {\n\tcustomElements.define(ELEMENT_NAME, LiformaExperienceThumbnailElement);\n}\n\nexport { LiformaExperienceThumbnailElement };\n","import { resolvePublicApiBaseUrl } from '../../shims/apiBaseUrl';\nimport type { GalleryThumb } from '../../thumbnail/types';\n\nexport type ExperiencePreview = {\n\treadonly experienceId: string;\n\treadonly title: string;\n\treadonly galleryThumb: GalleryThumb;\n};\n\nfunction parseGalleryThumb(raw: unknown): GalleryThumb | null {\n\tif (!raw || typeof raw !== 'object') return null;\n\tconst thumb = raw as Record<string, unknown>;\n\tconst avatarImage = typeof thumb.avatarImage === 'string' ? thumb.avatarImage.trim() : '';\n\tif (!avatarImage) return null;\n\treturn {\n\t\tavatarImage,\n\t\t...(typeof thumb.backgroundImage === 'string' && thumb.backgroundImage.trim()\n\t\t\t? { backgroundImage: thumb.backgroundImage.trim() }\n\t\t\t: {}),\n\t\t...(typeof thumb.foregroundImage === 'string' && thumb.foregroundImage.trim()\n\t\t\t? { foregroundImage: thumb.foregroundImage.trim() }\n\t\t\t: {})\n\t};\n}\n\n/**\n * Fetch public widget preview (title + gallery thumb plates) by experience id.\n * No API key / origin allowlist — safe for collapsed FAB paint before mint.\n */\nexport async function fetchExperiencePreview(\n\texperienceId: string,\n\toptions?: {\n\t\treadonly apiBaseUrl?: string;\n\t\treadonly signal?: AbortSignal;\n\t}\n): Promise<ExperiencePreview> {\n\tconst trimmed = experienceId.trim();\n\tif (!trimmed) {\n\t\tthrow new Error('Experience preview requires experienceId.');\n\t}\n\n\tconst apiBaseUrl = (options?.apiBaseUrl ?? resolvePublicApiBaseUrl()).replace(/\\/$/, '');\n\tconst res = await fetch(\n\t\t`${apiBaseUrl}/v1/experiences/${encodeURIComponent(trimmed)}/preview`,\n\t\t{\n\t\t\tmethod: 'GET',\n\t\t\theaders: { Accept: 'application/json' },\n\t\t\tsignal: options?.signal\n\t\t}\n\t);\n\n\tif (!res.ok) {\n\t\tlet message = `Experience preview failed (${res.status})`;\n\t\ttry {\n\t\t\tconst body = (await res.json()) as { message?: string };\n\t\t\tif (body.message?.trim()) message = body.message.trim();\n\t\t} catch {\n\t\t\t/* keep */\n\t\t}\n\t\tthrow new Error(message);\n\t}\n\n\tlet raw: unknown;\n\ttry {\n\t\traw = await res.json();\n\t} catch {\n\t\tthrow new Error('Experience preview returned invalid JSON.');\n\t}\n\n\tif (!raw || typeof raw !== 'object') {\n\t\tthrow new Error('Experience preview returned an invalid payload.');\n\t}\n\tconst payload = raw as Record<string, unknown>;\n\tconst id =\n\t\ttypeof payload.experienceId === 'string' ? payload.experienceId.trim() : trimmed;\n\tconst title = typeof payload.title === 'string' ? payload.title.trim() : '';\n\tconst galleryThumb = parseGalleryThumb(payload.galleryThumb);\n\tif (!galleryThumb) {\n\t\tthrow new Error('Experience preview is missing gallery thumb images.');\n\t}\n\n\treturn {\n\t\texperienceId: id || trimmed,\n\t\ttitle: title || id || trimmed,\n\t\tgalleryThumb\n\t};\n}\n","/** Desktop breakpoint for reduce / dock chrome (matches CSS). */\nexport const WIDGET_DESKTOP_MEDIA = '(min-width: 768px)';\n\nconst DOCK_MARGIN = 24;\n/** Fraction of the device screen height (not the browser chrome box). */\nexport const DOCK_HEIGHT_FRAC = 0.5;\nexport const DOCK_HEIGHT_MAX = 1200;\n/** Never taller than this share of the browser viewport (keeps margins). */\nconst DOCK_VIEWPORT_FRAC = 0.85;\n/** Portrait width / height. */\nexport const DOCK_ASPECT = 9 / 16;\n\nexport type DockSize = {\n\treadonly width: number;\n\treadonly height: number;\n};\n\nexport type DockPoint = {\n\treadonly left: number;\n\treadonly top: number;\n};\n\nfunction readScreenHeight(): number {\n\tif (typeof window === 'undefined') return 800;\n\tconst avail = window.screen?.availHeight;\n\treturn typeof avail === 'number' && avail > 0 ? avail : window.innerHeight;\n}\n\n/**\n * Docked panel size: ~50% of device screen height (cap 1200), 9:16 portrait,\n * then clamped to fit the browser viewport with margin.\n */\nexport function computeDockSize(\n\tviewportWidth = typeof window !== 'undefined' ? window.innerWidth : 1280,\n\tviewportHeight = typeof window !== 'undefined' ? window.innerHeight : 800,\n\tmargin = DOCK_MARGIN,\n\tscreenHeight = typeof window !== 'undefined' ? readScreenHeight() : 800\n): DockSize {\n\tconst maxH = Math.max(\n\t\t160,\n\t\tMath.min(viewportHeight - margin * 2, viewportHeight * DOCK_VIEWPORT_FRAC)\n\t);\n\tconst maxW = Math.max(120, viewportWidth - margin * 2);\n\tlet height = Math.min(screenHeight * DOCK_HEIGHT_FRAC, DOCK_HEIGHT_MAX, maxH);\n\tlet width = height * DOCK_ASPECT;\n\tif (width > maxW) {\n\t\twidth = maxW;\n\t\theight = width / DOCK_ASPECT;\n\t\tif (height > maxH) {\n\t\t\theight = maxH;\n\t\t\twidth = height * DOCK_ASPECT;\n\t\t}\n\t}\n\treturn {\n\t\twidth: Math.round(width),\n\t\theight: Math.round(height)\n\t};\n}\n\n/** Default bottom-right placement for a docked panel. */\nexport function defaultDockOrigin(\n\tsize: DockSize,\n\tviewportWidth = typeof window !== 'undefined' ? window.innerWidth : 1280,\n\tviewportHeight = typeof window !== 'undefined' ? window.innerHeight : 800,\n\tmargin = DOCK_MARGIN\n): DockPoint {\n\treturn {\n\t\tleft: Math.max(margin, viewportWidth - size.width - margin),\n\t\ttop: Math.max(margin, viewportHeight - size.height - margin)\n\t};\n}\n\n/** Keep a docked panel fully inside the viewport. */\nexport function clampDockOrigin(\n\tleft: number,\n\ttop: number,\n\tsize: DockSize,\n\tviewportWidth = typeof window !== 'undefined' ? window.innerWidth : 1280,\n\tviewportHeight = typeof window !== 'undefined' ? window.innerHeight : 800,\n\tmargin = DOCK_MARGIN\n): DockPoint {\n\tconst maxLeft = Math.max(margin, viewportWidth - size.width - margin);\n\tconst maxTop = Math.max(margin, viewportHeight - size.height - margin);\n\treturn {\n\t\tleft: Math.min(Math.max(margin, left), maxLeft),\n\t\ttop: Math.min(Math.max(margin, top), maxTop)\n\t};\n}\n\nexport function isDesktopViewport(\n\tmatchMediaFn: (query: string) => MediaQueryList = typeof window !== 'undefined'\n\t\t? window.matchMedia.bind(window)\n\t\t: () => ({ matches: true }) as MediaQueryList\n): boolean {\n\treturn matchMediaFn(WIDGET_DESKTOP_MEDIA).matches;\n}\n","/** Styles for ExperienceWidget (class prefix `liforma-ew`). */\nexport const EXPERIENCE_WIDGET_CSS = `\n.liforma-ew {\n\tbox-sizing: border-box;\n\tposition: relative;\n\tdisplay: block;\n\twidth: 100%;\n\theight: 100%;\n\tmin-height: 0;\n\tmargin: 0;\n\tpadding: 0;\n\tborder: 0;\n}\n\n/* Optional self-positioned FAB (position=bottom-right|bottom-left).\n * Cap at 50vmin so small phones don't get an oversized thumb (desktop 224px stays). */\n.liforma-ew[data-position='bottom-right'],\n.liforma-ew[data-position='bottom-left'] {\n\tposition: fixed;\n\twidth: min(var(--liforma-ew-fab-size, 224px), 50vmin);\n\theight: min(var(--liforma-ew-fab-size, 224px), 50vmin);\n\tbottom: var(--liforma-ew-offset, 16px);\n\tz-index: 2147482900;\n}\n\n.liforma-ew[data-position='bottom-right'] {\n\tright: var(--liforma-ew-offset, 16px);\n\tleft: auto;\n}\n\n.liforma-ew[data-position='bottom-left'] {\n\tleft: var(--liforma-ew-offset, 16px);\n\tright: auto;\n}\n\n/* While open, collapse the FAB box so a corner hole / thumb never remains. */\n.liforma-ew[data-position='bottom-right'][data-state='expanded'],\n.liforma-ew[data-position='bottom-right'][data-state='docked'],\n.liforma-ew[data-position='bottom-left'][data-state='expanded'],\n.liforma-ew[data-position='bottom-left'][data-state='docked'] {\n\twidth: 0;\n\theight: 0;\n\tmin-width: 0;\n\tmin-height: 0;\n\toverflow: visible;\n\tpointer-events: none;\n}\n\n.liforma-ew[data-state='expanded'] .liforma-ew__warm,\n.liforma-ew[data-state='docked'] .liforma-ew__warm,\n.liforma-ew[data-state='expanded'] .liforma-ew__overlay,\n.liforma-ew[data-state='expanded'] .liforma-ew__toolbar,\n.liforma-ew[data-state='docked'] .liforma-ew__toolbar {\n\tpointer-events: auto;\n}\n\n.liforma-ew[data-state='docked'] .liforma-ew__overlay {\n\tpointer-events: none;\n}\n\n.liforma-ew[data-state='docked'] .liforma-ew__panel {\n\tpointer-events: auto;\n}\n\n.liforma-ew__hit {\n\tbox-sizing: border-box;\n\tdisplay: block;\n\twidth: 100%;\n\theight: 100%;\n\tmargin: 0;\n\tpadding: 0;\n\tborder: 0;\n\tbackground: transparent;\n\tcursor: pointer;\n\tfont: inherit;\n\tappearance: none;\n\t-webkit-tap-highlight-color: transparent;\n\ttransform: scale(1);\n\ttransform-origin: center center;\n\ttransition:\n\t\ttransform 180ms ease,\n\t\tfilter 180ms ease;\n\twill-change: transform;\n}\n\n/* Author display must not defeat the HTML hidden attribute. */\n.liforma-ew__hit[hidden] {\n\tdisplay: none !important;\n}\n\n.liforma-ew__hit:focus-visible {\n\toutline: 2px solid #5b8def;\n\toutline-offset: 3px;\n\tborder-radius: 0.75rem;\n}\n\n@media (hover: hover) and (pointer: fine) {\n\t/* Scale the thumb whenever the collapsed FAB is hovered — including via the warm iframe hit layer. */\n\t.liforma-ew[data-state='collapsed'] .liforma-ew__hit:not(:disabled):hover,\n\t.liforma-ew[data-state='collapsed']:has(.liforma-ew__warm:hover) .liforma-ew__hit {\n\t\ttransform: scale(1.06);\n\t}\n\n\t.liforma-ew[data-state='collapsed'] .liforma-ew__hit:not(:disabled):hover .liforma-et,\n\t.liforma-ew[data-state='collapsed']:has(.liforma-ew__warm:hover) .liforma-et {\n\t\tbox-shadow: 0 12px 36px rgba(0, 0, 0, 0.34);\n\t}\n}\n\n.liforma-ew__face {\n\twidth: 100%;\n\theight: 100%;\n\tmin-height: 0;\n\tposition: relative;\n}\n\n.liforma-ew__face-status {\n\tposition: absolute;\n\tinset: 0;\n\tz-index: 1;\n\tdisplay: grid;\n\tplace-items: center;\n\tpadding: 0.5rem;\n\ttext-align: center;\n\tfont: 600 0.75rem/1.3 system-ui, sans-serif;\n\tcolor: #e8ecf4;\n\tbackground: rgba(10, 12, 18, 0.72);\n\tborder-radius: 0.75rem;\n\tpointer-events: none;\n}\n\n.liforma-ew__face-status[hidden] {\n\tdisplay: none !important;\n}\n\n.liforma-ew__hit:disabled {\n\tcursor: default;\n\topacity: 0.85;\n}\n\n.liforma-ew__face .liforma-et {\n\tborder-radius: 0.75rem;\n\tbox-shadow: 0 8px 28px rgba(0, 0, 0, 0.28);\n\ttransition: box-shadow 180ms ease;\n}\n\n.liforma-ew__warm {\n\tbox-sizing: border-box;\n\tposition: absolute;\n\tinset: 0;\n\tz-index: 2;\n\tborder-radius: 0.75rem;\n\toverflow: hidden;\n\tbackground: transparent;\n\tpointer-events: none;\n\topacity: 1;\n\ttransition: opacity 1000ms ease;\n}\n\n/*\n * Collapsed: keep the warm iframe as an invisible hit target only.\n * Painting it over the thumb causes a “ghost enlarge” (face scales under a static iframe).\n */\n.liforma-ew[data-state='collapsed'] .liforma-ew__warm {\n\topacity: 0;\n\ttransition: none;\n}\n\n.liforma-ew[data-warm='true'] .liforma-ew__warm {\n\tpointer-events: auto;\n}\n\n.liforma-ew[data-warm='true']:not([data-live='true']) .liforma-ew__hit {\n\tpointer-events: none;\n}\n\n.liforma-ew[data-live='true'][data-state='collapsed'] .liforma-ew__hit {\n\tpointer-events: auto;\n}\n\n.liforma-ew[data-live='true'] .liforma-ew__warm,\n.liforma-ew[data-live='true'] .liforma-ew__stage {\n\t/* Hidden under the thumb / until post-resize fade-in (matches cold avatar reveal). */\n\topacity: 0;\n}\n\n.liforma-ew[data-live='true'].liforma-ew--session-revealed[data-state='expanded'] .liforma-ew__warm,\n.liforma-ew[data-live='true'].liforma-ew--session-revealed[data-state='expanded'] .liforma-ew__stage,\n.liforma-ew[data-live='true'].liforma-ew--session-revealed[data-state='docked'] .liforma-ew__warm,\n.liforma-ew[data-live='true'].liforma-ew--session-revealed[data-state='docked'] .liforma-ew__stage {\n\topacity: 1;\n}\n\n.liforma-ew--session-concealing .liforma-ew__warm,\n.liforma-ew--session-concealing .liforma-ew__stage {\n\ttransition-duration: 280ms;\n}\n\n/* Instant hide while dock/maximize geometry settles (player morph resize ~160ms). */\n.liforma-ew--layout-settling .liforma-ew__warm,\n.liforma-ew--layout-settling .liforma-ew__stage {\n\topacity: 0 !important;\n\ttransition: none !important;\n}\n\n.liforma-ew[data-live='true'][data-state='collapsed'] .liforma-ew__warm {\n\t/* Keep the live iframe under the thumb; FAB click resumes the shell. */\n\tpointer-events: none;\n\topacity: 0;\n\t/* Instant hide when collapsed so the FAB never shows a resized live mesh. */\n\ttransition: none;\n}\n\n.liforma-ew[data-state='expanded'] .liforma-ew__warm {\n\tposition: fixed;\n\t/* Match overlay panel inset so the warm iframe fills the conversation shell. */\n\tinset: clamp(12px, 3vw, 28px);\n\tz-index: 2147483001;\n\twidth: auto;\n\theight: auto;\n\tborder-radius: 16px;\n\tpointer-events: auto;\n}\n\n.liforma-ew[data-state='docked'] .liforma-ew__warm {\n\tposition: fixed;\n\tinset: auto;\n\tz-index: 2147483001;\n\tborder-radius: 16px;\n\tpointer-events: auto;\n\t/* left/top/width/height set inline from dock geometry */\n}\n\n.liforma-ew__toolbar {\n\tdisplay: none;\n\tposition: fixed;\n\tz-index: 2147483002;\n\tpointer-events: auto;\n\tbox-sizing: border-box;\n\talign-items: center;\n\tgap: 0.35rem;\n\tpadding: 0.35rem 0.4rem;\n\tborder-radius: 999px;\n\tbackground: rgba(12, 14, 20, 0.72);\n\tbackdrop-filter: blur(8px);\n\t-webkit-backdrop-filter: blur(8px);\n\tbox-shadow: 0 8px 24px rgba(0, 0, 0, 0.28);\n}\n\n.liforma-ew[data-state='expanded'] .liforma-ew__toolbar {\n\tdisplay: flex;\n\ttop: calc(clamp(12px, 3vw, 28px) + 10px);\n\tright: calc(clamp(12px, 3vw, 28px) + 10px);\n\tleft: auto;\n\tcursor: default;\n}\n\n.liforma-ew[data-state='docked'] .liforma-ew__toolbar {\n\tdisplay: flex;\n\t/* left/top set inline to track the panel; strip remains a drag handle */\n\tcursor: grab;\n\ttouch-action: none;\n\tuser-select: none;\n}\n\n.liforma-ew[data-state='docked'] .liforma-ew__panel,\n.liforma-ew[data-state='docked'] .liforma-ew__warm,\n.liforma-ew[data-state='docked'] .liforma-ew__stage {\n\t/* Player also reports surface drags via postMessage when docked. */\n\tcursor: grab;\n\ttouch-action: none;\n}\n\n.liforma-ew[data-state='docked'] .liforma-ew__toolbar.liforma-ew__toolbar--dragging,\n.liforma-ew--dock-dragging,\n.liforma-ew--dock-dragging .liforma-ew__toolbar,\n.liforma-ew--dock-dragging .liforma-ew__panel,\n.liforma-ew--dock-dragging .liforma-ew__warm,\n.liforma-ew--dock-dragging .liforma-ew__stage {\n\tcursor: grabbing;\n}\n\n\n.liforma-ew__toolbar[hidden] {\n\tdisplay: none !important;\n}\n\n.liforma-ew__icon-btn {\n\tappearance: none;\n\tbox-sizing: border-box;\n\tdisplay: inline-grid;\n\tplace-items: center;\n\twidth: 2rem;\n\theight: 2rem;\n\tmargin: 0;\n\tpadding: 0;\n\tborder: 0;\n\tborder-radius: 999px;\n\tcolor: #f5f7fb;\n\tbackground: transparent;\n\tcursor: pointer;\n}\n\n.liforma-ew__icon-btn:hover {\n\tbackground: rgba(255, 255, 255, 0.12);\n}\n\n.liforma-ew__icon-btn[aria-pressed='true'] {\n\tbackground: rgba(255, 255, 255, 0.2);\n}\n\n.liforma-ew__icon-btn:focus-visible {\n\toutline: 2px solid #5b8def;\n\toutline-offset: 2px;\n}\n\n.liforma-ew__icon-btn svg {\n\twidth: 1.05rem;\n\theight: 1.05rem;\n\tdisplay: block;\n}\n\n.liforma-ew__drag-grip {\n\tdisplay: none;\n\twidth: 0.85rem;\n\theight: 1.25rem;\n\tmargin: 0 0.15rem 0 0.25rem;\n\tbackground:\n\t\tradial-gradient(circle, currentColor 1.1px, transparent 1.2px) 0 0 / 0.35rem 0.35rem,\n\t\tradial-gradient(circle, currentColor 1.1px, transparent 1.2px) 0.35rem 0 / 0.35rem 0.35rem;\n\tbackground-repeat: repeat-y;\n\topacity: 0.55;\n\tcolor: #f5f7fb;\n\tpointer-events: none;\n}\n\n.liforma-ew[data-state='docked'] .liforma-ew__drag-grip {\n\tdisplay: block;\n}\n\n.liforma-ew__dock-toggle[hidden] {\n\tdisplay: none !important;\n}\n\n.liforma-ew__overlay {\n\tposition: fixed;\n\tinset: 0;\n\tz-index: 2147483000;\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\tpadding: clamp(12px, 3vw, 28px);\n\tbox-sizing: border-box;\n}\n\n.liforma-ew[data-state='docked'] .liforma-ew__overlay {\n\tpadding: 0;\n\talign-items: stretch;\n\tjustify-content: stretch;\n\tpointer-events: none;\n\tbackground: transparent;\n}\n\n.liforma-ew__overlay[hidden] {\n\tdisplay: none !important;\n}\n\n.liforma-ew__backdrop {\n\tposition: absolute;\n\tinset: 0;\n\tborder: 0;\n\tpadding: 0;\n\tmargin: 0;\n\tbackground: rgba(6, 8, 14, 0.62);\n\tcursor: pointer;\n}\n\n.liforma-ew[data-state='docked'] .liforma-ew__backdrop {\n\tdisplay: none !important;\n}\n\n.liforma-ew__panel {\n\tposition: relative;\n\tz-index: 1;\n\tdisplay: flex;\n\tflex-direction: column;\n\t/* Fill the overlay so player loading / start UI centers in the visible window. */\n\twidth: var(--liforma-ew-max-w, 100%);\n\theight: var(--liforma-ew-max-h, 100%);\n\tmax-width: 100%;\n\tmax-height: 100%;\n\tborder-radius: 16px;\n\toverflow: hidden;\n\tbackground: #030508;\n\tbox-shadow: 0 24px 80px rgba(0, 0, 0, 0.45);\n\t/* Warm iframe sits above as a sibling; keep panel as chrome shell. */\n\tpointer-events: none;\n}\n\n.liforma-ew[data-state='docked'] .liforma-ew__panel {\n\tposition: fixed;\n\tinset: auto;\n\tmax-width: none;\n\tmax-height: none;\n\tpointer-events: auto;\n\t/* left/top/width/height set inline from dock geometry */\n}\n\n.liforma-ew:not([data-warm='true']) .liforma-ew__panel {\n\tpointer-events: auto;\n}\n\n.liforma-ew__stage {\n\tposition: relative;\n\tflex: 1;\n\tmin-height: 0;\n\twidth: 100%;\n\theight: 100%;\n\tbackground: #030508;\n\topacity: 1;\n\ttransition: opacity 1000ms ease;\n}\n\n.liforma-ew__status {\n\tposition: absolute;\n\tinset: 0;\n\tz-index: 1;\n\tdisplay: grid;\n\tplace-items: center;\n\tpadding: 1.5rem;\n\ttext-align: center;\n\tcolor: #d7dde8;\n\tfont: 500 0.9375rem/1.45 system-ui, sans-serif;\n\tbackground: rgba(3, 5, 8, 0.92);\n\tpointer-events: none;\n}\n\n.liforma-ew__status[hidden] {\n\tdisplay: none !important;\n}\n\n/* Mobile expanded: ~90% viewport, no reduce control. */\n@media (max-width: 767px) {\n\t.liforma-ew[data-state='expanded'] .liforma-ew__overlay {\n\t\tpadding: 5vh 5vw;\n\t}\n\n\t.liforma-ew[data-state='expanded'] .liforma-ew__panel {\n\t\twidth: 90vw;\n\t\theight: 90vh;\n\t\tmax-width: 90vw;\n\t\tmax-height: 90vh;\n\t}\n\n\t.liforma-ew[data-state='expanded'] .liforma-ew__warm {\n\t\tinset: 5vh 5vw;\n\t}\n\n\t.liforma-ew[data-state='expanded'] .liforma-ew__toolbar {\n\t\ttop: calc(5vh + 10px);\n\t\tright: calc(5vw + 10px);\n\t}\n\n\t.liforma-ew__dock-toggle {\n\t\tdisplay: none !important;\n\t}\n}\n\n@media (prefers-reduced-motion: reduce) {\n\t.liforma-ew__hit {\n\t\ttransition: none;\n\t\twill-change: auto;\n\t}\n\n\t.liforma-ew[data-state='collapsed'] .liforma-ew__hit:not(:disabled):hover,\n\t.liforma-ew[data-state='collapsed']:has(.liforma-ew__warm:hover) .liforma-ew__hit {\n\t\ttransform: none;\n\t}\n\n\t.liforma-ew__face .liforma-et {\n\t\tbox-shadow: none;\n\t\ttransition: none;\n\t}\n\n\t.liforma-ew__warm,\n\t.liforma-ew__stage {\n\t\ttransition: none;\n\t}\n}\n`;\n\nconst STYLE_ID = 'liforma-experience-widget-styles';\n\nexport function ensureExperienceWidgetStyles(css: string = EXPERIENCE_WIDGET_CSS): void {\n\tif (typeof document === 'undefined') return;\n\tconst existing = document.getElementById(STYLE_ID);\n\tif (existing) {\n\t\t/* Keep injected CSS current across local HMR / preview reloads. */\n\t\texisting.textContent = css;\n\t\treturn;\n\t}\n\tconst style = document.createElement('style');\n\tstyle.id = STYLE_ID;\n\tstyle.textContent = css;\n\tdocument.head.append(style);\n}\n","import { fetchExperiencePreview } from '../v2/api/fetchExperiencePreview';\nimport { Experience } from '../v2/experience';\nimport type { StartButtonOptions } from '../v2/types/sessionInteraction';\nimport {\n\tattachThumbnailParallax,\n\tensureExperienceThumbnailStyles,\n\tEXPERIENCE_THUMBNAIL_CSS,\n\tresolveThumbnailLayers\n} from '../thumbnail/index';\nimport type { GalleryThumb } from '../thumbnail/types';\nimport {\n\tclampDockOrigin,\n\tcomputeDockSize,\n\tdefaultDockOrigin,\n\tisDesktopViewport,\n\tWIDGET_DESKTOP_MEDIA\n} from './dockGeometry';\nimport { ensureExperienceWidgetStyles, EXPERIENCE_WIDGET_CSS } from './styles';\nimport type {\n\tExperienceWidgetController,\n\tExperienceWidgetOptions,\n\tExperienceWidgetPosition,\n\tExperienceWidgetPrefetch\n} from './types';\n\nfunction resolvePosition(value: ExperienceWidgetPosition | undefined): ExperienceWidgetPosition {\n\treturn value === 'bottom-right' || value === 'bottom-left' ? value : 'static';\n}\n\n/** Normalize offset to a CSS length (default 16px). */\nexport function resolveWidgetOffsetCss(offset?: number | string): string {\n\tif (typeof offset === 'number' && Number.isFinite(offset)) {\n\t\treturn `${offset}px`;\n\t}\n\tconst raw = typeof offset === 'string' ? offset.trim() : '';\n\tif (!raw) return '16px';\n\tif (/^\\d+(\\.\\d+)?$/.test(raw)) return `${raw}px`;\n\treturn raw;\n}\n\ntype FaceLayers = {\n\treadonly galleryThumb?: GalleryThumb;\n\treadonly avatarImage?: string;\n\treadonly backgroundImage?: string;\n\treadonly foregroundImage?: string;\n\treadonly fallbackImage?: string;\n};\n\ntype ShellState = 'collapsed' | 'expanded' | 'docked';\n\nfunction isOpenShell(state: string | undefined): state is 'expanded' | 'docked' {\n\treturn state === 'expanded' || state === 'docked';\n}\n\n/** Tabler `settings` — audio device panel. */\nconst ICON_SETTINGS = `<svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 0 0 2.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 0 0 1.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 0 0-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 0 0-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 0 0-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 0 0-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 0 0 1.066-2.573c-.94-1.543.826-3.31 2.37-2.37c1 .608 2.296.07 2.572-1.065\"/><path d=\"M9 12a3 3 0 1 0 6 0a3 3 0 0 0-6 0\"/></svg>`;\n\nconst ICON_CLOSE = `<svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\"><path fill=\"currentColor\" d=\"M18.3 5.71a1 1 0 0 0-1.41 0L12 10.59 7.11 5.7A1 1 0 0 0 5.7 7.11L10.59 12l-4.9 4.89a1 1 0 1 0 1.42 1.42L12 13.41l4.89 4.9a1 1 0 0 0 1.42-1.42L13.41 12l4.9-4.89a1 1 0 0 0-.01-1.4Z\"/></svg>`;\n\n/** Tabler `arrows-diagonal-minimize-2` — reduce / dock. */\nconst ICON_REDUCE = `<svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M18 10h-4V6m6-2l-6 6m-8 4h4v4m0-4l-6 6\"/></svg>`;\n\n/** Tabler `arrows-diagonal` — maximize (pair for minimize-2). */\nconst ICON_MAXIMIZE = `<svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M16 4h4v4m-6 2l6-6M8 20H4v-4m0 4l6-6\"/></svg>`;\n\nfunction renderFace(\n\tcontainer: HTMLElement,\n\tlayersInput: FaceLayers,\n\toptions?: { readonly parallax?: boolean }\n): (() => void) | null {\n\tensureExperienceThumbnailStyles(EXPERIENCE_THUMBNAIL_CSS);\n\tconst layers = resolveThumbnailLayers({\n\t\tgalleryThumb: layersInput.galleryThumb,\n\t\tavatarImage: layersInput.avatarImage,\n\t\tbackgroundImage: layersInput.backgroundImage,\n\t\tforegroundImage: layersInput.foregroundImage,\n\t\tfallbackImage: layersInput.fallbackImage\n\t});\n\tif (!layers) {\n\t\tcontainer.replaceChildren();\n\t\treturn null;\n\t}\n\n\tconst root = document.createElement('div');\n\troot.className = `liforma-et${layers.backgroundImage ? ' liforma-et--scene' : ''} liforma-et--revealed`;\n\troot.setAttribute('aria-hidden', 'true');\n\n\tconst placeholder = document.createElement('span');\n\tplaceholder.className = 'liforma-et__placeholder';\n\tplaceholder.setAttribute('aria-hidden', 'true');\n\troot.append(placeholder);\n\n\tconst markReady = (img: HTMLImageElement) => {\n\t\timg.classList.add('liforma-et__media--ready');\n\t};\n\n\tif (layers.backgroundImage) {\n\t\tconst bg = document.createElement('img');\n\t\tbg.className = 'liforma-et__background liforma-et__media';\n\t\tbg.alt = '';\n\t\tbg.decoding = 'async';\n\t\tbg.src = layers.backgroundImage;\n\t\tbg.addEventListener('load', () => markReady(bg), { once: true });\n\t\troot.append(bg);\n\t}\n\n\tconst avatar = document.createElement('img');\n\tavatar.className = 'liforma-et__avatar liforma-et__media';\n\tavatar.alt = '';\n\tavatar.decoding = 'async';\n\tavatar.src = layers.avatarImage;\n\tavatar.addEventListener('load', () => markReady(avatar), { once: true });\n\tavatar.addEventListener(\n\t\t'error',\n\t\t() => {\n\t\t\tif (layers.fallbackImage) {\n\t\t\t\tavatar.src = layers.fallbackImage;\n\t\t\t}\n\t\t},\n\t\t{ once: true }\n\t);\n\troot.append(avatar);\n\n\tif (layers.foregroundImage) {\n\t\tconst fg = document.createElement('img');\n\t\tfg.className = 'liforma-et__foreground liforma-et__media';\n\t\tfg.alt = '';\n\t\tfg.decoding = 'async';\n\t\tfg.src = layers.foregroundImage;\n\t\tfg.addEventListener('load', () => markReady(fg), { once: true });\n\t\troot.append(fg);\n\t}\n\n\tcontainer.replaceChildren(root);\n\n\tif (options?.parallax === false) return () => {};\n\treturn attachThumbnailParallax(root);\n}\n\nfunction resolveStartButton(options: ExperienceWidgetOptions): StartButtonOptions {\n\tif (options.startButton) {\n\t\treturn {\n\t\t\t...options.startButton,\n\t\t\tlabel: options.startButton.label ?? options.startButtonLabel ?? 'Tap to talk',\n\t\t\tplacement: options.startButton.placement ?? 'bottom-center',\n\t\t\tvariant: options.startButton.variant ?? 'primary'\n\t\t};\n\t}\n\treturn {\n\t\tlabel: options.startButtonLabel ?? 'Tap to talk',\n\t\tplacement: 'bottom-center',\n\t\tvariant: 'primary'\n\t};\n}\n\nfunction scheduleIdle(callback: () => void): () => void {\n\tlet cancelled = false;\n\tconst run = () => {\n\t\tif (cancelled) return;\n\t\tcallback();\n\t};\n\n\tconst onLoad = () => {\n\t\tif (cancelled) return;\n\t\tconst ric = (\n\t\t\twindow as Window & {\n\t\t\t\trequestIdleCallback?: (cb: () => void, opts?: { timeout: number }) => number;\n\t\t\t\tcancelIdleCallback?: (id: number) => void;\n\t\t\t}\n\t\t).requestIdleCallback;\n\t\tif (typeof ric === 'function') {\n\t\t\tconst id = ric(run, { timeout: 3000 });\n\t\t\tcancelIdle = () => {\n\t\t\t\t(\n\t\t\t\t\twindow as Window & { cancelIdleCallback?: (id: number) => void }\n\t\t\t\t).cancelIdleCallback?.(id);\n\t\t\t};\n\t\t\treturn;\n\t\t}\n\t\tconst t = window.setTimeout(run, 1);\n\t\tcancelIdle = () => window.clearTimeout(t);\n\t};\n\n\tlet cancelIdle: (() => void) | null = null;\n\tif (document.readyState === 'complete') {\n\t\tonLoad();\n\t} else {\n\t\twindow.addEventListener('load', onLoad, { once: true });\n\t}\n\n\treturn () => {\n\t\tcancelled = true;\n\t\twindow.removeEventListener('load', onLoad);\n\t\tcancelIdle?.();\n\t};\n}\n\n/**\n * Mount a light collapsed widget that expands an Experience overlay on click.\n * Default `prefetch=\"onExpand\"` is cold (second tap unlocks audio).\n * `idle` / `eager` warm a transparent iframe so one in-iframe click unlocks + expands.\n *\n * When gallery thumb layers are not provided, fetches\n * `GET /v1/experiences/{id}/preview` (no auth / allowlist).\n */\nexport function mountExperienceWidget(\n\thost: HTMLElement,\n\toptions: ExperienceWidgetOptions\n): ExperienceWidgetController {\n\tensureExperienceWidgetStyles(EXPERIENCE_WIDGET_CSS);\n\n\tconst experienceId = options.experienceId?.trim();\n\tif (!experienceId) {\n\t\tthrow new Error('ExperienceWidget requires experienceId.');\n\t}\n\n\tconst prefetchMode: ExperienceWidgetPrefetch = options.prefetch ?? 'onExpand';\n\tconst position = resolvePosition(options.position);\n\n\tconst root = document.createElement('div');\n\troot.className = 'liforma-ew';\n\troot.dataset.state = 'collapsed';\n\troot.dataset.prefetch = prefetchMode;\n\troot.dataset.position = position;\n\troot.style.setProperty('--liforma-ew-max-w', options.expandedMaxWidth ?? '100%');\n\troot.style.setProperty('--liforma-ew-max-h', options.expandedMaxHeight ?? '100%');\n\tif (position !== 'static') {\n\t\troot.style.setProperty('--liforma-ew-offset', resolveWidgetOffsetCss(options.offset));\n\t}\n\n\tconst hit = document.createElement('button');\n\thit.type = 'button';\n\thit.className = 'liforma-ew__hit';\n\thit.setAttribute('aria-label', options.alt);\n\thit.disabled = true;\n\n\tconst face = document.createElement('div');\n\tface.className = 'liforma-ew__face';\n\thit.append(face);\n\n\tconst faceLoading = document.createElement('div');\n\tfaceLoading.className = 'liforma-ew__face-status';\n\tfaceLoading.textContent = 'Loading…';\n\tface.append(faceLoading);\n\n\tconst warmStage = document.createElement('div');\n\twarmStage.className = 'liforma-ew__warm';\n\twarmStage.hidden = true;\n\twarmStage.setAttribute('aria-hidden', 'true');\n\n\tconst overlay = document.createElement('div');\n\toverlay.className = 'liforma-ew__overlay';\n\toverlay.hidden = true;\n\toverlay.setAttribute('role', 'dialog');\n\toverlay.setAttribute('aria-modal', 'true');\n\toverlay.setAttribute('aria-label', options.alt);\n\n\tconst backdrop = document.createElement('button');\n\tbackdrop.type = 'button';\n\tbackdrop.className = 'liforma-ew__backdrop';\n\tbackdrop.setAttribute('aria-label', 'Close experience');\n\n\tconst panel = document.createElement('div');\n\tpanel.className = 'liforma-ew__panel';\n\n\tconst toolbar = document.createElement('div');\n\ttoolbar.className = 'liforma-ew__toolbar';\n\ttoolbar.hidden = true;\n\n\tconst dragGrip = document.createElement('span');\n\tdragGrip.className = 'liforma-ew__drag-grip';\n\tdragGrip.setAttribute('aria-hidden', 'true');\n\n\tconst dockToggleBtn = document.createElement('button');\n\tdockToggleBtn.type = 'button';\n\tdockToggleBtn.className = 'liforma-ew__icon-btn liforma-ew__dock-toggle';\n\tdockToggleBtn.innerHTML = ICON_REDUCE;\n\tdockToggleBtn.setAttribute('aria-label', 'Reduce');\n\n\tconst settingsBtn = document.createElement('button');\n\tsettingsBtn.type = 'button';\n\tsettingsBtn.className = 'liforma-ew__icon-btn liforma-ew__settings';\n\tsettingsBtn.innerHTML = ICON_SETTINGS;\n\tsettingsBtn.setAttribute('aria-label', 'Audio settings');\n\tsettingsBtn.setAttribute('aria-pressed', 'false');\n\n\tconst closeBtn = document.createElement('button');\n\tcloseBtn.type = 'button';\n\tcloseBtn.className = 'liforma-ew__icon-btn liforma-ew__close';\n\tcloseBtn.innerHTML = ICON_CLOSE;\n\tcloseBtn.setAttribute('aria-label', 'Close');\n\n\ttoolbar.append(dragGrip, dockToggleBtn, settingsBtn, closeBtn);\n\n\tconst stage = document.createElement('div');\n\tstage.className = 'liforma-ew__stage';\n\n\tconst status = document.createElement('div');\n\tstatus.className = 'liforma-ew__status';\n\tstatus.hidden = true;\n\n\tpanel.append(stage, status);\n\toverlay.append(backdrop, panel);\n\t/* Toolbar must be a root sibling above the warm iframe stacking context. */\n\troot.append(hit, warmStage, overlay, toolbar);\n\thost.replaceChildren(root);\n\n\tlet experience: Experience | null = null;\n\tlet expanding = false;\n\tlet destroyed = false;\n\tlet previewReady = false;\n\tlet warmReady = false;\n\tlet warmInFlight = false;\n\t/** True after iframe attach (warm or cold); retain across collapse until hard teardown. */\n\tlet shellMounted = false;\n\t/** True after the player has unlocked audio / started conversation. */\n\tlet sessionStarted = false;\n\t/** Bumped on hard teardown so in-flight expand/warm work is discarded. */\n\tlet expandEpoch = 0;\n\tlet previousOverflow = '';\n\tlet detachFaceParallax: (() => void) | null = null;\n\tlet cancelWarmSchedule: (() => void) | null = null;\n\tlet collapseRevealTimer: ReturnType<typeof setTimeout> | null = null;\n\tlet layoutSettleTimer: ReturnType<typeof setTimeout> | null = null;\n\t/** Last docked origin; null means use default bottom-right for current size. */\n\tlet dockLeft: number | null = null;\n\tlet dockTop: number | null = null;\n\tlet dragPointerId: number | null = null;\n\tlet dragOffsetX = 0;\n\tlet dragOffsetY = 0;\n\t/** `toolbar` = parent pointer capture; `player` = iframe postMessage stream. */\n\tlet dockDragSource: 'toolbar' | 'player' | null = null;\n\tlet dockDragWindowBound = false;\n\tconst previewAbort = new AbortController();\n\tconst parallaxEnabled = options.parallax !== false;\n\tconst SESSION_CONCEAL_MS = 280;\n\t/**\n\t * Past FaceSessionView resize debounce (160ms) + morph restart so dock/maximize\n\t * never flashes a wrong-aspect canvas stretched into the new box.\n\t */\n\tconst LAYOUT_SETTLE_MS = 220;\n\tconst TOOLBAR_GAP = 10;\n\n\tconst shellState = (): ShellState => {\n\t\tconst state = root.dataset.state;\n\t\tif (state === 'expanded' || state === 'docked' || state === 'collapsed') return state;\n\t\treturn 'collapsed';\n\t};\n\n\tconst clearCollapseRevealTimer = () => {\n\t\tif (collapseRevealTimer !== null) {\n\t\t\tclearTimeout(collapseRevealTimer);\n\t\t\tcollapseRevealTimer = null;\n\t\t}\n\t};\n\n\tconst clearLayoutSettleTimer = () => {\n\t\tif (layoutSettleTimer !== null) {\n\t\t\tclearTimeout(layoutSettleTimer);\n\t\t\tlayoutSettleTimer = null;\n\t\t}\n\t\troot.classList.remove('liforma-ew--layout-settling');\n\t};\n\n\tconst clearExpandedLayoutInline = () => {\n\t\tfor (const el of [panel, warmStage, toolbar]) {\n\t\t\tel.style.removeProperty('left');\n\t\t\tel.style.removeProperty('top');\n\t\t\tel.style.removeProperty('width');\n\t\t\tel.style.removeProperty('height');\n\t\t\tel.style.removeProperty('right');\n\t\t\tel.style.removeProperty('bottom');\n\t\t}\n\t\ttoolbar.classList.remove('liforma-ew__toolbar--dragging');\n\t\troot.classList.remove('liforma-ew--dock-dragging');\n\t};\n\n\tconst applyDockedGeometry = (left: number, top: number, width: number, height: number) => {\n\t\tconst box = (el: HTMLElement) => {\n\t\t\tel.style.left = `${left}px`;\n\t\t\tel.style.top = `${top}px`;\n\t\t\tel.style.width = `${width}px`;\n\t\t\tel.style.height = `${height}px`;\n\t\t\tel.style.right = 'auto';\n\t\t\tel.style.bottom = 'auto';\n\t\t};\n\t\tbox(panel);\n\t\tbox(warmStage);\n\t\tconst toolbarWidth = toolbar.offsetWidth || 120;\n\t\ttoolbar.style.left = `${left + width - toolbarWidth - TOOLBAR_GAP}px`;\n\t\ttoolbar.style.top = `${top + TOOLBAR_GAP}px`;\n\t\ttoolbar.style.right = 'auto';\n\t\ttoolbar.style.bottom = 'auto';\n\t\ttoolbar.style.width = 'auto';\n\t\ttoolbar.style.height = 'auto';\n\t};\n\n\tconst applyDockDragPosition = (clientX: number, clientY: number) => {\n\t\tif (shellState() !== 'docked') return;\n\t\tconst size = computeDockSize();\n\t\tconst origin = clampDockOrigin(clientX - dragOffsetX, clientY - dragOffsetY, size);\n\t\tdockLeft = origin.left;\n\t\tdockTop = origin.top;\n\t\tapplyDockedGeometry(origin.left, origin.top, size.width, size.height);\n\t};\n\n\tconst applyDockDragDelta = (dx: number, dy: number) => {\n\t\tif (shellState() !== 'docked') return;\n\t\tif (!Number.isFinite(dx) || !Number.isFinite(dy)) return;\n\t\tif (dx === 0 && dy === 0) return;\n\t\tconst size = computeDockSize();\n\t\tconst currentLeft = dockLeft ?? panel.getBoundingClientRect().left;\n\t\tconst currentTop = dockTop ?? panel.getBoundingClientRect().top;\n\t\tconst origin = clampDockOrigin(currentLeft + dx, currentTop + dy, size);\n\t\tdockLeft = origin.left;\n\t\tdockTop = origin.top;\n\t\tapplyDockedGeometry(origin.left, origin.top, size.width, size.height);\n\t};\n\n\tconst onToolbarDockPointerMove = (event: PointerEvent) => {\n\t\tif (dockDragSource !== 'toolbar') return;\n\t\tif (dragPointerId === null || event.pointerId !== dragPointerId) return;\n\t\tevent.preventDefault();\n\t\tapplyDockDragPosition(event.clientX, event.clientY);\n\t};\n\n\tconst endDockDrag = (event?: PointerEvent) => {\n\t\tif (dragPointerId === null) return;\n\t\tif (event && event.pointerId !== dragPointerId) return;\n\t\tconst source = dockDragSource;\n\t\tdragPointerId = null;\n\t\tdockDragSource = null;\n\t\ttoolbar.classList.remove('liforma-ew__toolbar--dragging');\n\t\troot.classList.remove('liforma-ew--dock-dragging');\n\t\tif (source === 'toolbar' && dockDragWindowBound) {\n\t\t\tdockDragWindowBound = false;\n\t\t\twindow.removeEventListener('pointermove', onToolbarDockPointerMove);\n\t\t\twindow.removeEventListener('pointerup', endDockDrag);\n\t\t\twindow.removeEventListener('pointercancel', endDockDrag);\n\t\t}\n\t};\n\n\tconst beginToolbarDockDrag = (clientX: number, clientY: number, pointerId: number) => {\n\t\tif (destroyed) return;\n\t\tif (shellState() !== 'docked') return;\n\t\tif (!isDesktopViewport()) return;\n\t\tif (dragPointerId !== null) return;\n\t\tdragPointerId = pointerId;\n\t\tdockDragSource = 'toolbar';\n\t\ttoolbar.classList.add('liforma-ew__toolbar--dragging');\n\t\troot.classList.add('liforma-ew--dock-dragging');\n\t\tconst panelRect = panel.getBoundingClientRect();\n\t\tdragOffsetX = clientX - panelRect.left;\n\t\tdragOffsetY = clientY - panelRect.top;\n\t\tif (!dockDragWindowBound) {\n\t\t\tdockDragWindowBound = true;\n\t\t\twindow.addEventListener('pointermove', onToolbarDockPointerMove);\n\t\t\twindow.addEventListener('pointerup', endDockDrag);\n\t\t\twindow.addEventListener('pointercancel', endDockDrag);\n\t\t}\n\t};\n\n\tconst beginDockDragFromPlayer = (event: { readonly pointerId: number }) => {\n\t\tif (destroyed) return;\n\t\tif (shellState() !== 'docked') return;\n\t\tif (!isDesktopViewport()) return;\n\t\tif (dragPointerId !== null) return;\n\t\tdragPointerId = event.pointerId;\n\t\tdockDragSource = 'player';\n\t\ttoolbar.classList.add('liforma-ew__toolbar--dragging');\n\t\troot.classList.add('liforma-ew--dock-dragging');\n\t\t/*\n\t\t * Delta-based player drags: keep dockLeft/dockTop as the live origin.\n\t\t * Do not convert iframe client coordinates (async layout feedback jumps).\n\t\t */\n\t\tconst panelRect = panel.getBoundingClientRect();\n\t\tdockLeft = panelRect.left;\n\t\tdockTop = panelRect.top;\n\t};\n\n\tconst moveDockDragFromPlayer = (event: {\n\t\treadonly pointerId: number;\n\t\treadonly dx: number;\n\t\treadonly dy: number;\n\t}) => {\n\t\tif (dockDragSource !== 'player') return;\n\t\tif (dragPointerId === null || event.pointerId !== dragPointerId) return;\n\t\tapplyDockDragDelta(event.dx, event.dy);\n\t};\n\n\tconst endDockDragFromPlayer = (event: { readonly pointerId: number }) => {\n\t\tif (dockDragSource !== 'player') return;\n\t\tif (dragPointerId === null || event.pointerId !== dragPointerId) return;\n\t\tendDockDrag();\n\t};\n\n\tconst syncDockToggleChrome = () => {\n\t\tconst desktop = isDesktopViewport();\n\t\tconst state = shellState();\n\t\tif (!desktop || (state !== 'expanded' && state !== 'docked')) {\n\t\t\tdockToggleBtn.hidden = true;\n\t\t\treturn;\n\t\t}\n\t\tdockToggleBtn.hidden = false;\n\t\tif (state === 'docked') {\n\t\t\tdockToggleBtn.innerHTML = ICON_MAXIMIZE;\n\t\t\tdockToggleBtn.setAttribute('aria-label', 'Maximize');\n\t\t} else {\n\t\t\tdockToggleBtn.innerHTML = ICON_REDUCE;\n\t\t\tdockToggleBtn.setAttribute('aria-label', 'Reduce');\n\t\t}\n\t};\n\n\tconst layoutDockedShell = (preserveOrigin: boolean) => {\n\t\tconst size = computeDockSize();\n\t\tlet origin =\n\t\t\tpreserveOrigin && dockLeft !== null && dockTop !== null\n\t\t\t\t? clampDockOrigin(dockLeft, dockTop, size)\n\t\t\t\t: defaultDockOrigin(size);\n\t\torigin = clampDockOrigin(origin.left, origin.top, size);\n\t\tdockLeft = origin.left;\n\t\tdockTop = origin.top;\n\t\tapplyDockedGeometry(origin.left, origin.top, size.width, size.height);\n\t\tsyncDockToggleChrome();\n\t};\n\n\t/** Fade the live session in after layout/resize has settled (matches cold avatar reveal). */\n\tconst revealLiveSession = () => {\n\t\troot.classList.remove('liforma-ew--session-revealed');\n\t\trequestAnimationFrame(() => {\n\t\t\trequestAnimationFrame(() => {\n\t\t\t\tif (destroyed || !isOpenShell(root.dataset.state)) return;\n\t\t\t\troot.classList.add('liforma-ew--session-revealed');\n\t\t\t});\n\t\t});\n\t};\n\n\t/**\n\t * Hide the live surface instantly, apply a layout change, then reveal after the\n\t * player has had time to rebuild the morph canvas for the new aspect.\n\t */\n\tconst settleLayoutThenReveal = (applyLayout: () => void) => {\n\t\tclearLayoutSettleTimer();\n\t\troot.classList.remove('liforma-ew--session-revealed');\n\t\troot.classList.add('liforma-ew--layout-settling');\n\t\tapplyLayout();\n\t\tif (!sessionStarted) {\n\t\t\troot.classList.remove('liforma-ew--layout-settling');\n\t\t\treturn;\n\t\t}\n\t\tlayoutSettleTimer = setTimeout(() => {\n\t\t\tlayoutSettleTimer = null;\n\t\t\tif (destroyed || !isOpenShell(root.dataset.state)) {\n\t\t\t\troot.classList.remove('liforma-ew--layout-settling');\n\t\t\t\treturn;\n\t\t\t}\n\t\t\troot.classList.remove('liforma-ew--layout-settling');\n\t\t\trevealLiveSession();\n\t\t}, LAYOUT_SETTLE_MS);\n\t};\n\n\tconst concealLiveSession = (): Promise<void> => {\n\t\troot.classList.add('liforma-ew--session-concealing');\n\t\troot.classList.remove('liforma-ew--session-revealed');\n\t\treturn new Promise((resolve) => {\n\t\t\tclearCollapseRevealTimer();\n\t\t\tcollapseRevealTimer = setTimeout(() => {\n\t\t\t\tcollapseRevealTimer = null;\n\t\t\t\troot.classList.remove('liforma-ew--session-concealing');\n\t\t\t\tresolve();\n\t\t\t}, SESSION_CONCEAL_MS);\n\t\t});\n\t};\n\n\tconst initialLayers: FaceLayers = {\n\t\tgalleryThumb: options.galleryThumb,\n\t\tavatarImage: options.avatarImage,\n\t\tbackgroundImage: options.backgroundImage,\n\t\tforegroundImage: options.foregroundImage,\n\t\tfallbackImage: options.fallbackImage\n\t};\n\n\tconst setStatus = (text: string | null) => {\n\t\tif (!text) {\n\t\t\tstatus.hidden = true;\n\t\t\tstatus.textContent = '';\n\t\t\treturn;\n\t\t}\n\t\tstatus.hidden = false;\n\t\tstatus.textContent = text;\n\t};\n\n\tconst setFaceStatus = (text: string | null) => {\n\t\tif (!text) {\n\t\t\tfaceLoading.hidden = true;\n\t\t\tfaceLoading.textContent = '';\n\t\t\treturn;\n\t\t}\n\t\tfaceLoading.hidden = false;\n\t\tfaceLoading.textContent = text;\n\t\tif (!face.contains(faceLoading)) {\n\t\t\tface.append(faceLoading);\n\t\t}\n\t};\n\n\tconst markPreviewReady = () => {\n\t\tpreviewReady = true;\n\t\thit.disabled = false;\n\t\tsetFaceStatus(null);\n\t\tif (prefetchMode === 'onExpand') {\n\t\t\toptions.onPrefetchReady?.();\n\t\t}\n\t};\n\n\tconst armWarmHit = () => {\n\t\twarmReady = true;\n\t\troot.dataset.warm = 'true';\n\t\twarmStage.hidden = false;\n\t\tif (!sessionStarted) {\n\t\t\thit.tabIndex = -1;\n\t\t\thit.setAttribute('aria-hidden', 'true');\n\t\t}\n\t\toptions.onPrefetchReady?.();\n\t};\n\n\tconst paintFace = (layers: FaceLayers): boolean => {\n\t\tdetachFaceParallax?.();\n\t\tdetachFaceParallax = null;\n\t\tconst detach = renderFace(face, layers, { parallax: parallaxEnabled });\n\t\tif (!detach) return false;\n\t\tdetachFaceParallax = detach;\n\t\treturn true;\n\t};\n\n\tconst paintOrFetchPreview = () => {\n\t\tif (paintFace(initialLayers)) {\n\t\t\tmarkPreviewReady();\n\t\t\treturn;\n\t\t}\n\n\t\tsetFaceStatus('Loading…');\n\t\tvoid fetchExperiencePreview(experienceId, { signal: previewAbort.signal })\n\t\t\t.then((preview) => {\n\t\t\t\tif (destroyed) return;\n\t\t\t\tif (!paintFace({ galleryThumb: preview.galleryThumb })) {\n\t\t\t\t\tthrow new Error('Experience preview is missing gallery thumb images.');\n\t\t\t\t}\n\t\t\t\tmarkPreviewReady();\n\t\t\t})\n\t\t\t.catch((error) => {\n\t\t\t\tif (destroyed || previewAbort.signal.aborted) return;\n\t\t\t\tconst err = error instanceof Error ? error : new Error(String(error));\n\t\t\t\tsetFaceStatus('Preview unavailable');\n\t\t\t\thit.disabled = true;\n\t\t\t\toptions.onError?.(err);\n\t\t\t\tconsole.error('[ExperienceWidget]', err);\n\t\t\t});\n\t};\n\n\tconst sessionLaunchOptions = () => ({\n\t\texperienceId,\n\t\tui: 'widget' as const,\n\t\t...(options.sessionEndpoint ? { sessionEndpoint: options.sessionEndpoint } : {}),\n\t\t...(options.locale ? { locale: options.locale } : {}),\n\t\t...(options.language ? { language: options.language } : {}),\n\t\t...(options.mode ? { mode: options.mode } : {}),\n\t\t...(options.speechInputMode ? { speechInputMode: options.speechInputMode } : {}),\n\t\t...(options.speechOnly !== undefined ? { speechOnly: options.speechOnly } : {}),\n\t\t...(options.embedBaseUrl ? { embedBaseUrl: options.embedBaseUrl } : {})\n\t});\n\n\tconst sessionAttachPresentation = () => ({\n\t\t...(options.fit ? { fit: options.fit } : {}),\n\t\tstartButton: resolveStartButton(options),\n\t\t...(options.showFeedback !== undefined ? { showFeedback: options.showFeedback } : {})\n\t});\n\n\tconst showExpandedShell = (announce = true) => {\n\t\thit.hidden = true;\n\t\toverlay.hidden = false;\n\t\ttoolbar.hidden = false;\n\t\toverlay.setAttribute('aria-modal', 'true');\n\t\tclearExpandedLayoutInline();\n\t\troot.dataset.state = 'expanded';\n\t\tsetStatus(null);\n\t\tif (typeof document !== 'undefined') {\n\t\t\tpreviousOverflow = document.body.style.overflow;\n\t\t\tdocument.body.style.overflow = 'hidden';\n\t\t}\n\t\tsyncDockToggleChrome();\n\t\tif (announce) {\n\t\t\toptions.onExpanded?.();\n\t\t}\n\t};\n\n\tconst showDockedShell = () => {\n\t\thit.hidden = true;\n\t\toverlay.hidden = false;\n\t\ttoolbar.hidden = false;\n\t\toverlay.setAttribute('aria-modal', 'false');\n\t\troot.dataset.state = 'docked';\n\t\tsetStatus(null);\n\t\tif (typeof document !== 'undefined') {\n\t\t\tdocument.body.style.overflow = previousOverflow;\n\t\t}\n\t\tlayoutDockedShell(true);\n\t\tsyncDockToggleChrome();\n\t\texperience?.setWidgetChrome('docked');\n\t};\n\n\tconst bindSessionLifecycle = (session: Experience) => {\n\t\tsession.on('started', () => {\n\t\t\tif (destroyed) return;\n\t\t\tsessionStarted = true;\n\t\t\troot.dataset.live = 'true';\n\t\t\tsetStatus(null);\n\t\t\toptions.onStarted?.();\n\t\t\tif (isOpenShell(root.dataset.state)) {\n\t\t\t\trevealLiveSession();\n\t\t\t}\n\t\t});\n\t\tsession.on('feedback', (event) => {\n\t\t\tif (destroyed) return;\n\t\t\toptions.onFeedback?.(event);\n\t\t});\n\t\tsession.on('feedbackFailed', (event) => {\n\t\t\tif (destroyed) return;\n\t\t\toptions.onFeedbackFailed?.(event);\n\t\t});\n\t};\n\n\tconst restoreCollapsedChrome = () => {\n\t\tclearLayoutSettleTimer();\n\t\toverlay.hidden = true;\n\t\ttoolbar.hidden = true;\n\t\tclearExpandedLayoutInline();\n\t\troot.dataset.state = 'collapsed';\n\t\troot.classList.remove('liforma-ew--session-revealed');\n\t\troot.classList.remove('liforma-ew--session-concealing');\n\t\tsettingsBtn.setAttribute('aria-pressed', 'false');\n\t\thit.hidden = false;\n\t\thit.tabIndex = 0;\n\t\thit.removeAttribute('aria-hidden');\n\t\tif (previewReady) hit.disabled = false;\n\t\tsetStatus(null);\n\t\tif (typeof document !== 'undefined') {\n\t\t\tdocument.body.style.overflow = previousOverflow;\n\t\t}\n\t\tsyncDockToggleChrome();\n\t};\n\n\tconst hardTeardownSession = () => {\n\t\tclearCollapseRevealTimer();\n\t\tclearLayoutSettleTimer();\n\t\texpandEpoch += 1;\n\t\texpanding = false;\n\t\twarmReady = false;\n\t\twarmInFlight = false;\n\t\tshellMounted = false;\n\t\tsessionStarted = false;\n\t\tconst session = experience;\n\t\texperience = null;\n\t\tsession?.destroy();\n\t\tstage.replaceChildren();\n\t\twarmStage.replaceChildren();\n\t\twarmStage.hidden = true;\n\t\tdelete root.dataset.warm;\n\t\tdelete root.dataset.live;\n\t\troot.classList.remove('liforma-ew--session-revealed');\n\t};\n\n\tconst collapse = () => {\n\t\tif (destroyed) return;\n\t\tif (!isOpenShell(root.dataset.state)) return;\n\t\tendDockDrag();\n\t\texpanding = false;\n\n\t\t/*\n\t\t * Keep any attached session (warm or cold, started or waiting on Tap to talk).\n\t\t * Only CSS-collapse / pause — never remint on the next open.\n\t\t */\n\t\tif (experience && shellMounted) {\n\t\t\tconst session = experience;\n\t\t\tvoid (async () => {\n\t\t\t\tif (sessionStarted) {\n\t\t\t\t\tawait concealLiveSession();\n\t\t\t\t\tif (destroyed || experience !== session) return;\n\t\t\t\t}\n\t\t\t\tif (warmReady) {\n\t\t\t\t\tsession.setWidgetChrome('collapsed');\n\t\t\t\t}\n\t\t\t\trestoreCollapsedChrome();\n\t\t\t\tif (sessionStarted) {\n\t\t\t\t\troot.dataset.live = 'true';\n\t\t\t\t\t/* Face receives the next click; warm iframe stays hidden under the thumb. */\n\t\t\t\t\thit.tabIndex = 0;\n\t\t\t\t\thit.removeAttribute('aria-hidden');\n\t\t\t\t\thit.disabled = false;\n\t\t\t\t} else if (warmReady) {\n\t\t\t\t\tarmWarmHit();\n\t\t\t\t}\n\t\t\t\toptions.onCollapsed?.();\n\t\t\t\toptions.onClose?.();\n\t\t\t})();\n\t\t\treturn;\n\t\t}\n\n\t\thardTeardownSession();\n\t\trestoreCollapsedChrome();\n\t\toptions.onCollapsed?.();\n\t\toptions.onClose?.();\n\t};\n\n\tconst dock = () => {\n\t\tif (destroyed) return;\n\t\tif (!isDesktopViewport()) return;\n\t\tif (shellState() !== 'expanded') return;\n\t\tsettleLayoutThenReveal(() => {\n\t\t\tshowDockedShell();\n\t\t});\n\t};\n\n\tconst maximize = () => {\n\t\tif (destroyed) return;\n\t\tif (shellState() !== 'docked') return;\n\t\tendDockDrag();\n\t\tsettleLayoutThenReveal(() => {\n\t\t\tclearExpandedLayoutInline();\n\t\t\tshowExpandedShell(false);\n\t\t\texperience?.setWidgetChrome('expanded');\n\t\t});\n\t};\n\n\tconst resumeLiveSession = () => {\n\t\tif (!experience || !sessionStarted) return;\n\t\tclearCollapseRevealTimer();\n\t\troot.classList.remove('liforma-ew--session-revealed');\n\t\tshowExpandedShell();\n\t\texperience.setWidgetChrome('expanded');\n\t\texpanding = false;\n\t\trevealLiveSession();\n\t};\n\n\t/**\n\t * Re-open an attached session that has not unlocked yet.\n\t * Warm path should click the iframe hit (one-gesture start). This is a fallback when\n\t * the parent FAB receives the click instead — open the shell only; do not force\n\t * Tap-to-start chrome (that fights the warm “start talking immediately” contract).\n\t */\n\tconst resumeMountedSession = () => {\n\t\tif (!experience || !shellMounted || sessionStarted) return;\n\t\tclearCollapseRevealTimer();\n\t\troot.classList.remove('liforma-ew--session-revealed');\n\t\tshowExpandedShell();\n\t\texpanding = false;\n\t\tif (!warmReady) {\n\t\t\t/* Cold / still warming: need an in-player Start once ready. */\n\t\t\texperience.setWidgetChrome('expanded');\n\t\t}\n\t};\n\n\t/** Warm iframe hit unlocked audio and is starting — parent only CSS-expands. */\n\tconst onWarmExpand = (epoch: number) => {\n\t\tif (destroyed) return;\n\t\tif (epoch !== expandEpoch && !sessionStarted) return;\n\t\tclearCollapseRevealTimer();\n\t\troot.classList.remove('liforma-ew--session-revealed');\n\t\tshowExpandedShell();\n\t\texpanding = false;\n\t\t/*\n\t\t * Do not post setWidgetChrome('expanded'): the player already flipped chrome and\n\t\t * began startSession in the click turn. Echoing expanded causes a face fade +\n\t\t * Tap-to-start flash before speech.\n\t\t */\n\t\tif (sessionStarted) {\n\t\t\trevealLiveSession();\n\t\t}\n\t};\n\n\tconst beginWarm = () => {\n\t\tif (\n\t\t\tdestroyed ||\n\t\t\twarmInFlight ||\n\t\t\twarmReady ||\n\t\t\texperience ||\n\t\t\tisOpenShell(root.dataset.state)\n\t\t) {\n\t\t\treturn;\n\t\t}\n\t\twarmInFlight = true;\n\t\tconst epoch = expandEpoch;\n\t\twarmStage.hidden = false;\n\t\tvoid (async () => {\n\t\t\ttry {\n\t\t\t\tconst session = await Experience.startSession(sessionLaunchOptions());\n\t\t\t\tif (destroyed || epoch !== expandEpoch) {\n\t\t\t\t\tsession.destroy();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\texperience = session;\n\t\t\t\tbindSessionLifecycle(session);\n\t\t\t\tconst player = await session.attach({\n\t\t\t\t\tcontainer: warmStage,\n\t\t\t\t\t...sessionAttachPresentation(),\n\t\t\t\t\tonWidgetReady: () => {\n\t\t\t\t\t\tif (destroyed || epoch !== expandEpoch) return;\n\t\t\t\t\t\twarmInFlight = false;\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t * Mark mounted only when the iframe hit is armed. Setting this earlier\n\t\t\t\t\t\t * let the FAB steal clicks (no data-warm yet) and show Tap-to-start\n\t\t\t\t\t\t * instead of one-gesture warm start.\n\t\t\t\t\t\t */\n\t\t\t\t\t\tshellMounted = true;\n\t\t\t\t\t\tif (isOpenShell(root.dataset.state)) {\n\t\t\t\t\t\t\t/* Opened while warming — missed iframe gesture; need Tap to start. */\n\t\t\t\t\t\t\tsession.setWidgetChrome('expanded');\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tarmWarmHit();\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\tonWidgetExpand: () => {\n\t\t\t\t\t\tonWarmExpand(epoch);\n\t\t\t\t\t},\n\t\t\t\t\tonWidgetAudioSettings: (open) => {\n\t\t\t\t\t\tif (!destroyed) syncSettingsPressed(open);\n\t\t\t\t\t},\n\t\t\t\t\tonWidgetDragStart: (event) => {\n\t\t\t\t\t\tif (!destroyed) beginDockDragFromPlayer(event);\n\t\t\t\t\t},\n\t\t\t\t\tonWidgetDragMove: (event) => {\n\t\t\t\t\t\tif (!destroyed) moveDockDragFromPlayer(event);\n\t\t\t\t\t},\n\t\t\t\t\tonWidgetDragEnd: (event) => {\n\t\t\t\t\t\tif (!destroyed) endDockDragFromPlayer(event);\n\t\t\t\t\t},\n\t\t\t\t\tonPlayerStatusChange: (state) => {\n\t\t\t\t\t\tif (destroyed) return;\n\t\t\t\t\t\tif (state === 'error' && isOpenShell(root.dataset.state)) {\n\t\t\t\t\t\t\tsetStatus('Could not load the experience.');\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tif (destroyed || epoch !== expandEpoch) {\n\t\t\t\t\tsession.destroy();\n\t\t\t\t\tif (experience === session) experience = null;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tplayer.on('close', () => {\n\t\t\t\t\tif (destroyed) return;\n\t\t\t\t\tcollapse();\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tif (destroyed || epoch !== expandEpoch) return;\n\t\t\t\twarmInFlight = false;\n\t\t\t\twarmStage.hidden = true;\n\t\t\t\twarmStage.replaceChildren();\n\t\t\t\texperience = null;\n\t\t\t\tconst err = error instanceof Error ? error : new Error(String(error));\n\t\t\t\toptions.onError?.(err);\n\t\t\t\tconsole.error('[ExperienceWidget] warm prefetch failed', err);\n\t\t\t}\n\t\t})();\n\t};\n\n\tconst expandCold = () => {\n\t\tif (destroyed || expanding || !previewReady || isOpenShell(root.dataset.state)) return;\n\t\tif (experience && sessionStarted) {\n\t\t\tresumeLiveSession();\n\t\t\treturn;\n\t\t}\n\t\tif (experience && shellMounted) {\n\t\t\tresumeMountedSession();\n\t\t\treturn;\n\t\t}\n\t\tconst epoch = ++expandEpoch;\n\t\texpanding = true;\n\t\twarmReady = false;\n\t\twarmInFlight = false;\n\t\tshellMounted = false;\n\t\tsessionStarted = false;\n\t\tdelete root.dataset.warm;\n\t\tdelete root.dataset.live;\n\t\twarmStage.hidden = true;\n\t\twarmStage.replaceChildren();\n\t\tif (experience) {\n\t\t\texperience.destroy();\n\t\t\texperience = null;\n\t\t}\n\t\tvoid (async () => {\n\t\t\ttry {\n\t\t\t\tshowExpandedShell();\n\t\t\t\tconst session = await Experience.startSession(sessionLaunchOptions());\n\t\t\t\tif (destroyed || epoch !== expandEpoch) {\n\t\t\t\t\tsession.destroy();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\texperience = session;\n\t\t\t\tbindSessionLifecycle(session);\n\t\t\t\tconst player = await session.attach({\n\t\t\t\t\tcontainer: stage,\n\t\t\t\t\t...sessionAttachPresentation(),\n\t\t\t\t\tonWidgetAudioSettings: (open) => {\n\t\t\t\t\t\tif (!destroyed) syncSettingsPressed(open);\n\t\t\t\t\t},\n\t\t\t\t\tonWidgetDragStart: (event) => {\n\t\t\t\t\t\tif (!destroyed) beginDockDragFromPlayer(event);\n\t\t\t\t\t},\n\t\t\t\t\tonWidgetDragMove: (event) => {\n\t\t\t\t\t\tif (!destroyed) moveDockDragFromPlayer(event);\n\t\t\t\t\t},\n\t\t\t\t\tonWidgetDragEnd: (event) => {\n\t\t\t\t\t\tif (!destroyed) endDockDragFromPlayer(event);\n\t\t\t\t\t},\n\t\t\t\t\tonPlayerStatusChange: (state) => {\n\t\t\t\t\t\tif (destroyed) return;\n\t\t\t\t\t\tif (state === 'error') {\n\t\t\t\t\t\t\tsetStatus('Could not load the experience.');\n\t\t\t\t\t\t} else if (isOpenShell(root.dataset.state)) {\n\t\t\t\t\t\t\tsetStatus(null);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tif (destroyed || epoch !== expandEpoch) {\n\t\t\t\t\tsession.destroy();\n\t\t\t\t\tif (experience === session) experience = null;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tshellMounted = true;\n\t\t\t\t/* Cold expand: second gesture unlocks audio via in-player Start. */\n\t\t\t\tsession.setWidgetChrome('expanded');\n\t\t\t\tplayer.on('close', () => {\n\t\t\t\t\tif (destroyed) return;\n\t\t\t\t\tcollapse();\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tif (destroyed || epoch !== expandEpoch) return;\n\t\t\t\tconst err = error instanceof Error ? error : new Error(String(error));\n\t\t\t\tsetStatus(err.message);\n\t\t\t\toptions.onError?.(err);\n\t\t\t\tconsole.error('[ExperienceWidget]', err);\n\t\t\t} finally {\n\t\t\t\tif (epoch === expandEpoch) {\n\t\t\t\t\texpanding = false;\n\t\t\t\t}\n\t\t\t}\n\t\t})();\n\t};\n\n\tconst expand = () => {\n\t\tif (destroyed || isOpenShell(root.dataset.state)) return;\n\t\tif (experience && sessionStarted) {\n\t\t\tresumeLiveSession();\n\t\t\treturn;\n\t\t}\n\t\tif (experience && shellMounted) {\n\t\t\t/*\n\t\t\t * Warm path: clicks should hit the transparent iframe (data-warm). If the FAB\n\t\t\t * still receives the event, open the shell without reminting or forcing Start.\n\t\t\t */\n\t\t\tresumeMountedSession();\n\t\t\treturn;\n\t\t}\n\t\t/*\n\t\t * Warm prefetch still attaching: open the shell and wait for onWidgetReady.\n\t\t * Do not expandCold — that destroys the in-flight warm session.\n\t\t */\n\t\tif (warmInFlight || experience) {\n\t\t\tshowExpandedShell();\n\t\t\treturn;\n\t\t}\n\t\texpandCold();\n\t};\n\n\tconst onDockToggleClick = (event: MouseEvent) => {\n\t\tevent.stopPropagation();\n\t\tif (shellState() === 'expanded') {\n\t\t\tdock();\n\t\t\treturn;\n\t\t}\n\t\tif (shellState() === 'docked') {\n\t\t\tmaximize();\n\t\t}\n\t};\n\n\tconst syncSettingsPressed = (open: boolean) => {\n\t\tsettingsBtn.setAttribute('aria-pressed', open ? 'true' : 'false');\n\t};\n\n\tconst onSettingsClick = (event: MouseEvent) => {\n\t\tevent.stopPropagation();\n\t\tif (!isOpenShell(root.dataset.state)) return;\n\t\texperience?.toggleAudioSettings();\n\t};\n\n\tconst onToolbarPointerDown = (event: PointerEvent) => {\n\t\tif (shellState() !== 'docked') return;\n\t\tif (!isDesktopViewport()) return;\n\t\tconst target = event.target as HTMLElement | null;\n\t\tif (target?.closest('button')) return;\n\t\tevent.preventDefault();\n\t\tbeginToolbarDockDrag(event.clientX, event.clientY, event.pointerId);\n\t};\n\n\tconst onKeyDown = (event: KeyboardEvent) => {\n\t\tif (event.key === 'Escape' && isOpenShell(root.dataset.state)) {\n\t\t\tevent.preventDefault();\n\t\t\tcollapse();\n\t\t}\n\t};\n\n\tconst onWindowResize = () => {\n\t\tif (destroyed) return;\n\t\tif (shellState() === 'docked') {\n\t\t\tif (!isDesktopViewport()) {\n\t\t\t\tmaximize();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (sessionStarted) {\n\t\t\t\tsettleLayoutThenReveal(() => {\n\t\t\t\t\tlayoutDockedShell(true);\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tlayoutDockedShell(true);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tsyncDockToggleChrome();\n\t};\n\n\tconst desktopMq =\n\t\ttypeof window !== 'undefined' ? window.matchMedia(WIDGET_DESKTOP_MEDIA) : null;\n\tconst onDesktopMqChange = () => {\n\t\tif (destroyed) return;\n\t\tif (shellState() === 'docked' && !isDesktopViewport()) {\n\t\t\tmaximize();\n\t\t\treturn;\n\t\t}\n\t\tsyncDockToggleChrome();\n\t};\n\n\thit.addEventListener('click', expand);\n\tbackdrop.addEventListener('click', collapse);\n\tcloseBtn.addEventListener('click', (event) => {\n\t\tevent.stopPropagation();\n\t\tcollapse();\n\t});\n\tdockToggleBtn.addEventListener('click', onDockToggleClick);\n\tsettingsBtn.addEventListener('click', onSettingsClick);\n\ttoolbar.addEventListener('pointerdown', onToolbarPointerDown);\n\twindow.addEventListener('keydown', onKeyDown);\n\twindow.addEventListener('resize', onWindowResize);\n\tdesktopMq?.addEventListener('change', onDesktopMqChange);\n\n\tpaintOrFetchPreview();\n\tsyncDockToggleChrome();\n\n\tif (prefetchMode === 'eager') {\n\t\tqueueMicrotask(() => {\n\t\t\tif (!destroyed) beginWarm();\n\t\t});\n\t} else if (prefetchMode === 'idle') {\n\t\tcancelWarmSchedule = scheduleIdle(() => {\n\t\t\tif (!destroyed) beginWarm();\n\t\t});\n\t}\n\n\treturn {\n\t\troot,\n\t\texpand,\n\t\tcollapse,\n\t\tdock,\n\t\tmaximize,\n\t\tprefetch() {\n\t\t\tif (prefetchMode === 'onExpand') {\n\t\t\t\tbeginWarm();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tbeginWarm();\n\t\t},\n\t\tdestroy() {\n\t\t\tif (destroyed) return;\n\t\t\tdestroyed = true;\n\t\t\tendDockDrag();\n\t\t\tcancelWarmSchedule?.();\n\t\t\tcancelWarmSchedule = null;\n\t\t\tpreviewAbort.abort();\n\t\t\tdetachFaceParallax?.();\n\t\t\tdetachFaceParallax = null;\n\t\t\twindow.removeEventListener('keydown', onKeyDown);\n\t\t\twindow.removeEventListener('resize', onWindowResize);\n\t\t\tdesktopMq?.removeEventListener('change', onDesktopMqChange);\n\t\t\thardTeardownSession();\n\t\t\trestoreCollapsedChrome();\n\t\t\thost.replaceChildren();\n\t\t}\n\t};\n}\n","import { mountExperienceWidget } from '../widget/mountExperienceWidget';\nimport type {\n\tExperienceWidgetController,\n\tExperienceWidgetPosition\n} from '../widget/types';\nimport type { GalleryThumb } from '../thumbnail/types';\nimport {\n\tparseExperienceFit,\n\ttype ExperienceFit,\n\ttype ExperienceMode,\n\ttype SpeechInputMode\n} from './types/sessionInteraction';\n\nconst ELEMENT_NAME = 'liforma-experience-widget';\n\nfunction parseGalleryThumb(raw: string | null): GalleryThumb | undefined {\n\tif (!raw?.trim()) return undefined;\n\ttry {\n\t\tconst parsed = JSON.parse(raw) as unknown;\n\t\tif (parsed && typeof parsed === 'object' && typeof (parsed as GalleryThumb).avatarImage === 'string') {\n\t\t\treturn parsed as GalleryThumb;\n\t\t}\n\t} catch {\n\t\t/* ignore */\n\t}\n\treturn undefined;\n}\n\nfunction parseMode(value: string | null): ExperienceMode | undefined {\n\tconst trimmed = value?.trim();\n\treturn trimmed === 'conversation' || trimmed === 'presenter' ? trimmed : undefined;\n}\n\nfunction parseSpeechInputMode(value: string | null): SpeechInputMode | undefined {\n\tconst trimmed = value?.trim();\n\treturn trimmed === 'auto' || trimmed === 'manual' || trimmed === 'off' ? trimmed : undefined;\n}\n\nfunction parseFit(value: string | null): ExperienceFit | undefined {\n\treturn parseExperienceFit(value);\n}\n\nfunction parseLanguage(value: string | null): 'en' | 'es' | undefined {\n\tconst trimmed = value?.trim();\n\treturn trimmed === 'en' || trimmed === 'es' ? trimmed : undefined;\n}\n\nfunction parsePosition(value: string | null): ExperienceWidgetPosition {\n\tconst trimmed = value?.trim();\n\treturn trimmed === 'bottom-right' || trimmed === 'bottom-left' ? trimmed : 'static';\n}\n\nexport class LiformaExperienceWidgetElement extends HTMLElement {\n\tstatic get observedAttributes() {\n\t\treturn [\n\t\t\t'experience-id',\n\t\t\t'alt',\n\t\t\t'gallery-thumb',\n\t\t\t'avatar-image',\n\t\t\t'background-image',\n\t\t\t'foreground-image',\n\t\t\t'fallback-image',\n\t\t\t'prefetch',\n\t\t\t'parallax',\n\t\t\t'position',\n\t\t\t'offset',\n\t\t\t'session-endpoint',\n\t\t\t'locale',\n\t\t\t'secondary-locale',\n\t\t\t'learning-locale',\n\t\t\t'language',\n\t\t\t'mode',\n\t\t\t'speech-input-mode',\n\t\t\t'speech-only',\n\t\t\t'fit',\n\t\t\t'expanded-max-width',\n\t\t\t'expanded-max-height',\n\t\t\t'start-button-label'\n\t\t];\n\t}\n\n\t#controller: ExperienceWidgetController | null = null;\n\t#host: HTMLDivElement | null = null;\n\n\tconnectedCallback() {\n\t\tthis.#applyHostBox();\n\t\tthis.#mount();\n\t}\n\n\tdisconnectedCallback() {\n\t\tthis.#controller?.destroy();\n\t\tthis.#controller = null;\n\t}\n\n\tattributeChangedCallback() {\n\t\tif (!this.isConnected) return;\n\t\tthis.#applyHostBox();\n\t\tthis.#mount();\n\t}\n\n\t#applyHostBox() {\n\t\tconst position = parsePosition(this.getAttribute('position'));\n\t\tif (position !== 'static') {\n\t\t\t// Fixed FAB lives on `.liforma-ew`; collapse CE + host so they leave no layout hole.\n\t\t\tthis.style.display = 'contents';\n\t\t\tthis.style.removeProperty('width');\n\t\t\tthis.style.removeProperty('height');\n\t\t\tthis.style.removeProperty('min-height');\n\t\t\treturn;\n\t\t}\n\n\t\tthis.style.display = 'block';\n\t\tthis.style.width ||= '100%';\n\t\tthis.style.height ||= '100%';\n\t\tif (!this.style.minHeight) {\n\t\t\tconst min = getComputedStyle(this).minHeight;\n\t\t\tif (min === '0px' || min === 'auto') {\n\t\t\t\tthis.style.minHeight = '224px';\n\t\t\t}\n\t\t}\n\t}\n\n\t#mount() {\n\t\tthis.#controller?.destroy();\n\t\tthis.#controller = null;\n\n\t\tconst experienceId = this.getAttribute('experience-id')?.trim();\n\t\tconst alt = this.getAttribute('alt')?.trim() || 'Open experience';\n\t\tif (!experienceId) return;\n\n\t\tconst position = parsePosition(this.getAttribute('position'));\n\n\t\tif (!this.#host) {\n\t\t\tthis.#host = document.createElement('div');\n\t\t\tthis.replaceChildren(this.#host);\n\t\t}\n\t\tthis.#host.style.cssText =\n\t\t\tposition === 'static'\n\t\t\t\t? 'width:100%;height:100%;min-height:inherit;'\n\t\t\t\t: 'display:contents;';\n\n\t\tconst prefetchAttr = this.getAttribute('prefetch')?.trim();\n\t\tconst prefetch =\n\t\t\tprefetchAttr === 'idle' || prefetchAttr === 'eager' || prefetchAttr === 'onExpand'\n\t\t\t\t? prefetchAttr\n\t\t\t\t: 'onExpand';\n\n\t\tconst speechOnlyAttr = this.getAttribute('speech-only');\n\t\tconst offsetAttr = this.getAttribute('offset');\n\n\t\ttry {\n\t\t\tthis.#controller = mountExperienceWidget(this.#host, {\n\t\t\t\texperienceId,\n\t\t\t\talt,\n\t\t\t\tgalleryThumb: parseGalleryThumb(this.getAttribute('gallery-thumb')),\n\t\t\t\tavatarImage: this.getAttribute('avatar-image') ?? undefined,\n\t\t\t\tbackgroundImage: this.getAttribute('background-image') ?? undefined,\n\t\t\t\tforegroundImage: this.getAttribute('foreground-image') ?? undefined,\n\t\t\t\tfallbackImage: this.getAttribute('fallback-image') ?? undefined,\n\t\t\t\tprefetch,\n\t\t\t\tparallax: this.getAttribute('parallax') !== 'false',\n\t\t\t\tposition,\n\t\t\t\toffset: offsetAttr === null ? undefined : offsetAttr,\n\t\t\t\tsessionEndpoint: this.getAttribute('session-endpoint') ?? undefined,\n\t\t\t\tlocale: this.getAttribute('locale')?.trim() || undefined,\n\t\t\t\tsecondaryLocale: this.getAttribute('secondary-locale')?.trim() || undefined,\n\t\t\t\tlearningLocale: this.getAttribute('learning-locale')?.trim() || undefined,\n\t\t\t\tlanguage: parseLanguage(this.getAttribute('language')),\n\t\t\t\tmode: parseMode(this.getAttribute('mode')),\n\t\t\t\tspeechInputMode: parseSpeechInputMode(this.getAttribute('speech-input-mode')),\n\t\t\t\tspeechOnly:\n\t\t\t\t\tspeechOnlyAttr === null\n\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t: speechOnlyAttr === '' || speechOnlyAttr === 'true',\n\t\t\t\tfit: parseFit(this.getAttribute('fit')),\n\t\t\t\texpandedMaxWidth: this.getAttribute('expanded-max-width') ?? undefined,\n\t\t\t\texpandedMaxHeight: this.getAttribute('expanded-max-height') ?? undefined,\n\t\t\t\tstartButtonLabel: this.getAttribute('start-button-label') ?? undefined,\n\t\t\t\tonExpanded: () => {\n\t\t\t\t\tthis.dispatchEvent(new CustomEvent('expanded', { bubbles: true }));\n\t\t\t\t},\n\t\t\t\tonCollapsed: () => {\n\t\t\t\t\tthis.dispatchEvent(new CustomEvent('collapsed', { bubbles: true }));\n\t\t\t\t},\n\t\t\t\tonStarted: () => {\n\t\t\t\t\tthis.dispatchEvent(new CustomEvent('started', { bubbles: true }));\n\t\t\t\t},\n\t\t\t\tonClose: () => {\n\t\t\t\t\tthis.dispatchEvent(new CustomEvent('close', { bubbles: true }));\n\t\t\t\t},\n\t\t\t\tonError: (error) => {\n\t\t\t\t\tthis.dispatchEvent(\n\t\t\t\t\t\tnew CustomEvent('error', { detail: error, bubbles: true })\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tconsole.error('[liforma-experience-widget]', error);\n\t\t}\n\t}\n}\n\nif (typeof customElements !== 'undefined' && !customElements.get(ELEMENT_NAME)) {\n\tcustomElements.define(ELEMENT_NAME, LiformaExperienceWidgetElement);\n}\n","import './liforma-experience';\nimport './liforma-experience-thumbnail';\nimport './liforma-experience-widget';\n\nexport { Experience, type ExperienceSession } from './experience';\nexport { LiformaError, liformaErrorFromResponse } from './errors/LiformaError';\nexport type {\n\tAttachOptions,\n\tCloseEvent,\n\tEmbedState,\n\tLiformaPlayer,\n\tReadyEvent,\n\tStartSessionOptions\n} from './experience';\nexport type { SessionManifest, SessionManifestCharacter } from './types/sessionManifest';\nexport type {\n\tCloseButtonOptions,\n\tLocaleVoiceFallback,\n\tPlayerStatus,\n\tSession,\n\tSessionActivity,\n\tSessionCapabilities,\n\tSessionLaunchResponse,\n\tSessionMode\n} from './types/sessionLaunch';\nexport { warnLocaleVoiceFallbacks } from './types/sessionLaunch';\nexport type { ExperienceEventName, ExperienceEvents, ExperienceEventEnvelope, FeedbackResult, FeedbackFailedEvent } from './experienceEvents';\nexport type {\n\tCharacterSpeechEvent,\n\tCharacterSpeechSource,\n\tCharacterSpeechTerminalReason,\n\tConversationMessage,\n\tSpeakOptions,\n\tTextSpeakResult,\n\tSpeechResult as LegacySpeakResult,\n\tUserTranscriptUpdate,\n\tUtteranceResult,\n\tExperienceFit,\n\tExperienceMode,\n\tResponseMode,\n\tSpeechInputMode,\n\tStartButtonAppearance,\n\tStartButtonOptions,\n\tStartButtonPlacement,\n\tStartButtonShadow,\n\tStartButtonSize,\n\tStartButtonVariant,\n\tStartedEvent,\n} from './types/sessionInteraction';\nexport type {\n\tSpeechAudioFormat,\n\tSpeechController,\n\tSpeechCreateUtteranceOptions,\n\tSpeechInterruptOptions,\n\tSpeechPlayAudio,\n\tSpeechPlayEncodedAudio,\n\tSpeechPlayOptions,\n\tSpeechPlayPcmAudio,\n\tSpeechPlayUrlAudio,\n\tSpeechResult,\n\tSpeechSpeakOptions,\n\tSpeechUtterance\n} from './speech';\nexport {\n\tSPEECH_LIMITS,\n\tSpeechBufferTimeoutError,\n\tSpeechRuntimeError,\n\tSpeechUtteranceClosedError,\n\tUnsupportedSpeechOperationError\n} from './speech';\n\nimport { Experience } from './experience';\n\ndeclare global {\n\tinterface Window {\n\t\tLiforma?: {\n\t\t\tExperience: typeof Experience;\n\t\t\tsdkVersion: 'v2';\n\t\t\treadonly features: {\n\t\t\t\treadonly speakApi: boolean;\n\t\t\t\treadonly speechAudioApi: boolean;\n\t\t\t};\n\t\t};\n\t}\n}\n\nwindow.Liforma = {\n\tExperience,\n\tsdkVersion: 'v2',\n\tfeatures: {\n\t\tspeakApi: true,\n\t\tspeechAudioApi: true\n\t}\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"client.js","names":["#render","#teardown","#cleanup","#root","#boolAttr","#shouldLoad","#hovering","#scrollParallax","#avatarReady","#backgroundReady","#foregroundReady","#parallaxX","#parallaxY","#avatarLoadFailed","#pointerDown","#applyHostBox","#mount","#controller","#host"],"sources":["../../../src/shims/localStack.ts","../../../src/shims/apiBaseUrl.ts","../../../src/v2/errors/LiformaError.ts","../../../src/v2/api/captureAttribution.ts","../../../src/v2/api/sessionEndpointRequest.ts","../../../src/v2/types/sessionLaunch.ts","../../../src/v2/api/mintPublicSession.ts","../../../src/v2/conversationProcessor.ts","../../../src/v2/embed/playerReachability.ts","../../../src/v2/embed/protocol.ts","../../../src/v2/embed/fullWindowHost.ts","../../../src/v2/embed/iframeHost.ts","../../../src/v2/embed/readyGate.ts","../../../src/v2/shims/embedBaseUrl.ts","../../../src/v2/experienceEvents.ts","../../../src/v2/speech/types.ts","../../../src/byo/pcm.ts","../../../src/byo/resample.ts","../../../src/byo/audioCapture.ts","../../../src/v2/speech/captureMediaStreamTrack.ts","../../../src/v2/speech/speechController.ts","../../../src/v2/types/sessionInteraction.ts","../../../src/v2/experience.ts","../../../src/v2/embed/returnNavigation.ts","../../../src/svelte/experienceLoadError.ts","../../../src/svelte/experienceLoadErrorStyles.ts","../../../src/svelte/experienceLoadErrorOverlay.ts","../../../src/v2/liforma-experience.ts","../../../src/thumbnail/types.ts","../../../src/thumbnail/toGalleryThumbUrl.ts","../../../src/thumbnail/hostedLaunchUrl.ts","../../../src/thumbnail/thumbLoadScheduler.ts","../../../src/thumbnail/lazyLoad.ts","../../../src/thumbnail/scrollParallax.ts","../../../src/thumbnail/attachThumbnailParallax.ts","../../../src/thumbnail/styles.ts","../../../src/thumbnail/ensureStyles.ts","../../../src/v2/liforma-experience-thumbnail.ts","../../../src/v2/api/fetchExperiencePreview.ts","../../../src/widget/dockGeometry.ts","../../../src/widget/styles.ts","../../../src/widget/mountExperienceWidget.ts","../../../src/v2/liforma-experience-widget.ts","../../../src/v2/index.ts"],"sourcesContent":["/**\n * Opt-in local monorepo stack (api :3001, player :3002).\n * Never inferred from hostname alone — third-party demos run on localhost against production.\n */\nexport function useExplicitLocalStack(): boolean {\n\tif ((globalThis as { __LIFORMA_STACK?: string }).__LIFORMA_STACK === 'local') {\n\t\treturn true;\n\t}\n\n\tif (typeof location === 'undefined') {\n\t\treturn false;\n\t}\n\n\ttry {\n\t\treturn new URLSearchParams(location.search).get('stack') === 'local';\n\t} catch {\n\t\treturn false;\n\t}\n}\n","import { useExplicitLocalStack } from './localStack';\n\nconst PRODUCTION_API_BASE_URL = 'https://api.liforma.ai';\nconst LOCAL_API_BASE_URL = 'http://localhost:3001';\n\nfunction readConfiguredApiBaseUrl(): string | null {\n\tconst globalValue = (globalThis as { __LIFORMA_API_BASE_URL?: string }).__LIFORMA_API_BASE_URL;\n\tif (globalValue?.trim()) {\n\t\treturn globalValue.trim().replace(/\\/$/, '');\n\t}\n\n\tif (typeof document === 'undefined') {\n\t\treturn null;\n\t}\n\n\tconst script =\n\t\tdocument.querySelector('script[data-liforma-sdk][src*=\"client.js\"]') ??\n\t\tdocument.querySelector('script[src*=\"/sdk/v2/client.js\"]');\n\tconst fromScript = script?.getAttribute('data-api-base-url')?.trim();\n\tif (fromScript) {\n\t\treturn fromScript.replace(/\\/$/, '');\n\t}\n\n\treturn null;\n}\n\n/**\n * Browser-visible API origin for Liforma HTTP calls from the CDN SDK.\n *\n * Defaults to production. Local stack is opt-in only — never inferred from\n * `localhost` hostname (third-party demos run on localhost against production).\n *\n * Override order:\n * 1. `window.__LIFORMA_API_BASE_URL`\n * 2. `data-api-base-url` on the SDK script tag\n * 3. `window.__LIFORMA_STACK === 'local'` or host `?stack=local` → http://localhost:3001\n * 4. production default → https://api.liforma.ai\n */\nexport function resolvePublicApiBaseUrl(): string {\n\tconst configured = readConfiguredApiBaseUrl();\n\tif (configured) {\n\t\treturn configured;\n\t}\n\n\tif (useExplicitLocalStack()) {\n\t\treturn LOCAL_API_BASE_URL;\n\t}\n\n\treturn PRODUCTION_API_BASE_URL;\n}\n","/**\n * Structured API / mint error from Liforma.\n * Prefer `.code` and `.requestId` over parsing `.message`.\n */\nexport class LiformaError extends Error {\n\treadonly code: string;\n\treadonly requestId: string | undefined;\n\treadonly status: number | undefined;\n\treadonly details: Record<string, unknown> | undefined;\n\n\tconstructor(\n\t\tmessage: string,\n\t\toptions?: {\n\t\t\treadonly code?: string;\n\t\t\treadonly requestId?: string;\n\t\t\treadonly status?: number;\n\t\t\treadonly details?: Record<string, unknown>;\n\t\t\treadonly cause?: unknown;\n\t\t}\n\t) {\n\t\tsuper(message, options?.cause !== undefined ? { cause: options.cause } : undefined);\n\t\tthis.name = 'LiformaError';\n\t\tthis.code = options?.code?.trim() || 'UNKNOWN';\n\t\tthis.requestId = options?.requestId?.trim() || undefined;\n\t\tthis.status = options?.status;\n\t\tthis.details = options?.details;\n\t}\n}\n\ntype NestedErrorShape = {\n\treadonly error?: {\n\t\treadonly code?: unknown;\n\t\treadonly message?: unknown;\n\t\treadonly requestId?: unknown;\n\t\treadonly [key: string]: unknown;\n\t};\n\treadonly code?: unknown;\n\treadonly message?: unknown;\n\treadonly requestId?: unknown;\n};\n\nfunction asRecord(value: unknown): Record<string, unknown> | undefined {\n\tif (!value || typeof value !== 'object' || Array.isArray(value)) return undefined;\n\treturn value as Record<string, unknown>;\n}\n\n/**\n * Parse a mint/API error response body + optional `Liforma-Request-Id` header\n * into a {@link LiformaError}. Falls back to flat `{ code, message }` shapes.\n */\nexport function liformaErrorFromResponse(\n\tstatus: number,\n\ttext: string,\n\theaders?: Headers\n): LiformaError {\n\tconst headerRequestId = headers?.get('Liforma-Request-Id')?.trim() || undefined;\n\tlet parsed: NestedErrorShape | undefined;\n\ttry {\n\t\tparsed = JSON.parse(text) as NestedErrorShape;\n\t} catch {\n\t\t/* keep */\n\t}\n\n\tconst nested = parsed?.error && typeof parsed.error === 'object' ? parsed.error : undefined;\n\tconst codeRaw = nested?.code ?? parsed?.code;\n\tconst messageRaw = nested?.message ?? parsed?.message;\n\tconst requestIdRaw = nested?.requestId ?? parsed?.requestId ?? headerRequestId;\n\n\tconst code = typeof codeRaw === 'string' && codeRaw.trim() ? codeRaw.trim() : 'HTTP_ERROR';\n\tconst message =\n\t\ttypeof messageRaw === 'string' && messageRaw.trim()\n\t\t\t? messageRaw.trim()\n\t\t\t: text.trim().slice(0, 500) || `Request failed (${status})`;\n\tconst requestId =\n\t\ttypeof requestIdRaw === 'string' && requestIdRaw.trim() ? requestIdRaw.trim() : headerRequestId;\n\n\tconst details = asRecord(nested) ?? asRecord(parsed);\n\tconst { code: _c, message: _m, requestId: _r, ...rest } = details ?? {};\n\tvoid _c;\n\tvoid _m;\n\tvoid _r;\n\n\treturn new LiformaError(message, {\n\t\tcode,\n\t\trequestId,\n\t\tstatus,\n\t\tdetails: Object.keys(rest).length > 0 ? rest : undefined\n\t});\n}\n","export type ExperienceAttribution = {\n\treadonly utmSource?: string;\n\treadonly utmMedium?: string;\n\treadonly utmCampaign?: string;\n\treadonly utmContent?: string;\n\treadonly utmTerm?: string;\n\treadonly referrer?: string;\n};\n\nconst ATTR_MAX = 128;\nconst CONTROL = /[\\u0000-\\u001f\\u007f]/g;\n\nfunction bound(value: string | null | undefined): string | undefined {\n\tif (!value) return undefined;\n\tconst trimmed = value.replace(CONTROL, '').trim();\n\tif (!trimmed) return undefined;\n\treturn trimmed.slice(0, ATTR_MAX);\n}\n\nfunction utmFromSearch(search: string | null | undefined): ExperienceAttribution {\n\tconst params = new URLSearchParams((search ?? '').startsWith('?') ? (search ?? '').slice(1) : (search ?? ''));\n\treturn {\n\t\t...(bound(params.get('utm_source')) ? { utmSource: bound(params.get('utm_source')) } : {}),\n\t\t...(bound(params.get('utm_medium')) ? { utmMedium: bound(params.get('utm_medium')) } : {}),\n\t\t...(bound(params.get('utm_campaign')) ? { utmCampaign: bound(params.get('utm_campaign')) } : {}),\n\t\t...(bound(params.get('utm_content')) ? { utmContent: bound(params.get('utm_content')) } : {}),\n\t\t...(bound(params.get('utm_term')) ? { utmTerm: bound(params.get('utm_term')) } : {})\n\t};\n}\n\nexport function resolveMintAttribution(\n\tattribution: ExperienceAttribution | false | undefined\n): ExperienceAttribution | undefined {\n\tif (attribution === false) return undefined;\n\tif (attribution && typeof attribution === 'object') {\n\t\tconst explicit: ExperienceAttribution = {\n\t\t\t...(bound(attribution.utmSource) ? { utmSource: bound(attribution.utmSource) } : {}),\n\t\t\t...(bound(attribution.utmMedium) ? { utmMedium: bound(attribution.utmMedium) } : {}),\n\t\t\t...(bound(attribution.utmCampaign) ? { utmCampaign: bound(attribution.utmCampaign) } : {}),\n\t\t\t...(bound(attribution.utmContent) ? { utmContent: bound(attribution.utmContent) } : {}),\n\t\t\t...(bound(attribution.utmTerm) ? { utmTerm: bound(attribution.utmTerm) } : {}),\n\t\t\t...(bound(attribution.referrer) ? { referrer: bound(attribution.referrer) } : {})\n\t\t};\n\t\treturn Object.keys(explicit).length ? explicit : undefined;\n\t}\n\tif (typeof window === 'undefined') return undefined;\n\tconst fromPage = utmFromSearch(window.location?.search);\n\tconst referrer = bound(document.referrer || undefined);\n\tconst auto: ExperienceAttribution = {\n\t\t...fromPage,\n\t\t...(referrer ? { referrer } : {})\n\t};\n\treturn Object.keys(auto).length ? auto : undefined;\n}\n","import { resolveMintAttribution } from './captureAttribution';\n\nexport type SessionEndpointRequestOptions = {\n\treadonly experienceId: string;\n\t/** BCP 47 locale (preferred). When omitted in the browser, defaults to `navigator.language`. */\n\treadonly locale?: string;\n\t/** Paired / learning language for dual experiences (BCP 47). */\n\treadonly secondaryLocale?: string;\n\t/** Alias of `secondaryLocale` — language-learning apps (tutors + roleplay). */\n\treadonly learningLocale?: string;\n\t/** @deprecated Prefer `locale`. */\n\treadonly language?: 'en' | 'es' | string;\n\treadonly mode?: 'conversation' | 'presenter';\n\treadonly speechInputMode?: 'auto' | 'manual' | 'off';\n\treadonly speechOnly?: boolean;\n\t/** Optional Idempotency-Key header value (max 256 chars). */\n\treadonly idempotencyKey?: string;\n\treadonly attribution?: import('./captureAttribution').ExperienceAttribution | false;\n};\n\nfunction resolveLocale(options: SessionEndpointRequestOptions): string | undefined {\n\tconst locale = options.locale?.trim();\n\tif (locale) return locale;\n\tconst language = options.language?.trim();\n\tif (language) return language;\n\tif (typeof navigator !== 'undefined' && navigator.language?.trim()) {\n\t\treturn navigator.language.trim();\n\t}\n\treturn undefined;\n}\n\nfunction resolveSecondaryLocale(options: SessionEndpointRequestOptions): string | undefined {\n\tconst secondary = options.secondaryLocale?.trim();\n\tconst learning = options.learningLocale?.trim();\n\tif (secondary && learning && secondary !== learning) {\n\t\tthrow new Error(\n\t\t\t'secondaryLocale and learningLocale disagree; pass only one or make them equal.'\n\t\t);\n\t}\n\treturn secondary || learning || undefined;\n}\n\nexport function createSessionEndpointRequest(\n\tsessionEndpoint: string,\n\toptions: SessionEndpointRequestOptions\n): { readonly url: string; readonly init: RequestInit } {\n\tconst endpoint = sessionEndpoint.trim();\n\tif (!endpoint) {\n\t\tthrow new Error('sessionEndpoint must not be empty.');\n\t}\n\tif (typeof location === 'undefined') {\n\t\tthrow new Error('sessionEndpoint is only available in a browser.');\n\t}\n\n\tconst url = new URL(endpoint, location.href);\n\tif (url.origin !== location.origin) {\n\t\tthrow new Error('sessionEndpoint must be same-origin.');\n\t}\n\n\tconst experienceId = options.experienceId.trim();\n\tif (!experienceId) {\n\t\tthrow new Error('sessionEndpoint requires experienceId.');\n\t}\n\n\tconst locale = resolveLocale(options);\n\tconst secondaryLocale = resolveSecondaryLocale(options);\n\tconst idempotencyKey = options.idempotencyKey?.trim();\n\n\treturn {\n\t\turl: url.toString(),\n\t\tinit: {\n\t\t\tmethod: 'POST',\n\t\t\tcredentials: 'same-origin',\n\t\t\theaders: {\n\t\t\t\tAccept: 'application/json',\n\t\t\t\t'Content-Type': 'application/json',\n\t\t\t\t...(idempotencyKey ? { 'Idempotency-Key': idempotencyKey } : {})\n\t\t\t},\n\t\t\tbody: JSON.stringify({\n\t\t\t\texperienceId,\n\t\t\t\t...(locale ? { locale } : {}),\n\t\t\t\t...(secondaryLocale ? { secondaryLocale } : {}),\n\t\t\t\t...(options.mode ? { mode: options.mode } : {}),\n\t\t\t\t...(options.speechInputMode ? { speechInputMode: options.speechInputMode } : {}),\n\t\t\t\t...(options.speechOnly !== undefined ? { speechOnly: options.speechOnly } : {}),\n\t\t\t\t...(() => {\n\t\t\t\t\tconst attribution = resolveMintAttribution(options.attribution);\n\t\t\t\t\treturn attribution ? { attribution } : {};\n\t\t\t\t})()\n\t\t\t})\n\t\t}\n\t};\n}\n","/**\n * Public Session Launch contract (integrator-facing).\n * Runtime internals live inside the opaque `launch` string — do not parse it.\n */\n\nexport type SessionCapabilities = {\n\treadonly speech: { readonly input: boolean; readonly output: boolean; readonly externalAudio: boolean };\n\treadonly conversation: { readonly managed: boolean };\n\treadonly avatar: { readonly enabled: boolean };\n\treadonly tools: { readonly enabled: boolean };\n};\n\nexport type SessionMode = 'conversation' | 'presenter';\n\nexport type SessionStatus = 'active' | 'ended';\n\n/** Mint collapsed a non-English locale onto English because TTS cannot synthesize it. */\nexport type LocaleVoiceFallback = {\n\treadonly axis: 'locale' | 'secondaryLocale';\n\treadonly requested: string;\n\treadonly resolved: string;\n\treadonly reason: 'voice_unsupported';\n};\n\nexport type Session = {\n\treadonly id: string;\n\treadonly experienceId: string;\n\treadonly experienceRevisionId: string;\n\treadonly expiresAt: string;\n\treadonly locale: string;\n\t/** Paired / learning language when dual; omitted for plain sessions. */\n\treadonly secondaryLocale?: string;\n\treadonly mode: SessionMode;\n\treadonly capabilities: SessionCapabilities;\n\treadonly localeVoiceFallbacks?: readonly LocaleVoiceFallback[];\n\t/** Present on mint when RuntimeSession was persisted. */\n\treadonly status?: SessionStatus;\n\treadonly createdAt?: string;\n};\n\nexport type SessionLaunchResponse = {\n\treadonly session: Session;\n\t/**\n\t * Asymmetrically signed opaque runtime bootstrap for the player.\n\t * Contract-opaque (unsupported to parse), not confidentiality-opaque.\n\t */\n\treadonly launch: string;\n};\n\nexport type PlayerStatus = 'loading' | 'scene' | 'ready' | 'error';\n\n/** Listening / speaking activity (replaces overloaded modeChange). */\nexport type SessionActivity = 'idle' | 'listening' | 'thinking' | 'speaking';\n\nexport type CloseButtonOptions = {\n\treadonly visible?: boolean;\n\treadonly style?: 'icon' | 'text';\n\treadonly label?: string | null;\n\treadonly position?: string;\n};\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn Boolean(value) && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction isBoolean(value: unknown): value is boolean {\n\treturn typeof value === 'boolean';\n}\n\nfunction parseNestedCapabilities(raw: unknown): SessionCapabilities | null {\n\tif (!isRecord(raw)) return null;\n\tconst speech = raw.speech;\n\tconst conversation = raw.conversation;\n\tconst avatar = raw.avatar;\n\tconst tools = raw.tools;\n\tif (\n\t\t!isRecord(speech) ||\n\t\t!isRecord(conversation) ||\n\t\t!isRecord(avatar) ||\n\t\t!isRecord(tools) ||\n\t\t!isBoolean(speech.input) ||\n\t\t!isBoolean(speech.output) ||\n\t\t!isBoolean(speech.externalAudio) ||\n\t\t!isBoolean(conversation.managed) ||\n\t\t!isBoolean(avatar.enabled) ||\n\t\t!isBoolean(tools.enabled)\n\t) {\n\t\treturn null;\n\t}\n\treturn {\n\t\tspeech: {\n\t\t\tinput: speech.input,\n\t\t\toutput: speech.output,\n\t\t\texternalAudio: speech.externalAudio\n\t\t},\n\t\tconversation: { managed: conversation.managed },\n\t\tavatar: { enabled: avatar.enabled },\n\t\ttools: { enabled: tools.enabled }\n\t};\n}\n\n/** @deprecated Flat capabilities from pre-v1 mint responses. */\nfunction parseLegacyFlatCapabilities(raw: unknown): SessionCapabilities | null {\n\tif (!isRecord(raw)) return null;\n\tif (\n\t\t!isBoolean(raw.avatar) ||\n\t\t!isBoolean(raw.speechInput) ||\n\t\t!isBoolean(raw.speechOutput) ||\n\t\t!isBoolean(raw.tools) ||\n\t\t!isBoolean(raw.externalAudio)\n\t) {\n\t\treturn null;\n\t}\n\treturn {\n\t\tspeech: {\n\t\t\tinput: raw.speechInput,\n\t\t\toutput: raw.speechOutput,\n\t\t\texternalAudio: raw.externalAudio\n\t\t},\n\t\tconversation: { managed: true },\n\t\tavatar: { enabled: raw.avatar },\n\t\ttools: { enabled: raw.tools }\n\t};\n}\n\nfunction parseCapabilities(raw: unknown): SessionCapabilities | null {\n\treturn parseNestedCapabilities(raw) ?? parseLegacyFlatCapabilities(raw);\n}\n\nexport function parseSession(raw: unknown): Session {\n\tif (!isRecord(raw)) {\n\t\tthrow new Error('Session Launch response is missing session.');\n\t}\n\tconst id = typeof raw.id === 'string' ? raw.id.trim() : '';\n\tconst experienceId = typeof raw.experienceId === 'string' ? raw.experienceId.trim() : '';\n\tconst experienceRevisionId =\n\t\ttypeof raw.experienceRevisionId === 'string' ? raw.experienceRevisionId.trim() : '';\n\tconst expiresAt = typeof raw.expiresAt === 'string' ? raw.expiresAt.trim() : '';\n\tconst locale = typeof raw.locale === 'string' ? raw.locale.trim() : '';\n\tconst secondaryLocale =\n\t\ttypeof raw.secondaryLocale === 'string' && raw.secondaryLocale.trim()\n\t\t\t? raw.secondaryLocale.trim()\n\t\t\t: undefined;\n\tconst mode = raw.mode === 'conversation' || raw.mode === 'presenter' ? raw.mode : null;\n\tconst capabilities = parseCapabilities(raw.capabilities);\n\tconst localeVoiceFallbacks = parseLocaleVoiceFallbacks(raw.localeVoiceFallbacks);\n\tconst status =\n\t\traw.status === 'active' || raw.status === 'ended' ? raw.status : undefined;\n\tconst createdAt =\n\t\ttypeof raw.createdAt === 'string' && raw.createdAt.trim() ? raw.createdAt.trim() : undefined;\n\n\tif (!id || !experienceId || !experienceRevisionId || !expiresAt || !locale || !mode || !capabilities) {\n\t\tthrow new Error('Session Launch response has an invalid session object.');\n\t}\n\n\treturn {\n\t\tid,\n\t\texperienceId,\n\t\texperienceRevisionId,\n\t\texpiresAt,\n\t\tlocale,\n\t\t...(secondaryLocale ? { secondaryLocale } : {}),\n\t\tmode,\n\t\tcapabilities,\n\t\t...(localeVoiceFallbacks ? { localeVoiceFallbacks } : {}),\n\t\t...(status ? { status } : {}),\n\t\t...(createdAt ? { createdAt } : {})\n\t};\n}\n\nfunction parseLocaleVoiceFallbacks(raw: unknown): readonly LocaleVoiceFallback[] | undefined {\n\tif (!Array.isArray(raw) || raw.length === 0) return undefined;\n\tconst out: LocaleVoiceFallback[] = [];\n\tfor (const item of raw) {\n\t\tif (!isRecord(item)) continue;\n\t\tconst axis = item.axis === 'locale' || item.axis === 'secondaryLocale' ? item.axis : null;\n\t\tconst requested = typeof item.requested === 'string' ? item.requested.trim() : '';\n\t\tconst resolved = typeof item.resolved === 'string' ? item.resolved.trim() : '';\n\t\tif (!axis || !requested || !resolved || item.reason !== 'voice_unsupported') continue;\n\t\tout.push({ axis, requested, resolved, reason: 'voice_unsupported' });\n\t}\n\treturn out.length > 0 ? out : undefined;\n}\n\n/** Browser console warnings when mint fell back to English for voice support. */\nexport function warnLocaleVoiceFallbacks(session: Session): void {\n\tconst fallbacks = session.localeVoiceFallbacks;\n\tif (!fallbacks?.length) return;\n\tfor (const fb of fallbacks) {\n\t\tconst label = fb.axis === 'secondaryLocale' ? 'secondaryLocale' : 'locale';\n\t\tconsole.warn(\n\t\t\t`[liforma] Session ${label} fell back to ${fb.resolved} because the voice does not support ${fb.requested}.`\n\t\t);\n\t}\n}\n\nexport function parseSessionLaunchResponse(raw: unknown): SessionLaunchResponse {\n\tif (!isRecord(raw)) {\n\t\tthrow new Error('Session Launch response must be a JSON object.');\n\t}\n\tconst launch = typeof raw.launch === 'string' ? raw.launch.trim() : '';\n\tif (!launch) {\n\t\tthrow new Error('Session Launch response is missing launch.');\n\t}\n\tconst session = parseSession(raw.session);\n\twarnLocaleVoiceFallbacks(session);\n\treturn {\n\t\tsession,\n\t\tlaunch\n\t};\n}\n","import { resolvePublicApiBaseUrl } from '../../shims/apiBaseUrl';\nimport { LiformaError, liformaErrorFromResponse } from '../errors/LiformaError';\nimport { createSessionEndpointRequest } from './sessionEndpointRequest';\nimport { resolveMintAttribution } from './captureAttribution';\nimport {\n\tparseSessionLaunchResponse,\n\ttype SessionLaunchResponse\n} from '../types/sessionLaunch';\n\nexport type MintBrowserSessionOptions = {\n\t/** BCP 47 locale (preferred). When omitted in the browser, defaults to `navigator.language`. */\n\treadonly locale?: string;\n\t/** Paired / learning language for dual experiences (BCP 47). */\n\treadonly secondaryLocale?: string;\n\t/** Alias of `secondaryLocale` — language-learning apps (tutors + roleplay). */\n\treadonly learningLocale?: string;\n\t/** @deprecated Prefer `locale`. */\n\treadonly language?: 'en' | 'es' | string;\n\treadonly mode?: 'conversation' | 'presenter';\n\treadonly speechInputMode?: 'auto' | 'manual' | 'off';\n\treadonly speechOnly?: boolean;\n\t/** Optional Idempotency-Key header value (max 256 chars). */\n\treadonly idempotencyKey?: string;\n\treadonly attribution?: import('./captureAttribution').ExperienceAttribution | false;\n};\n\nfunction currentPageOrigin(): string | undefined {\n\ttry {\n\t\tif (typeof globalThis.location?.origin === 'string' && globalThis.location.origin) {\n\t\t\treturn globalThis.location.origin;\n\t\t}\n\t} catch {\n\t\t/* ignore */\n\t}\n\treturn undefined;\n}\n\nfunction normalizeMintErrorCode(code: string | undefined): string {\n\tif (!code) return '';\n\treturn code.trim().toUpperCase().replace(/-/g, '_');\n}\n\nfunction enrichPublicMintError(experienceId: string, err: LiformaError): LiformaError {\n\tconst pageOrigin = currentPageOrigin();\n\tconst code = normalizeMintErrorCode(err.code);\n\tconst apiMessage = err.message.trim();\n\tconst details = err.details ?? {};\n\n\tif (\n\t\tcode === 'ORIGIN_NOT_ALLOWED' ||\n\t\t(/^Origin not allowed/i.test(apiMessage) && pageOrigin)\n\t) {\n\t\tif (apiMessage.includes(experienceId) && apiMessage.includes('allowed origin')) {\n\t\t\treturn err;\n\t\t}\n\t\tconst origin =\n\t\t\t(typeof details.origin === 'string' && details.origin) ||\n\t\t\tpageOrigin ||\n\t\t\t'(unknown origin)';\n\t\treturn new LiformaError(\n\t\t\t`Your origin ${origin} is not an allowed origin for experience ${experienceId}. ` +\n\t\t\t\t`If you own this experience, sign in to the Liforma developer portal (app.liforma.ai), ` +\n\t\t\t\t`open Workspace → Settings → Origins, add ${origin} to the list of allowed origins, ` +\n\t\t\t\t`and ensure “Allow public client-only embeds” is enabled.`,\n\t\t\t{ code: 'ORIGIN_NOT_ALLOWED', requestId: err.requestId, status: err.status, details }\n\t\t);\n\t}\n\n\tif (code === 'BROWSER_EMBEDS_DISABLED' || /^Browser embeds are not enabled/i.test(apiMessage)) {\n\t\tif (apiMessage.includes('Allow public client-only embeds')) {\n\t\t\treturn err;\n\t\t}\n\t\treturn new LiformaError(\n\t\t\t`Browser embeds are not enabled for experience ${experienceId}. ` +\n\t\t\t\t`If you own this experience, sign in to the Liforma developer portal (app.liforma.ai), ` +\n\t\t\t\t`open Workspace → Settings → Origins, and enable “Allow public client-only embeds”.`,\n\t\t\t{\n\t\t\t\tcode: 'BROWSER_EMBEDS_DISABLED',\n\t\t\t\trequestId: err.requestId,\n\t\t\t\tstatus: err.status,\n\t\t\t\tdetails\n\t\t\t}\n\t\t);\n\t}\n\n\tif (\n\t\tcode === 'EXPERIENCE_UNAVAILABLE' ||\n\t\t/^Experience is not available\\.?$/i.test(apiMessage) ||\n\t\t/^Unknown experience\\.?$/i.test(apiMessage)\n\t) {\n\t\tif (apiMessage.includes('No playable experience found')) {\n\t\t\treturn err;\n\t\t}\n\t\treturn new LiformaError(\n\t\t\t`No playable experience found for “${experienceId}”. ` +\n\t\t\t\t`Check that the experience id is correct (it should look like exp_…), ` +\n\t\t\t\t`and that the experience exists and is published in the Liforma developer portal (app.liforma.ai). ` +\n\t\t\t\t`Deleted, archived, and unpublished experiences cannot be started from a browser embed.`,\n\t\t\t{\n\t\t\t\tcode: 'EXPERIENCE_UNAVAILABLE',\n\t\t\t\trequestId: err.requestId,\n\t\t\t\tstatus: err.status,\n\t\t\t\tdetails\n\t\t\t}\n\t\t);\n\t}\n\n\treturn err;\n}\n\nfunction resolveLocale(options?: MintBrowserSessionOptions): string | undefined {\n\tconst locale = options?.locale?.trim();\n\tif (locale) return locale;\n\tconst language = options?.language?.trim();\n\tif (language) return language;\n\tif (typeof navigator !== 'undefined' && navigator.language?.trim()) {\n\t\treturn navigator.language.trim();\n\t}\n\treturn undefined;\n}\n\nfunction resolveSecondaryLocale(options?: MintBrowserSessionOptions): string | undefined {\n\tconst secondary = options?.secondaryLocale?.trim();\n\tconst learning = options?.learningLocale?.trim();\n\tif (secondary && learning && secondary !== learning) {\n\t\tthrow new LiformaError(\n\t\t\t'secondaryLocale and learningLocale disagree; pass only one or make them equal.',\n\t\t\t{ code: 'INVALID_BODY' }\n\t\t);\n\t}\n\treturn secondary || learning || undefined;\n}\n\nfunction buildMintBody(\n\texperienceId: string,\n\toptions?: MintBrowserSessionOptions\n): Record<string, unknown> {\n\tconst body: Record<string, unknown> = { experienceId };\n\tconst locale = resolveLocale(options);\n\tif (locale) body.locale = locale;\n\tconst secondaryLocale = resolveSecondaryLocale(options);\n\tif (secondaryLocale) body.secondaryLocale = secondaryLocale;\n\tif (options?.mode) body.mode = options.mode;\n\tif (options?.speechInputMode) body.speechInputMode = options.speechInputMode;\n\tif (options?.speechOnly !== undefined) body.speechOnly = options.speechOnly;\n\tconst attribution = resolveMintAttribution(options?.attribution);\n\tif (attribution) body.attribution = attribution;\n\treturn body;\n}\n\n/** Mint a browser session via `POST /v1/browser-sessions`. */\nexport async function mintBrowserSession(\n\texperienceId: string,\n\toptions?: MintBrowserSessionOptions\n): Promise<SessionLaunchResponse> {\n\tconst apiBaseUrl = resolvePublicApiBaseUrl();\n\tconst idempotencyKey = options?.idempotencyKey?.trim();\n\tconst res = await fetch(`${apiBaseUrl}/v1/browser-sessions`, {\n\t\tmethod: 'POST',\n\t\theaders: {\n\t\t\t'Content-Type': 'application/json',\n\t\t\t...(idempotencyKey ? { 'Idempotency-Key': idempotencyKey } : {})\n\t\t},\n\t\tbody: JSON.stringify(buildMintBody(experienceId, options))\n\t});\n\n\tif (!res.ok) {\n\t\tconst text = await res.text();\n\t\tthrow enrichPublicMintError(experienceId, liformaErrorFromResponse(res.status, text, res.headers));\n\t}\n\tlet raw: unknown;\n\ttry {\n\t\traw = await res.json();\n\t} catch {\n\t\tthrow new LiformaError('Browser session mint returned invalid JSON.', {\n\t\t\tcode: 'INVALID_RESPONSE',\n\t\t\tstatus: res.status,\n\t\t\trequestId: res.headers.get('Liforma-Request-Id') ?? undefined\n\t\t});\n\t}\n\n\treturn parseSessionLaunchResponse(raw);\n}\n\n/** @deprecated Prefer {@link mintBrowserSession}. */\nexport const mintPublicSession = mintBrowserSession;\n\nexport type FetchSessionLaunchOptions = MintBrowserSessionOptions & {\n\treadonly experienceId: string;\n\treadonly signal?: AbortSignal;\n\treadonly timeoutMs?: number;\n};\n\n/** @deprecated Prefer {@link FetchSessionLaunchOptions}. */\nexport type FetchSessionManifestOptions = FetchSessionLaunchOptions;\n\nexport async function fetchSessionLaunch(\n\tsessionEndpoint: string,\n\toptions: FetchSessionLaunchOptions\n): Promise<SessionLaunchResponse> {\n\tconst request = createSessionEndpointRequest(sessionEndpoint, options);\n\n\tconst controller = new AbortController();\n\tconst abortFromCaller = () => controller.abort(options.signal?.reason);\n\tif (options.signal?.aborted) {\n\t\tabortFromCaller();\n\t} else {\n\t\toptions.signal?.addEventListener('abort', abortFromCaller, { once: true });\n\t}\n\tconst timeoutId = globalThis.setTimeout(\n\t\t() => controller.abort(new DOMException('Session endpoint timed out.', 'TimeoutError')),\n\t\toptions.timeoutMs ?? 30_000\n\t);\n\n\tlet res: Response;\n\ttry {\n\t\tres = await fetch(request.url, {\n\t\t\t...request.init,\n\t\t\tsignal: controller.signal\n\t\t});\n\t} finally {\n\t\tglobalThis.clearTimeout(timeoutId);\n\t\toptions.signal?.removeEventListener('abort', abortFromCaller);\n\t}\n\n\tif (!res.ok) {\n\t\tconst text = await res.text();\n\t\tthrow liformaErrorFromResponse(res.status, text, res.headers);\n\t}\n\tif (!res.headers.get('content-type')?.toLowerCase().includes('application/json')) {\n\t\tthrow new LiformaError('Session endpoint must return a JSON Session Launch response.', {\n\t\t\tcode: 'INVALID_RESPONSE',\n\t\t\tstatus: res.status,\n\t\t\trequestId: res.headers.get('Liforma-Request-Id') ?? undefined\n\t\t});\n\t}\n\n\tlet raw: unknown;\n\ttry {\n\t\traw = await res.json();\n\t} catch {\n\t\tthrow new LiformaError('Session endpoint returned invalid JSON.', {\n\t\t\tcode: 'INVALID_RESPONSE',\n\t\t\tstatus: res.status,\n\t\t\trequestId: res.headers.get('Liforma-Request-Id') ?? undefined\n\t\t});\n\t}\n\n\treturn parseSessionLaunchResponse(raw);\n}\n\n/** @deprecated Prefer {@link fetchSessionLaunch}. */\nexport const fetchSessionManifest = fetchSessionLaunch;\n","import type {\n\tConversationProcessorContext,\n\tConversationProcessorFn,\n\tConversationProcessorResult,\n\tNormalizedProcessorSpeech,\n\tProcessorDispatchResult,\n\tSpeakBehavior\n} from './types/sessionInteraction';\n\nexport function isAsyncIterableString(value: unknown): value is AsyncIterable<string> {\n\tif (value == null || typeof value !== 'object') return false;\n\treturn typeof (value as AsyncIterable<string>)[Symbol.asyncIterator] === 'function';\n}\n\nexport function normalizeConversationProcessorResult(\n\tresult: ConversationProcessorResult\n): NormalizedProcessorSpeech {\n\tif (typeof result === 'string') {\n\t\tconst text = result.trim();\n\t\tif (!text) {\n\t\t\tthrow new Error('conversationProcessor returned empty text.');\n\t\t}\n\t\treturn { text };\n\t}\n\n\tconst text = result.text.trim();\n\tif (!text) {\n\t\tthrow new Error('conversationProcessor returned empty text.');\n\t}\n\n\treturn {\n\t\ttext,\n\t\t...(result.characterId ? { characterId: result.characterId } : {}),\n\t\t...(result.behavior ? { behavior: result.behavior } : {})\n\t};\n}\n\nexport async function runConversationProcessor(\n\tprocessor: ConversationProcessorFn,\n\tcontext: ConversationProcessorContext\n): Promise<NormalizedProcessorSpeech> {\n\tconst dispatch = await dispatchConversationProcessor(processor, context);\n\tif (dispatch.kind !== 'complete') {\n\t\tthrow new Error('conversationProcessor returned a stream; use dispatchConversationProcessor.');\n\t}\n\treturn dispatch.speech;\n}\n\nexport async function dispatchConversationProcessor(\n\tprocessor: ConversationProcessorFn,\n\tcontext: ConversationProcessorContext\n): Promise<ProcessorDispatchResult> {\n\tconst result = await processor(context);\n\tif (isAsyncIterableString(result)) {\n\t\treturn { kind: 'stream', stream: result };\n\t}\n\treturn {\n\t\tkind: 'complete',\n\t\tspeech: normalizeConversationProcessorResult(result)\n\t};\n}\n","const LOCAL_PLAYER_DEV_PORT = '3002';\n\nexport function formatPlayerUnreachableMessage(playerOrigin: string): string {\n\tconst url = new URL(playerOrigin);\n\tconst isLocalHost = url.hostname === 'localhost' || url.hostname === '127.0.0.1';\n\n\tif (isLocalHost) {\n\t\tconst devPort = url.port || LOCAL_PLAYER_DEV_PORT;\n\t\treturn (\n\t\t\t`Cannot connect to the Liforma player at ${playerOrigin} — nothing is listening on port ${devPort}. ` +\n\t\t\t`Start the player dev server in player.liforma.ai (npm run dev).`\n\t\t);\n\t}\n\n\treturn (\n\t\t`Cannot connect to the Liforma player at ${playerOrigin}. ` +\n\t\t`The embed did not load; check your network connection and player URL configuration.`\n\t);\n}\n\nexport async function assertPlayerReachable(\n\tembedUrl: string,\n\tplayerOrigin: string\n): Promise<void> {\n\ttry {\n\t\tawait fetch(embedUrl, {\n\t\t\tmethod: 'GET',\n\t\t\tmode: 'no-cors',\n\t\t\tsignal: AbortSignal.timeout(8_000)\n\t\t});\n\t} catch {\n\t\tthrow new Error(formatPlayerUnreachableMessage(playerOrigin));\n\t}\n}\n\nexport function postMessageToPlayer(\n\ttarget: Window,\n\tmessage: unknown,\n\tplayerOrigin: string,\n\ttransfer?: Transferable[]\n): void {\n\ttry {\n\t\tif (transfer && transfer.length > 0) {\n\t\t\ttarget.postMessage(message, playerOrigin, transfer);\n\t\t} else {\n\t\t\ttarget.postMessage(message, playerOrigin);\n\t\t}\n\t} catch (error) {\n\t\tif (error instanceof DOMException && error.name === 'SecurityError') {\n\t\t\tthrow new Error(formatPlayerUnreachableMessage(playerOrigin), { cause: error });\n\t\t}\n\t\tthrow error;\n\t}\n}\n","import type {\n\tCharacterSpeechEvent,\n\tConversationMessage,\n\tSpeakBehavior,\n\tSessionModeChange,\n\tUserSpeechBoundaryEvent,\n\tUserTranscriptUpdate,\n\tUtteranceResult\n} from '../types/sessionInteraction';\nimport type { Session, SessionActivity } from '../types/sessionLaunch';\nimport type { StartButtonOptions, PlayerTheme } from '../types/sessionInteraction';\nimport type {\n\tCompleteSpeechQueuePolicy,\n\tSpeechAudioFormat,\n\tSpeechInterruptOptions,\n\tSpeechPlayEmbedSource,\n\tStreamingSpeechQueuePolicy\n} from '../speech/types';\n\nexport const EMBED_MESSAGE_SCHEMA_VERSION = 2 as const;\n\n/** Outbound postMessage versions accepted from the player embed. */\nexport const SUPPORTED_EMBED_MESSAGE_SCHEMA_VERSIONS: ReadonlySet<number> = new Set([1, 2]);\n\nexport function isSupportedEmbedMessageSchemaVersion(version: unknown): boolean {\n\treturn typeof version === 'number' && SUPPORTED_EMBED_MESSAGE_SCHEMA_VERSIONS.has(version);\n}\n\nexport type CloseEvent = {\n\treadonly sessionId?: string;\n\treadonly reason: 'user' | 'destroy';\n\treadonly returnUrl?: string;\n};\n\nexport type EmbedOutboundMessage =\n\t| {\n\t\t\treadonly type: 'close';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: CloseEvent;\n\t }\n\t| {\n\t\t\treadonly type: 'stateUpdate';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: { readonly state: 'loading' | 'scene' | 'ready' | 'error' };\n\t }\n\t| {\n\t\t\treadonly type: 'commandResult';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: {\n\t\t\t\treadonly correlationId: string;\n\t\t\t\treadonly ok: boolean;\n\t\t\t\treadonly error?: string;\n\t\t\t\treadonly result?: unknown;\n\t\t\t};\n\t }\n\t| {\n\t\t\treadonly type: 'userTranscript';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: UserTranscriptUpdate;\n\t }\n\t| {\n\t\t\treadonly type: 'characterSpeechStarted';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: CharacterSpeechEvent;\n\t }\n\t| {\n\t\t\treadonly type: 'characterSpeechEnded';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: CharacterSpeechEvent;\n\t }\n\t| {\n\t\t\treadonly type: 'characterFocusChanged';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: {\n\t\t\t\treadonly characterId: string;\n\t\t\t\treadonly reason: 'speech' | 'api' | 'node_enter';\n\t\t\t};\n\t }\n\t| {\n\t\t\treadonly type: 'conversationUpdate';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: { readonly conversation: readonly ConversationMessage[] };\n\t }\n\t| {\n\t\t\treadonly type: 'listeningState';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: { readonly listening: boolean };\n\t }\n\t| {\n\t\t\treadonly type: 'userSpeechStarted';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: UserSpeechBoundaryEvent;\n\t }\n\t| {\n\t\t\treadonly type: 'userSpeechEnded';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: UserSpeechBoundaryEvent;\n\t }\n\t| {\n\t\t\treadonly type: 'modeChange';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: { readonly mode: SessionModeChange };\n\t }\n\t| {\n\t\t\treadonly type: 'activityChange';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: { readonly activity: SessionActivity };\n\t }\n\t| {\n\t\t\treadonly type: 'feedback';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: {\n\t\t\t\treadonly contractVersion: 'feedback.v1';\n\t\t\t\treadonly types: readonly {\n\t\t\t\t\treadonly feedbackId: string;\n\t\t\t\t\treadonly name: string;\n\t\t\t\t\treadonly criteria: readonly {\n\t\t\t\t\t\treadonly criterionId: string;\n\t\t\t\t\t\treadonly label: string;\n\t\t\t\t\t\treadonly score: number | string;\n\t\t\t\t\t\treadonly rationale: string;\n\t\t\t\t\t}[];\n\t\t\t\t}[];\n\t\t\t};\n\t }\n\t| {\n\t\t\treadonly type: 'feedbackFailed';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: { readonly error: string; readonly model?: string };\n\t }\n\t| {\n\t\t\treadonly type: 'message';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: ConversationMessage;\n\t }\n\t| {\n\t\t\treadonly type: 'started';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: { readonly mode: 'conversation' | 'presenter' };\n\t }\n\t| {\n\t\t\t/**\n\t\t\t * Player document is ready to receive `launch` (iframe HMR / Vite reload).\n\t\t\t * Parent re-posts the last launch token so bootstrap does not hang.\n\t\t\t */\n\t\t\treadonly type: 'embedReady';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t }\n\t| {\n\t\t\treadonly type: 'widgetReady';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: Record<string, never>;\n\t }\n\t| {\n\t\t\treadonly type: 'widgetExpand';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: Record<string, never>;\n\t }\n\t| {\n\t\t\treadonly type: 'widgetCollapse';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: Record<string, never>;\n\t }\n\t| {\n\t\t\treadonly type: 'widgetAudioSettings';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: { readonly open: boolean };\n\t }\n\t| {\n\t\t\treadonly type: 'widgetDragStart';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: {\n\t\t\t\treadonly pointerId: number;\n\t\t\t};\n\t }\n\t| {\n\t\t\treadonly type: 'widgetDragMove';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: {\n\t\t\t\treadonly pointerId: number;\n\t\t\t\t/** Pointer deltas in CSS pixels (not iframe-local coordinates). */\n\t\t\t\treadonly dx: number;\n\t\t\t\treadonly dy: number;\n\t\t\t};\n\t }\n\t| {\n\t\t\treadonly type: 'widgetDragEnd';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: {\n\t\t\t\treadonly pointerId: number;\n\t\t\t};\n\t }\n\t| {\n\t\t\t/** Expand / collapse the parent embed host to fill the browser window. */\n\t\t\treadonly type: 'fullWindow';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: { readonly active: boolean };\n\t };\n\nexport type WidgetChromeMode = 'collapsed' | 'loading' | 'expanded' | 'docked';\n\nexport type WidgetDragStartEvent = {\n\treadonly pointerId: number;\n};\n\nexport type WidgetDragMoveEvent = {\n\treadonly pointerId: number;\n\treadonly dx: number;\n\treadonly dy: number;\n};\n\n/** @deprecated Prefer WidgetDragStartEvent / WidgetDragMoveEvent */\nexport type WidgetDragPointerEvent = WidgetDragMoveEvent;\n\nexport type EmbedInboundMessage =\n\t| {\n\t\t\treadonly type: 'launch';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: {\n\t\t\t\treadonly launch: string;\n\t\t\t\treadonly session?: Session | object;\n\t\t\t\t/** Player chrome from attach — not part of the signed launch string. */\n\t\t\t\treadonly startButton?: StartButtonOptions;\n\t\t\t\t/** Global player chrome theme from attach. */\n\t\t\t\treadonly theme?: PlayerTheme;\n\t\t\t\t/**\n\t\t\t\t * When false, player skips Feedback step-through UI (host renders scores).\n\t\t\t\t * Default when omitted: true (player shows Feedback).\n\t\t\t\t */\n\t\t\t\treadonly showFeedback?: boolean;\n\t\t\t};\n\t }\n\t| {\n\t\t\t/** @deprecated Prefer `launch`. Kept briefly for older players. */\n\t\t\treadonly type: 'manifest';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: { readonly manifest: unknown };\n\t }\n\t| {\n\t\t\treadonly type: 'parentHello';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t }\n\t| {\n\t\t\treadonly type: 'speak';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: {\n\t\t\t\treadonly correlationId: string;\n\t\t\t\treadonly text: string;\n\t\t\t\treadonly characterId?: string;\n\t\t\t\treadonly behavior?: SpeakBehavior;\n\t\t\t};\n\t }\n\t| {\n\t\t\treadonly type: 'startListening';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: { readonly correlationId: string };\n\t }\n\t| {\n\t\t\treadonly type: 'stopListening';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: { readonly correlationId: string };\n\t }\n\t| {\n\t\t\treadonly type: 'getConversation';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: { readonly correlationId: string };\n\t }\n\t| {\n\t\t\treadonly type: 'listenOnce';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: {\n\t\t\t\treadonly correlationId: string;\n\t\t\t\treadonly timeoutMs?: number;\n\t\t\t};\n\t }\n\t| {\n\t\t\treadonly type: 'speakStreamStart';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: {\n\t\t\t\treadonly correlationId: string;\n\t\t\t\treadonly characterId?: string;\n\t\t\t\treadonly behavior?: SpeakBehavior;\n\t\t\t};\n\t }\n\t| {\n\t\t\treadonly type: 'speakStreamChunk';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: {\n\t\t\t\treadonly streamId: string;\n\t\t\t\treadonly text: string;\n\t\t\t};\n\t }\n\t| {\n\t\t\treadonly type: 'speakStreamEnd';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: {\n\t\t\t\treadonly correlationId: string;\n\t\t\t\treadonly streamId: string;\n\t\t\t};\n\t }\n\t| {\n\t\t\treadonly type: 'widgetSetChrome';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: { readonly mode: WidgetChromeMode };\n\t }\n\t| {\n\t\t\treadonly type: 'widgetToggleAudioSettings';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: Record<string, never>;\n\t }\n\t| {\n\t\t\treadonly type: 'speechPlay';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: {\n\t\t\t\treadonly correlationId: string;\n\t\t\t\treadonly source: SpeechPlayEmbedSource;\n\t\t\t\treadonly characterId?: string;\n\t\t\t\treadonly transcript?: string;\n\t\t\t\treadonly history?: 'none' | 'commit';\n\t\t\t\treadonly queue?: CompleteSpeechQueuePolicy;\n\t\t\t};\n\t }\n\t| {\n\t\t\treadonly type: 'speechUtteranceBegin';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: {\n\t\t\t\treadonly correlationId: string;\n\t\t\t\treadonly utteranceId: string;\n\t\t\t\treadonly format: SpeechAudioFormat;\n\t\t\t\treadonly characterId?: string;\n\t\t\t\treadonly queue: StreamingSpeechQueuePolicy;\n\t\t\t\treadonly transcript?: string;\n\t\t\t};\n\t }\n\t| {\n\t\t\treadonly type: 'speechUtteranceWrite';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: {\n\t\t\t\treadonly correlationId: string;\n\t\t\t\treadonly utteranceId: string;\n\t\t\t\treadonly audio: ArrayBuffer;\n\t\t\t\treadonly byteLength: number;\n\t\t\t};\n\t }\n\t| {\n\t\t\treadonly type: 'speechUtteranceSetTranscript';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: {\n\t\t\t\treadonly correlationId: string;\n\t\t\t\treadonly utteranceId: string;\n\t\t\t\treadonly transcript: string;\n\t\t\t};\n\t }\n\t| {\n\t\t\treadonly type: 'speechUtteranceClose';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: {\n\t\t\t\treadonly correlationId: string;\n\t\t\t\treadonly utteranceId: string;\n\t\t\t\treadonly transcript?: string;\n\t\t\t\treadonly history?: 'none' | 'commit';\n\t\t\t};\n\t }\n\t| {\n\t\t\treadonly type: 'speechUtteranceCancel';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: {\n\t\t\t\treadonly correlationId: string;\n\t\t\t\treadonly utteranceId: string;\n\t\t\t};\n\t }\n\t| {\n\t\t\treadonly type: 'speechInterrupt';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: {\n\t\t\t\treadonly correlationId: string;\n\t\t\t} & SpeechInterruptOptions;\n\t }\n\t| {\n\t\t\treadonly type: 'focusCharacter';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: {\n\t\t\t\treadonly correlationId: string;\n\t\t\t\treadonly characterId: string;\n\t\t\t};\n\t }\n\t| {\n\t\t\treadonly type: 'setFit';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: {\n\t\t\t\treadonly correlationId: string;\n\t\t\t\treadonly fit: 'full' | 'face' | 'medium';\n\t\t\t};\n\t }\n\t| {\n\t\t\treadonly type: 'conversion';\n\t\t\treadonly schemaVersion: typeof EMBED_MESSAGE_SCHEMA_VERSION;\n\t\t\treadonly payload: {\n\t\t\t\treadonly correlationId: string;\n\t\t\t\treadonly key: string;\n\t\t\t};\n\t };\n\nexport type EmbedState = 'loading' | 'scene' | 'ready' | 'error';\n\nconst OUTBOUND_TYPES = new Set([\n\t'close',\n\t'stateUpdate',\n\t'commandResult',\n\t'userTranscript',\n\t'characterSpeechStarted',\n\t'characterSpeechEnded',\n\t'characterFocusChanged',\n\t'conversationUpdate',\n\t'listeningState',\n\t'userSpeechStarted',\n\t'userSpeechEnded',\n\t'modeChange',\n\t'activityChange',\n\t'feedback',\n\t'feedbackFailed',\n\t'message',\n\t'started',\n\t'embedReady',\n\t'widgetReady',\n\t'widgetExpand',\n\t'widgetCollapse',\n\t'widgetAudioSettings',\n\t'widgetDragStart',\n\t'widgetDragMove',\n\t'widgetDragEnd',\n\t'fullWindow'\n]);\n\nfunction isWidgetChromeMode(value: unknown): value is WidgetChromeMode {\n\treturn (\n\t\tvalue === 'collapsed' ||\n\t\tvalue === 'loading' ||\n\t\tvalue === 'expanded' ||\n\t\tvalue === 'docked'\n\t);\n}\n\nexport function parseOutboundMessage(data: unknown): EmbedOutboundMessage | null {\n\tif (!data || typeof data !== 'object') return null;\n\tconst msg = data as Partial<EmbedOutboundMessage>;\n\tif (!isSupportedEmbedMessageSchemaVersion(msg.schemaVersion)) return null;\n\tif (!msg.type || !OUTBOUND_TYPES.has(msg.type)) return null;\n\tif (\n\t\tmsg.type === 'embedReady' ||\n\t\tmsg.type === 'widgetReady' ||\n\t\tmsg.type === 'widgetExpand' ||\n\t\tmsg.type === 'widgetCollapse'\n\t) {\n\t\treturn msg as EmbedOutboundMessage;\n\t}\n\tif (msg.type === 'fullWindow') {\n\t\tconst payload = (msg as { payload?: unknown }).payload;\n\t\tif (!payload || typeof payload !== 'object') return null;\n\t\tif (typeof (payload as { active?: unknown }).active !== 'boolean') return null;\n\t\treturn msg as EmbedOutboundMessage;\n\t}\n\tif (msg.type === 'widgetAudioSettings') {\n\t\tconst payload = (msg as { payload?: unknown }).payload;\n\t\tif (!payload || typeof payload !== 'object') return null;\n\t\tif (typeof (payload as { open?: unknown }).open !== 'boolean') return null;\n\t\treturn msg as EmbedOutboundMessage;\n\t}\n\tif (msg.type === 'widgetDragStart' || msg.type === 'widgetDragEnd') {\n\t\tconst payload = (msg as { payload?: unknown }).payload;\n\t\tif (!payload || typeof payload !== 'object') return null;\n\t\tif (typeof (payload as { pointerId?: unknown }).pointerId !== 'number') return null;\n\t\tif (!Number.isFinite((payload as { pointerId: number }).pointerId)) return null;\n\t\treturn msg as EmbedOutboundMessage;\n\t}\n\tif (msg.type === 'widgetDragMove') {\n\t\tconst payload = (msg as { payload?: unknown }).payload;\n\t\tif (!payload || typeof payload !== 'object') return null;\n\t\tconst { pointerId, dx, dy } = payload as {\n\t\t\tpointerId?: unknown;\n\t\t\tdx?: unknown;\n\t\t\tdy?: unknown;\n\t\t};\n\t\tif (typeof pointerId !== 'number' || !Number.isFinite(pointerId)) return null;\n\t\tif (typeof dx !== 'number' || !Number.isFinite(dx)) return null;\n\t\tif (typeof dy !== 'number' || !Number.isFinite(dy)) return null;\n\t\treturn msg as EmbedOutboundMessage;\n\t}\n\tconst payload = (msg as { payload?: unknown }).payload;\n\tif (!payload || typeof payload !== 'object') return null;\n\treturn msg as EmbedOutboundMessage;\n}\n\nconst INBOUND_TYPES = new Set([\n\t'launch',\n\t'manifest',\n\t'parentHello',\n\t'speak',\n\t'startListening',\n\t'stopListening',\n\t'getConversation',\n\t'listenOnce',\n\t'speakStreamStart',\n\t'speakStreamChunk',\n\t'speakStreamEnd',\n\t'widgetSetChrome',\n\t'widgetToggleAudioSettings',\n\t'speechPlay',\n\t'speechUtteranceBegin',\n\t'speechUtteranceWrite',\n\t'speechUtteranceSetTranscript',\n\t'speechUtteranceClose',\n\t'speechUtteranceCancel',\n\t'speechInterrupt',\n\t'focusCharacter',\n\t'setFit',\n\t'conversion'\n]);\n\nexport function parseInboundMessage(data: unknown): EmbedInboundMessage | null {\n\tif (!data || typeof data !== 'object') return null;\n\tconst msg = data as Partial<EmbedInboundMessage>;\n\tif (!isSupportedEmbedMessageSchemaVersion(msg.schemaVersion)) return null;\n\tif (!msg.type || !INBOUND_TYPES.has(msg.type)) return null;\n\tif (msg.type === 'parentHello') return msg as EmbedInboundMessage;\n\tif (msg.type === 'widgetToggleAudioSettings') return msg as EmbedInboundMessage;\n\tif (msg.type === 'widgetSetChrome') {\n\t\tconst payload = (msg as { payload?: unknown }).payload;\n\t\tif (!payload || typeof payload !== 'object') return null;\n\t\tconst mode = (payload as { mode?: unknown }).mode;\n\t\tif (!isWidgetChromeMode(mode)) return null;\n\t\treturn msg as EmbedInboundMessage;\n\t}\n\tif (msg.type === 'setFit') {\n\t\tconst payload = (msg as { payload?: unknown }).payload;\n\t\tif (!payload || typeof payload !== 'object') return null;\n\t\tconst fit = (payload as { fit?: unknown }).fit;\n\t\tconst correlationId = (payload as { correlationId?: unknown }).correlationId;\n\t\tif (fit !== 'full' && fit !== 'medium' && fit !== 'face') return null;\n\t\tif (typeof correlationId !== 'string' || !correlationId.trim()) return null;\n\t\treturn msg as EmbedInboundMessage;\n\t}\n\tif (msg.type === 'speakStreamChunk') {\n\t\tconst payload = (msg as { payload?: unknown }).payload;\n\t\tif (!payload || typeof payload !== 'object') return null;\n\t\tconst streamId = (payload as { streamId?: unknown }).streamId;\n\t\tconst text = (payload as { text?: unknown }).text;\n\t\tif (typeof streamId !== 'string' || !streamId.trim()) return null;\n\t\tif (typeof text !== 'string') return null;\n\t\treturn msg as EmbedInboundMessage;\n\t}\n\tif (msg.type === 'launch') {\n\t\tconst payload = (msg as { payload?: unknown }).payload;\n\t\tif (!payload || typeof payload !== 'object') return null;\n\t\tconst launch = (payload as { launch?: unknown }).launch;\n\t\tif (typeof launch !== 'string' || !launch.trim()) return null;\n\t\treturn msg as EmbedInboundMessage;\n\t}\n\tconst payload = (msg as { payload?: unknown }).payload;\n\tif (!payload || typeof payload !== 'object') return null;\n\treturn msg as EmbedInboundMessage;\n}\n\nexport function createLaunchMessage(\n\tlaunch: string,\n\tsession?: Session | object,\n\tchrome?: {\n\t\treadonly startButton?: StartButtonOptions;\n\t\treadonly theme?: PlayerTheme;\n\t\treadonly showFeedback?: boolean;\n\t}\n): EmbedInboundMessage {\n\tconst trimmed = launch.trim();\n\tif (!trimmed) {\n\t\tthrow new Error('createLaunchMessage requires a non-empty launch string.');\n\t}\n\tconst startButton = chrome?.startButton;\n\tconst theme = chrome?.theme;\n\tconst showFeedback = chrome?.showFeedback;\n\treturn {\n\t\ttype: 'launch',\n\t\tschemaVersion: EMBED_MESSAGE_SCHEMA_VERSION,\n\t\tpayload: {\n\t\t\tlaunch: trimmed,\n\t\t\t...(session !== undefined ? { session } : {}),\n\t\t\t...(startButton ? { startButton } : {}),\n\t\t\t...(theme ? { theme } : {}),\n\t\t\t...(showFeedback !== undefined ? { showFeedback } : {})\n\t\t}\n\t};\n}\n\n/** @deprecated Prefer {@link createLaunchMessage}. */\nexport function createManifestMessage(manifest: unknown): EmbedInboundMessage {\n\treturn {\n\t\ttype: 'manifest',\n\t\tschemaVersion: EMBED_MESSAGE_SCHEMA_VERSION,\n\t\tpayload: { manifest }\n\t};\n}\n\nexport function createParentHelloMessage(): EmbedInboundMessage {\n\treturn {\n\t\ttype: 'parentHello',\n\t\tschemaVersion: EMBED_MESSAGE_SCHEMA_VERSION\n\t};\n}\n\nexport function createEmbedReadyMessage(): EmbedOutboundMessage {\n\treturn {\n\t\ttype: 'embedReady',\n\t\tschemaVersion: EMBED_MESSAGE_SCHEMA_VERSION\n\t};\n}\n\nexport function createWidgetSetChromeMessage(mode: WidgetChromeMode): EmbedInboundMessage {\n\treturn {\n\t\ttype: 'widgetSetChrome',\n\t\tschemaVersion: EMBED_MESSAGE_SCHEMA_VERSION,\n\t\tpayload: { mode }\n\t};\n}\n\nexport function createWidgetToggleAudioSettingsMessage(): EmbedInboundMessage {\n\treturn {\n\t\ttype: 'widgetToggleAudioSettings',\n\t\tschemaVersion: EMBED_MESSAGE_SCHEMA_VERSION,\n\t\tpayload: {}\n\t};\n}\n","/** Saved inline styles so full-window can be restored cleanly. */\nexport type FullWindowStyleSnapshot = {\n\treadonly position: string;\n\treadonly top: string;\n\treadonly right: string;\n\treadonly bottom: string;\n\treadonly left: string;\n\treadonly inset: string;\n\treadonly width: string;\n\treadonly height: string;\n\treadonly zIndex: string;\n\treadonly margin: string;\n\treadonly borderRadius: string;\n\treadonly maxWidth: string;\n\treadonly maxHeight: string;\n};\n\nconst FULL_WINDOW_Z_INDEX = '2147483000';\n\nexport function snapshotFullWindowStyles(el: HTMLElement): FullWindowStyleSnapshot {\n\tconst { style } = el;\n\treturn {\n\t\tposition: style.position,\n\t\ttop: style.top,\n\t\tright: style.right,\n\t\tbottom: style.bottom,\n\t\tleft: style.left,\n\t\tinset: style.inset,\n\t\twidth: style.width,\n\t\theight: style.height,\n\t\tzIndex: style.zIndex,\n\t\tmargin: style.margin,\n\t\tborderRadius: style.borderRadius,\n\t\tmaxWidth: style.maxWidth,\n\t\tmaxHeight: style.maxHeight\n\t};\n}\n\nexport function applyFullWindowStyles(el: HTMLElement): void {\n\tconst { style } = el;\n\tstyle.position = 'fixed';\n\tstyle.top = '0';\n\tstyle.right = '0';\n\tstyle.bottom = '0';\n\tstyle.left = '0';\n\tstyle.inset = '0';\n\tstyle.width = '100vw';\n\tstyle.width = '100dvw';\n\tstyle.height = '100vh';\n\tstyle.height = '100dvh';\n\tstyle.zIndex = FULL_WINDOW_Z_INDEX;\n\tstyle.margin = '0';\n\tstyle.borderRadius = '0';\n\tstyle.maxWidth = 'none';\n\tstyle.maxHeight = 'none';\n\tel.dataset.liformaFullWindow = '1';\n}\n\nexport function restoreFullWindowStyles(\n\tel: HTMLElement,\n\tsnapshot: FullWindowStyleSnapshot | null\n): void {\n\tdelete el.dataset.liformaFullWindow;\n\tif (!snapshot) {\n\t\tel.style.position = '';\n\t\tel.style.top = '';\n\t\tel.style.right = '';\n\t\tel.style.bottom = '';\n\t\tel.style.left = '';\n\t\tel.style.inset = '';\n\t\tel.style.width = '';\n\t\tel.style.height = '';\n\t\tel.style.zIndex = '';\n\t\tel.style.margin = '';\n\t\tel.style.borderRadius = '';\n\t\tel.style.maxWidth = '';\n\t\tel.style.maxHeight = '';\n\t\treturn;\n\t}\n\tconst { style } = el;\n\tstyle.position = snapshot.position;\n\tstyle.top = snapshot.top;\n\tstyle.right = snapshot.right;\n\tstyle.bottom = snapshot.bottom;\n\tstyle.left = snapshot.left;\n\tstyle.inset = snapshot.inset;\n\tstyle.width = snapshot.width;\n\tstyle.height = snapshot.height;\n\tstyle.zIndex = snapshot.zIndex;\n\tstyle.margin = snapshot.margin;\n\tstyle.borderRadius = snapshot.borderRadius;\n\tstyle.maxWidth = snapshot.maxWidth;\n\tstyle.maxHeight = snapshot.maxHeight;\n}\n","import {\n\tassertPlayerReachable,\n\tformatPlayerUnreachableMessage,\n\tpostMessageToPlayer\n} from './playerReachability';\nimport type { CloseEvent, EmbedInboundMessage, EmbedOutboundMessage, EmbedState } from './protocol';\nimport {\n\tcreateLaunchMessage,\n\tcreateManifestMessage,\n\tcreateParentHelloMessage,\n\tcreateWidgetSetChromeMessage,\n\tcreateWidgetToggleAudioSettingsMessage,\n\tEMBED_MESSAGE_SCHEMA_VERSION,\n\tparseOutboundMessage,\n\ttype WidgetChromeMode,\n\ttype WidgetDragMoveEvent,\n\ttype WidgetDragStartEvent\n} from './protocol';\nimport {\n\tapplyFullWindowStyles,\n\trestoreFullWindowStyles,\n\tsnapshotFullWindowStyles,\n\ttype FullWindowStyleSnapshot\n} from './fullWindowHost';\nimport type {\n\tCharacterSpeechEvent,\n\tConversationMessage,\n\tSpeakBehavior,\n\tSpeechResult as LegacySpeakResult,\n\tUserSpeechBoundaryEvent,\n\tUserTranscriptUpdate,\n\tUtteranceResult\n} from '../types/sessionInteraction';\nimport type { PlayerStatus, Session, SessionActivity } from '../types/sessionLaunch';\nimport type { StartButtonOptions, PlayerTheme } from '../types/sessionInteraction';\nimport type {\n\tCompleteSpeechQueuePolicy,\n\tSpeechAudioFormat,\n\tSpeechInterruptOptions,\n\tSpeechPlayEmbedSource,\n\tSpeechResult,\n\tStreamingSpeechQueuePolicy\n} from '../speech/types';\n\nexport type IframeHostEventMap = {\n\tclose: CloseEvent;\n\tstateUpdate: EmbedState;\n\tuserTranscript: UserTranscriptUpdate;\n\tcharacterSpeechStarted: CharacterSpeechEvent;\n\tcharacterSpeechEnded: CharacterSpeechEvent;\n\tconversationUpdate: readonly ConversationMessage[];\n\tlisteningState: boolean;\n\tstarted: { readonly mode: 'conversation' | 'presenter' };\n\twidgetReady: void;\n\twidgetExpand: void;\n\twidgetCollapse: void;\n\twidgetAudioSettings: boolean;\n\twidgetDragStart: WidgetDragStartEvent;\n\twidgetDragMove: WidgetDragMoveEvent;\n\twidgetDragEnd: { readonly pointerId: number };\n};\n\nexport type IframeHostOptions = {\n\treadonly container: HTMLElement;\n\treadonly src: string;\n\treadonly playerOrigin: string;\n\t/** Transparent iframe for ExperienceWidget warm overlay over a thumb. */\n\treadonly transparent?: boolean;\n\treadonly onClose?: (event: CloseEvent) => void;\n\t/** @deprecated Prefer {@link IframeHostOptions.onPlayerStatusChange}. */\n\treadonly onStateUpdate?: (state: EmbedState) => void;\n\treadonly onPlayerStatusChange?: (status: PlayerStatus) => void;\n\treadonly onUserTranscript?: (update: UserTranscriptUpdate) => void;\n\treadonly onUserSpeechStarted?: (event: UserSpeechBoundaryEvent) => void;\n\treadonly onUserSpeechEnded?: (event: UserSpeechBoundaryEvent) => void;\n\treadonly onCharacterSpeechStarted?: (event: CharacterSpeechEvent) => void;\n\treadonly onCharacterSpeechEnded?: (event: CharacterSpeechEvent) => void;\n\treadonly onCharacterFocusChanged?: (event: {\n\t\treadonly characterId: string;\n\t\treadonly reason: 'speech' | 'api' | 'node_enter';\n\t}) => void;\n\treadonly onConversationUpdate?: (conversation: readonly ConversationMessage[]) => void;\n\treadonly onMessage?: (message: ConversationMessage) => void;\n\treadonly onListeningState?: (listening: boolean) => void;\n\treadonly onActivityChange?: (activity: SessionActivity) => void;\n\treadonly onFeedback?: (result: {\n\t\treadonly contractVersion: 'feedback.v1';\n\t\treadonly types: readonly {\n\t\t\treadonly feedbackId: string;\n\t\t\treadonly name: string;\n\t\t\treadonly criteria: readonly {\n\t\t\t\treadonly criterionId: string;\n\t\t\t\treadonly label: string;\n\t\t\t\treadonly score: number | string;\n\t\t\t\treadonly rationale: string;\n\t\t\t}[];\n\t\t}[];\n\t}) => void;\n\treadonly onFeedbackFailed?: (event: {\n\t\treadonly error: string;\n\t\treadonly model?: string;\n\t}) => void;\n\treadonly onStarted?: (event: { readonly mode: 'conversation' | 'presenter' }) => void;\n\treadonly onWidgetReady?: () => void;\n\treadonly onWidgetExpand?: () => void;\n\treadonly onWidgetCollapse?: () => void;\n\treadonly onWidgetAudioSettings?: (open: boolean) => void;\n\treadonly onWidgetDragStart?: (event: WidgetDragStartEvent) => void;\n\treadonly onWidgetDragMove?: (event: WidgetDragMoveEvent) => void;\n\treadonly onWidgetDragEnd?: (event: { readonly pointerId: number }) => void;\n\treadonly onFullWindowChange?: (active: boolean) => void;\n};\n\nfunction legacyModeToActivity(mode: 'speaking' | 'listening' | 'thinking'): SessionActivity {\n\treturn mode;\n}\n\nexport type IframeHost = {\n\twaitForLoad(): Promise<void>;\n\tpostParentHello(): void;\n\tpostLaunch(\n\t\tlaunch: string,\n\t\tsession?: Session | object,\n\t\tchrome?: {\n\t\t\treadonly startButton?: StartButtonOptions;\n\t\t\treadonly theme?: PlayerTheme;\n\t\t\treadonly showFeedback?: boolean;\n\t\t}\n\t): void;\n\t/** @deprecated Prefer {@link IframeHost.postLaunch}. */\n\tpostManifest(manifest: unknown): void;\n\tpostWidgetSetChrome(mode: WidgetChromeMode): void;\n\tpostWidgetToggleAudioSettings(): void;\n\tspeak(options: {\n\t\ttext: string;\n\t\tcharacterId?: string;\n\t\tbehavior?: SpeakBehavior;\n\t}): Promise<LegacySpeakResult>;\n\tstartListening(): Promise<void>;\n\tstopListening(): Promise<UtteranceResult>;\n\tlistenOnce(options?: { readonly timeoutMs?: number }): Promise<UtteranceResult>;\n\tspeakProcessorStream(\n\t\tstream: AsyncIterable<string>,\n\t\toptions?: {\n\t\t\treadonly characterId?: string;\n\t\t\treadonly behavior?: SpeakBehavior;\n\t\t\treadonly signal?: AbortSignal;\n\t\t}\n\t): Promise<LegacySpeakResult>;\n\tspeechPlay(options: {\n\t\tsource: SpeechPlayEmbedSource;\n\t\tcharacterId?: string;\n\t\ttranscript?: string;\n\t\thistory?: 'none' | 'commit';\n\t\tqueue?: CompleteSpeechQueuePolicy;\n\t\tsignal?: AbortSignal;\n\t}): Promise<SpeechResult>;\n\tspeechUtteranceBegin(options: {\n\t\tutteranceId: string;\n\t\tformat: SpeechAudioFormat;\n\t\tcharacterId?: string;\n\t\tqueue: StreamingSpeechQueuePolicy;\n\t\ttranscript?: string;\n\t}): Promise<void>;\n\tspeechUtteranceWrite(options: {\n\t\tutteranceId: string;\n\t\taudio: ArrayBuffer;\n\t\tbyteLength: number;\n\t}): Promise<{ readonly bufferedMs: number }>;\n\tspeechUtteranceSetTranscript(options: {\n\t\tutteranceId: string;\n\t\ttranscript: string;\n\t}): Promise<void>;\n\tspeechUtteranceClose(options: {\n\t\tutteranceId: string;\n\t\ttranscript?: string;\n\t\thistory?: 'none' | 'commit';\n\t}): Promise<SpeechResult>;\n\tspeechUtteranceCancel(options: { utteranceId: string }): Promise<SpeechResult>;\n\tspeechInterrupt(options: SpeechInterruptOptions): Promise<readonly SpeechResult[]>;\n\tgetConversation(): Promise<readonly ConversationMessage[]>;\n\tfocusCharacter(characterId: string): Promise<void>;\n\tsetFit(fit: 'full' | 'face' | 'medium'): Promise<void>;\n\tconversion(key: string): Promise<void>;\n\tdestroy(): void;\n};\n\nfunction newCorrelationId(): string {\n\tif (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n\t\treturn `cmd_${crypto.randomUUID().replace(/-/g, '').slice(0, 12)}`;\n\t}\n\treturn `cmd_${Date.now().toString(36)}${Math.random().toString(36).slice(2, 8)}`;\n}\n\nconst SPEECH_WRITE_COMMAND_TIMEOUT_MS = 30_000;\n\nexport function createIframeHost(options: IframeHostOptions): IframeHost {\n\tconst container = options.container;\n\tconst containerPosition = getComputedStyle(container).position;\n\tif (containerPosition === 'static') {\n\t\tcontainer.style.position = 'relative';\n\t}\n\n\tconst iframe = document.createElement('iframe');\n\tiframe.src = options.src;\n\tiframe.title = 'Liforma experience';\n\tiframe.setAttribute(\n\t\t'allow',\n\t\t'microphone; autoplay; clipboard-read; clipboard-write'\n\t);\n\t// Absolute fill so the player viewport matches the host box even when the host\n\t// height comes from flex or min-height (percentage height:100% often collapses).\n\tiframe.style.cssText = options.transparent\n\t\t? 'display:block;position:absolute;inset:0;width:100%;height:100%;border:0;background:transparent;'\n\t\t: 'display:block;position:absolute;inset:0;width:100%;height:100%;border:0;background:#030508;';\n\tif (options.transparent) {\n\t\tiframe.setAttribute('allowtransparency', 'true');\n\t}\n\n\tlet destroyed = false;\n\tlet fullWindowActive = false;\n\tlet fullWindowSnapshot: FullWindowStyleSnapshot | null = null;\n\tconst fullWindowTarget =\n\t\tcontainer.closest('.liforma-experience-shell') instanceof HTMLElement\n\t\t\t? (container.closest('.liforma-experience-shell') as HTMLElement)\n\t\t\t: container;\n\n\tconst setFullWindowActive = (active: boolean) => {\n\t\tif (fullWindowActive === active) return;\n\t\tfullWindowActive = active;\n\t\tif (active) {\n\t\t\tfullWindowSnapshot = snapshotFullWindowStyles(fullWindowTarget);\n\t\t\tapplyFullWindowStyles(fullWindowTarget);\n\t\t} else {\n\t\t\trestoreFullWindowStyles(fullWindowTarget, fullWindowSnapshot);\n\t\t\tfullWindowSnapshot = null;\n\t\t}\n\t\toptions.onFullWindowChange?.(active);\n\t};\n\tconst pendingCommands = new Map<\n\t\tstring,\n\t\t{\n\t\t\tresolve: (value: unknown) => void;\n\t\t\treject: (error: Error) => void;\n\t\t}\n\t>();\n\n\ttype StoredLaunch = {\n\t\treadonly launch: string;\n\t\treadonly session?: Session | object;\n\t\treadonly chrome?: {\n\t\t\treadonly startButton?: StartButtonOptions;\n\t\t\treadonly theme?: PlayerTheme;\n\t\t\treadonly showFeedback?: boolean;\n\t\t};\n\t};\n\n\tlet storedLaunch: StoredLaunch | null = null;\n\tlet loadGeneration = 0;\n\tlet lastLaunchBurstAt = 0;\n\tconst LAUNCH_BURST_COOLDOWN_MS = 250;\n\tconst launchBurstTimers: number[] = [];\n\n\tlet firstLoadResolve: (() => void) | null = null;\n\tlet firstLoadReject: ((error: Error) => void) | null = null;\n\tconst loadPromise = new Promise<void>((resolve, reject) => {\n\t\tfirstLoadResolve = resolve;\n\t\tfirstLoadReject = reject;\n\t});\n\n\tconst clearLaunchBurstTimers = () => {\n\t\tfor (const id of launchBurstTimers) window.clearTimeout(id);\n\t\tlaunchBurstTimers.length = 0;\n\t};\n\n\tconst postLaunchBurst = (stored: StoredLaunch): void => {\n\t\tif (destroyed) return;\n\t\tconst now = Date.now();\n\t\tif (lastLaunchBurstAt > 0 && now - lastLaunchBurstAt < LAUNCH_BURST_COOLDOWN_MS) {\n\t\t\treturn;\n\t\t}\n\t\tlastLaunchBurstAt = now;\n\t\tclearLaunchBurstTimers();\n\t\tconst message = createLaunchMessage(stored.launch, stored.session, stored.chrome);\n\t\tconst postOnce = () => {\n\t\t\tif (destroyed) return;\n\t\t\tconst target = iframe.contentWindow;\n\t\t\tif (!target) return;\n\t\t\tpostMessageToPlayer(target, message, options.playerOrigin);\n\t\t};\n\t\tpostOnce();\n\t\tfor (let attempt = 1; attempt <= 12; attempt += 1) {\n\t\t\tlaunchBurstTimers.push(window.setTimeout(postOnce, attempt * 75));\n\t\t}\n\t};\n\n\tconst replayStoredLaunch = (): void => {\n\t\tif (destroyed || !storedLaunch) return;\n\t\tpostLaunchBurst(storedLaunch);\n\t};\n\n\tconst onIframeLoad = () => {\n\t\tloadGeneration += 1;\n\t\tif (loadGeneration === 1) {\n\t\t\tvoid assertPlayerReachable(options.src, options.playerOrigin).then(\n\t\t\t\t() => firstLoadResolve?.(),\n\t\t\t\t(error) =>\n\t\t\t\t\tfirstLoadReject?.(error instanceof Error ? error : new Error(String(error)))\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\t// Vite / HMR reloads the player document in-place. The first-load handshake\n\t\t// already completed, so re-post launch to the new contentWindow.\n\t\tlastLaunchBurstAt = 0;\n\t\treplayStoredLaunch();\n\t};\n\tconst onIframeError = () => {\n\t\tif (loadGeneration > 0) return;\n\t\tfirstLoadReject?.(new Error(formatPlayerUnreachableMessage(options.playerOrigin)));\n\t};\n\tiframe.addEventListener('load', onIframeLoad);\n\tiframe.addEventListener('error', onIframeError);\n\n\tconst onMessage = (event: MessageEvent) => {\n\t\tif (destroyed) return;\n\t\tif (event.source !== iframe.contentWindow) return;\n\t\tif (event.origin !== options.playerOrigin) return;\n\n\t\tconst message = parseOutboundMessage(event.data);\n\t\tif (!message) return;\n\n\t\tswitch (message.type) {\n\t\t\tcase 'close':\n\t\t\t\toptions.onClose?.(message.payload);\n\t\t\t\tbreak;\n\t\t\tcase 'stateUpdate':\n\t\t\t\toptions.onPlayerStatusChange?.(message.payload.state);\n\t\t\t\toptions.onStateUpdate?.(message.payload.state);\n\t\t\t\tbreak;\n\t\t\tcase 'commandResult': {\n\t\t\t\tconst pending = pendingCommands.get(message.payload.correlationId);\n\t\t\t\tif (!pending) return;\n\t\t\t\tpendingCommands.delete(message.payload.correlationId);\n\t\t\t\tif (message.payload.ok) {\n\t\t\t\t\tpending.resolve(message.payload.result);\n\t\t\t\t} else {\n\t\t\t\t\tpending.reject(new Error(message.payload.error ?? 'Command failed.'));\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'userTranscript':\n\t\t\t\toptions.onUserTranscript?.(message.payload);\n\t\t\t\tbreak;\n\t\t\tcase 'characterSpeechStarted':\n\t\t\t\toptions.onCharacterSpeechStarted?.(message.payload);\n\t\t\t\tbreak;\n\t\t\tcase 'characterSpeechEnded':\n\t\t\t\toptions.onCharacterSpeechEnded?.(message.payload);\n\t\t\t\tbreak;\n\t\t\tcase 'characterFocusChanged':\n\t\t\t\toptions.onCharacterFocusChanged?.(message.payload);\n\t\t\t\tbreak;\n\t\t\tcase 'conversationUpdate':\n\t\t\t\toptions.onConversationUpdate?.(message.payload.conversation);\n\t\t\t\tbreak;\n\t\t\tcase 'listeningState':\n\t\t\t\toptions.onListeningState?.(message.payload.listening);\n\t\t\t\tbreak;\n\t\t\tcase 'userSpeechStarted':\n\t\t\t\toptions.onUserSpeechStarted?.(message.payload);\n\t\t\t\tbreak;\n\t\t\tcase 'userSpeechEnded':\n\t\t\t\toptions.onUserSpeechEnded?.(message.payload);\n\t\t\t\tbreak;\n\t\t\tcase 'modeChange':\n\t\t\t\toptions.onActivityChange?.(legacyModeToActivity(message.payload.mode));\n\t\t\t\tbreak;\n\t\t\tcase 'activityChange':\n\t\t\t\toptions.onActivityChange?.(message.payload.activity);\n\t\t\t\tbreak;\n\t\t\tcase 'feedback':\n\t\t\t\toptions.onFeedback?.(message.payload);\n\t\t\t\tbreak;\n\t\t\tcase 'feedbackFailed':\n\t\t\t\toptions.onFeedbackFailed?.(message.payload);\n\t\t\t\tbreak;\n\t\t\tcase 'message':\n\t\t\t\toptions.onMessage?.(message.payload);\n\t\t\t\tbreak;\n\t\t\tcase 'started':\n\t\t\t\toptions.onStarted?.(message.payload);\n\t\t\t\tbreak;\n\t\t\tcase 'embedReady':\n\t\t\t\t// Soft HMR remounts the player without an iframe `load` event.\n\t\t\t\treplayStoredLaunch();\n\t\t\t\tbreak;\n\t\t\tcase 'widgetReady':\n\t\t\t\toptions.onWidgetReady?.();\n\t\t\t\tbreak;\n\t\t\tcase 'widgetExpand':\n\t\t\t\toptions.onWidgetExpand?.();\n\t\t\t\tbreak;\n\t\t\tcase 'widgetCollapse':\n\t\t\t\toptions.onWidgetCollapse?.();\n\t\t\t\tbreak;\n\t\t\tcase 'widgetAudioSettings':\n\t\t\t\toptions.onWidgetAudioSettings?.(message.payload.open);\n\t\t\t\tbreak;\n\t\t\tcase 'widgetDragStart':\n\t\t\t\toptions.onWidgetDragStart?.(message.payload);\n\t\t\t\tbreak;\n\t\t\tcase 'widgetDragMove':\n\t\t\t\toptions.onWidgetDragMove?.(message.payload);\n\t\t\t\tbreak;\n\t\t\tcase 'widgetDragEnd':\n\t\t\t\toptions.onWidgetDragEnd?.(message.payload);\n\t\t\t\tbreak;\n\t\t\tcase 'fullWindow':\n\t\t\t\tsetFullWindowActive(message.payload.active);\n\t\t\t\tbreak;\n\t\t}\n\t};\n\n\twindow.addEventListener('message', onMessage);\n\tcontainer.replaceChildren(iframe);\n\t// Absolute iframe does not contribute to parent height — keep a usable floor when\n\t// the host only sized itself from previous in-flow content.\n\tif (container.clientHeight < 2 && !container.style.minHeight) {\n\t\tconst computedMin = getComputedStyle(container).minHeight;\n\t\tif (computedMin === '0px' || computedMin === 'auto') {\n\t\t\tcontainer.style.minHeight = '480px';\n\t\t}\n\t}\n\n\tconst postToIframe = (message: EmbedInboundMessage, transfer?: Transferable[]) => {\n\t\tif (destroyed) return;\n\t\tconst target = iframe.contentWindow;\n\t\tif (!target) {\n\t\t\tthrow new Error(formatPlayerUnreachableMessage(options.playerOrigin));\n\t\t}\n\t\tpostMessageToPlayer(target, message, options.playerOrigin, transfer);\n\t};\n\n\tconst postCommand = <T>(\n\t\tmessage: EmbedInboundMessage,\n\t\ttimeoutMs = 120_000,\n\t\ttransfer?: Transferable[]\n\t): Promise<T> => {\n\t\tconst correlationId =\n\t\t\tmessage.type !== 'parentHello' && message.payload && 'correlationId' in message.payload\n\t\t\t\t? (message.payload.correlationId as string)\n\t\t\t\t: newCorrelationId();\n\n\t\tif (message.type !== 'parentHello' && message.payload && 'correlationId' in message.payload) {\n\t\t\t// correlationId already set\n\t\t} else if (message.type !== 'parentHello' && message.payload) {\n\t\t\t(message.payload as { correlationId: string }).correlationId = correlationId;\n\t\t}\n\n\t\treturn new Promise<T>((resolve, reject) => {\n\t\t\tpendingCommands.set(correlationId, {\n\t\t\t\tresolve: (value) => resolve(value as T),\n\t\t\t\treject\n\t\t\t});\n\t\t\tpostToIframe(message, transfer);\n\t\t\tsetTimeout(() => {\n\t\t\t\tif (!pendingCommands.has(correlationId)) return;\n\t\t\t\tpendingCommands.delete(correlationId);\n\t\t\t\treject(new Error('Timed out waiting for player command result.'));\n\t\t\t}, timeoutMs);\n\t\t});\n\t};\n\n\treturn {\n\t\twaitForLoad() {\n\t\t\treturn loadPromise;\n\t\t},\n\t\tpostParentHello() {\n\t\t\tpostToIframe(createParentHelloMessage());\n\t\t},\n\t\tpostLaunch(launch, session, chrome) {\n\t\t\tif (destroyed) return;\n\t\t\tstoredLaunch = { launch, session, chrome };\n\t\t\tlastLaunchBurstAt = 0;\n\t\t\tpostLaunchBurst(storedLaunch);\n\t\t},\n\t\tpostManifest(manifest) {\n\t\t\tif (typeof manifest === 'string') {\n\t\t\t\tthis.postLaunch(manifest);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (\n\t\t\t\tmanifest &&\n\t\t\t\ttypeof manifest === 'object' &&\n\t\t\t\ttypeof (manifest as { launch?: unknown }).launch === 'string'\n\t\t\t) {\n\t\t\t\tconst launch = String((manifest as { launch: string }).launch);\n\t\t\t\tconst session = (manifest as { session?: object }).session;\n\t\t\t\tthis.postLaunch(launch, session);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthrow new Error(\n\t\t\t\t'postManifest is deprecated; use postLaunch with an opaque launch string from SessionLaunchResponse.'\n\t\t\t);\n\t\t},\n\t\tpostWidgetSetChrome(mode) {\n\t\t\tpostToIframe(createWidgetSetChromeMessage(mode));\n\t\t},\n\t\tpostWidgetToggleAudioSettings() {\n\t\t\tpostToIframe(createWidgetToggleAudioSettingsMessage());\n\t\t},\n\t\tspeak(options) {\n\t\t\tconst correlationId = newCorrelationId();\n\t\t\treturn postCommand<LegacySpeakResult>({\n\t\t\t\ttype: 'speak',\n\t\t\t\tschemaVersion: EMBED_MESSAGE_SCHEMA_VERSION,\n\t\t\t\tpayload: {\n\t\t\t\t\tcorrelationId,\n\t\t\t\t\ttext: options.text,\n\t\t\t\t\t...(options.characterId ? { characterId: options.characterId } : {}),\n\t\t\t\t\t...(options.behavior ? { behavior: options.behavior } : {})\n\t\t\t\t}\n\t\t\t});\n\t\t},\n\t\tstartListening() {\n\t\t\tconst correlationId = newCorrelationId();\n\t\t\treturn postCommand<void>({\n\t\t\t\ttype: 'startListening',\n\t\t\t\tschemaVersion: EMBED_MESSAGE_SCHEMA_VERSION,\n\t\t\t\tpayload: { correlationId }\n\t\t\t});\n\t\t},\n\t\tstopListening() {\n\t\t\tconst correlationId = newCorrelationId();\n\t\t\treturn postCommand<UtteranceResult>({\n\t\t\t\ttype: 'stopListening',\n\t\t\t\tschemaVersion: EMBED_MESSAGE_SCHEMA_VERSION,\n\t\t\t\tpayload: { correlationId }\n\t\t\t});\n\t\t},\n\t\tlistenOnce(options) {\n\t\t\tconst correlationId = newCorrelationId();\n\t\t\treturn postCommand<UtteranceResult>({\n\t\t\t\ttype: 'listenOnce',\n\t\t\t\tschemaVersion: EMBED_MESSAGE_SCHEMA_VERSION,\n\t\t\t\tpayload: {\n\t\t\t\t\tcorrelationId,\n\t\t\t\t\t...(options?.timeoutMs !== undefined ? { timeoutMs: options.timeoutMs } : {})\n\t\t\t\t}\n\t\t\t});\n\t\t},\n\t\tasync speakProcessorStream(stream, options) {\n\t\t\tconst startCorrelationId = newCorrelationId();\n\t\t\tconst endCorrelationId = newCorrelationId();\n\t\t\tconst started = await postCommand<{ streamId: string }>({\n\t\t\t\ttype: 'speakStreamStart',\n\t\t\t\tschemaVersion: EMBED_MESSAGE_SCHEMA_VERSION,\n\t\t\t\tpayload: {\n\t\t\t\t\tcorrelationId: startCorrelationId,\n\t\t\t\t\t...(options?.characterId ? { characterId: options.characterId } : {}),\n\t\t\t\t\t...(options?.behavior ? { behavior: options.behavior } : {})\n\t\t\t\t}\n\t\t\t});\n\t\t\tconst streamId = started.streamId;\n\t\t\ttry {\n\t\t\t\tfor await (const chunk of stream) {\n\t\t\t\t\tif (options?.signal?.aborted) {\n\t\t\t\t\t\tthrow new DOMException('Processor stream aborted.', 'AbortError');\n\t\t\t\t\t}\n\t\t\t\t\tif (!chunk) continue;\n\t\t\t\t\tpostToIframe({\n\t\t\t\t\t\ttype: 'speakStreamChunk',\n\t\t\t\t\t\tschemaVersion: EMBED_MESSAGE_SCHEMA_VERSION,\n\t\t\t\t\t\tpayload: { streamId, text: chunk }\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tif (options?.signal?.aborted) {\n\t\t\t\t\tthrow new DOMException('Processor stream aborted.', 'AbortError');\n\t\t\t\t}\n\t\t\t\treturn await postCommand<LegacySpeakResult>({\n\t\t\t\t\ttype: 'speakStreamEnd',\n\t\t\t\t\tschemaVersion: EMBED_MESSAGE_SCHEMA_VERSION,\n\t\t\t\t\tpayload: { correlationId: endCorrelationId, streamId }\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\ttry {\n\t\t\t\t\tawait postCommand<LegacySpeakResult>({\n\t\t\t\t\t\ttype: 'speakStreamEnd',\n\t\t\t\t\t\tschemaVersion: EMBED_MESSAGE_SCHEMA_VERSION,\n\t\t\t\t\t\tpayload: { correlationId: endCorrelationId, streamId }\n\t\t\t\t\t});\n\t\t\t\t} catch {\n\t\t\t\t\t/* stream may already be torn down */\n\t\t\t\t}\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t},\n\t\tspeechPlay(options) {\n\t\t\tconst correlationId = newCorrelationId();\n\t\t\tconst transfer: Transferable[] = [];\n\t\t\tif (options.source.kind === 'pcm' || options.source.kind === 'encoded') {\n\t\t\t\ttransfer.push(options.source.data);\n\t\t\t}\n\t\t\treturn postCommand<SpeechResult>(\n\t\t\t\t{\n\t\t\t\t\ttype: 'speechPlay',\n\t\t\t\t\tschemaVersion: EMBED_MESSAGE_SCHEMA_VERSION,\n\t\t\t\t\tpayload: {\n\t\t\t\t\t\tcorrelationId,\n\t\t\t\t\t\tsource: options.source,\n\t\t\t\t\t\t...(options.characterId ? { characterId: options.characterId } : {}),\n\t\t\t\t\t\t...(options.transcript !== undefined ? { transcript: options.transcript } : {}),\n\t\t\t\t\t\t...(options.history ? { history: options.history } : {}),\n\t\t\t\t\t\t...(options.queue ? { queue: options.queue } : {})\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t120_000,\n\t\t\t\ttransfer\n\t\t\t);\n\t\t},\n\t\tspeechUtteranceBegin(options) {\n\t\t\tconst correlationId = newCorrelationId();\n\t\t\treturn postCommand<void>({\n\t\t\t\ttype: 'speechUtteranceBegin',\n\t\t\t\tschemaVersion: EMBED_MESSAGE_SCHEMA_VERSION,\n\t\t\t\tpayload: {\n\t\t\t\t\tcorrelationId,\n\t\t\t\t\tutteranceId: options.utteranceId,\n\t\t\t\t\tformat: options.format,\n\t\t\t\t\tqueue: options.queue,\n\t\t\t\t\t...(options.characterId ? { characterId: options.characterId } : {}),\n\t\t\t\t\t...(options.transcript !== undefined ? { transcript: options.transcript } : {})\n\t\t\t\t}\n\t\t\t});\n\t\t},\n\t\tspeechUtteranceWrite(options) {\n\t\t\tconst correlationId = newCorrelationId();\n\t\t\treturn postCommand<{ bufferedMs: number }>(\n\t\t\t\t{\n\t\t\t\t\ttype: 'speechUtteranceWrite',\n\t\t\t\t\tschemaVersion: EMBED_MESSAGE_SCHEMA_VERSION,\n\t\t\t\t\tpayload: {\n\t\t\t\t\t\tcorrelationId,\n\t\t\t\t\t\tutteranceId: options.utteranceId,\n\t\t\t\t\t\taudio: options.audio,\n\t\t\t\t\t\tbyteLength: options.byteLength\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tSPEECH_WRITE_COMMAND_TIMEOUT_MS,\n\t\t\t\t[options.audio]\n\t\t\t);\n\t\t},\n\t\tspeechUtteranceSetTranscript(options) {\n\t\t\tconst correlationId = newCorrelationId();\n\t\t\treturn postCommand<void>({\n\t\t\t\ttype: 'speechUtteranceSetTranscript',\n\t\t\t\tschemaVersion: EMBED_MESSAGE_SCHEMA_VERSION,\n\t\t\t\tpayload: {\n\t\t\t\t\tcorrelationId,\n\t\t\t\t\tutteranceId: options.utteranceId,\n\t\t\t\t\ttranscript: options.transcript\n\t\t\t\t}\n\t\t\t});\n\t\t},\n\t\tspeechUtteranceClose(options) {\n\t\t\tconst correlationId = newCorrelationId();\n\t\t\treturn postCommand<SpeechResult>({\n\t\t\t\ttype: 'speechUtteranceClose',\n\t\t\t\tschemaVersion: EMBED_MESSAGE_SCHEMA_VERSION,\n\t\t\t\tpayload: {\n\t\t\t\t\tcorrelationId,\n\t\t\t\t\tutteranceId: options.utteranceId,\n\t\t\t\t\t...(options.transcript !== undefined ? { transcript: options.transcript } : {}),\n\t\t\t\t\t...(options.history ? { history: options.history } : {})\n\t\t\t\t}\n\t\t\t});\n\t\t},\n\t\tspeechUtteranceCancel(options) {\n\t\t\tconst correlationId = newCorrelationId();\n\t\t\treturn postCommand<SpeechResult>({\n\t\t\t\ttype: 'speechUtteranceCancel',\n\t\t\t\tschemaVersion: EMBED_MESSAGE_SCHEMA_VERSION,\n\t\t\t\tpayload: {\n\t\t\t\t\tcorrelationId,\n\t\t\t\t\tutteranceId: options.utteranceId\n\t\t\t\t}\n\t\t\t});\n\t\t},\n\t\tspeechInterrupt(options) {\n\t\t\tconst correlationId = newCorrelationId();\n\t\t\treturn postCommand<readonly SpeechResult[]>({\n\t\t\t\ttype: 'speechInterrupt',\n\t\t\t\tschemaVersion: EMBED_MESSAGE_SCHEMA_VERSION,\n\t\t\t\tpayload: {\n\t\t\t\t\tcorrelationId,\n\t\t\t\t\t...options\n\t\t\t\t}\n\t\t\t});\n\t\t},\n\t\tgetConversation() {\n\t\t\tconst correlationId = newCorrelationId();\n\t\t\treturn postCommand<readonly ConversationMessage[]>({\n\t\t\t\ttype: 'getConversation',\n\t\t\t\tschemaVersion: EMBED_MESSAGE_SCHEMA_VERSION,\n\t\t\t\tpayload: { correlationId }\n\t\t\t});\n\t\t},\n\t\tfocusCharacter(characterId) {\n\t\t\tconst correlationId = newCorrelationId();\n\t\t\treturn postCommand<void>({\n\t\t\t\ttype: 'focusCharacter',\n\t\t\t\tschemaVersion: EMBED_MESSAGE_SCHEMA_VERSION,\n\t\t\t\tpayload: { correlationId, characterId }\n\t\t\t});\n\t\t},\n\t\tsetFit(fit) {\n\t\t\tconst correlationId = newCorrelationId();\n\t\t\treturn postCommand<void>({\n\t\t\t\ttype: 'setFit',\n\t\t\t\tschemaVersion: EMBED_MESSAGE_SCHEMA_VERSION,\n\t\t\t\tpayload: { correlationId, fit }\n\t\t\t});\n\t\t},\n\t\tconversion(key) {\n\t\t\tconst correlationId = newCorrelationId();\n\t\t\treturn postCommand<void>({\n\t\t\t\ttype: 'conversion',\n\t\t\t\tschemaVersion: EMBED_MESSAGE_SCHEMA_VERSION,\n\t\t\t\tpayload: { correlationId, key }\n\t\t\t});\n\t\t},\n\t\tdestroy() {\n\t\t\tif (destroyed) return;\n\t\t\tdestroyed = true;\n\t\t\tif (fullWindowActive) {\n\t\t\t\tsetFullWindowActive(false);\n\t\t\t}\n\t\t\tfor (const pending of pendingCommands.values()) {\n\t\t\t\tpending.reject(new Error('Iframe host destroyed.'));\n\t\t\t}\n\t\t\tpendingCommands.clear();\n\t\t\tclearLaunchBurstTimers();\n\t\t\tstoredLaunch = null;\n\t\t\tiframe.removeEventListener('load', onIframeLoad);\n\t\t\tiframe.removeEventListener('error', onIframeError);\n\t\t\twindow.removeEventListener('message', onMessage);\n\t\t\tiframe.remove();\n\t\t}\n\t};\n}\n\n","export type ReadyGate<T> = {\n\tsetValue(value: T): void;\n\tmarkReady(): void;\n};\n\n/** Emits once after both an asynchronous value and readiness signal are available. */\nexport function createReadyGate<T>(onReady: (value: T) => void): ReadyGate<T> {\n\tlet value: T | undefined;\n\tlet ready = false;\n\tlet emitted = false;\n\n\tconst emitIfPossible = (): void => {\n\t\tif (emitted || !ready || value === undefined) return;\n\t\temitted = true;\n\t\tonReady(value);\n\t};\n\n\treturn {\n\t\tsetValue(nextValue) {\n\t\t\tvalue = nextValue;\n\t\t\temitIfPossible();\n\t\t},\n\t\tmarkReady() {\n\t\t\tready = true;\n\t\t\temitIfPossible();\n\t\t}\n\t};\n}\n","import { useExplicitLocalStack } from '../../shims/localStack';\n\nconst PRODUCTION_PLAYER_EMBED_URL = 'https://player.liforma.ai/embed';\nconst LOCAL_PLAYER_EMBED_URL = 'http://localhost:3002/embed';\n\nfunction readConfiguredEmbedUrl(): string | null {\n\tconst globalValue = (globalThis as { __LIFORMA_PLAYER_EMBED_URL?: string })\n\t\t.__LIFORMA_PLAYER_EMBED_URL;\n\tif (globalValue?.trim()) {\n\t\treturn globalValue.trim().replace(/\\/$/, '');\n\t}\n\n\tif (typeof document === 'undefined') {\n\t\treturn null;\n\t}\n\n\tconst script =\n\t\tdocument.querySelector('script[data-liforma-sdk][src*=\"client.js\"]') ??\n\t\tdocument.querySelector('script[src*=\"/sdk/v2/client.js\"]');\n\tconst fromScript = script?.getAttribute('data-player-embed-url')?.trim();\n\tif (fromScript) {\n\t\treturn fromScript.replace(/\\/$/, '');\n\t}\n\n\treturn null;\n}\n\n/**\n * Hosted player embed document URL (iframe `src` base).\n *\n * Defaults to production. Local stack is opt-in only — never inferred from\n * `localhost` hostname (third-party demos run on localhost against production).\n *\n * Override order:\n * 1. `window.__LIFORMA_PLAYER_EMBED_URL`\n * 2. `data-player-embed-url` on the SDK script tag\n * 3. `window.__LIFORMA_STACK === 'local'` or host `?stack=local` → http://localhost:3002/embed\n * 4. production → https://player.liforma.ai/embed\n */\nexport function resolvePublicPlayerEmbedUrl(): string {\n\tconst configured = readConfiguredEmbedUrl();\n\tif (configured) {\n\t\treturn configured.endsWith('/embed') ? configured : `${configured}/embed`;\n\t}\n\n\tif (useExplicitLocalStack()) {\n\t\treturn LOCAL_PLAYER_EMBED_URL;\n\t}\n\n\treturn PRODUCTION_PLAYER_EMBED_URL;\n}\n\nexport function resolveSceneHintId(backdropId?: string, locationId?: string): string | undefined {\n\tconst id = backdropId?.trim() || locationId?.trim();\n\treturn id || undefined;\n}\n\nexport function buildManifestEmbedUrl(\n\tembedBaseUrl?: string,\n\tdebug?: boolean,\n\tbackdropId?: string,\n\tavatarId?: string,\n\tfit?: 'full' | 'face' | 'medium',\n\tui?: 'widget',\n\tshowClose?: boolean,\n\tcastSize?: 2 | 3 | 4\n): string {\n\tconst base = (embedBaseUrl ?? resolvePublicPlayerEmbedUrl()).replace(/\\/$/, '');\n\tconst embedRoot = base.endsWith('/embed') ? base : `${base}/embed`;\n\tconst url = new URL(embedRoot);\n\tconst loc = backdropId?.trim();\n\tif (loc) {\n\t\turl.searchParams.set('backdropId', loc);\n\t\turl.searchParams.set('locationId', loc);\n\t}\n\tconst avatar = avatarId?.trim();\n\tif (avatar) {\n\t\turl.searchParams.set('avatarId', avatar);\n\t}\n\tif (fit === 'face' || fit === 'medium') {\n\t\turl.searchParams.set('fit', fit);\n\t}\n\tif (ui === 'widget') {\n\t\turl.searchParams.set('ui', 'widget');\n\t}\n\tif (showClose) {\n\t\turl.searchParams.set('close', '1');\n\t}\n\tif (debug) {\n\t\turl.searchParams.set('debug', 'true');\n\t}\n\tif (castSize === 2 || castSize === 3 || castSize === 4) {\n\t\turl.searchParams.set('cast', String(castSize));\n\t}\n\treturn withEmbedParentOrigin(url.toString());\n}\n\nexport function buildPublicEmbedUrl(\n\texperienceId: string,\n\tembedBaseUrl?: string,\n\tlanguage?: 'en' | 'es',\n\tbackdropId?: string,\n\tavatarId?: string,\n\tdebug?: boolean\n): string {\n\tconst base = (embedBaseUrl ?? resolvePublicPlayerEmbedUrl()).replace(/\\/$/, '');\n\tconst embedRoot = base.endsWith('/embed') ? base : `${base}/embed`;\n\tconst url = new URL(embedRoot);\n\turl.searchParams.set('experienceId', experienceId);\n\tif (language && language !== 'en') {\n\t\turl.searchParams.set('language', language);\n\t}\n\tconst loc = backdropId?.trim();\n\tif (loc) {\n\t\turl.searchParams.set('backdropId', loc);\n\t\turl.searchParams.set('locationId', loc);\n\t}\n\tconst avatar = avatarId?.trim();\n\tif (avatar) {\n\t\turl.searchParams.set('avatarId', avatar);\n\t}\n\tif (debug) {\n\t\turl.searchParams.set('debug', 'true');\n\t}\n\treturn withEmbedParentOrigin(url.toString());\n}\n\n/** Tell the player embed which origin may receive postMessage events (SDK parent page). */\nexport function withEmbedParentOrigin(embedUrl: string): string {\n\tif (typeof location === 'undefined') return embedUrl;\n\tconst url = new URL(embedUrl);\n\turl.searchParams.set('parentOrigin', location.origin);\n\treturn url.toString();\n}\n\nexport function resolvePlayerOrigin(embedUrl: string): string {\n\treturn new URL(embedUrl).origin;\n}\n","import type { ReadyEvent } from './experience';\nimport type {\n\tCharacterSpeechEvent,\n\tConversationMessage,\n\tConversationProcessorErrorEvent,\n\tStartedEvent,\n\tUserSpeechBoundaryEvent,\n\tUserTranscriptUpdate\n} from './types/sessionInteraction';\nimport type { SessionActivity } from './types/sessionLaunch';\n\nexport type FeedbackResult = {\n\treadonly contractVersion: 'feedback.v1';\n\treadonly types: readonly {\n\t\treadonly feedbackId: string;\n\t\treadonly name: string;\n\t\treadonly criteria: readonly {\n\t\t\treadonly criterionId: string;\n\t\t\treadonly label: string;\n\t\t\treadonly score: number | string;\n\t\t\treadonly rationale: string;\n\t\t}[];\n\t}[];\n};\n\nexport type FeedbackFailedEvent = {\n\treadonly error: string;\n\treadonly model?: string;\n};\n\nexport type ExperienceEventEnvelope<T = unknown> = {\n\treadonly id: string;\n\treadonly type: string;\n\treadonly sessionId: string;\n\treadonly timestamp: string;\n\treadonly data: T;\n};\n\nfunction newEventId(): string {\n\tif (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n\t\treturn `evt_${crypto.randomUUID().replace(/-/g, '').slice(0, 12)}`;\n\t}\n\treturn `evt_${Date.now().toString(36)}${Math.random().toString(36).slice(2, 8)}`;\n}\n\nexport function createExperienceEventEnvelope<T>(\n\ttype: string,\n\tsessionId: string,\n\tdata: T\n): ExperienceEventEnvelope<T> {\n\treturn {\n\t\tid: newEventId(),\n\t\ttype,\n\t\tsessionId,\n\t\ttimestamp: new Date().toISOString(),\n\t\tdata\n\t};\n}\n\nexport type ExperienceEvents = {\n\treadonly ready: ExperienceEventEnvelope<ReadyEvent>;\n\treadonly started: ExperienceEventEnvelope<StartedEvent>;\n\treadonly userTranscript: ExperienceEventEnvelope<UserTranscriptUpdate>;\n\treadonly userSpeechStarted: ExperienceEventEnvelope<UserSpeechBoundaryEvent>;\n\treadonly userSpeechEnded: ExperienceEventEnvelope<UserSpeechBoundaryEvent>;\n\treadonly characterSpeechStarted: ExperienceEventEnvelope<CharacterSpeechEvent>;\n\treadonly characterSpeechEnded: ExperienceEventEnvelope<CharacterSpeechEvent>;\n\treadonly characterFocusChanged: ExperienceEventEnvelope<{\n\t\treadonly characterId: string;\n\t\treadonly reason: 'speech' | 'api' | 'node_enter';\n\t}>;\n\treadonly conversationUpdate: ExperienceEventEnvelope<readonly ConversationMessage[]>;\n\treadonly message: ExperienceEventEnvelope<ConversationMessage>;\n\treadonly listeningState: ExperienceEventEnvelope<boolean>;\n\treadonly activityChange: ExperienceEventEnvelope<SessionActivity>;\n\t/** End-of-session Feedback scores (`feedback.v1`). Emitted when scoring succeeds. */\n\treadonly feedback: ExperienceEventEnvelope<FeedbackResult>;\n\t/** End-of-session Feedback scoring failed. */\n\treadonly feedbackFailed: ExperienceEventEnvelope<FeedbackFailedEvent>;\n\treadonly conversationProcessorError: ExperienceEventEnvelope<ConversationProcessorErrorEvent>;\n};\n\n/**\n * Reserved integrator event name: `backdropChange`.\n * The hosted player does not emit `locationChange` or `backdropChange` today.\n * Do not invent a client-side fake until the runtime actually changes scene mid-session.\n */\n\nexport type ExperienceEventName = keyof ExperienceEvents;\n\ntype HandlerSet<K extends ExperienceEventName> = Set<(event: ExperienceEvents[K]) => void>;\n\nexport type ExperienceEventRegistry = {\n\treadonly [K in ExperienceEventName]: HandlerSet<K>;\n};\n\nexport function createExperienceEventRegistry(): ExperienceEventRegistry {\n\treturn {\n\t\tready: new Set(),\n\t\tstarted: new Set(),\n\t\tuserTranscript: new Set(),\n\t\tuserSpeechStarted: new Set(),\n\t\tuserSpeechEnded: new Set(),\n\t\tcharacterSpeechStarted: new Set(),\n\t\tcharacterSpeechEnded: new Set(),\n\t\tcharacterFocusChanged: new Set(),\n\t\tconversationUpdate: new Set(),\n\t\tmessage: new Set(),\n\t\tlisteningState: new Set(),\n\t\tactivityChange: new Set(),\n\t\tfeedback: new Set(),\n\t\tfeedbackFailed: new Set(),\n\t\tconversationProcessorError: new Set()\n\t};\n}\n\nexport function subscribeExperienceEvent<K extends ExperienceEventName>(\n\tregistry: ExperienceEventRegistry,\n\tevent: K,\n\thandler: (payload: ExperienceEvents[K]) => void\n): () => void {\n\tregistry[event].add(handler);\n\treturn () => registry[event].delete(handler);\n}\n\nexport function emitExperienceEvent<K extends ExperienceEventName>(\n\tregistry: ExperienceEventRegistry,\n\tevent: K,\n\tpayload: ExperienceEvents[K]\n): void {\n\tfor (const handler of registry[event]) {\n\t\thandler(payload);\n\t}\n}\n\nexport function clearExperienceEventRegistry(registry: ExperienceEventRegistry): void {\n\tfor (const key of Object.keys(registry) as ExperienceEventName[]) {\n\t\tregistry[key].clear();\n\t}\n}\n","/**\n * Phase A public speech contract for Experience.speech\n * (docs/plans/2026/08/experience-play-audio-api.plan.md).\n */\n\nexport type SpeechAudioFormat = {\n\treadonly encoding: 'pcm_s16le';\n\treadonly sampleRate: number;\n\treadonly channels: 1;\n};\n\n/** Complete one-shot ops only (`speak` / `play`). */\nexport type CompleteSpeechQueuePolicy = 'append' | 'replace-active' | 'replace-all';\n\n/** Live streams only — `append` is forbidden. */\nexport type StreamingSpeechQueuePolicy = 'replace-active' | 'replace-all';\n\nexport type SpeechTerminalStatus = 'completed' | 'interrupted' | 'cancelled' | 'destroyed';\n\nexport type SpeechAnimationMode = 'tracked' | 'partially-tracked' | 'energy-fallback' | 'none';\n\nexport type SpeechResult = {\n\treadonly utteranceId: string;\n\treadonly characterId: string;\n\treadonly status: SpeechTerminalStatus;\n\treadonly durationMs: number;\n\treadonly transcript?: string;\n\treadonly animation: {\n\t\treadonly mode: SpeechAnimationMode;\n\t\treadonly trackedDurationMs: number;\n\t\treadonly fallbackDurationMs: number;\n\t\treadonly warningCodes?: readonly string[];\n\t};\n};\n\nexport type SpeechInterruptOptions =\n\t| { readonly utteranceId: string }\n\t| { readonly characterId: string }\n\t| { readonly scope?: 'active' | 'all' };\n\nexport type SpeechSpeakOptions = {\n\treadonly text: string;\n\treadonly characterId?: string;\n\treadonly queue?: CompleteSpeechQueuePolicy;\n\treadonly signal?: AbortSignal;\n};\n\n/** Raw PCM one-shot (Phase A). */\nexport type SpeechPlayPcmAudio = {\n\treadonly data: ArrayBuffer | Uint8Array;\n\treadonly format: SpeechAudioFormat;\n};\n\n/**\n * Encoded media bytes (wav/mp3/…). Player decodes via Web Audio — not api/models.\n * `encoding` is a MIME type such as `audio/mpeg` or `audio/wav`.\n */\nexport type SpeechPlayEncodedAudio = {\n\treadonly data: ArrayBuffer | Uint8Array | Blob;\n\treadonly encoding: string;\n\t/** Preferred pcm sample rate after decode (default 24000 when decoded rate is unsupported). */\n\treadonly sampleRate?: number;\n};\n\n/** Fetch + decode in the player (URL must allow CORS from player.liforma.ai). */\nexport type SpeechPlayUrlAudio = {\n\treadonly url: string;\n\treadonly sampleRate?: number;\n};\n\n/**\n * Live audio track. Captured to PCM in the **host** SDK (not transferable across the embed).\n * Prefer `createUtterance({ track })` for long streams; `play({ audio: { track } })` runs until\n * the track ends then closes the utterance.\n */\nexport type SpeechPlayMediaStreamAudio = {\n\treadonly track: MediaStreamTrack;\n\t/** Target pcm sample rate after capture (default 24000). */\n\treadonly sampleRate?: number;\n};\n\nexport type SpeechPlayAudio =\n\t| SpeechPlayPcmAudio\n\t| SpeechPlayEncodedAudio\n\t| SpeechPlayUrlAudio\n\t| SpeechPlayMediaStreamAudio;\n\nexport type SpeechPlayOptions = {\n\treadonly audio: SpeechPlayAudio;\n\treadonly characterId?: string;\n\treadonly transcript?: string;\n\treadonly history?: 'none' | 'commit';\n\treadonly queue?: CompleteSpeechQueuePolicy;\n\treadonly signal?: AbortSignal;\n};\n\n/** Embed / player wire format for {@link SpeechPlayOptions.audio}. */\nexport type SpeechPlayEmbedSource =\n\t| {\n\t\t\treadonly kind: 'pcm';\n\t\t\treadonly data: ArrayBuffer;\n\t\t\treadonly format: SpeechAudioFormat;\n\t }\n\t| {\n\t\t\treadonly kind: 'encoded';\n\t\t\treadonly data: ArrayBuffer;\n\t\t\treadonly encoding: string;\n\t\t\treadonly sampleRate?: number;\n\t }\n\t| {\n\t\t\treadonly kind: 'url';\n\t\t\treadonly url: string;\n\t\t\treadonly sampleRate?: number;\n\t };\n\nexport function isSpeechPlayPcmAudio(audio: SpeechPlayAudio): audio is SpeechPlayPcmAudio {\n\treturn (\n\t\t'data' in audio &&\n\t\t'format' in audio &&\n\t\t(audio as SpeechPlayPcmAudio).format != null &&\n\t\t!('track' in audio)\n\t);\n}\n\nexport function isSpeechPlayUrlAudio(audio: SpeechPlayAudio): audio is SpeechPlayUrlAudio {\n\treturn 'url' in audio && typeof (audio as SpeechPlayUrlAudio).url === 'string';\n}\n\nexport function isSpeechPlayMediaStreamAudio(\n\taudio: SpeechPlayAudio\n): audio is SpeechPlayMediaStreamAudio {\n\tif (!('track' in audio)) return false;\n\tconst track = (audio as SpeechPlayMediaStreamAudio).track as unknown;\n\tif (track == null || typeof track !== 'object') return false;\n\tif (typeof MediaStreamTrack !== 'undefined' && track instanceof MediaStreamTrack) {\n\t\treturn true;\n\t}\n\t// Duck-type for non-DOM / test environments.\n\treturn 'kind' in track && 'readyState' in track;\n}\n\nexport function isSpeechPlayEncodedAudio(audio: SpeechPlayAudio): audio is SpeechPlayEncodedAudio {\n\treturn (\n\t\t!isSpeechPlayPcmAudio(audio) &&\n\t\t!isSpeechPlayUrlAudio(audio) &&\n\t\t!isSpeechPlayMediaStreamAudio(audio) &&\n\t\t'encoding' in audio &&\n\t\ttypeof (audio as SpeechPlayEncodedAudio).encoding === 'string'\n\t);\n}\n\nexport type SpeechCreateUtteranceOptions = {\n\t/**\n\t * Required unless `track` is set. When `track` is provided without `format`, defaults to\n\t * pcm_s16le mono at `sampleRate` (or 24000).\n\t */\n\treadonly format?: SpeechAudioFormat;\n\t/** Optional live track — host captures and writes until the track ends. */\n\treadonly track?: MediaStreamTrack;\n\t/** Used with `track` when `format` is omitted (default 24000). */\n\treadonly sampleRate?: number;\n\treadonly characterId?: string;\n\treadonly queue?: StreamingSpeechQueuePolicy;\n\t/**\n\t * Optional spoken text seed for force-aligned STA (lip sync).\n\t * Non-empty transcript enables force-align for this utterance; omit / whitespace → free CTC.\n\t * May be updated mid-turn via {@link SpeechUtterance.setTranscript} or finalized on\n\t * {@link SpeechUtterance.close} `{ transcript }`.\n\t */\n\treadonly transcript?: string;\n\treadonly signal?: AbortSignal;\n};\n\nexport type SpeechUtteranceCloseOptions = {\n\treadonly transcript?: string;\n\treadonly history?: 'none' | 'commit';\n};\n\nexport type SpeechWriteOptions = {\n\treadonly timeoutMs?: number;\n};\n\nexport type SpeechUtterance = {\n\treadonly id: string;\n\treadonly characterId: string;\n\treadonly result: Promise<SpeechResult>;\n\twrite(audio: ArrayBuffer | Uint8Array, options?: SpeechWriteOptions): Promise<void>;\n\t/**\n\t * Replace / set spoken text mid-turn for force-aligned STA.\n\t * Non-empty text upgrades free decode → force-align (full-utterance on close).\n\t * Empty / whitespace is a no-op.\n\t */\n\tsetTranscript(text: string): Promise<void>;\n\tclose(options?: SpeechUtteranceCloseOptions): Promise<SpeechResult>;\n\tcancel(): Promise<SpeechResult>;\n};\n\nexport type SpeechController = {\n\tspeak(options: SpeechSpeakOptions): Promise<SpeechResult>;\n\tplay(options: SpeechPlayOptions): Promise<SpeechResult>;\n\tcreateUtterance(options: SpeechCreateUtteranceOptions): SpeechUtterance;\n\tinterrupt(options?: SpeechInterruptOptions): Promise<readonly SpeechResult[]>;\n};\n\n/** Accepted PCM sample rates (Phase A). */\nexport const SPEECH_ACCEPTED_SAMPLE_RATES = [\n\t8000, 16000, 22050, 24000, 44100, 48000\n] as const;\n\nexport const SPEECH_LIMITS = {\n\tMAX_CHUNK_BYTES: 64 * 1024,\n\tRECOMMENDED_CHUNK_MS_MIN: 20,\n\tRECOMMENDED_CHUNK_MS_MAX: 200,\n\tMAX_BUFFERED_AUDIO_MS: 8_000,\n\tPLAYBACK_JITTER_BUFFER_MS: 150,\n\tPLAYBACK_JITTER_BUFFER_MS_MIN: 80,\n\tPLAYBACK_JITTER_BUFFER_MS_MAX: 250,\n\tMAX_UTTERANCE_DURATION_MS: 90_000,\n\tMAX_PENDING_WRITES: 8,\n\tDEFAULT_WRITE_TIMEOUT_MS: 5_000\n} as const;\n\nexport class UnsupportedSpeechOperationError extends Error {\n\treadonly name = 'UnsupportedSpeechOperationError';\n\tconstructor(\n\t\treadonly operation: string,\n\t\treadonly requiredCapability: string\n\t) {\n\t\tsuper(`Speech operation \"${operation}\" requires capability \"${requiredCapability}\".`);\n\t}\n}\n\nexport class SpeechUtteranceClosedError extends Error {\n\treadonly name = 'SpeechUtteranceClosedError';\n\tconstructor(message = 'Cannot write to a closed or cancelled speech utterance.') {\n\t\tsuper(message);\n\t}\n}\n\nexport class SpeechRuntimeError extends Error {\n\treadonly name = 'SpeechRuntimeError';\n\tconstructor(message: string, options?: { cause?: unknown }) {\n\t\tsuper(message, options);\n\t}\n}\n\nexport class SpeechBufferTimeoutError extends Error {\n\treadonly name = 'SpeechBufferTimeoutError';\n\tconstructor(message = 'Timed out waiting for speech buffer capacity.') {\n\t\tsuper(message);\n\t}\n}\n\nexport function emptyAnimationResult(\n\tmode: SpeechAnimationMode = 'none'\n): SpeechResult['animation'] {\n\treturn {\n\t\tmode,\n\t\ttrackedDurationMs: 0,\n\t\tfallbackDurationMs: 0\n\t};\n}\n\nexport function isAcceptedSampleRate(rate: number): boolean {\n\treturn (SPEECH_ACCEPTED_SAMPLE_RATES as readonly number[]).includes(rate);\n}\n\nexport function assertPcmFormat(format: SpeechAudioFormat): void {\n\tif (format.encoding !== 'pcm_s16le') {\n\t\tthrow new SpeechRuntimeError(`Unsupported audio encoding: ${format.encoding}`);\n\t}\n\tif (format.channels !== 1) {\n\t\tthrow new SpeechRuntimeError(`Unsupported channel count: ${format.channels}`);\n\t}\n\tif (!isAcceptedSampleRate(format.sampleRate)) {\n\t\tthrow new SpeechRuntimeError(`Unsupported sample rate: ${format.sampleRate}`);\n\t}\n}\n\nexport function assertSpeechPlayAudio(audio: SpeechPlayAudio): void {\n\tif (isSpeechPlayPcmAudio(audio)) {\n\t\tassertPcmFormat(audio.format);\n\t\treturn;\n\t}\n\tif (isSpeechPlayUrlAudio(audio)) {\n\t\tconst url = audio.url.trim();\n\t\tif (!url) throw new SpeechRuntimeError('Audio URL must be non-empty.');\n\t\tif (audio.sampleRate !== undefined && !isAcceptedSampleRate(audio.sampleRate)) {\n\t\t\tthrow new SpeechRuntimeError(`Unsupported sample rate: ${audio.sampleRate}`);\n\t\t}\n\t\treturn;\n\t}\n\tif (isSpeechPlayMediaStreamAudio(audio)) {\n\t\tif (audio.track.kind !== 'audio') {\n\t\t\tthrow new SpeechRuntimeError('speech.play track must be an audio MediaStreamTrack.');\n\t\t}\n\t\tif (audio.sampleRate !== undefined && !isAcceptedSampleRate(audio.sampleRate)) {\n\t\t\tthrow new SpeechRuntimeError(`Unsupported sample rate: ${audio.sampleRate}`);\n\t\t}\n\t\treturn;\n\t}\n\tif (isSpeechPlayEncodedAudio(audio)) {\n\t\tconst encoding = audio.encoding.trim().toLowerCase();\n\t\tif (!encoding) throw new SpeechRuntimeError('Encoded audio MIME type is required.');\n\t\tif (encoding === 'pcm_s16le') {\n\t\t\tthrow new SpeechRuntimeError('Use format: { encoding: \"pcm_s16le\", … } for raw PCM.');\n\t\t}\n\t\tif (audio.sampleRate !== undefined && !isAcceptedSampleRate(audio.sampleRate)) {\n\t\t\tthrow new SpeechRuntimeError(`Unsupported sample rate: ${audio.sampleRate}`);\n\t\t}\n\t\treturn;\n\t}\n\tthrow new SpeechRuntimeError('Unsupported speech.play audio shape.');\n}\n\nexport function resolveCreateUtteranceFormat(\n\toptions: SpeechCreateUtteranceOptions\n): SpeechAudioFormat {\n\tif (options.format) {\n\t\tassertPcmFormat(options.format);\n\t\treturn options.format;\n\t}\n\tif (options.track) {\n\t\tconst sampleRate = options.sampleRate ?? 24_000;\n\t\tconst format: SpeechAudioFormat = {\n\t\t\tencoding: 'pcm_s16le',\n\t\t\tsampleRate,\n\t\t\tchannels: 1\n\t\t};\n\t\tassertPcmFormat(format);\n\t\treturn format;\n\t}\n\tthrow new SpeechRuntimeError('createUtterance requires format or track.');\n}\n\nexport function toUint8Array(data: ArrayBuffer | Uint8Array): Uint8Array {\n\treturn data instanceof Uint8Array ? data : new Uint8Array(data);\n}\n\nexport function queuePolicyToEmbedBehavior(\n\tqueue: CompleteSpeechQueuePolicy | StreamingSpeechQueuePolicy | undefined\n): 'enqueue' | 'interrupt' {\n\tif (queue === 'replace-active' || queue === 'replace-all') return 'interrupt';\n\treturn 'enqueue';\n}\n","/** Liforma `speech.write` rejects chunks larger than 64 KiB. */\nexport const MAX_PCM_CHUNK_BYTES = 64 * 1024;\n\n/**\n * Playback / chunk guidance for custom BYO bridges (mirrors speech controller limits).\n * Prefer ~20–200 ms PCM frames; the player starts after an adaptive ~80–250 ms jitter\n * buffer (default 150 ms) based on chunk arrival variance.\n */\nexport const BYO_PLAYBACK_GUIDANCE = {\n\tRECOMMENDED_CHUNK_MS_MIN: 20,\n\tRECOMMENDED_CHUNK_MS_MAX: 200,\n\tPLAYBACK_JITTER_BUFFER_MS: 150,\n\tPLAYBACK_JITTER_BUFFER_MS_MIN: 80,\n\tPLAYBACK_JITTER_BUFFER_MS_MAX: 250\n} as const;\n\nexport function base64ToPcmBytes(b64: string): Uint8Array {\n\tconst bin = atob(b64);\n\tconst out = new Uint8Array(bin.length);\n\tfor (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);\n\treturn out;\n}\n\nexport function pcmBytesToBase64(bytes: Uint8Array): string {\n\tlet bin = '';\n\tconst chunk = 0x8000;\n\tfor (let i = 0; i < bytes.length; i += chunk) {\n\t\tbin += String.fromCharCode(...bytes.subarray(i, i + chunk));\n\t}\n\treturn btoa(bin);\n}\n\nexport function floatToPcm16Le(input: Float32Array): Uint8Array {\n\tconst out = new Uint8Array(input.length * 2);\n\tconst view = new DataView(out.buffer);\n\tfor (let i = 0; i < input.length; i++) {\n\t\tconst s = Math.max(-1, Math.min(1, input[i]!));\n\t\tview.setInt16(i * 2, s < 0 ? s * 0x8000 : s * 0x7fff, true);\n\t}\n\treturn out;\n}\n\n/** Naive linear resample Float32 → target rate. */\nexport function resampleFloat32(input: Float32Array, fromRate: number, toRate: number): Float32Array {\n\tif (fromRate === toRate || input.length === 0) return input;\n\tconst ratio = fromRate / toRate;\n\tconst outLen = Math.max(1, Math.round(input.length / ratio));\n\tconst out = new Float32Array(outLen);\n\tfor (let i = 0; i < outLen; i++) {\n\t\tconst src = i * ratio;\n\t\tconst i0 = Math.floor(src);\n\t\tconst i1 = Math.min(i0 + 1, input.length - 1);\n\t\tconst t = src - i0;\n\t\tout[i] = input[i0]! * (1 - t) + input[i1]! * t;\n\t}\n\treturn out;\n}\n\n/**\n * Parse vendor format strings like `pcm_16000` / `pcm_24000`.\n * Returns null when the format is missing or not PCM.\n */\nexport function parsePcmSampleRate(format: string | null | undefined): number | null {\n\tconst m = /^pcm_(\\d+)$/.exec(format ?? '');\n\tif (!m) return null;\n\tconst rate = Number(m[1]);\n\treturn Number.isFinite(rate) && rate > 0 ? rate : null;\n}\n\n/**\n * Write even-length PCM, splitting at MAX_PCM_CHUNK_BYTES on even boundaries.\n * Callers that may receive odd-length frames should run bytes through\n * {@link takeEvenPcmBytes} first so samples are not dropped across chunks.\n */\nexport async function writePcmInChunks(\n\tutterance: { write: (chunk: ArrayBuffer | ArrayBufferView) => Promise<unknown> },\n\tbytes: Uint8Array\n): Promise<void> {\n\tif (bytes.byteLength === 0) return;\n\tif ((bytes.byteLength & 1) !== 0) {\n\t\t// Defensive: drop trailing unpaired byte within a single call.\n\t\tbytes = bytes.subarray(0, bytes.byteLength - 1);\n\t\tif (bytes.byteLength === 0) return;\n\t}\n\tif (bytes.byteLength <= MAX_PCM_CHUNK_BYTES) {\n\t\tawait utterance.write(bytes);\n\t\treturn;\n\t}\n\tlet offset = 0;\n\twhile (offset < bytes.byteLength) {\n\t\tlet end = Math.min(offset + MAX_PCM_CHUNK_BYTES, bytes.byteLength);\n\t\tif ((end - offset) % 2 === 1) end -= 1;\n\t\tif (end <= offset) break;\n\t\tawait utterance.write(bytes.subarray(offset, end));\n\t\toffset = end;\n\t}\n}\n","/**\n * Stateful cubic (Catmull-Rom) resampler for continuous PCM capture streams.\n * Better than naive linear for uplink mic / track capture.\n */\n\nexport type FloatResampler = {\n\t/** Push a mono float buffer; returns samples at the target rate (may be empty). */\n\tpush(input: Float32Array): Float32Array;\n\t/** Flush any remaining fractional sample (usually silence). */\n\tflush(): Float32Array;\n\treadonly fromRate: number;\n\treadonly toRate: number;\n};\n\nfunction catmullRom(y0: number, y1: number, y2: number, y3: number, t: number): number {\n\tconst a0 = -0.5 * y0 + 1.5 * y1 - 1.5 * y2 + 0.5 * y3;\n\tconst a1 = y0 - 2.5 * y1 + 2 * y2 - 0.5 * y3;\n\tconst a2 = -0.5 * y0 + 0.5 * y2;\n\tconst a3 = y1;\n\treturn ((a0 * t + a1) * t + a2) * t + a3;\n}\n\n/**\n * Create a continuous resampler. When rates match, push() returns the input as-is\n * (no copy) when possible.\n */\nexport function createCubicResampler(fromRate: number, toRate: number): FloatResampler {\n\tif (!(fromRate > 0) || !(toRate > 0)) {\n\t\tthrow new Error('Resampler rates must be positive');\n\t}\n\n\tif (fromRate === toRate) {\n\t\treturn {\n\t\t\tfromRate,\n\t\t\ttoRate,\n\t\t\tpush(input) {\n\t\t\t\treturn input;\n\t\t\t},\n\t\t\tflush() {\n\t\t\t\treturn new Float32Array(0);\n\t\t\t}\n\t\t};\n\t}\n\n\tconst ratio = fromRate / toRate;\n\t/** History + pending input (keeps 3 samples of lookbehind for cubic). */\n\tlet pending = new Float32Array(0);\n\tlet pos = 0; // position in pending sample space for next output\n\n\treturn {\n\t\tfromRate,\n\t\ttoRate,\n\t\tpush(input) {\n\t\t\tif (input.length === 0) return new Float32Array(0);\n\t\t\tconst merged = new Float32Array(pending.length + input.length);\n\t\t\tmerged.set(pending);\n\t\t\tmerged.set(input, pending.length);\n\t\t\tpending = merged;\n\n\t\t\tif (pending.length < 4) return new Float32Array(0);\n\n\t\t\tconst out: number[] = [];\n\t\t\t// Need i+2 available for cubic; stop while pos+2 < length-1 equivalently floor(pos)+2 < len-?\n\t\t\twhile (pos + 2 < pending.length - 1) {\n\t\t\t\tconst i1 = Math.floor(pos);\n\t\t\t\tconst t = pos - i1;\n\t\t\t\tconst i0 = Math.max(0, i1 - 1);\n\t\t\t\tconst i2 = Math.min(pending.length - 1, i1 + 1);\n\t\t\t\tconst i3 = Math.min(pending.length - 1, i1 + 2);\n\t\t\t\tout.push(\n\t\t\t\t\tcatmullRom(pending[i0]!, pending[i1]!, pending[i2]!, pending[i3]!, t)\n\t\t\t\t);\n\t\t\t\tpos += ratio;\n\t\t\t}\n\n\t\t\t// Drop consumed samples but keep lookbehind (≥1) and unread tail.\n\t\t\tconst drop = Math.max(0, Math.floor(pos) - 1);\n\t\t\tif (drop > 0) {\n\t\t\t\tpending = pending.subarray(drop);\n\t\t\t\tpos -= drop;\n\t\t\t}\n\n\t\t\treturn out.length ? Float32Array.from(out) : new Float32Array(0);\n\t\t},\n\t\tflush() {\n\t\t\tif (pending.length === 0) return new Float32Array(0);\n\t\t\t// Emit one last sample if we have enough history; otherwise drop.\n\t\t\tpending = new Float32Array(0);\n\t\t\tpos = 0;\n\t\t\treturn new Float32Array(0);\n\t\t}\n\t};\n}\n","/**\n * Shared Web Audio capture: AudioWorklet when available, ScriptProcessor fallback.\n * Used by BYO mic uplink and MediaStreamTrack → PCM piping.\n */\n\nimport { floatToPcm16Le } from './pcm';\nimport { createCubicResampler, type FloatResampler } from './resample';\n\nexport type AudioCaptureSession = {\n\tstop(): void;\n\treadonly fromSampleRate: number;\n\treadonly usingWorklet: boolean;\n};\n\nexport type StartAudioCaptureOptions = {\n\treadonly mediaStream: MediaStream;\n\treadonly targetSampleRate: number;\n\t/** Called with pcm_s16le mono at targetSampleRate. */\n\treadonly onPcm: (bytes: Uint8Array) => void;\n\t/** When false, frames are dropped. */\n\treadonly shouldSend?: () => boolean;\n\treadonly onLog?: (line: string, kind?: 'info' | 'warn') => void;\n};\n\nconst WORKLET_NAME = 'liforma-pcm-capture';\n\nconst WORKLET_SOURCE = `\nclass LiformaPcmCaptureProcessor extends AudioWorkletProcessor {\n process(inputs) {\n const ch = inputs[0] && inputs[0][0];\n if (ch && ch.length) {\n const copy = new Float32Array(ch.length);\n copy.set(ch);\n this.port.postMessage(copy, [copy.buffer]);\n }\n return true;\n }\n}\nregisterProcessor('${WORKLET_NAME}', LiformaPcmCaptureProcessor);\n`;\n\nlet workletUrl: string | null = null;\n\nfunction getWorkletUrl(): string {\n\tif (!workletUrl) {\n\t\tworkletUrl = URL.createObjectURL(\n\t\t\tnew Blob([WORKLET_SOURCE], { type: 'application/javascript' })\n\t\t);\n\t}\n\treturn workletUrl;\n}\n\nfunction getAudioContextCtor(): typeof AudioContext | null {\n\treturn (\n\t\twindow.AudioContext ||\n\t\t(window as unknown as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext ||\n\t\tnull\n\t);\n}\n\n/**\n * Capture a MediaStream as pcm_s16le at targetSampleRate.\n * Prefers AudioWorklet; falls back to ScriptProcessorNode.\n */\nexport async function startAudioCapture(\n\toptions: StartAudioCaptureOptions\n): Promise<AudioCaptureSession> {\n\tconst { mediaStream, targetSampleRate, onPcm, shouldSend, onLog } = options;\n\tconst AudioContextCtor = getAudioContextCtor();\n\tif (!AudioContextCtor) throw new Error('Web Audio is required for audio capture');\n\n\tconst audioCtx = new AudioContextCtor();\n\tconst source = audioCtx.createMediaStreamSource(mediaStream);\n\tconst silent = audioCtx.createGain();\n\tsilent.gain.value = 0;\n\tconst fromSampleRate = audioCtx.sampleRate;\n\tconst resampler: FloatResampler = createCubicResampler(fromSampleRate, targetSampleRate);\n\tlet stopped = false;\n\tlet usingWorklet = false;\n\n\tconst emitFloat = (input: Float32Array) => {\n\t\tif (stopped) return;\n\t\tif (shouldSend && !shouldSend()) return;\n\t\tconst resampled = resampler.push(input);\n\t\tif (resampled.length === 0) return;\n\t\tonPcm(floatToPcm16Le(resampled));\n\t};\n\n\tlet disconnectGraph: () => void = () => undefined;\n\n\tconst tryWorklet = async (): Promise<boolean> => {\n\t\tif (typeof audioCtx.audioWorklet?.addModule !== 'function') return false;\n\t\ttry {\n\t\t\tawait audioCtx.audioWorklet.addModule(getWorkletUrl());\n\t\t\tconst node = new AudioWorkletNode(audioCtx, WORKLET_NAME, {\n\t\t\t\tnumberOfInputs: 1,\n\t\t\t\tnumberOfOutputs: 1,\n\t\t\t\toutputChannelCount: [1]\n\t\t\t});\n\t\t\tnode.port.onmessage = (ev) => {\n\t\t\t\tif (ev.data instanceof Float32Array) emitFloat(ev.data);\n\t\t\t};\n\t\t\tsource.connect(node);\n\t\t\tnode.connect(silent);\n\t\t\tsilent.connect(audioCtx.destination);\n\t\t\tdisconnectGraph = () => {\n\t\t\t\tnode.port.onmessage = null;\n\t\t\t\ttry {\n\t\t\t\t\tnode.disconnect();\n\t\t\t\t} catch {\n\t\t\t\t\t/* ignore */\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tsource.disconnect();\n\t\t\t\t} catch {\n\t\t\t\t\t/* ignore */\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tsilent.disconnect();\n\t\t\t\t} catch {\n\t\t\t\t\t/* ignore */\n\t\t\t\t}\n\t\t\t};\n\t\t\tusingWorklet = true;\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t};\n\n\tconst useScriptProcessor = (): void => {\n\t\tconst createScriptProcessor = (\n\t\t\taudioCtx as AudioContext & {\n\t\t\t\tcreateScriptProcessor?: (\n\t\t\t\t\tbufferSize: number,\n\t\t\t\t\tinputChannels: number,\n\t\t\t\t\toutputChannels: number\n\t\t\t\t) => ScriptProcessorNode;\n\t\t\t}\n\t\t).createScriptProcessor?.bind(audioCtx);\n\n\t\tif (!createScriptProcessor) {\n\t\t\tthrow new Error('Neither AudioWorklet nor ScriptProcessor is available');\n\t\t}\n\n\t\tconst processor = createScriptProcessor(4096, 1, 1);\n\t\tprocessor.onaudioprocess = (ev) => {\n\t\t\temitFloat(ev.inputBuffer.getChannelData(0));\n\t\t};\n\t\tsource.connect(processor);\n\t\tprocessor.connect(silent);\n\t\tsilent.connect(audioCtx.destination);\n\t\tdisconnectGraph = () => {\n\t\t\tprocessor.onaudioprocess = null;\n\t\t\ttry {\n\t\t\t\tprocessor.disconnect();\n\t\t\t} catch {\n\t\t\t\t/* ignore */\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tsource.disconnect();\n\t\t\t} catch {\n\t\t\t\t/* ignore */\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tsilent.disconnect();\n\t\t\t} catch {\n\t\t\t\t/* ignore */\n\t\t\t}\n\t\t};\n\t\tusingWorklet = false;\n\t};\n\n\tif (!(await tryWorklet())) {\n\t\tuseScriptProcessor();\n\t\tonLog?.(\n\t\t\t`Mic/track capture via ScriptProcessor at ${fromSampleRate} Hz → ${targetSampleRate} Hz`,\n\t\t\t'warn'\n\t\t);\n\t} else {\n\t\tonLog?.(`Mic/track capture via AudioWorklet at ${fromSampleRate} Hz → ${targetSampleRate} Hz`);\n\t}\n\n\tif (audioCtx.state === 'suspended') {\n\t\tawait audioCtx.resume().catch(() => undefined);\n\t}\n\n\treturn {\n\t\tfromSampleRate,\n\t\tusingWorklet,\n\t\tstop() {\n\t\t\tif (stopped) return;\n\t\t\tstopped = true;\n\t\t\tresampler.flush();\n\t\t\tdisconnectGraph();\n\t\t\tvoid audioCtx.close().catch(() => undefined);\n\t\t}\n\t};\n}\n","/**\n * Host-side MediaStreamTrack → mono pcm_s16le capture for experience.speech.\n * Capture stays in the SDK (host page); player only receives PCM chunks.\n */\n\nimport { startAudioCapture } from '../../byo/audioCapture';\nimport { SPEECH_LIMITS, SpeechRuntimeError, toUint8Array } from './types';\nimport type { SpeechUtterance } from './types';\n\nexport type CaptureMediaStreamTrackOptions = {\n\treadonly track: MediaStreamTrack;\n\treadonly utterance: SpeechUtterance;\n\treadonly sampleRate: number;\n\treadonly signal?: AbortSignal;\n};\n\n/**\n * Capture a live audio track into an open utterance until the track ends or abort.\n * Prefers AudioWorklet + cubic resample (ScriptProcessor fallback).\n */\nexport async function pipeMediaStreamTrackToUtterance(\n\toptions: CaptureMediaStreamTrackOptions\n): Promise<void> {\n\tconst { track, utterance, sampleRate, signal } = options;\n\tif (typeof MediaStreamTrack === 'undefined' || !(track instanceof MediaStreamTrack)) {\n\t\tthrow new SpeechRuntimeError('MediaStreamTrack is not available in this environment.');\n\t}\n\tif (track.kind !== 'audio') {\n\t\tthrow new SpeechRuntimeError('speech.play track must be an audio MediaStreamTrack.');\n\t}\n\tif (track.readyState === 'ended') {\n\t\treturn;\n\t}\n\tif (signal?.aborted) {\n\t\treturn;\n\t}\n\n\tconst stream = new MediaStream([track]);\n\tlet writeChain: Promise<void> = Promise.resolve();\n\tlet stopped = false;\n\tlet capture: Awaited<ReturnType<typeof startAudioCapture>> | null = null;\n\n\tconst teardown = () => {\n\t\tif (stopped) return;\n\t\tstopped = true;\n\t\tcapture?.stop();\n\t\tcapture = null;\n\t};\n\n\ttry {\n\t\tcapture = await startAudioCapture({\n\t\t\tmediaStream: stream,\n\t\t\ttargetSampleRate: sampleRate,\n\t\t\tshouldSend: () => !stopped && !signal?.aborted,\n\t\t\tonPcm: (pcm) => {\n\t\t\t\tif (stopped || signal?.aborted) return;\n\t\t\t\tconst bytes = toUint8Array(pcm);\n\t\t\t\twriteChain = writeChain\n\t\t\t\t\t.then(async () => {\n\t\t\t\t\t\tif (stopped || signal?.aborted) return;\n\t\t\t\t\t\tlet offset = 0;\n\t\t\t\t\t\twhile (offset < bytes.byteLength) {\n\t\t\t\t\t\t\tlet end = Math.min(offset + SPEECH_LIMITS.MAX_CHUNK_BYTES, bytes.byteLength);\n\t\t\t\t\t\t\tif ((end - offset) % 2 === 1) end -= 1;\n\t\t\t\t\t\t\tif (end <= offset) break;\n\t\t\t\t\t\t\tconst slice = bytes.subarray(offset, end);\n\t\t\t\t\t\t\toffset = end;\n\t\t\t\t\t\t\tif (slice.byteLength === 0) continue;\n\t\t\t\t\t\t\tawait utterance.write(slice);\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\t.catch(() => {\n\t\t\t\t\t\tteardown();\n\t\t\t\t\t});\n\t\t\t}\n\t\t});\n\t} catch (err) {\n\t\tteardown();\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tthrow new SpeechRuntimeError(`MediaStreamTrack capture failed: ${message}`);\n\t}\n\n\tawait new Promise<void>((resolve) => {\n\t\tconst finish = () => {\n\t\t\tteardown();\n\t\t\tvoid writeChain.finally(resolve);\n\t\t};\n\t\ttrack.addEventListener('ended', finish, { once: true });\n\t\tsignal?.addEventListener(\n\t\t\t'abort',\n\t\t\t() => {\n\t\t\t\tvoid utterance.cancel().catch(() => undefined);\n\t\t\t\tfinish();\n\t\t\t},\n\t\t\t{ once: true }\n\t\t);\n\t\tif (track.readyState === 'ended' || signal?.aborted) {\n\t\t\tfinish();\n\t\t}\n\t});\n}\n","import type { IframeHost } from '../embed/iframeHost';\nimport type { SpeakBehavior } from '../types/sessionInteraction';\nimport type { SpeechResult as EmbedSpeakResult } from '../types/sessionInteraction';\nimport {\n\tSPEECH_LIMITS,\n\tSpeechBufferTimeoutError,\n\tSpeechRuntimeError,\n\tSpeechUtteranceClosedError,\n\tUnsupportedSpeechOperationError,\n\tassertSpeechPlayAudio,\n\temptyAnimationResult,\n\tisSpeechPlayEncodedAudio,\n\tisSpeechPlayMediaStreamAudio,\n\tisSpeechPlayPcmAudio,\n\tisSpeechPlayUrlAudio,\n\tqueuePolicyToEmbedBehavior,\n\tresolveCreateUtteranceFormat,\n\ttoUint8Array,\n\ttype SpeechAudioFormat,\n\ttype SpeechController,\n\ttype SpeechCreateUtteranceOptions,\n\ttype SpeechInterruptOptions,\n\ttype SpeechPlayAudio,\n\ttype SpeechPlayEmbedSource,\n\ttype SpeechPlayOptions,\n\ttype SpeechResult,\n\ttype SpeechSpeakOptions,\n\ttype SpeechTerminalStatus,\n\ttype SpeechUtterance,\n\ttype SpeechUtteranceCloseOptions,\n\ttype SpeechWriteOptions,\n\ttype StreamingSpeechQueuePolicy\n} from './types';\nimport { pipeMediaStreamTrackToUtterance } from './captureMediaStreamTrack';\n\nasync function toSpeechPlayEmbedSource(\n\taudio: SpeechPlayAudio,\n\tsignal?: AbortSignal\n): Promise<SpeechPlayEmbedSource> {\n\tif (isSpeechPlayMediaStreamAudio(audio)) {\n\t\tthrow new SpeechRuntimeError('MediaStreamTrack play is handled on the host before embed.');\n\t}\n\tif (isSpeechPlayPcmAudio(audio)) {\n\t\tconst data = toUint8Array(audio.data);\n\t\tif ((data.byteLength & 1) !== 0) {\n\t\t\tthrow new SpeechRuntimeError('PCM byte length must be even.');\n\t\t}\n\t\treturn {\n\t\t\tkind: 'pcm',\n\t\t\tdata: data.slice().buffer,\n\t\t\tformat: audio.format\n\t\t};\n\t}\n\tif (isSpeechPlayUrlAudio(audio)) {\n\t\treturn {\n\t\t\tkind: 'url',\n\t\t\turl: audio.url.trim(),\n\t\t\t...(audio.sampleRate !== undefined ? { sampleRate: audio.sampleRate } : {})\n\t\t};\n\t}\n\tif (!isSpeechPlayEncodedAudio(audio)) {\n\t\tthrow new SpeechRuntimeError('Unsupported speech.play audio shape.');\n\t}\n\tlet bytes: Uint8Array;\n\tif (typeof Blob !== 'undefined' && audio.data instanceof Blob) {\n\t\tif (signal?.aborted) throw new DOMException('Play aborted.', 'AbortError');\n\t\tbytes = new Uint8Array(await audio.data.arrayBuffer());\n\t} else {\n\t\tbytes = toUint8Array(audio.data as ArrayBuffer | Uint8Array);\n\t}\n\treturn {\n\t\tkind: 'encoded',\n\t\tdata: bytes.slice().buffer,\n\t\tencoding: audio.encoding.trim(),\n\t\t...(audio.sampleRate !== undefined ? { sampleRate: audio.sampleRate } : {})\n\t};\n}\n\nexport type {\n\tSpeechAudioFormat,\n\tSpeechController,\n\tSpeechCreateUtteranceOptions,\n\tSpeechInterruptOptions,\n\tSpeechPlayOptions,\n\tSpeechResult,\n\tSpeechSpeakOptions,\n\tSpeechUtterance,\n\tSpeechUtteranceCloseOptions,\n\tSpeechWriteOptions\n} from './types';\n\nexport type SpeechCapabilities = {\n\treadonly textSpeech: boolean;\n\treadonly externalSpeechAudio: boolean;\n};\n\nexport type SpeechControllerHost = {\n\treadonly getIframeHost: () => IframeHost | null;\n\treadonly assertSessionStarted: () => void;\n\treadonly getCapabilities: () => SpeechCapabilities;\n\treadonly getActiveCharacterId: () => string;\n\treadonly onProcessorInterrupt?: () => void;\n};\n\ntype PendingWrite = {\n\treadonly resolve: () => void;\n\treadonly reject: (error: Error) => void;\n};\n\nfunction newUtteranceId(): string {\n\tif (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n\t\treturn `utt_${crypto.randomUUID().replace(/-/g, '').slice(0, 12)}`;\n\t}\n\treturn `utt_${Date.now().toString(36)}${Math.random().toString(36).slice(2, 8)}`;\n}\n\nfunction mapEmbedSpeakResult(\n\tresult: EmbedSpeakResult,\n\tstatus: SpeechTerminalStatus = 'completed'\n): SpeechResult {\n\treturn {\n\t\tutteranceId: result.speechId,\n\t\tcharacterId: result.characterId,\n\t\tstatus,\n\t\tdurationMs: result.durationMs,\n\t\ttranscript: result.text,\n\t\tanimation: {\n\t\t\tmode: 'tracked',\n\t\t\ttrackedDurationMs: result.durationMs,\n\t\t\tfallbackDurationMs: 0\n\t\t}\n\t};\n}\n\nfunction settleError(error: unknown): Error {\n\tif (error instanceof Error) return error;\n\treturn new SpeechRuntimeError(String(error));\n}\n\nfunction isAbortError(error: unknown): boolean {\n\treturn (\n\t\t(error instanceof DOMException && error.name === 'AbortError') ||\n\t\t(error instanceof Error && error.name === 'AbortError')\n\t);\n}\n\nclass HostSpeechUtterance implements SpeechUtterance {\n\treadonly id: string;\n\treadonly characterId: string;\n\treadonly result: Promise<SpeechResult>;\n\n\tprivate readonly format: SpeechAudioFormat;\n\tprivate readonly host: SpeechControllerHost;\n\tprivate readonly signal?: AbortSignal;\n\tprivate settleResolve!: (value: SpeechResult) => void;\n\tprivate settleReject!: (error: Error) => void;\n\tprivate settled = false;\n\tprivate closed = false;\n\tprivate writeChain: Promise<void> = Promise.resolve();\n\tprivate pendingWriteCount = 0;\n\tprivate readonly pendingWriteWaiters: PendingWrite[] = [];\n\tprivate bufferedMs = 0;\n\tprivate readonly sampleRate: number;\n\tprivate readonly beginPromise: Promise<void>;\n\n\tconstructor(options: {\n\t\treadonly id: string;\n\t\treadonly characterId: string;\n\t\treadonly format: SpeechAudioFormat;\n\t\treadonly queue: StreamingSpeechQueuePolicy;\n\t\treadonly host: SpeechControllerHost;\n\t\treadonly transcript?: string;\n\t\treadonly signal?: AbortSignal;\n\t}) {\n\t\tthis.id = options.id;\n\t\tthis.characterId = options.characterId;\n\t\tthis.format = options.format;\n\t\tthis.host = options.host;\n\t\tthis.signal = options.signal;\n\t\tthis.sampleRate = options.format.sampleRate;\n\t\tthis.result = new Promise<SpeechResult>((resolve, reject) => {\n\t\t\tthis.settleResolve = resolve;\n\t\t\tthis.settleReject = reject;\n\t\t});\n\t\t// Prevent unhandled rejection if caller only uses close()/cancel().\n\t\tvoid this.result.catch(() => undefined);\n\n\t\tconst iframe = this.requireHost();\n\t\tthis.beginPromise = iframe\n\t\t\t.speechUtteranceBegin({\n\t\t\t\tutteranceId: this.id,\n\t\t\t\tformat: this.format,\n\t\t\t\tcharacterId: this.characterId,\n\t\t\t\tqueue: options.queue,\n\t\t\t\t...(options.transcript !== undefined ? { transcript: options.transcript } : {})\n\t\t\t})\n\t\t\t.catch((error: unknown) => {\n\t\t\t\tthis.fail(settleError(error));\n\t\t\t\tthrow settleError(error);\n\t\t\t});\n\n\t\tthis.signal?.addEventListener(\n\t\t\t'abort',\n\t\t\t() => {\n\t\t\t\tvoid this.cancel();\n\t\t\t},\n\t\t\t{ once: true }\n\t\t);\n\t}\n\n\tasync write(audio: ArrayBuffer | Uint8Array, options?: SpeechWriteOptions): Promise<void> {\n\t\tif (this.closed || this.settled) {\n\t\t\tthrow new SpeechUtteranceClosedError();\n\t\t}\n\t\tawait this.beginPromise.catch(() => undefined);\n\t\tif (this.closed || this.settled) {\n\t\t\tthrow new SpeechUtteranceClosedError();\n\t\t}\n\t\tconst bytes = toUint8Array(audio);\n\t\tif ((bytes.byteLength & 1) !== 0) {\n\t\t\tthrow new SpeechRuntimeError('PCM chunk byte length must be even (complete s16 samples).');\n\t\t}\n\t\tif (bytes.byteLength > SPEECH_LIMITS.MAX_CHUNK_BYTES) {\n\t\t\tthrow new SpeechRuntimeError(\n\t\t\t\t`PCM chunk exceeds MAX_CHUNK_BYTES (${SPEECH_LIMITS.MAX_CHUNK_BYTES}).`\n\t\t\t);\n\t\t}\n\t\tif (bytes.byteLength === 0) return;\n\n\t\tconst timeoutMs = options?.timeoutMs ?? SPEECH_LIMITS.DEFAULT_WRITE_TIMEOUT_MS;\n\t\tawait this.acquireWriteSlot(timeoutMs);\n\n\t\tconst chunkMs = (bytes.byteLength / 2 / this.sampleRate) * 1000;\n\t\tif (this.bufferedMs + chunkMs > SPEECH_LIMITS.MAX_UTTERANCE_DURATION_MS) {\n\t\t\tthis.releaseWriteSlot();\n\t\t\tthrow new SpeechRuntimeError('Utterance exceeds MAX_UTTERANCE_DURATION_MS.');\n\t\t}\n\n\t\tconst run = async () => {\n\t\t\tif (this.closed || this.settled) {\n\t\t\t\tthrow new SpeechUtteranceClosedError();\n\t\t\t}\n\t\t\tconst iframe = this.requireHost();\n\t\t\tconst copy = bytes.slice();\n\t\t\tconst writePromise = iframe.speechUtteranceWrite({\n\t\t\t\tutteranceId: this.id,\n\t\t\t\taudio: copy.buffer,\n\t\t\t\tbyteLength: copy.byteLength\n\t\t\t});\n\t\t\tconst ack = await Promise.race([\n\t\t\t\twritePromise,\n\t\t\t\tnew Promise<never>((_, reject) => {\n\t\t\t\t\tsetTimeout(() => reject(new SpeechBufferTimeoutError()), timeoutMs);\n\t\t\t\t})\n\t\t\t]);\n\t\t\tthis.bufferedMs = Math.max(0, ack.bufferedMs);\n\t\t};\n\n\t\tconst tracked = this.writeChain.then(run, run);\n\t\tthis.writeChain = tracked.then(\n\t\t\t() => undefined,\n\t\t\t() => undefined\n\t\t);\n\t\ttry {\n\t\t\tawait tracked;\n\t\t} finally {\n\t\t\tthis.releaseWriteSlot();\n\t\t}\n\t}\n\n\tasync setTranscript(text: string): Promise<void> {\n\t\tif (this.closed || this.settled) {\n\t\t\tthrow new SpeechUtteranceClosedError();\n\t\t}\n\t\tawait this.beginPromise.catch(() => undefined);\n\t\tif (this.closed || this.settled) {\n\t\t\tthrow new SpeechUtteranceClosedError();\n\t\t}\n\t\tconst iframe = this.requireHost();\n\t\tawait iframe.speechUtteranceSetTranscript({\n\t\t\tutteranceId: this.id,\n\t\t\ttranscript: text\n\t\t});\n\t}\n\n\tasync close(options?: SpeechUtteranceCloseOptions): Promise<SpeechResult> {\n\t\tif (this.settled) return this.result;\n\t\tthis.closed = true;\n\t\tawait this.beginPromise.catch(() => undefined);\n\t\tawait this.writeChain.catch(() => undefined);\n\t\ttry {\n\t\t\tconst iframe = this.requireHost();\n\t\t\tconst result = await iframe.speechUtteranceClose({\n\t\t\t\tutteranceId: this.id,\n\t\t\t\ttranscript: options?.transcript,\n\t\t\t\thistory: options?.history ?? 'none'\n\t\t\t});\n\t\t\tthis.resolve(result);\n\t\t\treturn result;\n\t\t} catch (error) {\n\t\t\tconst err = settleError(error);\n\t\t\tthis.fail(err);\n\t\t\tthrow err;\n\t\t}\n\t}\n\n\tasync cancel(): Promise<SpeechResult> {\n\t\tif (this.settled) return this.result;\n\t\tthis.closed = true;\n\t\ttry {\n\t\t\tconst iframe = this.host.getIframeHost();\n\t\t\tif (iframe) {\n\t\t\t\tconst result = await iframe.speechUtteranceCancel({ utteranceId: this.id });\n\t\t\t\tthis.resolve(result);\n\t\t\t\treturn result;\n\t\t\t}\n\t\t\tconst cancelled: SpeechResult = {\n\t\t\t\tutteranceId: this.id,\n\t\t\t\tcharacterId: this.characterId,\n\t\t\t\tstatus: 'cancelled',\n\t\t\t\tdurationMs: 0,\n\t\t\t\tanimation: emptyAnimationResult('none')\n\t\t\t};\n\t\t\tthis.resolve(cancelled);\n\t\t\treturn cancelled;\n\t\t} catch (error) {\n\t\t\tconst err = settleError(error);\n\t\t\tthis.fail(err);\n\t\t\tthrow err;\n\t\t}\n\t}\n\n\t/** Adopt an interrupt settlement from the player without a second cancel RPC. */\n\tadoptExternalResult(result: SpeechResult): void {\n\t\tthis.resolve(result);\n\t}\n\n\tprivate requireHost(): IframeHost {\n\t\tconst iframe = this.host.getIframeHost();\n\t\tif (!iframe) {\n\t\t\tthrow new SpeechRuntimeError('Speech session is not attached.');\n\t\t}\n\t\treturn iframe;\n\t}\n\n\tprivate resolve(result: SpeechResult): void {\n\t\tif (this.settled) return;\n\t\tthis.settled = true;\n\t\tthis.settleResolve(Object.freeze(result));\n\t\tthis.flushWriteWaiters(new SpeechUtteranceClosedError());\n\t}\n\n\tprivate fail(error: Error): void {\n\t\tif (this.settled) return;\n\t\tthis.settled = true;\n\t\tthis.settleReject(error);\n\t\tthis.flushWriteWaiters(error);\n\t}\n\n\tprivate async acquireWriteSlot(timeoutMs: number): Promise<void> {\n\t\tif (this.pendingWriteCount < SPEECH_LIMITS.MAX_PENDING_WRITES) {\n\t\t\tthis.pendingWriteCount += 1;\n\t\t\treturn;\n\t\t}\n\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\tconst timer = setTimeout(() => {\n\t\t\t\treject(new SpeechBufferTimeoutError());\n\t\t\t}, timeoutMs);\n\t\t\tthis.pendingWriteWaiters.push({\n\t\t\t\tresolve: () => {\n\t\t\t\t\tclearTimeout(timer);\n\t\t\t\t\tthis.pendingWriteCount += 1;\n\t\t\t\t\tresolve();\n\t\t\t\t},\n\t\t\t\treject: (error) => {\n\t\t\t\t\tclearTimeout(timer);\n\t\t\t\t\treject(error);\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\t}\n\n\tprivate releaseWriteSlot(): void {\n\t\tthis.pendingWriteCount = Math.max(0, this.pendingWriteCount - 1);\n\t\tconst next = this.pendingWriteWaiters.shift();\n\t\tnext?.resolve();\n\t}\n\n\tprivate flushWriteWaiters(error: Error): void {\n\t\twhile (this.pendingWriteWaiters.length > 0) {\n\t\t\tthis.pendingWriteWaiters.shift()?.reject(error);\n\t\t}\n\t}\n}\n\nexport function createSpeechController(host: SpeechControllerHost): SpeechController {\n\tlet openUtterance: HostSpeechUtterance | null = null;\n\n\tconst requireCapability = (operation: string, capability: keyof SpeechCapabilities) => {\n\t\tconst caps = host.getCapabilities();\n\t\tif (!caps[capability]) {\n\t\t\tthrow new UnsupportedSpeechOperationError(operation, capability);\n\t\t}\n\t};\n\n\tconst controller: SpeechController = {\n\t\tasync speak(options: SpeechSpeakOptions): Promise<SpeechResult> {\n\t\t\thost.assertSessionStarted();\n\t\t\trequireCapability('speak', 'textSpeech');\n\t\t\tif (options.signal?.aborted) {\n\t\t\t\tthrow new DOMException('Speak aborted.', 'AbortError');\n\t\t\t}\n\t\t\tconst queue = options.queue ?? 'append';\n\t\t\tconst behavior: SpeakBehavior = queuePolicyToEmbedBehavior(queue);\n\t\t\tif (behavior === 'interrupt') {\n\t\t\t\thost.onProcessorInterrupt?.();\n\t\t\t\tif (openUtterance) {\n\t\t\t\t\tvoid openUtterance.cancel();\n\t\t\t\t\topenUtterance = null;\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst iframe = host.getIframeHost();\n\t\t\tif (!iframe) throw new SpeechRuntimeError('Speech session is not attached.');\n\n\t\t\tconst speakPromise = iframe.speak({\n\t\t\t\ttext: options.text,\n\t\t\t\tcharacterId: options.characterId,\n\t\t\t\tbehavior\n\t\t\t});\n\n\t\t\tif (options.signal) {\n\t\t\t\tconst signal = options.signal;\n\t\t\t\treturn await new Promise<SpeechResult>((resolve, reject) => {\n\t\t\t\t\tconst onAbort = () => {\n\t\t\t\t\t\tvoid iframe.speechInterrupt({ scope: 'active' }).catch(() => undefined);\n\t\t\t\t\t\treject(new DOMException('Speak aborted.', 'AbortError'));\n\t\t\t\t\t};\n\t\t\t\t\tif (signal.aborted) {\n\t\t\t\t\t\tonAbort();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tsignal.addEventListener('abort', onAbort, { once: true });\n\t\t\t\t\tspeakPromise.then(\n\t\t\t\t\t\t(legacy) => {\n\t\t\t\t\t\t\tsignal.removeEventListener('abort', onAbort);\n\t\t\t\t\t\t\tresolve(mapEmbedSpeakResult(legacy, 'completed'));\n\t\t\t\t\t\t},\n\t\t\t\t\t\t(error: unknown) => {\n\t\t\t\t\t\t\tsignal.removeEventListener('abort', onAbort);\n\t\t\t\t\t\t\tif (isAbortError(error)) {\n\t\t\t\t\t\t\t\tresolve({\n\t\t\t\t\t\t\t\t\tutteranceId: `utt_interrupted`,\n\t\t\t\t\t\t\t\t\tcharacterId: options.characterId ?? host.getActiveCharacterId(),\n\t\t\t\t\t\t\t\t\tstatus: 'interrupted',\n\t\t\t\t\t\t\t\t\tdurationMs: 0,\n\t\t\t\t\t\t\t\t\ttranscript: options.text,\n\t\t\t\t\t\t\t\t\tanimation: emptyAnimationResult('none')\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treject(settleError(error));\n\t\t\t\t\t\t}\n\t\t\t\t\t);\n\t\t\t\t});\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst legacy = await speakPromise;\n\t\t\t\treturn mapEmbedSpeakResult(legacy, 'completed');\n\t\t\t} catch (error) {\n\t\t\t\tif (isAbortError(error)) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tutteranceId: `utt_interrupted`,\n\t\t\t\t\t\tcharacterId: options.characterId ?? host.getActiveCharacterId(),\n\t\t\t\t\t\tstatus: 'interrupted',\n\t\t\t\t\t\tdurationMs: 0,\n\t\t\t\t\t\ttranscript: options.text,\n\t\t\t\t\t\tanimation: emptyAnimationResult('none')\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\tthrow settleError(error);\n\t\t\t}\n\t\t},\n\n\t\tasync play(options: SpeechPlayOptions): Promise<SpeechResult> {\n\t\t\thost.assertSessionStarted();\n\t\t\trequireCapability('play', 'externalSpeechAudio');\n\t\t\tassertSpeechPlayAudio(options.audio);\n\t\t\tif (options.signal?.aborted) {\n\t\t\t\tthrow new DOMException('Play aborted.', 'AbortError');\n\t\t\t}\n\t\t\tconst queue = options.queue ?? 'append';\n\t\t\tif (queue === 'replace-active' || queue === 'replace-all') {\n\t\t\t\thost.onProcessorInterrupt?.();\n\t\t\t\tif (openUtterance) {\n\t\t\t\t\tvoid openUtterance.cancel();\n\t\t\t\t\topenUtterance = null;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (isSpeechPlayMediaStreamAudio(options.audio)) {\n\t\t\t\tconst sampleRate = options.audio.sampleRate ?? 24_000;\n\t\t\t\tconst utterance = controller.createUtterance({\n\t\t\t\t\tformat: { encoding: 'pcm_s16le', sampleRate, channels: 1 },\n\t\t\t\t\ttrack: options.audio.track,\n\t\t\t\t\tcharacterId: options.characterId,\n\t\t\t\t\tqueue: queue === 'append' ? 'replace-active' : queue,\n\t\t\t\t\tsignal: options.signal\n\t\t\t\t});\n\t\t\t\treturn utterance.result;\n\t\t\t}\n\n\t\t\tconst iframe = host.getIframeHost();\n\t\t\tif (!iframe) throw new SpeechRuntimeError('Speech session is not attached.');\n\t\t\tconst source = await toSpeechPlayEmbedSource(options.audio, options.signal);\n\t\t\treturn iframe.speechPlay({\n\t\t\t\tsource,\n\t\t\t\tcharacterId: options.characterId,\n\t\t\t\ttranscript: options.transcript,\n\t\t\t\thistory: options.history ?? 'none',\n\t\t\t\tqueue,\n\t\t\t\tsignal: options.signal\n\t\t\t});\n\t\t},\n\n\t\tcreateUtterance(options: SpeechCreateUtteranceOptions): SpeechUtterance {\n\t\t\thost.assertSessionStarted();\n\t\t\trequireCapability('createUtterance', 'externalSpeechAudio');\n\t\t\tconst format = resolveCreateUtteranceFormat(options);\n\t\t\tif (options.track && options.track.kind !== 'audio') {\n\t\t\t\tthrow new SpeechRuntimeError('createUtterance track must be an audio MediaStreamTrack.');\n\t\t\t}\n\t\t\tconst queue: StreamingSpeechQueuePolicy = options.queue ?? 'replace-active';\n\t\t\tif (queue !== 'replace-active' && queue !== 'replace-all') {\n\t\t\t\tthrow new SpeechRuntimeError('createUtterance queue must be replace-active or replace-all.');\n\t\t\t}\n\t\t\thost.onProcessorInterrupt?.();\n\t\t\tif (openUtterance) {\n\t\t\t\tvoid openUtterance.cancel();\n\t\t\t\topenUtterance = null;\n\t\t\t}\n\t\t\tconst utterance = new HostSpeechUtterance({\n\t\t\t\tid: newUtteranceId(),\n\t\t\t\tcharacterId: options.characterId ?? host.getActiveCharacterId(),\n\t\t\t\tformat,\n\t\t\t\tqueue,\n\t\t\t\thost,\n\t\t\t\t...(options.transcript !== undefined ? { transcript: options.transcript } : {}),\n\t\t\t\tsignal: options.signal\n\t\t\t});\n\t\t\topenUtterance = utterance;\n\t\t\tvoid utterance.result.finally(() => {\n\t\t\t\tif (openUtterance === utterance) openUtterance = null;\n\t\t\t});\n\t\t\tif (options.track) {\n\t\t\t\tvoid (async () => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait pipeMediaStreamTrackToUtterance({\n\t\t\t\t\t\t\ttrack: options.track!,\n\t\t\t\t\t\t\tutterance,\n\t\t\t\t\t\t\tsampleRate: format.sampleRate,\n\t\t\t\t\t\t\tsignal: options.signal\n\t\t\t\t\t\t});\n\t\t\t\t\t\tif (options.signal?.aborted) return;\n\t\t\t\t\t\tawait utterance.close({ history: 'none' });\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tawait utterance.cancel().catch(() => undefined);\n\t\t\t\t\t}\n\t\t\t\t})();\n\t\t\t}\n\t\t\treturn utterance;\n\t\t},\n\n\t\tasync interrupt(options?: SpeechInterruptOptions): Promise<readonly SpeechResult[]> {\n\t\t\thost.assertSessionStarted();\n\t\t\thost.onProcessorInterrupt?.();\n\t\t\tconst iframe = host.getIframeHost();\n\t\t\tif (!iframe) return [];\n\t\t\tconst results = await iframe.speechInterrupt(options ?? { scope: 'active' });\n\t\t\tif (openUtterance) {\n\t\t\t\tconst match = results.find((r) => r.utteranceId === openUtterance!.id);\n\t\t\t\tif (match) openUtterance.adoptExternalResult(match);\n\t\t\t\topenUtterance = null;\n\t\t\t}\n\t\t\treturn results;\n\t\t}\n\t};\n\n\treturn controller;\n}\n","export type ExperienceMode = 'conversation' | 'presenter';\nexport type ResponseMode = 'managed' | 'custom' | 'manual';\nexport type SpeechInputMode = 'auto' | 'manual' | 'off';\n/**\n * Viewport framing for the composed location + avatar scene.\n * - `full` (default): height-fit the full avatar into the host div.\n * - `medium`: height-fit a bust window — 0.75H above and 2H below the mesh face oval (3.75H total).\n * - `face`: cover-zoom the scene onto the mesh face oval (inset / PIP use cases).\n */\nexport type ExperienceFit = 'full' | 'face' | 'medium';\n\nexport function parseExperienceFit(value: string | null | undefined): ExperienceFit | undefined {\n\tconst trimmed = value?.trim();\n\treturn trimmed === 'full' || trimmed === 'face' || trimmed === 'medium' ? trimmed : undefined;\n}\n\nexport type SessionInteractionModes = {\n\treadonly mode: ExperienceMode;\n\treadonly responseMode: ResponseMode;\n\treadonly speechInputMode: SpeechInputMode;\n\treadonly processorId?: string;\n};\n\nexport type StartedEvent = {\n\treadonly mode: ExperienceMode;\n};\n\nexport type StartButtonPlacement = 'center' | 'bottom-center' | 'bottom-left' | 'bottom-right';\nexport type StartButtonVariant = 'primary' | 'secondary' | 'minimal';\nexport type StartButtonSize = 'small' | 'medium' | 'large';\nexport type StartButtonShadow = 'none' | 'soft' | 'strong';\nexport type StartButtonIconPosition = 'above' | 'left';\n\nexport type StartButtonAppearance = {\n\treadonly backgroundColor?: string;\n\treadonly textColor?: string;\n\treadonly borderColor?: string;\n\treadonly borderRadiusPx?: number;\n\treadonly borderWidthPx?: number;\n\t/** Explicit label size in px; when set, overrides `size` font scaling. */\n\treadonly fontSizePx?: number;\n\t/**\n\t * Font family stack available in the player iframe (system fonts / generic stacks).\n\t * Host-page webfonts are not loaded into the player frame.\n\t */\n\treadonly fontFamily?: string;\n\t/** CSS font-weight (100–900). */\n\treadonly fontWeight?: number;\n\treadonly size?: StartButtonSize;\n\treadonly shadow?: StartButtonShadow;\n};\n\nexport type StartButtonOptions = {\n\treadonly label?: string;\n\treadonly ariaLabel?: string;\n\treadonly placement?: StartButtonPlacement;\n\treadonly variant?: StartButtonVariant;\n\t/**\n\t * Tabler Iconify id (e.g. `player-play`, `tabler:microphone`) or an image URL\n\t * (`https://…` or `data:image/…`). Site-relative paths are not supported — they\n\t * would resolve against the player iframe origin, not the host page.\n\t */\n\treadonly icon?: string;\n\t/** Icon placement relative to the label. Defaults to `left`. */\n\treadonly iconPosition?: StartButtonIconPosition;\n\treadonly appearance?: StartButtonAppearance;\n};\n\n/**\n * Global player chrome theme (modals, shared buttons, panels).\n * Distinct from {@link StartButtonOptions} — start CTA keeps its own overrides.\n */\nexport type PlayerThemeColors = {\n\treadonly background?: string;\n\treadonly surface?: string;\n\treadonly surfaceMuted?: string;\n\treadonly text?: string;\n\treadonly textMuted?: string;\n\treadonly primary?: string;\n\treadonly primaryText?: string;\n\treadonly border?: string;\n};\n\nexport type PlayerThemeTypography = {\n\treadonly fontFamily?: string;\n\treadonly fontWeight?: number;\n\treadonly fontSizePx?: number;\n};\n\nexport type PlayerThemeButton = {\n\treadonly borderRadiusPx?: number;\n\treadonly borderWidthPx?: number;\n\treadonly shadow?: StartButtonShadow;\n};\n\nexport type PlayerTheme = {\n\treadonly colors?: PlayerThemeColors;\n\treadonly typography?: PlayerThemeTypography;\n\treadonly radiusPx?: number;\n\treadonly button?: PlayerThemeButton;\n};\n\nexport type SpeakBehavior = 'enqueue' | 'interrupt';\n\nexport type SpeakOptions = {\n\treadonly text: string;\n\treadonly characterId?: string;\n\treadonly behavior?: SpeakBehavior;\n};\n\n/** @deprecated Prefer {@link import('../speech').SpeechResult} from Experience.speech. */\nexport type TextSpeakResult = {\n\treadonly speechId: string;\n\treadonly turnId: string;\n\treadonly characterId: string;\n\treadonly text: string;\n\treadonly durationMs: number;\n};\n\n/** @deprecated Alias for TextSpeakResult — use Experience.speech SpeechResult for new code. */\nexport type SpeechResult = TextSpeakResult;\n\nexport type UtteranceResult = {\n\treadonly utteranceId: string;\n\treadonly text: string;\n};\n\nexport type UserTranscriptUpdate = {\n\treadonly utteranceId: string;\n\treadonly text: string;\n\treadonly revision: number;\n\treadonly isFinal: boolean;\n\treadonly delta?: string;\n};\n\nexport type ConversationMessageSource = 'user' | 'llm' | 'speak' | 'opening' | 'speechAudio';\n\nexport type ConversationMessage = {\n\treadonly messageId: string;\n\treadonly turnId: string;\n\treadonly role: 'user' | 'assistant';\n\treadonly text: string;\n\treadonly characterId?: string;\n\treadonly source: ConversationMessageSource;\n\treadonly status: 'final';\n\treadonly timestamp: number;\n};\n\nexport type CharacterSpeechSource = 'llm' | 'speak' | 'opening' | 'speechAudio';\n\nexport type CharacterSpeechTerminalReason =\n\t| 'completed'\n\t| 'interrupted'\n\t| 'error'\n\t| 'destroyed';\n\nexport type CharacterSpeechEvent = {\n\treadonly speechId: string;\n\treadonly turnId: string;\n\treadonly characterId: string;\n\treadonly text: string;\n\treadonly source: CharacterSpeechSource;\n\treadonly durationMs?: number;\n\treadonly reason?: CharacterSpeechTerminalReason;\n};\n\nexport type SessionModeChange = 'speaking' | 'listening' | 'thinking';\n\nexport type UserSpeechBoundaryEvent = {\n\treadonly utteranceId?: string;\n};\n\nexport type ConversationProcessorResult =\n\t| string\n\t| {\n\t\t\treadonly text: string;\n\t\t\treadonly characterId?: string;\n\t\t\treadonly behavior?: SpeakBehavior;\n\t };\n\nexport type ConversationProcessorContext = {\n\treadonly utteranceId: string;\n\treadonly text: string;\n\treadonly conversation: readonly ConversationMessage[];\n\treadonly signal: AbortSignal;\n};\n\nexport type ConversationProcessorFn = (\n\tcontext: ConversationProcessorContext\n) =>\n\t| ConversationProcessorResult\n\t| Promise<ConversationProcessorResult>\n\t| AsyncIterable<string>;\n\nexport type NormalizedProcessorSpeech = {\n\treadonly text: string;\n\treadonly characterId?: string;\n\treadonly behavior?: SpeakBehavior;\n};\n\nexport type ProcessorDispatchResult =\n\t| { readonly kind: 'complete'; readonly speech: NormalizedProcessorSpeech }\n\t| { readonly kind: 'stream'; readonly stream: AsyncIterable<string> };\n\nexport type ConversationProcessorErrorEvent = {\n\treadonly utteranceId: string;\n\treadonly message: string;\n};\n\nexport type ListenOnceOptions = {\n\treadonly signal?: AbortSignal;\n\treadonly timeoutMs?: number;\n};\n","import { fetchSessionLaunch, mintBrowserSession } from './api/mintPublicSession';\nimport { dispatchConversationProcessor } from './conversationProcessor';\nimport { createIframeHost, type IframeHost } from './embed/iframeHost';\nimport { createReadyGate } from './embed/readyGate';\nimport type { CloseEvent, EmbedState } from './embed/protocol';\nimport {\n\tbuildManifestEmbedUrl,\n\tresolvePlayerOrigin,\n\tresolvePublicPlayerEmbedUrl,\n\tresolveSceneHintId\n} from './shims/embedBaseUrl';\nimport type {\n\tCloseButtonOptions,\n\tPlayerStatus,\n\tSession,\n\tSessionActivity,\n\tSessionLaunchResponse\n} from './types/sessionLaunch';\nimport { warnLocaleVoiceFallbacks } from './types/sessionLaunch';\nimport {\n\tclearExperienceEventRegistry,\n\tcreateExperienceEventEnvelope,\n\tcreateExperienceEventRegistry,\n\temitExperienceEvent,\n\tsubscribeExperienceEvent,\n\ttype ExperienceEventName,\n\ttype ExperienceEvents\n} from './experienceEvents';\nimport {\n\tcreateSpeechController,\n\ttype SpeechCapabilities,\n\ttype SpeechController\n} from './speech/speechController';\nimport {\n\tparseExperienceFit,\n\ttype CharacterSpeechEvent,\n\ttype ConversationMessage,\n\ttype ConversationProcessorFn,\n\ttype ExperienceFit,\n\ttype ExperienceMode,\n\ttype ListenOnceOptions,\n\ttype SpeechInputMode,\n\ttype StartButtonOptions,\n\ttype PlayerTheme,\n\ttype StartedEvent,\n\ttype UserTranscriptUpdate,\n\ttype UtteranceResult\n} from './types/sessionInteraction';\nimport type { SessionManifest } from './types/sessionManifest';\n\nexport type { CloseEvent };\nexport type {\n\tCharacterSpeechEvent,\n\tCharacterSpeechSource,\n\tCharacterSpeechTerminalReason,\n\tConversationMessage,\n\tConversationProcessorContext,\n\tConversationProcessorErrorEvent,\n\tConversationProcessorFn,\n\tConversationProcessorResult,\n\tExperienceFit,\n\tListenOnceOptions,\n\tSpeakOptions,\n\tTextSpeakResult,\n\tSpeechResult as LegacySpeakResult,\n\tStartButtonAppearance,\n\tStartButtonOptions,\n\tStartButtonPlacement,\n\tStartButtonShadow,\n\tStartButtonSize,\n\tStartButtonVariant,\n\tStartButtonIconPosition,\n\tPlayerTheme,\n\tPlayerThemeButton,\n\tPlayerThemeColors,\n\tPlayerThemeTypography,\n\tUserSpeechBoundaryEvent,\n\tUserTranscriptUpdate,\n\tUtteranceResult\n} from './types/sessionInteraction';\nexport type {\n\tCloseButtonOptions,\n\tPlayerStatus,\n\tSession,\n\tSessionActivity,\n\tSessionCapabilities,\n\tSessionLaunchResponse,\n\tSessionMode\n} from './types/sessionLaunch';\nexport type {\n\tSpeechAudioFormat,\n\tSpeechController,\n\tSpeechCreateUtteranceOptions,\n\tSpeechInterruptOptions,\n\tSpeechPlayAudio,\n\tSpeechPlayEncodedAudio,\n\tSpeechPlayOptions,\n\tSpeechPlayPcmAudio,\n\tSpeechPlayUrlAudio,\n\tSpeechResult,\n\tSpeechSpeakOptions,\n\tSpeechUtterance\n} from './speech';\nexport {\n\tSPEECH_LIMITS,\n\tSpeechBufferTimeoutError,\n\tSpeechRuntimeError,\n\tSpeechUtteranceClosedError,\n\tUnsupportedSpeechOperationError\n} from './speech';\n\nexport type {\n\tExperienceEventEnvelope,\n\tExperienceEventName,\n\tExperienceEvents,\n\tFeedbackFailedEvent,\n\tFeedbackResult\n} from './experienceEvents';\n\ntype StartSessionShared = {\n\treadonly embedBaseUrl?: string;\n\treadonly debug?: boolean;\n\t/** ExperienceWidget warm embed chrome (`?ui=widget`). */\n\treadonly ui?: 'widget';\n\treadonly onStart?: (event: StartedEvent) => void | Promise<void>;\n\treadonly onUserTranscript?: (update: UserTranscriptUpdate) => void | Promise<void>;\n\treadonly conversationProcessor?: ConversationProcessorFn;\n\t/**\n\t * Whole-snapshot marketing attribution. `false` disables auto-capture.\n\t * When omitted, the SDK reads standard UTMs + document.referrer from the embedding page.\n\t */\n\treadonly attribution?: import('./api/captureAttribution').ExperienceAttribution | false;\n};\n\nexport type StartSessionOptions =\n\t| (StartSessionShared & {\n\t\t\treadonly experienceId: string;\n\t\t\t/** BCP 47 locale (preferred). When omitted in the browser, defaults to `navigator.language`. */\n\t\t\treadonly locale?: string;\n\t\t\treadonly secondaryLocale?: string;\n\t\t\treadonly learningLocale?: string;\n\t\t\t/** @deprecated Prefer `locale`. */\n\t\t\treadonly language?: 'en' | 'es' | string;\n\t\t\treadonly backdropId?: string;\n\t\t\t/** @deprecated Prefer `backdropId`. */\n\t\t\treadonly locationId?: string;\n\t\t\treadonly avatarId?: string;\n\t\t\treadonly castSize?: 2 | 3 | 4;\n\t\t\treadonly mode?: ExperienceMode;\n\t\t\treadonly speechInputMode?: SpeechInputMode;\n\t\t\t/** Voice-only session: skip avatar renderer and location scene asset loading. */\n\t\t\treadonly speechOnly?: boolean;\n\t })\n\t| (StartSessionShared & {\n\t\t\treadonly experienceId: string;\n\t\t\treadonly sessionEndpoint: string;\n\t\t\t/** BCP 47 locale (preferred). When omitted in the browser, defaults to `navigator.language`. */\n\t\t\treadonly locale?: string;\n\t\t\treadonly secondaryLocale?: string;\n\t\t\treadonly learningLocale?: string;\n\t\t\t/** @deprecated Prefer `locale`. */\n\t\t\treadonly language?: 'en' | 'es' | string;\n\t\t\treadonly backdropId?: string;\n\t\t\t/** @deprecated Prefer `backdropId`. */\n\t\t\treadonly locationId?: string;\n\t\t\treadonly avatarId?: string;\n\t\t\treadonly castSize?: 2 | 3 | 4;\n\t\t\treadonly mode?: ExperienceMode;\n\t\t\treadonly speechInputMode?: SpeechInputMode;\n\t\t\t/** Voice-only session: skip avatar renderer and location scene asset loading. */\n\t\t\treadonly speechOnly?: boolean;\n\t })\n\t| (StartSessionShared & {\n\t\t\t/**\n\t\t\t * Opaque signed launch string from a prior Session Launch response.\n\t\t\t * Prefer `sessionEndpoint` so page JS does not hold launch when avoidable.\n\t\t\t */\n\t\t\treadonly launch: string;\n\t\t\treadonly session?: Session;\n\t\t\treadonly backdropId?: string;\n\t\t\t/** @deprecated Prefer `backdropId`. */\n\t\t\treadonly locationId?: string;\n\t\t\treadonly avatarId?: string;\n\t\t\t/** Voice-only session: skip avatar renderer and location scene asset loading. */\n\t\t\treadonly speechOnly?: boolean;\n\t });\n\nexport type AttachOptions = {\n\treadonly container: HTMLElement;\n\t/** Initial scene framing. Change later with {@link Experience.setFit}. */\n\treadonly fit?: ExperienceFit;\n\treadonly startButton?: StartButtonOptions;\n\t/**\n\t * Global player chrome theme (introduction, feedback, shared buttons, panels).\n\t * Distinct from {@link AttachOptions.startButton} CTA overrides. Presentation only — does not remint.\n\t */\n\treadonly theme?: PlayerTheme;\n\t/**\n\t * When true (default), the Liforma player shows end-of-session Feedback UI.\n\t * When false, hosts receive scores via `feedback` / `feedbackFailed` and render their own UX.\n\t * Presentation only — does not remint.\n\t */\n\treadonly showFeedback?: boolean;\n\treadonly closeButton?: CloseButtonOptions | boolean;\n\treadonly returnUrl?: string;\n\treadonly onPlayerStatusChange?: (status: PlayerStatus) => void;\n\t/** @deprecated Prefer {@link AttachOptions.onPlayerStatusChange}. */\n\treadonly onStateUpdate?: (state: EmbedState) => void;\n\treadonly onWidgetReady?: () => void;\n\treadonly onWidgetExpand?: () => void;\n\treadonly onWidgetCollapse?: () => void;\n\treadonly onWidgetAudioSettings?: (open: boolean) => void;\n\t/** Docked shell: player surface drag → parent repositions the dock. */\n\treadonly onWidgetDragStart?: (event: { readonly pointerId: number }) => void;\n\treadonly onWidgetDragMove?: (event: {\n\t\treadonly pointerId: number;\n\t\treadonly dx: number;\n\t\treadonly dy: number;\n\t}) => void;\n\treadonly onWidgetDragEnd?: (event: { readonly pointerId: number }) => void;\n\t/**\n\t * Show the in-player close control. Set when the host provides `onClose`\n\t * and/or `returnUrl` / `closeButton`. Widget chrome keeps its own toolbar close separately.\n\t * @deprecated Prefer {@link AttachOptions.closeButton}.\n\t */\n\treadonly showClose?: boolean;\n};\n\nexport type { EmbedState };\n\nexport type ReadyEvent = {\n\treadonly session: Session;\n};\n\nexport type LiformaPlayer = {\n\tclose(): void;\n\tdestroy(): void;\n\ton(event: 'close', handler: (event: CloseEvent) => void): void;\n};\n\ntype SessionBootstrap =\n\t| {\n\t\t\treadonly kind: 'public';\n\t\t\treadonly experienceId: string;\n\t\t\treadonly locale?: string;\n\t\t\treadonly secondaryLocale?: string;\n\t\t\treadonly learningLocale?: string;\n\t\t\treadonly language?: string;\n\t\t\treadonly backdropId?: string;\n\t\t\treadonly locationId?: string;\n\t\t\treadonly avatarId?: string;\n\t\t\treadonly castSize?: 2 | 3 | 4;\n\t\t\treadonly embedBaseUrl?: string;\n\t\t\treadonly debug?: boolean;\n\t\t\treadonly mode?: ExperienceMode;\n\t\t\treadonly speechInputMode?: SpeechInputMode;\n\t\t\treadonly speechOnly?: boolean;\n\t\t\treadonly attribution?: import('./api/captureAttribution').ExperienceAttribution | false;\n\t\t\treadonly ui?: 'widget';\n\t\t\treadonly onStart?: (event: StartedEvent) => void | Promise<void>;\n\t\t\treadonly onUserTranscript?: (update: UserTranscriptUpdate) => void | Promise<void>;\n\t\t\treadonly conversationProcessor?: ConversationProcessorFn;\n\t }\n\t| {\n\t\t\treadonly kind: 'sessionEndpoint';\n\t\t\treadonly experienceId: string;\n\t\t\treadonly sessionEndpoint: string;\n\t\t\treadonly locale?: string;\n\t\t\treadonly secondaryLocale?: string;\n\t\t\treadonly learningLocale?: string;\n\t\t\treadonly language?: string;\n\t\t\treadonly backdropId?: string;\n\t\t\treadonly locationId?: string;\n\t\t\treadonly avatarId?: string;\n\t\t\treadonly castSize?: 2 | 3 | 4;\n\t\t\treadonly embedBaseUrl?: string;\n\t\t\treadonly debug?: boolean;\n\t\t\treadonly mode?: ExperienceMode;\n\t\t\treadonly speechInputMode?: SpeechInputMode;\n\t\t\treadonly speechOnly?: boolean;\n\t\t\treadonly attribution?: import('./api/captureAttribution').ExperienceAttribution | false;\n\t\t\treadonly ui?: 'widget';\n\t\t\treadonly onStart?: (event: StartedEvent) => void | Promise<void>;\n\t\t\treadonly onUserTranscript?: (update: UserTranscriptUpdate) => void | Promise<void>;\n\t\t\treadonly conversationProcessor?: ConversationProcessorFn;\n\t }\n\t| {\n\t\t\treadonly kind: 'launch';\n\t\t\treadonly launch: string;\n\t\t\treadonly session?: Session;\n\t\t\treadonly backdropId?: string;\n\t\t\treadonly locationId?: string;\n\t\t\treadonly avatarId?: string;\n\t\t\treadonly embedBaseUrl?: string;\n\t\t\treadonly debug?: boolean;\n\t\t\treadonly speechOnly?: boolean;\n\t\t\treadonly ui?: 'widget';\n\t\t\treadonly onStart?: (event: StartedEvent) => void | Promise<void>;\n\t\t\treadonly onUserTranscript?: (update: UserTranscriptUpdate) => void | Promise<void>;\n\t\t\treadonly conversationProcessor?: ConversationProcessorFn;\n\t };\n\nfunction resolveShowClose(options: AttachOptions): boolean {\n\tif (options.showClose === true) return true;\n\tif (options.returnUrl?.trim()) return true;\n\tif (options.closeButton === true) return true;\n\tif (\n\t\toptions.closeButton &&\n\t\ttypeof options.closeButton === 'object' &&\n\t\toptions.closeButton.visible !== false\n\t) {\n\t\treturn true;\n\t}\n\treturn false;\n}\n\nexport class Experience {\n\tprivate readonly bootstrap: SessionBootstrap;\n\tprivate readonly conversationProcessor: ConversationProcessorFn | null;\n\tprivate iframeHost: IframeHost | null = null;\n\tprivate attachInFlight: Promise<LiformaPlayer> | null = null;\n\tprivate endpointAbortController: AbortController | null = null;\n\tprivate processorAbortController: AbortController | null = null;\n\tprivate processorChain: Promise<void> = Promise.resolve();\n\tprivate resolvedSession: Session | null;\n\tprivate launchToken: string | null;\n\tprivate attachReturnUrl: string | null = null;\n\tprivate closed = false;\n\tprivate attached = false;\n\tprivate pendingFit: ExperienceFit | null = null;\n\tprivate readonly closeHandlers: ((event: CloseEvent) => void)[] = [];\n\tprivate readonly eventRegistry = createExperienceEventRegistry();\n\tprivate readyEnvelope: ExperienceEvents['ready'] | null = null;\n\tprivate startedEnvelope: ExperienceEvents['started'] | null = null;\n\tprivate conversationCache: readonly ConversationMessage[] = [];\n\t/** Public speech API — text TTS and BYO PCM (`speak` / `play` / `createUtterance` / `interrupt`). */\n\treadonly speech: SpeechController;\n\n\tprivate constructor(bootstrap: SessionBootstrap) {\n\t\tthis.bootstrap = bootstrap;\n\t\tthis.conversationProcessor = bootstrap.conversationProcessor ?? null;\n\t\tthis.resolvedSession = bootstrap.kind === 'launch' ? (bootstrap.session ?? null) : null;\n\t\tthis.launchToken = bootstrap.kind === 'launch' ? bootstrap.launch : null;\n\t\tthis.speech = createSpeechController({\n\t\t\tgetIframeHost: () => this.iframeHost,\n\t\t\tassertSessionStarted: () => this.assertSessionStarted(),\n\t\t\tgetCapabilities: () => resolveSpeechCapabilities(this.resolvedSession),\n\t\t\tgetActiveCharacterId: () => 'character_unknown',\n\t\t\tonProcessorInterrupt: () => {\n\t\t\t\tthis.processorAbortController?.abort();\n\t\t\t\tthis.processorAbortController = null;\n\t\t\t}\n\t\t});\n\t}\n\n\tstatic async startSession(options: StartSessionOptions): Promise<Experience> {\n\t\tif ('launch' in options && typeof options.launch === 'string') {\n\t\t\tconst launch = options.launch.trim();\n\t\t\tif (!launch) {\n\t\t\t\tthrow new Error('StartSessionOptions.launch must be a non-empty string.');\n\t\t\t}\n\t\t\treturn new Experience({\n\t\t\t\tkind: 'launch',\n\t\t\t\tlaunch,\n\t\t\t\tsession: options.session,\n\t\t\t\tbackdropId: options.backdropId,\n\t\t\t\tlocationId: options.locationId,\n\t\t\t\tavatarId: options.avatarId,\n\t\t\t\tembedBaseUrl: options.embedBaseUrl,\n\t\t\t\tdebug: options.debug,\n\t\t\t\tspeechOnly: options.speechOnly,\n\t\t\t\tui: options.ui,\n\t\t\t\tonStart: options.onStart,\n\t\t\t\tonUserTranscript: options.onUserTranscript,\n\t\t\t\tconversationProcessor: options.conversationProcessor\n\t\t\t});\n\t\t}\n\n\t\tif ('sessionEndpoint' in options) {\n\t\t\treturn new Experience({\n\t\t\t\tkind: 'sessionEndpoint',\n\t\t\t\texperienceId: options.experienceId,\n\t\t\t\tsessionEndpoint: options.sessionEndpoint,\n\t\t\t\tlocale: options.locale,\n\t\t\t\tsecondaryLocale: options.secondaryLocale,\n\t\t\t\tlearningLocale: options.learningLocale,\n\t\t\t\tlanguage: options.language,\n\t\t\t\tbackdropId: options.backdropId,\n\t\t\t\tlocationId: options.locationId,\n\t\t\t\tavatarId: options.avatarId,\n\t\t\t\tcastSize: options.castSize,\n\t\t\t\tembedBaseUrl: options.embedBaseUrl,\n\t\t\t\tdebug: options.debug,\n\t\t\t\tmode: options.mode,\n\t\t\t\tspeechInputMode: options.speechInputMode,\n\t\t\t\tspeechOnly: options.speechOnly,\n\t\t\t\tattribution: options.attribution,\n\t\t\t\tui: options.ui,\n\t\t\t\tonStart: options.onStart,\n\t\t\t\tonUserTranscript: options.onUserTranscript,\n\t\t\t\tconversationProcessor: options.conversationProcessor\n\t\t\t});\n\t\t}\n\n\t\tif ('experienceId' in options) {\n\t\t\treturn new Experience({\n\t\t\t\tkind: 'public',\n\t\t\t\texperienceId: options.experienceId,\n\t\t\t\tlocale: options.locale,\n\t\t\t\tsecondaryLocale: options.secondaryLocale,\n\t\t\t\tlearningLocale: options.learningLocale,\n\t\t\t\tlanguage: options.language,\n\t\t\t\tbackdropId: options.backdropId,\n\t\t\t\tlocationId: options.locationId,\n\t\t\t\tavatarId: options.avatarId,\n\t\t\t\tcastSize: options.castSize,\n\t\t\t\tembedBaseUrl: options.embedBaseUrl,\n\t\t\t\tdebug: options.debug,\n\t\t\t\tmode: options.mode,\n\t\t\t\tspeechInputMode: options.speechInputMode,\n\t\t\t\tspeechOnly: options.speechOnly,\n\t\t\t\tattribution: options.attribution,\n\t\t\t\tui: options.ui,\n\t\t\t\tonStart: options.onStart,\n\t\t\t\tonUserTranscript: options.onUserTranscript,\n\t\t\t\tconversationProcessor: options.conversationProcessor\n\t\t\t});\n\t\t}\n\n\t\tthrow new Error(\n\t\t\t'Experience.startSession requires experienceId, sessionEndpoint, or launch.'\n\t\t);\n\t}\n\n\tgetSession(): Session | null {\n\t\treturn this.resolvedSession;\n\t}\n\n\t/**\n\t * @deprecated Session Manifest is no longer a public SDK contract.\n\t * Use {@link Experience.getSession} for the public Session summary.\n\t */\n\tgetManifest(): SessionManifest | null {\n\t\treturn null;\n\t}\n\n\ton<K extends ExperienceEventName>(\n\t\tevent: K,\n\t\thandler: (event: ExperienceEvents[K]) => void\n\t): () => void {\n\t\tconst unsubscribe = subscribeExperienceEvent(this.eventRegistry, event, handler);\n\t\tif (event === 'ready' && this.readyEnvelope) {\n\t\t\tqueueMicrotask(() => handler(this.readyEnvelope as ExperienceEvents[K]));\n\t\t}\n\t\tif (event === 'started' && this.startedEnvelope) {\n\t\t\tqueueMicrotask(() => handler(this.startedEnvelope as ExperienceEvents[K]));\n\t\t}\n\t\treturn unsubscribe;\n\t}\n\n\tasync attach(options: AttachOptions): Promise<LiformaPlayer> {\n\t\tif (this.closed) {\n\t\t\tthrow new Error('Session has ended.');\n\t\t}\n\t\tif (!options.container) {\n\t\t\tthrow new Error('Experience.attach requires a DOM container element.');\n\t\t}\n\t\tif (this.attached && this.iframeHost) {\n\t\t\treturn this.createPlayerFacade();\n\t\t}\n\t\tif (this.attachInFlight) {\n\t\t\treturn this.attachInFlight;\n\t\t}\n\n\t\tthis.attachInFlight = this.attachInternal(options).finally(() => {\n\t\t\tthis.attachInFlight = null;\n\t\t});\n\t\treturn this.attachInFlight;\n\t}\n\n\tprivate async attachInternal(options: AttachOptions): Promise<LiformaPlayer> {\n\t\tif (this.closed) {\n\t\t\tthrow new Error('Session has ended.');\n\t\t}\n\t\tif (this.attached && this.iframeHost) {\n\t\t\treturn this.createPlayerFacade();\n\t\t}\n\t\tif (this.iframeHost) {\n\t\t\tthis.iframeHost.destroy();\n\t\t\tthis.iframeHost = null;\n\t\t}\n\n\t\tthis.attachReturnUrl = options.returnUrl?.trim() || null;\n\n\t\tconst embedBaseUrl = this.bootstrap.embedBaseUrl ?? resolvePublicPlayerEmbedUrl();\n\t\tconst debug = this.bootstrap.debug === true;\n\t\tconst speechOnly = this.bootstrap.speechOnly === true;\n\t\tconst locationId = speechOnly\n\t\t\t? undefined\n\t\t\t: resolveSceneHintId(this.bootstrap.backdropId, this.bootstrap.locationId);\n\t\tconst avatarId = speechOnly ? undefined : this.bootstrap.avatarId;\n\t\t// The SDK owns minting and posts the opaque launch. Do not put experienceId in the\n\t\t// iframe URL or the player will mint a second session before the parent launch arrives.\n\t\tconst iframeSrc = buildManifestEmbedUrl(\n\t\t\tembedBaseUrl,\n\t\t\tdebug,\n\t\t\tlocationId,\n\t\t\tavatarId,\n\t\t\toptions.fit,\n\t\t\tthis.bootstrap.ui,\n\t\t\tresolveShowClose(options),\n\t\t\tthis.bootstrap.kind === 'launch' ? undefined : this.bootstrap.castSize\n\t\t);\n\t\tconst playerOrigin = resolvePlayerOrigin(iframeSrc);\n\n\t\tlet resolveLoadingUi: (() => void) | null = null;\n\t\tconst loadingUiPromise = new Promise<void>((resolve) => {\n\t\t\tresolveLoadingUi = resolve;\n\t\t});\n\t\tlet resolveReadyUi: (() => void) | null = null;\n\t\tconst readyUiPromise = new Promise<void>((resolve) => {\n\t\t\tresolveReadyUi = resolve;\n\t\t});\n\t\tconst readyGate = createReadyGate<Session>((readySession) => {\n\t\t\tif (this.readyEnvelope) return;\n\t\t\tthis.resolvedSession = readySession;\n\t\t\tthis.readyEnvelope = this.emitWrapped('ready', { session: readySession });\n\t\t});\n\n\t\tconst onUserTranscript = this.bootstrap.onUserTranscript;\n\n\t\tconst onPlayerStatus = (status: PlayerStatus) => {\n\t\t\toptions.onPlayerStatusChange?.(status);\n\t\t\toptions.onStateUpdate?.(status);\n\t\t\tif (status === 'loading' && resolveLoadingUi) {\n\t\t\t\tresolveLoadingUi();\n\t\t\t\tresolveLoadingUi = null;\n\t\t\t}\n\t\t\tif (status === 'ready' && resolveReadyUi) {\n\t\t\t\tresolveReadyUi();\n\t\t\t\tresolveReadyUi = null;\n\t\t\t}\n\t\t\tif (status === 'ready') {\n\t\t\t\treadyGate.markReady();\n\t\t\t}\n\t\t};\n\n\t\tconst iframeHost = createIframeHost({\n\t\t\tcontainer: options.container,\n\t\t\tsrc: iframeSrc,\n\t\t\tplayerOrigin,\n\t\t\ttransparent: this.bootstrap.ui === 'widget',\n\t\t\tonClose: (event) => {\n\t\t\t\tconst enriched: CloseEvent =\n\t\t\t\t\tevent.returnUrl || !this.attachReturnUrl\n\t\t\t\t\t\t? event\n\t\t\t\t\t\t: { ...event, returnUrl: this.attachReturnUrl };\n\t\t\t\tfor (const handler of this.closeHandlers) {\n\t\t\t\t\thandler(enriched);\n\t\t\t\t}\n\t\t\t},\n\t\t\tonPlayerStatusChange: onPlayerStatus,\n\t\t\tonUserTranscript: (update) => {\n\t\t\t\tthis.emitWrapped('userTranscript', update);\n\t\t\t\tvoid onUserTranscript?.(update);\n\t\t\t\tthis.queueProcessorTurn(update);\n\t\t\t},\n\t\t\tonUserSpeechStarted: (event) => {\n\t\t\t\tthis.emitWrapped('userSpeechStarted', event);\n\t\t\t},\n\t\t\tonUserSpeechEnded: (event) => {\n\t\t\t\tthis.emitWrapped('userSpeechEnded', event);\n\t\t\t},\n\t\t\tonCharacterSpeechStarted: (event: CharacterSpeechEvent) => {\n\t\t\t\tthis.emitWrapped('characterSpeechStarted', event);\n\t\t\t},\n\t\t\tonCharacterSpeechEnded: (event: CharacterSpeechEvent) => {\n\t\t\t\tthis.emitWrapped('characterSpeechEnded', event);\n\t\t\t},\n\t\t\tonCharacterFocusChanged: (event) => {\n\t\t\t\tthis.emitWrapped('characterFocusChanged', event);\n\t\t\t},\n\t\t\tonListeningState: (listening) => {\n\t\t\t\tthis.emitWrapped('listeningState', listening);\n\t\t\t},\n\t\t\tonActivityChange: (activity: SessionActivity) => {\n\t\t\t\tthis.emitWrapped('activityChange', activity);\n\t\t\t},\n\t\t\tonFeedback: (result) => {\n\t\t\t\tthis.emitWrapped('feedback', result);\n\t\t\t},\n\t\t\tonFeedbackFailed: (event) => {\n\t\t\t\tthis.emitWrapped('feedbackFailed', event);\n\t\t\t},\n\t\t\tonMessage: (message) => {\n\t\t\t\tthis.emitWrapped('message', message);\n\t\t\t},\n\t\t\tonStarted: (event) => {\n\t\t\t\tif (!this.startedEnvelope) {\n\t\t\t\t\tthis.startedEnvelope = this.emitWrapped('started', event);\n\t\t\t\t\tvoid this.bootstrap.onStart?.(event);\n\t\t\t\t}\n\t\t\t},\n\t\t\tonConversationUpdate: (conversation) => {\n\t\t\t\tthis.conversationCache = conversation;\n\t\t\t\tthis.emitWrapped('conversationUpdate', conversation);\n\t\t\t},\n\t\t\tonWidgetReady: () => options.onWidgetReady?.(),\n\t\t\tonWidgetExpand: () => options.onWidgetExpand?.(),\n\t\t\tonWidgetCollapse: () => options.onWidgetCollapse?.(),\n\t\t\tonWidgetAudioSettings: (open) => options.onWidgetAudioSettings?.(open),\n\t\t\tonWidgetDragStart: (event) => options.onWidgetDragStart?.(event),\n\t\t\tonWidgetDragMove: (event) => options.onWidgetDragMove?.(event),\n\t\t\tonWidgetDragEnd: (event) => options.onWidgetDragEnd?.(event)\n\t\t});\n\t\tthis.iframeHost = iframeHost;\n\n\t\tconst mintInteraction = this.resolveMintInteraction();\n\n\t\tconst launchPromise: Promise<SessionLaunchResponse | null> =\n\t\t\tthis.bootstrap.kind === 'sessionEndpoint'\n\t\t\t\t? (() => {\n\t\t\t\t\t\tthis.endpointAbortController = new AbortController();\n\t\t\t\t\t\treturn fetchSessionLaunch(this.bootstrap.sessionEndpoint, {\n\t\t\t\t\t\t\texperienceId: this.bootstrap.experienceId,\n\t\t\t\t\t\t\tlocale: this.bootstrap.locale,\n\t\t\t\t\t\t\tsecondaryLocale: this.bootstrap.secondaryLocale,\n\t\t\t\t\t\t\tlearningLocale: this.bootstrap.learningLocale,\n\t\t\t\t\t\t\tlanguage: this.bootstrap.language,\n\t\t\t\t\t\t\tmode: mintInteraction.mode,\n\t\t\t\t\t\t\tspeechInputMode: mintInteraction.speechInputMode,\n\t\t\t\t\t\t\tspeechOnly: this.bootstrap.speechOnly,\n\t\t\t\t\t\t\tattribution: this.bootstrap.attribution,\n\t\t\t\t\t\t\tsignal: this.endpointAbortController.signal\n\t\t\t\t\t\t});\n\t\t\t\t\t})()\n\t\t\t\t: this.bootstrap.kind === 'public'\n\t\t\t\t\t? mintBrowserSession(this.bootstrap.experienceId, {\n\t\t\t\t\t\t\tlocale: this.bootstrap.locale,\n\t\t\t\t\t\t\tsecondaryLocale: this.bootstrap.secondaryLocale,\n\t\t\t\t\t\t\tlearningLocale: this.bootstrap.learningLocale,\n\t\t\t\t\t\t\tlanguage: this.bootstrap.language,\n\t\t\t\t\t\t\tmode: mintInteraction.mode,\n\t\t\t\t\t\t\tspeechInputMode: mintInteraction.speechInputMode,\n\t\t\t\t\t\t\tspeechOnly: this.bootstrap.speechOnly,\n\t\t\t\t\t\t\tattribution: this.bootstrap.attribution\n\t\t\t\t\t\t})\n\t\t\t\t\t: Promise.resolve(\n\t\t\t\t\t\t\tthis.bootstrap.session\n\t\t\t\t\t\t\t\t? { session: this.bootstrap.session, launch: this.bootstrap.launch }\n\t\t\t\t\t\t\t\t: null\n\t\t\t\t\t\t);\n\n\t\tlet launchResponse: SessionLaunchResponse | null;\n\t\ttry {\n\t\t\t[, launchResponse] = await Promise.all([iframeHost.waitForLoad(), launchPromise]);\n\t\t} catch (error) {\n\t\t\tiframeHost.destroy();\n\t\t\tif (this.iframeHost === iframeHost) {\n\t\t\t\tthis.iframeHost = null;\n\t\t\t}\n\t\t\tthrow error;\n\t\t} finally {\n\t\t\tthis.endpointAbortController = null;\n\t\t}\n\t\tiframeHost.postParentHello();\n\n\t\tconst launchToken =\n\t\t\tlaunchResponse?.launch ??\n\t\t\t(this.bootstrap.kind === 'launch' ? this.bootstrap.launch : null);\n\t\tconst session =\n\t\t\tlaunchResponse?.session ??\n\t\t\t(this.bootstrap.kind === 'launch' ? this.bootstrap.session : undefined);\n\n\t\tif (!launchToken) {\n\t\t\tiframeHost.destroy();\n\t\t\tif (this.iframeHost === iframeHost) {\n\t\t\t\tthis.iframeHost = null;\n\t\t\t}\n\t\t\tthrow new Error('Session Launch response did not include a launch token.');\n\t\t}\n\n\t\tthis.launchToken = launchToken;\n\t\tif (session) {\n\t\t\tthis.resolvedSession = session;\n\t\t\t// Mint/endpoint paths already warn inside parseSessionLaunchResponse.\n\t\t\tif (this.bootstrap.kind === 'launch') {\n\t\t\t\twarnLocaleVoiceFallbacks(session);\n\t\t\t}\n\t\t\tiframeHost.postLaunch(launchToken, session, {\n\t\t\t\tstartButton: options.startButton,\n\t\t\t\ttheme: options.theme,\n\t\t\t\tshowFeedback: options.showFeedback\n\t\t\t});\n\t\t\treadyGate.setValue(session);\n\t\t} else {\n\t\t\tiframeHost.postLaunch(launchToken, undefined, {\n\t\t\t\tstartButton: options.startButton,\n\t\t\t\ttheme: options.theme,\n\t\t\t\tshowFeedback: options.showFeedback\n\t\t\t});\n\t\t}\n\n\t\tawait Promise.race([\n\t\t\tloadingUiPromise,\n\t\t\tnew Promise<void>((_, reject) => {\n\t\t\t\tsetTimeout(\n\t\t\t\t\t() => reject(new Error('Timed out waiting for player loading UI.')),\n\t\t\t\t\t120_000\n\t\t\t\t);\n\t\t\t})\n\t\t]);\n\n\t\tawait Promise.race([\n\t\t\treadyUiPromise,\n\t\t\tnew Promise<void>((_, reject) => {\n\t\t\t\tsetTimeout(\n\t\t\t\t\t() => reject(new Error('Timed out waiting for player ready state.')),\n\t\t\t\t\t120_000\n\t\t\t\t);\n\t\t\t})\n\t\t]);\n\n\t\tthis.attached = true;\n\t\tif (this.pendingFit) {\n\t\t\tawait iframeHost.setFit(this.pendingFit);\n\t\t}\n\t\treturn this.createPlayerFacade();\n\t}\n\n\tasync startListening(): Promise<void> {\n\t\tthis.assertSessionStarted();\n\t\tawait this.iframeHost!.startListening();\n\t}\n\n\tasync stopListening(): Promise<UtteranceResult> {\n\t\tthis.assertSessionStarted();\n\t\treturn this.iframeHost!.stopListening();\n\t}\n\n\tasync listenOnce(options?: ListenOnceOptions): Promise<UtteranceResult> {\n\t\tthis.assertSessionStarted();\n\t\treturn this.iframeHost!.listenOnce({\n\t\t\ttimeoutMs: options?.timeoutMs\n\t\t});\n\t}\n\n\tgetConversation(): readonly ConversationMessage[] {\n\t\treturn this.conversationCache;\n\t}\n\n\t/** Parent → player chrome mode for ExperienceWidget warm/cold expand / dock. */\n\tsetWidgetChrome(mode: 'collapsed' | 'loading' | 'expanded' | 'docked'): void {\n\t\tthis.iframeHost?.postWidgetSetChrome(mode);\n\t}\n\n\t/** Parent → player toggle for mic/speaker settings (ExperienceWidget toolbar). */\n\ttoggleAudioSettings(): void {\n\t\tthis.iframeHost?.postWidgetToggleAudioSettings();\n\t}\n\n\t/** Presentation-only. No-op on single-Character nodes. */\n\tasync focusCharacter(characterId: string): Promise<void> {\n\t\tthis.assertSessionStarted();\n\t\tawait this.iframeHost!.focusCharacter(characterId);\n\t}\n\n\t/** Live scene framing. Does not remint or restart the session. Queues until `attach()` if needed. */\n\tasync setFit(fit: ExperienceFit): Promise<void> {\n\t\tconst parsed = parseExperienceFit(fit);\n\t\tif (!parsed) {\n\t\t\tthrow new Error('setFit requires \"full\", \"medium\", or \"face\".');\n\t\t}\n\t\tif (this.closed) {\n\t\t\tthrow new Error('Session has ended.');\n\t\t}\n\t\tthis.pendingFit = parsed;\n\t\tif (!this.attached || !this.iframeHost) return;\n\t\tawait this.iframeHost.setFit(parsed);\n\t}\n\n\t/** Record a conversion milestone (once per session per key). Client-reported. */\n\tasync conversion(key: string): Promise<void> {\n\t\tif (this.closed) {\n\t\t\tthrow new Error('Session has ended.');\n\t\t}\n\t\tif (!this.iframeHost) {\n\t\t\tthrow new Error('conversion() requires attach() first.');\n\t\t}\n\t\tawait this.iframeHost.conversion(key);\n\t}\n\n\tgetLastTurn(): readonly ConversationMessage[] {\n\t\tif (this.conversationCache.length === 0) return [];\n\t\tconst lastTurnId = this.conversationCache[this.conversationCache.length - 1]!.turnId;\n\t\treturn this.conversationCache.filter((m) => m.turnId === lastTurnId);\n\t}\n\n\tasync end(): Promise<void> {\n\t\tthis.close();\n\t}\n\n\tdestroy(): void {\n\t\tif (this.closed) return;\n\t\tthis.closed = true;\n\t\tthis.processorAbortController?.abort();\n\t\tthis.processorAbortController = null;\n\t\tthis.endpointAbortController?.abort();\n\t\tthis.endpointAbortController = null;\n\t\tthis.attachInFlight = null;\n\t\tthis.iframeHost?.destroy();\n\t\tthis.iframeHost = null;\n\t\tclearExperienceEventRegistry(this.eventRegistry);\n\t\tthis.readyEnvelope = null;\n\t\tthis.startedEnvelope = null;\n\t\tthis.closeHandlers.length = 0;\n\t\tthis.attached = false;\n\t}\n\n\tprivate resolveMintInteraction(): {\n\t\treadonly mode?: ExperienceMode;\n\t\treadonly speechInputMode?: SpeechInputMode;\n\t} {\n\t\tif (this.bootstrap.kind === 'launch') {\n\t\t\treturn {};\n\t\t}\n\t\treturn {\n\t\t\tmode: this.bootstrap.mode,\n\t\t\tspeechInputMode: this.bootstrap.speechInputMode\n\t\t};\n\t}\n\n\tprivate getSessionId(): string {\n\t\treturn this.resolvedSession?.id ?? '';\n\t}\n\n\tprivate emitWrapped<K extends ExperienceEventName>(\n\t\tevent: K,\n\t\tdata: ExperienceEvents[K]['data']\n\t): ExperienceEvents[K] {\n\t\tconst envelope = createExperienceEventEnvelope(event, this.getSessionId(), data) as unknown as ExperienceEvents[K];\n\t\temitExperienceEvent(this.eventRegistry, event, envelope);\n\t\treturn envelope;\n\t}\n\n\tprivate queueProcessorTurn(update: UserTranscriptUpdate): void {\n\t\tif (!update.isFinal || !update.text.trim() || !this.conversationProcessor) return;\n\n\t\tconst processor = this.conversationProcessor;\n\t\tthis.processorAbortController?.abort();\n\t\tthis.processorAbortController = new AbortController();\n\t\tconst signal = this.processorAbortController.signal;\n\n\t\tthis.processorChain = this.processorChain.then(async () => {\n\t\t\tif (signal.aborted) return;\n\t\t\ttry {\n\t\t\t\tconst dispatch = await dispatchConversationProcessor(processor, {\n\t\t\t\t\tutteranceId: update.utteranceId,\n\t\t\t\t\ttext: update.text,\n\t\t\t\t\tconversation: this.conversationCache,\n\t\t\t\t\tsignal\n\t\t\t\t});\n\t\t\t\tif (signal.aborted) return;\n\t\t\t\tif (dispatch.kind === 'complete') {\n\t\t\t\t\tconst speech = dispatch.speech;\n\t\t\t\t\tawait this.speech.speak({\n\t\t\t\t\t\ttext: speech.text,\n\t\t\t\t\t\t...(speech.characterId ? { characterId: speech.characterId } : {}),\n\t\t\t\t\t\tqueue: speech.behavior === 'interrupt' ? 'replace-active' : 'append'\n\t\t\t\t\t});\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tawait this.iframeHost!.speakProcessorStream(dispatch.stream, { signal });\n\t\t\t} catch (error) {\n\t\t\t\tif (signal.aborted) return;\n\t\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\t\tthis.emitWrapped('conversationProcessorError', {\n\t\t\t\t\tutteranceId: update.utteranceId,\n\t\t\t\t\tmessage\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate assertAttached(): void {\n\t\tif (!this.attached || !this.iframeHost) {\n\t\t\tthrow new Error('Call Experience.attach() before using speech APIs.');\n\t\t}\n\t}\n\n\tprivate assertSessionStarted(): void {\n\t\tthis.assertAttached();\n\t\tif (!this.startedEnvelope) {\n\t\t\tthrow new Error(\n\t\t\t\t'Session has not started. Wait for the player start button and the started event before using speak() or listening APIs.'\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate close(): void {\n\t\tif (this.closed) return;\n\t\tthis.closed = true;\n\t\tthis.endpointAbortController?.abort();\n\t\tthis.endpointAbortController = null;\n\t\tthis.attachInFlight = null;\n\t\tthis.iframeHost?.destroy();\n\t\tthis.iframeHost = null;\n\t\tthis.attached = false;\n\t}\n\n\tprivate createPlayerFacade(): LiformaPlayer {\n\t\treturn {\n\t\t\tclose: () => {\n\t\t\t\tthis.close();\n\t\t\t},\n\t\t\tdestroy: () => {\n\t\t\t\tthis.destroy();\n\t\t\t},\n\t\t\ton: (event, handler) => {\n\t\t\t\tif (event === 'close') {\n\t\t\t\t\tthis.closeHandlers.push(handler);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t}\n}\n\n/** Live session handle returned by {@link Experience.startSession}. */\nexport type ExperienceSession = Experience;\n\nfunction resolveSpeechCapabilities(session: Session | null): SpeechCapabilities {\n\tif (!session) {\n\t\treturn { textSpeech: true, externalSpeechAudio: true };\n\t}\n\treturn {\n\t\ttextSpeech: session.capabilities.speech.output,\n\t\texternalSpeechAudio: session.capabilities.speech.externalAudio\n\t};\n}\n","import type { CloseEvent } from './protocol';\n\nfunction resolveIntegrationReturnUrl(returnUrl: string, baseOrigin: string): URL | null {\n\tconst raw = returnUrl.trim();\n\tif (!raw) return null;\n\n\tif (/^https?:\\/\\//i.test(raw)) {\n\t\ttry {\n\t\t\treturn new URL(raw);\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tconst base = baseOrigin.replace(/\\/$/, '');\n\tconst path = raw.startsWith('/') ? raw : `/${raw}`;\n\ttry {\n\t\treturn new URL(path, `${base}/`);\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction resolveFallbackReturnUrl(fallbackReturnUrl: string): string {\n\ttry {\n\t\tif (document.referrer) {\n\t\t\tconst referrer = new URL(document.referrer);\n\t\t\tif (\n\t\t\t\treferrer.origin === window.location.origin &&\n\t\t\t\treferrer.pathname !== window.location.pathname\n\t\t\t) {\n\t\t\t\treturn `${referrer.pathname}${referrer.search}${referrer.hash}`;\n\t\t\t}\n\t\t}\n\t} catch {\n\t\t/* ignore invalid referrer */\n\t}\n\treturn fallbackReturnUrl;\n}\n\n/** Navigate the host page when an iframe session closes. */\nexport function navigateOnExperienceClose(\n\tdetail: CloseEvent,\n\tfallbackReturnUrl = '/meet'\n): void {\n\tif (detail.reason !== 'user') return;\n\n\tconst target =\n\t\tdetail.returnUrl?.trim() || resolveFallbackReturnUrl(fallbackReturnUrl);\n\tconst url = resolveIntegrationReturnUrl(target, window.location.origin);\n\tif (!url?.protocol.startsWith('http')) return;\n\n\twindow.location.assign(url.toString());\n}\n","export type ExperienceLoadErrorKind =\n\t| 'not_found'\n\t| 'origin_not_allowed'\n\t| 'browser_embeds_disabled'\n\t| 'generic';\n\nexport type ExperienceLoadErrorPresentation = {\n\treadonly kind: ExperienceLoadErrorKind;\n\treadonly title: string;\n\treadonly detail: string;\n\treadonly technicalMessage: string;\n\treadonly experienceId?: string;\n\treadonly origin?: string;\n};\n\nconst EXPERIENCE_ID_IN_MESSAGE =\n\t/\\b(exp_[A-Za-z0-9][A-Za-z0-9_-]{2,127})\\b/;\n\nconst ORIGIN_IN_MESSAGE =\n\t/(?:Your origin|origin)\\s+(\\S+)\\s+is not an allowed origin/i;\n\nfunction toErrorMessage(error: unknown): string {\n\tif (error instanceof Error) return error.message;\n\treturn String(error);\n}\n\nfunction currentPageOrigin(): string | undefined {\n\ttry {\n\t\tif (typeof globalThis.location?.origin === 'string' && globalThis.location.origin) {\n\t\t\treturn globalThis.location.origin;\n\t\t}\n\t} catch {\n\t\t/* ignore */\n\t}\n\treturn undefined;\n}\n\nexport function extractExperienceIdFromMessage(message: string): string | undefined {\n\treturn EXPERIENCE_ID_IN_MESSAGE.exec(message)?.[1];\n}\n\nexport function extractOriginFromMessage(message: string): string | undefined {\n\treturn ORIGIN_IN_MESSAGE.exec(message)?.[1];\n}\n\nexport function presentExperienceLoadError(\n\terror: unknown,\n\toptions?: { readonly experienceId?: string | null; readonly origin?: string | null }\n): ExperienceLoadErrorPresentation {\n\tconst technicalMessage = toErrorMessage(error).trim() || 'Unknown error';\n\tconst experienceId =\n\t\toptions?.experienceId?.trim() ||\n\t\textractExperienceIdFromMessage(technicalMessage) ||\n\t\tundefined;\n\tconst origin =\n\t\toptions?.origin?.trim() ||\n\t\textractOriginFromMessage(technicalMessage) ||\n\t\tcurrentPageOrigin() ||\n\t\tundefined;\n\n\tif (\n\t\t/No playable experience found/i.test(technicalMessage) ||\n\t\t/^Experience is not available\\.?$/i.test(technicalMessage) ||\n\t\t/^Unknown experience\\.?$/i.test(technicalMessage) ||\n\t\t/experience_unavailable/i.test(technicalMessage)\n\t) {\n\t\tconst idLabel = experienceId ?? 'the requested experience';\n\t\treturn {\n\t\t\tkind: 'not_found',\n\t\t\ttitle: 'The application tried to load an avatar experience that doesn’t exist.',\n\t\t\tdetail: `Experience with id ${idLabel} does not exist`,\n\t\t\ttechnicalMessage,\n\t\t\texperienceId,\n\t\t\torigin\n\t\t};\n\t}\n\n\tif (\n\t\t/origin_not_allowed/i.test(technicalMessage) ||\n\t\t/is not an allowed origin/i.test(technicalMessage) ||\n\t\t/^Origin not allowed/i.test(technicalMessage)\n\t) {\n\t\tconst originLabel = origin ?? 'this page';\n\t\tconst idLabel = experienceId ?? 'the requested experience';\n\t\treturn {\n\t\t\tkind: 'origin_not_allowed',\n\t\t\ttitle: 'The application tried to load an avatar experience from a website that isn’t allowed.',\n\t\t\tdetail: `Origin ${originLabel} is not allowed for experience ${idLabel}`,\n\t\t\ttechnicalMessage,\n\t\t\texperienceId,\n\t\t\torigin\n\t\t};\n\t}\n\n\tif (\n\t\t/browser_embeds_disabled/i.test(technicalMessage) ||\n\t\t/^Browser embeds are not enabled/i.test(technicalMessage)\n\t) {\n\t\tconst idLabel = experienceId ?? 'the requested experience';\n\t\treturn {\n\t\t\tkind: 'browser_embeds_disabled',\n\t\t\ttitle: 'The application tried to load an avatar experience that isn’t enabled for browser embeds.',\n\t\t\tdetail: `Browser embeds are not enabled for experience ${idLabel}`,\n\t\t\ttechnicalMessage,\n\t\t\texperienceId,\n\t\t\torigin\n\t\t};\n\t}\n\n\treturn {\n\t\tkind: 'generic',\n\t\ttitle: 'This avatar experience couldn’t be started.',\n\t\tdetail: technicalMessage,\n\t\ttechnicalMessage,\n\t\texperienceId,\n\t\torigin\n\t};\n}\n","/** Subset of session loading overlay styles for pre-attach Experience load errors. */\n\nexport const EXPERIENCE_LOAD_ERROR_STYLE_ID = 'liforma-experience-load-error-styles';\n\nexport function injectExperienceLoadErrorStyles(): void {\n\tif (typeof document === 'undefined') return;\n\tif (document.getElementById(EXPERIENCE_LOAD_ERROR_STYLE_ID)) return;\n\tconst style = document.createElement('style');\n\tstyle.id = EXPERIENCE_LOAD_ERROR_STYLE_ID;\n\tstyle.textContent = EXPERIENCE_LOAD_ERROR_CSS;\n\tdocument.head.append(style);\n}\n\nexport const EXPERIENCE_LOAD_ERROR_CSS = `\n\t.liforma-loading-screen {\n\t\tinset: 0;\n\t\tz-index: 20;\n\t\tbox-sizing: border-box;\n\t\toverflow-x: clip;\n\t\twidth: 100%;\n\t\theight: 100%;\n\t\tmax-width: 100vw;\n\t\tbackground:\n\t\t\tradial-gradient(circle at 50% 60%, rgba(79, 126, 196, 0.18), transparent 38%),\n\t\t\t#030508;\n\t\tfont-family: system-ui, -apple-system, sans-serif;\n\t\tcolor: #e8f0ff;\n\t\ttext-align: center;\n\t}\n\t.liforma-loading-screen-absolute {\n\t\tposition: absolute;\n\t}\n\t.liforma-loading-card {\n\t\tposition: absolute;\n\t\tleft: 50%;\n\t\ttop: 60%;\n\t\ttransform: translate(-50%, -50%);\n\t\twidth: min(22rem, calc(100% - 2.5rem));\n\t\tmax-width: calc(100% - max(1.25rem, env(safe-area-inset-left, 0px)) - max(1.25rem, env(safe-area-inset-right, 0px)));\n\t\tbox-sizing: border-box;\n\t\tmargin: 0;\n\t\tpadding: 0;\n\t\tborder: 0;\n\t\tbackground: transparent;\n\t\tbox-shadow: none;\n\t}\n\t.liforma-loading-card-error {\n\t\tdisplay: block;\n\t\ttext-align: center;\n\t\tpadding: 1rem 1.05rem;\n\t\tborder-radius: 14px;\n\t\tborder: 1px solid rgba(255, 154, 154, 0.28);\n\t\tbackground:\n\t\t\tlinear-gradient(180deg, rgba(21, 29, 42, 0.92), rgba(9, 13, 20, 0.92));\n\t}\n\t.liforma-loading-kicker {\n\t\tmargin: 0 0 0.18rem;\n\t\tcolor: #8ba4c5;\n\t\tfont-size: 0.68rem;\n\t\tfont-weight: 700;\n\t\ttext-transform: uppercase;\n\t\tletter-spacing: 0.08em;\n\t}\n\t.liforma-loading-error {\n\t\tmargin: 0;\n\t\tmax-width: 28rem;\n\t\tcolor: #ffb4b4;\n\t\tfont-size: 0.92rem;\n\t\tline-height: 1.4;\n\t}\n\t.liforma-loading-error-title {\n\t\tmargin: 0;\n\t\tcolor: #f6ebe8;\n\t\tfont-size: 0.98rem;\n\t\tfont-weight: 650;\n\t\tline-height: 1.35;\n\t}\n\t.liforma-loading-error-advanced {\n\t\tmargin: 0.85rem 0 0;\n\t\tpadding: 0;\n\t\tborder: 0;\n\t\tbackground: transparent;\n\t\tcolor: #9eb7d8;\n\t\tfont: inherit;\n\t\tfont-size: 0.78rem;\n\t\tfont-weight: 650;\n\t\tletter-spacing: 0.02em;\n\t\ttext-decoration: underline;\n\t\ttext-underline-offset: 0.18em;\n\t\tcursor: pointer;\n\t}\n\t.liforma-loading-error-advanced:hover,\n\t.liforma-loading-error-advanced:focus-visible {\n\t\tcolor: #d7e6ff;\n\t\toutline: none;\n\t}\n\t.liforma-loading-error-detail {\n\t\tmargin: 0.7rem 0 0;\n\t\tpadding: 0.65rem 0.7rem;\n\t\tborder-radius: 10px;\n\t\tborder: 1px solid rgba(158, 191, 255, 0.16);\n\t\tbackground: rgba(3, 5, 8, 0.45);\n\t\tcolor: #c6d4e8;\n\t\tfont-size: 0.78rem;\n\t\tfont-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;\n\t\tline-height: 1.4;\n\t\ttext-align: left;\n\t\tword-break: break-word;\n\t}\n\t.liforma-loading-error-detail[hidden] {\n\t\tdisplay: none;\n\t}\n`;\n","import type { ExperienceLoadErrorPresentation } from './experienceLoadError';\nimport { injectExperienceLoadErrorStyles } from './experienceLoadErrorStyles';\n\nexport type ExperienceLoadErrorOverlay = {\n\treadonly root: HTMLDivElement;\n\tsetPresentation(presentation: ExperienceLoadErrorPresentation): void;\n\tremove(): void;\n};\n\nexport function createExperienceLoadErrorOverlay(\n\tcontainer: HTMLElement,\n\tinitial: ExperienceLoadErrorPresentation\n): ExperienceLoadErrorOverlay {\n\tinjectExperienceLoadErrorStyles();\n\n\tlet presentation = initial;\n\tlet advancedOpen = false;\n\n\tconst root = document.createElement('div');\n\troot.className = 'liforma-loading-screen liforma-loading-screen-absolute';\n\troot.setAttribute('role', 'alert');\n\troot.setAttribute('aria-live', 'assertive');\n\n\tconst card = document.createElement('div');\n\tcard.className = 'liforma-loading-card liforma-loading-card-error';\n\n\tconst kicker = document.createElement('p');\n\tkicker.className = 'liforma-loading-kicker';\n\tkicker.textContent = 'Avatar experience';\n\n\tconst title = document.createElement('p');\n\ttitle.className = 'liforma-loading-error-title';\n\n\tconst advancedBtn = document.createElement('button');\n\tadvancedBtn.type = 'button';\n\tadvancedBtn.className = 'liforma-loading-error-advanced';\n\n\tconst detail = document.createElement('p');\n\tdetail.className = 'liforma-loading-error-detail';\n\n\tadvancedBtn.addEventListener('click', () => {\n\t\tadvancedOpen = !advancedOpen;\n\t\trender();\n\t});\n\n\tcard.append(kicker, title, advancedBtn, detail);\n\troot.append(card);\n\tcontainer.append(root);\n\n\tfunction render(): void {\n\t\ttitle.textContent = presentation.title;\n\t\tdetail.textContent = presentation.detail;\n\t\tdetail.hidden = !advancedOpen;\n\t\tadvancedBtn.textContent = advancedOpen ? 'Hide details' : 'Advanced';\n\t\tadvancedBtn.setAttribute('aria-expanded', advancedOpen ? 'true' : 'false');\n\t}\n\n\trender();\n\n\treturn {\n\t\troot,\n\t\tsetPresentation(next) {\n\t\t\tpresentation = next;\n\t\t\tadvancedOpen = false;\n\t\t\trender();\n\t\t},\n\t\tremove() {\n\t\t\troot.remove();\n\t\t}\n\t};\n}\n","import { Experience } from './experience';\nimport type { CloseEvent } from './embed/protocol';\nimport { navigateOnExperienceClose } from './embed/returnNavigation';\nimport {\n\tparseExperienceFit,\n\ttype ExperienceFit,\n\ttype ExperienceMode,\n\ttype SpeechInputMode\n} from './types/sessionInteraction';\nimport { presentExperienceLoadError } from '../svelte/experienceLoadError';\nimport {\n\tcreateExperienceLoadErrorOverlay,\n\ttype ExperienceLoadErrorOverlay\n} from '../svelte/experienceLoadErrorOverlay';\n\nconst ELEMENT_NAME = 'liforma-experience';\n\nfunction parseMode(value: string | null): ExperienceMode | undefined {\n\tconst trimmed = value?.trim();\n\treturn trimmed === 'conversation' || trimmed === 'presenter' ? trimmed : undefined;\n}\n\nfunction parseSpeechInputMode(value: string | null): SpeechInputMode | undefined {\n\tconst trimmed = value?.trim();\n\treturn trimmed === 'auto' || trimmed === 'manual' || trimmed === 'off' ? trimmed : undefined;\n}\n\nfunction parseFit(value: string | null): ExperienceFit | undefined {\n\treturn parseExperienceFit(value);\n}\n\nexport class LiformaExperienceElement extends HTMLElement {\n\tprivate host: HTMLDivElement | null = null;\n\tprivate errorHost: HTMLDivElement | null = null;\n\tprivate errorOverlay: ExperienceLoadErrorOverlay | null = null;\n\tprivate experience: Experience | null = null;\n\tprivate bootstrapGeneration = 0;\n\n\tstatic get observedAttributes(): string[] {\n\t\treturn [\n\t\t\t'experience-id',\n\t\t\t'locale',\n\t\t\t'language',\n\t\t\t'secondary-locale',\n\t\t\t'learning-locale',\n\t\t\t'session-endpoint',\n\t\t\t'speech-only',\n\t\t\t'mode',\n\t\t\t'speech-input-mode',\n\t\t\t'fit'\n\t\t];\n\t}\n\n\tconnectedCallback(): void {\n\t\tthis.style.display = 'block';\n\t\tthis.style.position = 'relative';\n\t\tthis.style.overflow = 'hidden';\n\t\tif (!this.style.width) this.style.width = '100%';\n\t\t// Fill the host box when it has a definite height (flex/grid/explicit).\n\t\t// Do not set minHeight:inherit — that overrides host CSS such as\n\t\t// `.liforma-embed { min-height: 420px }` and collapses absolute children.\n\t\tif (!this.style.height) this.style.height = '100%';\n\t\tconst computedMinHeight = getComputedStyle(this).minHeight;\n\t\tif (\n\t\t\t!this.style.minHeight &&\n\t\t\t(computedMinHeight === '0px' || computedMinHeight === 'auto')\n\t\t) {\n\t\t\tthis.style.minHeight = '480px';\n\t\t}\n\n\t\tif (!this.errorHost) {\n\t\t\tthis.errorHost = document.createElement('div');\n\t\t\tthis.errorHost.style.cssText =\n\t\t\t\t'position:absolute;inset:0;z-index:20;pointer-events:none;';\n\t\t\tthis.append(this.errorHost);\n\t\t}\n\n\t\tif (!this.host) {\n\t\t\tthis.host = document.createElement('div');\n\t\t\t// Absolute fill avoids height:100% collapsing when size comes from flex/min-height.\n\t\t\tthis.host.style.cssText = 'position:absolute;inset:0;overflow:hidden;';\n\t\t\tthis.append(this.host);\n\t\t}\n\n\t\tvoid this.bootstrap();\n\t}\n\n\tdisconnectedCallback(): void {\n\t\tthis.bootstrapGeneration += 1;\n\t\tthis.experience?.destroy();\n\t\tthis.experience = null;\n\t\tthis.clearError();\n\t}\n\n\tattributeChangedCallback(\n\t\tname: string,\n\t\toldValue: string | null,\n\t\tnewValue: string | null\n\t): void {\n\t\tif (!LiformaExperienceElement.observedAttributes.includes(name)) {\n\t\t\treturn;\n\t\t}\n\t\tif (oldValue === newValue || !this.isConnected) {\n\t\t\treturn;\n\t\t}\n\t\tif (name === 'fit') {\n\t\t\tif (this.experience) {\n\t\t\t\tvoid this.experience.setFit(parseFit(newValue) ?? 'full');\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tthis.bootstrapGeneration += 1;\n\t\tthis.experience?.destroy();\n\t\tthis.experience = null;\n\t\tthis.clearError();\n\t\tif (this.host) {\n\t\t\tthis.host.replaceChildren();\n\t\t}\n\t\tvoid this.bootstrap();\n\t}\n\n\tprivate async bootstrap(): Promise<void> {\n\t\tconst experienceId = this.getAttribute('experience-id')?.trim();\n\t\tconst sessionEndpoint = this.getAttribute('session-endpoint')?.trim();\n\t\tconst locale = this.getAttribute('locale')?.trim() || undefined;\n\t\tconst languageAttr = this.getAttribute('language')?.trim();\n\t\tconst language = languageAttr || undefined;\n\t\tconst secondaryLocale = this.getAttribute('secondary-locale')?.trim() || undefined;\n\t\tconst learningLocale = this.getAttribute('learning-locale')?.trim() || undefined;\n\t\tconst speechOnly = this.getAttribute('speech-only') === 'true';\n\t\tconst mode = parseMode(this.getAttribute('mode'));\n\t\tconst speechInputMode = parseSpeechInputMode(this.getAttribute('speech-input-mode'));\n\t\tconst fit = parseFit(this.getAttribute('fit'));\n\t\tif (!experienceId || !this.host) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst generation = ++this.bootstrapGeneration;\n\t\tthis.clearError();\n\n\t\ttry {\n\t\t\tconst mint = {\n\t\t\t\texperienceId,\n\t\t\t\tlocale,\n\t\t\t\tlanguage,\n\t\t\t\tsecondaryLocale,\n\t\t\t\tlearningLocale,\n\t\t\t\tspeechOnly,\n\t\t\t\tmode,\n\t\t\t\tspeechInputMode\n\t\t\t};\n\t\t\tconst experience = await Experience.startSession(\n\t\t\t\tsessionEndpoint\n\t\t\t\t\t? {\n\t\t\t\t\t\t\t...mint,\n\t\t\t\t\t\t\tsessionEndpoint\n\t\t\t\t\t\t}\n\t\t\t\t\t: mint\n\t\t\t);\n\t\t\tif (generation !== this.bootstrapGeneration) {\n\t\t\t\texperience.destroy();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.experience = experience;\n\t\t\tconst player = await experience.attach({\n\t\t\t\tcontainer: this.host,\n\t\t\t\t...(fit ? { fit } : {})\n\t\t\t});\n\t\t\tif (generation !== this.bootstrapGeneration) {\n\t\t\t\texperience.destroy();\n\t\t\t\tthis.experience = null;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tplayer.on('close', (detail: CloseEvent) => {\n\t\t\t\tthis.dispatchEvent(new CustomEvent<CloseEvent>('close', { detail, bubbles: true }));\n\t\t\t\tnavigateOnExperienceClose(detail);\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tif (generation !== this.bootstrapGeneration) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.showError(error, experienceId);\n\t\t\tconsole.error('[liforma-experience]', error);\n\t\t\tthis.experience?.destroy();\n\t\t\tthis.experience = null;\n\t\t}\n\t}\n\n\tprivate showError(error: unknown, experienceId?: string): void {\n\t\tthis.clearError();\n\t\tif (!this.errorHost) return;\n\t\tthis.errorHost.style.pointerEvents = 'auto';\n\t\tthis.errorOverlay = createExperienceLoadErrorOverlay(\n\t\t\tthis.errorHost,\n\t\t\tpresentExperienceLoadError(error, { experienceId })\n\t\t);\n\t}\n\n\tprivate clearError(): void {\n\t\tthis.errorOverlay?.remove();\n\t\tthis.errorOverlay = null;\n\t\tif (this.errorHost) {\n\t\t\tthis.errorHost.style.pointerEvents = 'none';\n\t\t\tthis.errorHost.replaceChildren();\n\t\t}\n\t}\n}\n\nif (typeof customElements !== 'undefined' && !customElements.get(ELEMENT_NAME)) {\n\tcustomElements.define(ELEMENT_NAME, LiformaExperienceElement);\n}\n","/** Catalog-shaped layered gallery preview (CDN URLs). */\nexport type GalleryThumb = {\n\treadonly avatarImage: string;\n\treadonly backgroundImage?: string;\n\treadonly foregroundImage?: string;\n};\n\nexport type ThumbnailInteractionKind = 'anchor' | 'button' | 'presentational';\n\nexport type ResolveThumbnailInteractionInput = {\n\treadonly href?: string | null;\n\treadonly hasClickHandler?: boolean;\n\treadonly experienceId?: string | null;\n};\n\nexport type ThumbnailLayers = {\n\treadonly avatarImage: string;\n\treadonly backgroundImage?: string;\n\treadonly foregroundImage?: string;\n\treadonly fallbackImage?: string;\n};\n\n/** Shared props across Svelte / React / Next (React uses `onClick`). */\nexport type ExperienceThumbnailBaseProps = {\n\treadonly galleryThumb?: GalleryThumb;\n\treadonly avatarImage?: string;\n\treadonly backgroundImage?: string;\n\treadonly foregroundImage?: string;\n\treadonly alt: string;\n\treadonly experienceId?: string;\n\treadonly href?: string;\n\treadonly target?: string;\n\treadonly rel?: string;\n\treadonly returnUrl?: string;\n\treadonly playerBaseUrl?: string;\n\treadonly parallax?: boolean;\n\t/** Same as `<Experience fit>`: `full` (default), `medium`, or `face`. */\n\treadonly fit?: 'full' | 'face' | 'medium';\n\treadonly lazy?: boolean;\n\treadonly className?: string;\n\treadonly fallbackImage?: string;\n};\n\nexport function thumbnailFitClass(fit?: 'full' | 'face' | 'medium'): string {\n\tif (fit === 'face') return 'liforma-et--fit-face';\n\tif (fit === 'medium') return 'liforma-et--fit-medium';\n\treturn '';\n}\n","const AVATAR_TIER_RE = /(\\/avatars\\/[^/]+\\/)(\\d+)(\\/)/i;\nconst AVATAR_CH_PLATE_RE = /_CH(\\.webp)/i;\nconst LOCATION_SIZE_RE = /(-)(\\d+x\\d+)(\\.webp)(\\?|$)/i;\n\n/**\n * Rewrite known Liforma CDN URLs to gallery thumb tiers.\n * Avatars: /{tier}/ → /256/; locations: -{WxH}.webp → -256x256.webp.\n * Unrecognized URLs are returned unchanged.\n */\nexport function toGalleryThumbUrl(url: string): string {\n\tconst raw = url.trim();\n\tif (!raw) return raw;\n\n\tlet next = raw.replace(AVATAR_TIER_RE, (_match, prefix: string, tier: string, slash: string) => {\n\t\tif (tier === '256') return `${prefix}${tier}${slash}`;\n\t\treturn `${prefix}256${slash}`;\n\t});\n\tnext = next.replace(AVATAR_CH_PLATE_RE, '_neutral$1');\n\n\tnext = next.replace(\n\t\tLOCATION_SIZE_RE,\n\t\t(_match, dash: string, size: string, ext: string, end: string) => {\n\t\t\tif (size === '256x256') return `${dash}${size}${ext}${end}`;\n\t\t\treturn `${dash}256x256${ext}${end}`;\n\t\t}\n\t);\n\n\treturn next;\n}\n\nexport function resolveThumbnailLayers(input: {\n\treadonly galleryThumb?: {\n\t\treadonly avatarImage?: string;\n\t\treadonly backgroundImage?: string;\n\t\treadonly foregroundImage?: string;\n\t};\n\treadonly avatarImage?: string;\n\treadonly backgroundImage?: string;\n\treadonly foregroundImage?: string;\n\treadonly fallbackImage?: string;\n}): {\n\treadonly avatarImage: string;\n\treadonly backgroundImage?: string;\n\treadonly foregroundImage?: string;\n\treadonly fallbackImage?: string;\n} | null {\n\tconst avatarSource =\n\t\tinput.avatarImage?.trim() ||\n\t\tinput.galleryThumb?.avatarImage?.trim() ||\n\t\t'';\n\tif (!avatarSource) return null;\n\n\tconst backgroundSource =\n\t\tinput.backgroundImage?.trim() || input.galleryThumb?.backgroundImage?.trim() || '';\n\tconst foregroundSource =\n\t\tinput.foregroundImage?.trim() || input.galleryThumb?.foregroundImage?.trim() || '';\n\tconst fallbackSource = input.fallbackImage?.trim() || '';\n\n\treturn {\n\t\tavatarImage: toGalleryThumbUrl(avatarSource),\n\t\t...(backgroundSource ? { backgroundImage: toGalleryThumbUrl(backgroundSource) } : {}),\n\t\t...(foregroundSource ? { foregroundImage: toGalleryThumbUrl(foregroundSource) } : {}),\n\t\t...(fallbackSource ? { fallbackImage: toGalleryThumbUrl(fallbackSource) } : {})\n\t};\n}\n","import { useExplicitLocalStack } from '../shims/localStack';\nimport type { ResolveThumbnailInteractionInput, ThumbnailInteractionKind } from './types';\n\nconst PRODUCTION_PLAYER_ORIGIN = 'https://player.liforma.ai';\nconst LOCAL_PLAYER_ORIGIN = 'http://localhost:3002';\n\n/** Priority: href → click-only → hosted (experienceId) → presentational. */\nexport function resolveThumbnailInteraction(\n\tinput: ResolveThumbnailInteractionInput\n): ThumbnailInteractionKind {\n\tif (input.href?.trim()) return 'anchor';\n\tif (input.hasClickHandler) return 'button';\n\tif (input.experienceId?.trim()) return 'anchor';\n\treturn 'presentational';\n}\n\nexport function resolvePlayerBaseUrl(playerBaseUrl?: string): string {\n\tconst configured = playerBaseUrl?.trim();\n\tif (configured) return configured.replace(/\\/$/, '');\n\n\tif (useExplicitLocalStack()) {\n\t\treturn LOCAL_PLAYER_ORIGIN;\n\t}\n\n\treturn PRODUCTION_PLAYER_ORIGIN;\n}\n\n/**\n * Build hosted player launch URL: `{player}/{experienceId}?returnUrl=…`\n * When `returnUrl` is omitted, callers should assign on click (SSR-safe) or pass an explicit URL.\n */\nexport function buildHostedLaunchUrl(options: {\n\treadonly experienceId: string;\n\treadonly returnUrl?: string;\n\treadonly playerBaseUrl?: string;\n}): string {\n\tconst experienceId = options.experienceId.trim();\n\tconst base = resolvePlayerBaseUrl(options.playerBaseUrl);\n\tconst url = new URL(`${base}/${encodeURIComponent(experienceId)}`);\n\tconst returnUrl = options.returnUrl?.trim();\n\tif (returnUrl) {\n\t\turl.searchParams.set('returnUrl', returnUrl);\n\t}\n\treturn url.toString();\n}\n\n/** Resolve href for anchor mode (app link or hosted launch). */\nexport function resolveThumbnailHref(options: {\n\treadonly href?: string;\n\treadonly experienceId?: string;\n\treadonly returnUrl?: string;\n\treadonly playerBaseUrl?: string;\n\t/** When true and returnUrl omitted, use window.location.href (browser only). */\n\treadonly useCurrentPageAsReturnUrl?: boolean;\n}): string | undefined {\n\tconst href = options.href?.trim();\n\tif (href) return href;\n\n\tconst experienceId = options.experienceId?.trim();\n\tif (!experienceId) return undefined;\n\n\tlet returnUrl = options.returnUrl?.trim();\n\tif (!returnUrl && options.useCurrentPageAsReturnUrl && typeof window !== 'undefined') {\n\t\treturnUrl = window.location.href;\n\t}\n\n\treturn buildHostedLaunchUrl({\n\t\texperienceId,\n\t\t...(returnUrl ? { returnUrl } : {}),\n\t\tplayerBaseUrl: options.playerBaseUrl\n\t});\n}\n","/** Touch / coarse pointers — limit how many thumbs start loading at once. */\nexport const COARSE_THUMB_LOAD_CONCURRENCY = 4;\n/** Delay before a started thumb frees its queue slot for the next card. */\nexport const COARSE_THUMB_LOAD_STAGGER_MS = 160;\n\nexport function prefersCoarsePointer(): boolean {\n\tif (typeof window === 'undefined') return false;\n\treturn window.matchMedia('(hover: none), (pointer: coarse)').matches;\n}\n\ntype QueuedLoad = {\n\treadonly start: () => void;\n\tcancelled: boolean;\n};\n\nlet inflight = 0;\nconst waiting: QueuedLoad[] = [];\n\nfunction maxConcurrent(): number {\n\treturn prefersCoarsePointer() ? COARSE_THUMB_LOAD_CONCURRENCY : Number.POSITIVE_INFINITY;\n}\n\nfunction staggerMs(): number {\n\treturn prefersCoarsePointer() ? COARSE_THUMB_LOAD_STAGGER_MS : 0;\n}\n\nfunction pump(): void {\n\tconst limit = maxConcurrent();\n\twhile (inflight < limit && waiting.length > 0) {\n\t\tconst next = waiting.shift();\n\t\tif (!next || next.cancelled) continue;\n\t\tinflight += 1;\n\t\tnext.start();\n\t\tconst delay = staggerMs();\n\t\tif (delay <= 0) {\n\t\t\tinflight = Math.max(0, inflight - 1);\n\t\t\tcontinue;\n\t\t}\n\t\twindow.setTimeout(() => {\n\t\t\tinflight = Math.max(0, inflight - 1);\n\t\t\tpump();\n\t\t}, delay);\n\t}\n}\n\n/**\n * On coarse pointers, start thumb image loads in small waves so old devices\n * are not hit with dozens of concurrent decodes. Fine pointers start immediately.\n * Returns a cancel function (no-op after start has run).\n */\nexport function scheduleThumbLoad(start: () => void): () => void {\n\tif (!prefersCoarsePointer()) {\n\t\tstart();\n\t\treturn () => {};\n\t}\n\n\tconst item: QueuedLoad = { start, cancelled: false };\n\twaiting.push(item);\n\tpump();\n\n\treturn () => {\n\t\titem.cancelled = true;\n\t};\n}\n\n/** Test helper — clear queue state between vitest cases. */\nexport function resetThumbLoadSchedulerForTests(): void {\n\tinflight = 0;\n\twaiting.length = 0;\n}\n","import { prefersCoarsePointer, scheduleThumbLoad } from './thumbLoadScheduler';\n\nexport type LazyThumbOptions = {\n\treadonly rootMargin?: string;\n\treadonly onVisibleChange: (visible: boolean) => void;\n};\n\n/** Desktop: prefetch earlier. Coarse/touch: tighter margin to cut burst loads. */\nexport function getDefaultLazyRootMargin(): string {\n\treturn prefersCoarsePointer() ? '96px 0px' : '280px 0px';\n}\n\nfunction parseVerticalRootMarginPx(rootMargin: string): number {\n\tconst first = rootMargin.trim().split(/\\s+/)[0] ?? '0px';\n\tconst value = Number.parseFloat(first);\n\treturn Number.isFinite(value) ? Math.abs(value) : 280;\n}\n\nfunction isNearViewport(element: HTMLElement, marginPx: number): boolean {\n\tif (typeof window === 'undefined') return false;\n\tconst rect = element.getBoundingClientRect();\n\treturn rect.bottom >= -marginPx && rect.top <= window.innerHeight + marginPx;\n}\n\n/** Observe the thumb and enable image loads when near the viewport. */\nexport function attachLazyThumb(element: HTMLElement, options: LazyThumbOptions): () => void {\n\tconst { onVisibleChange } = options;\n\tconst rootMargin = options.rootMargin ?? getDefaultLazyRootMargin();\n\tconst marginPx = parseVerticalRootMarginPx(rootMargin);\n\tlet visible = false;\n\tlet loadStarted = false;\n\tlet cancelScheduled: (() => void) | null = null;\n\n\tconst requestLoad = () => {\n\t\tif (loadStarted) return;\n\t\tcancelScheduled?.();\n\t\tcancelScheduled = scheduleThumbLoad(() => {\n\t\t\tcancelScheduled = null;\n\t\t\tloadStarted = true;\n\t\t\tonVisibleChange(true);\n\t\t});\n\t};\n\n\tconst setVisible = (next: boolean) => {\n\t\tif (next === visible) return;\n\t\tvisible = next;\n\t\tif (next) {\n\t\t\trequestLoad();\n\t\t\treturn;\n\t\t}\n\t\t// Keep images once started; only cancel a queued (not-yet-started) load.\n\t\tcancelScheduled?.();\n\t\tcancelScheduled = null;\n\t\tonVisibleChange(false);\n\t};\n\n\tif (typeof IntersectionObserver === 'undefined') {\n\t\trequestLoad();\n\t\treturn () => {\n\t\t\tcancelScheduled?.();\n\t\t\tcancelScheduled = null;\n\t\t};\n\t}\n\n\tconst observer = new IntersectionObserver(\n\t\t(entries) => {\n\t\t\tsetVisible(entries.some((entry) => entry.isIntersecting));\n\t\t},\n\t\t{ root: null, rootMargin, threshold: 0 }\n\t);\n\n\tobserver.observe(element);\n\n\tif (isNearViewport(element, marginPx)) {\n\t\tsetVisible(true);\n\t}\n\n\treturn () => {\n\t\tcancelScheduled?.();\n\t\tcancelScheduled = null;\n\t\tobserver.disconnect();\n\t};\n}\n\nexport function attachImageReady(element: HTMLImageElement, onReady: () => void): () => void {\n\tconst notify = () => onReady();\n\tif (element.complete) {\n\t\tqueueMicrotask(notify);\n\t} else {\n\t\telement.addEventListener('load', notify, { once: true });\n\t\telement.addEventListener('error', notify, { once: true });\n\t}\n\treturn () => {\n\t\telement.removeEventListener('load', notify);\n\t\telement.removeEventListener('error', notify);\n\t};\n}\n","/** Touch devices without hover — drive thumbnail parallax from scroll position. */\nexport function prefersScrollLinkedParallax(): boolean {\n\tif (typeof window === 'undefined') return false;\n\tif (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return false;\n\treturn window.matchMedia('(hover: none), (pointer: coarse)').matches;\n}\n\n/**\n * Map an element's vertical centre in the viewport to horizontal parallax in [-1, 1].\n */\nexport function horizontalParallaxFromViewportY(rect: DOMRect, viewportHeight: number): number {\n\tconst centerY = rect.top + rect.height / 2;\n\tconst t = centerY / Math.max(viewportHeight, 1);\n\treturn (t - 0.5) * 2;\n}\n\nexport type ScrollLinkedParallaxOptions = {\n\treadonly onParallax: (x: number) => void;\n\treadonly pointerActive?: () => boolean;\n};\n\ntype ScrollParallaxSubscriber = {\n\treadonly element: HTMLElement;\n\treadonly onParallax: (x: number) => void;\n\treadonly pointerActive?: () => boolean;\n};\n\nconst subscribers = new Set<ScrollParallaxSubscriber>();\nlet listening = false;\nlet frame = 0;\n\nfunction syncAll(): void {\n\tframe = 0;\n\tif (subscribers.size === 0 || typeof window === 'undefined') return;\n\tconst viewportHeight = window.innerHeight;\n\tfor (const entry of subscribers) {\n\t\tif (entry.pointerActive?.()) continue;\n\t\tconst rect = entry.element.getBoundingClientRect();\n\t\t// Skip off-screen thumbs — avoids N layout reads + framework updates per scroll.\n\t\tif (rect.bottom < -64 || rect.top > viewportHeight + 64) continue;\n\t\tentry.onParallax(horizontalParallaxFromViewportY(rect, viewportHeight));\n\t}\n}\n\nfunction scheduleSync(): void {\n\tif (frame !== 0) return;\n\tframe = requestAnimationFrame(syncAll);\n}\n\nfunction ensureListening(): void {\n\tif (listening || typeof window === 'undefined') return;\n\tlistening = true;\n\twindow.addEventListener('scroll', scheduleSync, { passive: true });\n\twindow.addEventListener('resize', scheduleSync, { passive: true });\n}\n\nfunction stopListeningIfIdle(): void {\n\tif (subscribers.size > 0 || !listening || typeof window === 'undefined') return;\n\tlistening = false;\n\tif (frame !== 0) {\n\t\tcancelAnimationFrame(frame);\n\t\tframe = 0;\n\t}\n\twindow.removeEventListener('scroll', scheduleSync);\n\twindow.removeEventListener('resize', scheduleSync);\n}\n\n/**\n * Subscribe to scroll/resize and update horizontal parallax from viewport Y.\n * Uses one shared window listener for all thumbs (old iPads with large galleries).\n */\nexport function attachScrollLinkedParallax(\n\telement: HTMLElement,\n\toptions: ScrollLinkedParallaxOptions\n): () => void {\n\tif (!prefersScrollLinkedParallax()) return () => {};\n\n\tconst entry: ScrollParallaxSubscriber = {\n\t\telement,\n\t\tonParallax: options.onParallax,\n\t\tpointerActive: options.pointerActive\n\t};\n\tsubscribers.add(entry);\n\tensureListening();\n\n\tif (!entry.pointerActive?.()) {\n\t\toptions.onParallax(\n\t\t\thorizontalParallaxFromViewportY(element.getBoundingClientRect(), window.innerHeight)\n\t\t);\n\t}\n\n\treturn () => {\n\t\tsubscribers.delete(entry);\n\t\tstopListeningIfIdle();\n\t};\n}\n\n/** Test helper — clear shared scroll state between vitest cases. */\nexport function resetScrollLinkedParallaxForTests(): void {\n\tsubscribers.clear();\n\tif (typeof window !== 'undefined' && listening) {\n\t\twindow.removeEventListener('scroll', scheduleSync);\n\t\twindow.removeEventListener('resize', scheduleSync);\n\t}\n\tlistening = false;\n\tif (frame !== 0) {\n\t\tcancelAnimationFrame(frame);\n\t\tframe = 0;\n\t}\n}\n","import {\n\tattachScrollLinkedParallax,\n\thorizontalParallaxFromViewportY\n} from './scrollParallax';\n\n/**\n * Drive `--liforma-et-parallax-*` CSS vars and `liforma-et--parallax` /\n * `liforma-et--scroll-parallax` classes from pointer (desktop) or scroll (touch).\n * Matches ExperienceThumbnail / `<liforma-experience-thumbnail>` behaviour.\n */\nexport function attachThumbnailParallax(root: HTMLElement): () => void {\n\tlet parallaxX = 0;\n\tlet parallaxY = 0;\n\tlet hovering = false;\n\tlet scrollParallax = false;\n\tlet pointerDown = false;\n\n\tconst sync = () => {\n\t\tconst parallaxActive = hovering || scrollParallax;\n\t\troot.classList.toggle('liforma-et--parallax', parallaxActive);\n\t\troot.classList.toggle('liforma-et--scroll-parallax', scrollParallax);\n\t\troot.style.setProperty('--liforma-et-parallax-x', String(parallaxX));\n\t\troot.style.setProperty('--liforma-et-parallax-y', String(parallaxY));\n\t};\n\n\tconst detachScroll = attachScrollLinkedParallax(root, {\n\t\tonParallax: (x) => {\n\t\t\tscrollParallax = true;\n\t\t\tif (!pointerDown) {\n\t\t\t\tparallaxX = x;\n\t\t\t\tparallaxY = 0;\n\t\t\t\tsync();\n\t\t\t}\n\t\t},\n\t\tpointerActive: () => pointerDown\n\t});\n\n\tconst onPointerMove = (event: PointerEvent) => {\n\t\tif (scrollParallax) return;\n\t\tconst rect = root.getBoundingClientRect();\n\t\tif (rect.width <= 0 || rect.height <= 0) return;\n\t\tparallaxX = ((event.clientX - rect.left) / rect.width - 0.5) * 2;\n\t\tparallaxY = ((event.clientY - rect.top) / rect.height - 0.5) * 2;\n\t\tsync();\n\t};\n\n\tconst onPointerDown = (event: PointerEvent) => {\n\t\tpointerDown = true;\n\t\tif (event.pointerType === 'mouse') hovering = true;\n\t\tsync();\n\t};\n\n\tconst onPointerUp = () => {\n\t\tconst wasDown = pointerDown;\n\t\tpointerDown = false;\n\t\thovering = false;\n\t\tif (scrollParallax && wasDown) {\n\t\t\tparallaxX = horizontalParallaxFromViewportY(\n\t\t\t\troot.getBoundingClientRect(),\n\t\t\t\twindow.innerHeight\n\t\t\t);\n\t\t\tparallaxY = 0;\n\t\t} else if (!scrollParallax) {\n\t\t\tparallaxX = 0;\n\t\t\tparallaxY = 0;\n\t\t}\n\t\tsync();\n\t};\n\n\tconst onPointerEnter = (event: PointerEvent) => {\n\t\tif (event.pointerType === 'mouse') {\n\t\t\thovering = true;\n\t\t\tsync();\n\t\t}\n\t};\n\n\tconst onPointerLeave = () => {\n\t\tif (!pointerDown) {\n\t\t\thovering = false;\n\t\t\tif (!scrollParallax) {\n\t\t\t\tparallaxX = 0;\n\t\t\t\tparallaxY = 0;\n\t\t\t}\n\t\t\tsync();\n\t\t}\n\t};\n\n\troot.addEventListener('pointermove', onPointerMove);\n\troot.addEventListener('pointerdown', onPointerDown);\n\troot.addEventListener('pointerup', onPointerUp);\n\troot.addEventListener('pointercancel', onPointerUp);\n\troot.addEventListener('pointerenter', onPointerEnter);\n\troot.addEventListener('pointerleave', onPointerLeave);\n\n\treturn () => {\n\t\tdetachScroll();\n\t\troot.removeEventListener('pointermove', onPointerMove);\n\t\troot.removeEventListener('pointerdown', onPointerDown);\n\t\troot.removeEventListener('pointerup', onPointerUp);\n\t\troot.removeEventListener('pointercancel', onPointerUp);\n\t\troot.removeEventListener('pointerenter', onPointerEnter);\n\t\troot.removeEventListener('pointerleave', onPointerLeave);\n\t\troot.classList.remove('liforma-et--parallax', 'liforma-et--scroll-parallax');\n\t\troot.style.removeProperty('--liforma-et-parallax-x');\n\t\troot.style.removeProperty('--liforma-et-parallax-y');\n\t};\n}\n","/** Shared styles for ExperienceThumbnail (class prefix `liforma-et`). */\nexport const EXPERIENCE_THUMBNAIL_CSS = `\n.liforma-et {\n\t--liforma-et-parallax-x: 0;\n\t--liforma-et-parallax-y: 0;\n\t--liforma-et-bg-scale: 1;\n\t--liforma-et-fg-scale: 1;\n\t--liforma-et-avatar-scale: 1;\n\t--liforma-et-bg-shift: 0px;\n\t--liforma-et-fg-shift: 0px;\n\t--liforma-et-avatar-shift: 0px;\n\t--liforma-et-reveal-duration: 420ms;\n\n\tbox-sizing: border-box;\n\tposition: relative;\n\tdisplay: block;\n\twidth: 100%;\n\theight: 100%;\n\tmin-height: 0;\n\tmargin: 0;\n\tpadding: 0;\n\tborder: 0;\n\tborder-radius: 0.65rem;\n\toverflow: hidden;\n\tbackground: #111;\n\tcolor: inherit;\n\ttext-decoration: none;\n\tcursor: inherit;\n\t-webkit-tap-highlight-color: transparent;\n}\n\na.liforma-et,\nbutton.liforma-et {\n\tcursor: pointer;\n}\n\nbutton.liforma-et {\n\tfont: inherit;\n\tappearance: none;\n}\n\n.liforma-et__placeholder {\n\tposition: absolute;\n\tinset: 0;\n\tz-index: 0;\n\tbackground:\n\t\tradial-gradient(120% 90% at 50% 0%, rgba(255, 255, 255, 0.06), transparent 58%),\n\t\tlinear-gradient(180deg, #1a1a1a 0%, #101010 100%);\n\topacity: 1;\n\ttransition: opacity var(--liforma-et-reveal-duration) ease;\n}\n\n.liforma-et--revealed .liforma-et__placeholder {\n\topacity: 0;\n}\n\n.liforma-et__media {\n\topacity: 0;\n\ttransition: opacity var(--liforma-et-reveal-duration) ease;\n}\n\n.liforma-et__media--ready {\n\topacity: 1;\n}\n\n.liforma-et__background {\n\tposition: absolute;\n\tinset: 0;\n\tz-index: 1;\n\twidth: 100%;\n\theight: 100%;\n\tobject-fit: cover;\n\tobject-position: center center;\n\ttransform: translate(\n\t\t\tcalc(var(--liforma-et-parallax-x) * var(--liforma-et-bg-shift)),\n\t\t\tcalc(var(--liforma-et-parallax-y) * var(--liforma-et-bg-shift))\n\t\t)\n\t\tscale(var(--liforma-et-bg-scale));\n\ttransform-origin: center center;\n\ttransition:\n\t\topacity var(--liforma-et-reveal-duration) ease,\n\t\ttransform 160ms ease;\n}\n\n.liforma-et__avatar {\n\tposition: relative;\n\tz-index: 2;\n\tdisplay: block;\n\twidth: 100%;\n\theight: 100%;\n\tobject-fit: contain;\n\tobject-position: center bottom;\n\ttransform: scale(var(--liforma-et-avatar-scale))\n\t\ttranslate(\n\t\t\tcalc(var(--liforma-et-parallax-x) * var(--liforma-et-avatar-shift)),\n\t\t\tcalc(var(--liforma-et-parallax-y) * var(--liforma-et-avatar-shift))\n\t\t);\n\ttransform-origin: center center;\n\ttransition:\n\t\topacity var(--liforma-et-reveal-duration) ease,\n\t\ttransform 160ms ease;\n}\n\n/* Gallery plates are typically square in a square tile — contain vs cover is a no-op\n without a zoom. Scale the whole scene from the upper third so Face matches\n player fit (location + avatar crop together). */\n.liforma-et--fit-face {\n\t--liforma-et-avatar-scale: 1.85;\n\t--liforma-et-bg-scale: 1.85;\n\t--liforma-et-fg-scale: 1.85;\n}\n\n.liforma-et--fit-face .liforma-et__avatar,\n.liforma-et--fit-face .liforma-et__background,\n.liforma-et--fit-face .liforma-et__foreground {\n\ttransform-origin: 50% 18%;\n}\n\n.liforma-et--fit-face .liforma-et__avatar {\n\tobject-fit: cover;\n\tobject-position: 50% 18%;\n}\n\n/* Bust / medium: less zoom than face so upper chest stays in frame (player 3.75H window). */\n.liforma-et--fit-medium {\n\t--liforma-et-avatar-scale: 1.35;\n\t--liforma-et-bg-scale: 1.35;\n\t--liforma-et-fg-scale: 1.35;\n}\n\n.liforma-et--fit-medium .liforma-et__avatar,\n.liforma-et--fit-medium .liforma-et__background,\n.liforma-et--fit-medium .liforma-et__foreground {\n\ttransform-origin: 50% 28%;\n}\n\n.liforma-et--fit-medium .liforma-et__avatar {\n\tobject-fit: cover;\n\tobject-position: 50% 28%;\n}\n\n.liforma-et__foreground {\n\tposition: absolute;\n\tinset: 0;\n\tz-index: 3;\n\twidth: 100%;\n\theight: 100%;\n\tobject-fit: cover;\n\tobject-position: center center;\n\tpointer-events: none;\n\ttransform: translate(\n\t\t\tcalc(var(--liforma-et-parallax-x) * var(--liforma-et-fg-shift) * -1),\n\t\t\tcalc(var(--liforma-et-parallax-y) * var(--liforma-et-fg-shift) * -1)\n\t\t)\n\t\tscale(var(--liforma-et-fg-scale));\n\ttransform-origin: center center;\n\ttransition:\n\t\topacity var(--liforma-et-reveal-duration) ease,\n\t\ttransform 160ms ease;\n}\n\n.liforma-et--parallax {\n\t--liforma-et-bg-scale: 1.08;\n\t--liforma-et-fg-scale: 1.05;\n\t--liforma-et-avatar-scale: 1.08;\n\t--liforma-et-bg-shift: 10px;\n\t--liforma-et-fg-shift: 4px;\n\t--liforma-et-avatar-shift: 4px;\n}\n\n/* Promote layers only while parallax is active — avoids N compositor surfaces on galleries. */\n.liforma-et--parallax .liforma-et__background,\n.liforma-et--parallax .liforma-et__foreground,\n.liforma-et--parallax .liforma-et__avatar {\n\twill-change: transform;\n}\n\n.liforma-et--parallax.liforma-et--fit-face {\n\t--liforma-et-avatar-scale: 1.95;\n\t--liforma-et-bg-scale: 1.95;\n\t--liforma-et-fg-scale: 1.95;\n\t--liforma-et-avatar-shift: 0px;\n}\n\n.liforma-et--parallax.liforma-et--fit-medium {\n\t--liforma-et-avatar-scale: 1.42;\n\t--liforma-et-bg-scale: 1.42;\n\t--liforma-et-fg-scale: 1.42;\n\t--liforma-et-avatar-shift: 0px;\n}\n\n.liforma-et--scroll-parallax .liforma-et__background,\n.liforma-et--scroll-parallax .liforma-et__foreground,\n.liforma-et--scroll-parallax .liforma-et__avatar {\n\ttransition: opacity var(--liforma-et-reveal-duration) ease;\n}\n\n@media (prefers-reduced-motion: reduce) {\n\t.liforma-et {\n\t\t--liforma-et-reveal-duration: 0ms;\n\t}\n\n\t.liforma-et--parallax {\n\t\t--liforma-et-bg-scale: 1;\n\t\t--liforma-et-fg-scale: 1;\n\t\t--liforma-et-avatar-scale: 1;\n\t\t--liforma-et-bg-shift: 0px;\n\t\t--liforma-et-fg-shift: 0px;\n\t\t--liforma-et-avatar-shift: 0px;\n\t}\n\n\t.liforma-et--parallax.liforma-et--fit-face {\n\t\t--liforma-et-avatar-scale: 1.85;\n\t\t--liforma-et-bg-scale: 1.85;\n\t\t--liforma-et-fg-scale: 1.85;\n\t}\n\n\t.liforma-et__background,\n\t.liforma-et__foreground,\n\t.liforma-et__avatar {\n\t\ttransition: none;\n\t}\n}\n`.trim();\n","let styleMounted = false;\n\n/** Inject shared thumbnail CSS once per document. */\nexport function ensureExperienceThumbnailStyles(css: string): void {\n\tif (typeof document === 'undefined' || styleMounted) return;\n\tif (document.querySelector('style[data-liforma-et]')) {\n\t\tstyleMounted = true;\n\t\treturn;\n\t}\n\tconst style = document.createElement('style');\n\tstyle.setAttribute('data-liforma-et', '');\n\tstyle.textContent = css;\n\tdocument.head.appendChild(style);\n\tstyleMounted = true;\n}\n","import {\n\tattachImageReady,\n\tattachLazyThumb,\n\tattachScrollLinkedParallax,\n\tEXPERIENCE_THUMBNAIL_CSS,\n\thorizontalParallaxFromViewportY,\n\tresolveThumbnailHref,\n\tresolveThumbnailInteraction,\n\tresolveThumbnailLayers,\n\tthumbnailFitClass,\n\ttype GalleryThumb\n} from '../thumbnail/index';\n\nconst ELEMENT_NAME = 'liforma-experience-thumbnail';\n\nfunction parseGalleryThumb(raw: string | null): GalleryThumb | undefined {\n\tif (!raw?.trim()) return undefined;\n\ttry {\n\t\tconst parsed = JSON.parse(raw) as GalleryThumb;\n\t\tif (parsed && typeof parsed.avatarImage === 'string') return parsed;\n\t} catch {\n\t\t/* ignore */\n\t}\n\treturn undefined;\n}\n\nclass LiformaExperienceThumbnailElement extends HTMLElement {\n\tstatic get observedAttributes(): string[] {\n\t\treturn [\n\t\t\t'experience-id',\n\t\t\t'gallery-thumb',\n\t\t\t'avatar-image',\n\t\t\t'background-image',\n\t\t\t'foreground-image',\n\t\t\t'fallback-image',\n\t\t\t'alt',\n\t\t\t'href',\n\t\t\t'target',\n\t\t\t'rel',\n\t\t\t'return-url',\n\t\t\t'player-base-url',\n\t\t\t'parallax',\n\t\t\t'fit',\n\t\t\t'lazy'\n\t\t];\n\t}\n\n\t#root: HTMLElement | null = null;\n\t#cleanup: (() => void)[] = [];\n\t#shouldLoad = false;\n\t#hovering = false;\n\t#pointerDown = false;\n\t#scrollParallax = false;\n\t#parallaxX = 0;\n\t#parallaxY = 0;\n\t#avatarLoadFailed = false;\n\t#backgroundReady = false;\n\t#avatarReady = false;\n\t#foregroundReady = false;\n\n\tconnectedCallback(): void {\n\t\tthis.#render();\n\t}\n\n\tdisconnectedCallback(): void {\n\t\tthis.#teardown();\n\t}\n\n\tattributeChangedCallback(): void {\n\t\tif (this.isConnected) this.#render();\n\t}\n\n\t#teardown(): void {\n\t\tfor (const fn of this.#cleanup) fn();\n\t\tthis.#cleanup = [];\n\t\tthis.#root = null;\n\t}\n\n\t#boolAttr(name: string, defaultValue: boolean): boolean {\n\t\tif (!this.hasAttribute(name)) return defaultValue;\n\t\tconst value = this.getAttribute(name);\n\t\tif (value === null || value === '' || value === 'true') return true;\n\t\tif (value === 'false') return false;\n\t\treturn defaultValue;\n\t}\n\n\t#render(): void {\n\t\tthis.#teardown();\n\n\t\tconst shadow = this.shadowRoot ?? this.attachShadow({ mode: 'open' });\n\t\tconst style = document.createElement('style');\n\t\tstyle.textContent = EXPERIENCE_THUMBNAIL_CSS;\n\t\tshadow.replaceChildren(style);\n\n\t\tconst alt = this.getAttribute('alt')?.trim() || 'Experience';\n\t\tconst hrefAttr = this.getAttribute('href');\n\t\tconst experienceId = this.getAttribute('experience-id');\n\t\tconst hasClickHandler = this.hasAttribute('onclick') || typeof this.onclick === 'function';\n\t\tconst interaction = resolveThumbnailInteraction({\n\t\t\thref: hrefAttr,\n\t\t\thasClickHandler,\n\t\t\texperienceId\n\t\t});\n\n\t\tconst layers = resolveThumbnailLayers({\n\t\t\tgalleryThumb: parseGalleryThumb(this.getAttribute('gallery-thumb')),\n\t\t\tavatarImage: this.getAttribute('avatar-image') ?? undefined,\n\t\t\tbackgroundImage: this.getAttribute('background-image') ?? undefined,\n\t\t\tforegroundImage: this.getAttribute('foreground-image') ?? undefined,\n\t\t\tfallbackImage: this.getAttribute('fallback-image') ?? undefined\n\t\t});\n\t\tif (!layers) return;\n\n\t\tconst parallaxEnabled = this.#boolAttr('parallax', true);\n\t\tconst fitAttr = this.getAttribute('fit')?.trim();\n\t\tconst fit = fitAttr === 'face' || fitAttr === 'medium' ? fitAttr : 'full';\n\t\tconst lazy = this.#boolAttr('lazy', true);\n\t\tthis.#shouldLoad = !lazy;\n\n\t\tconst isHostedLaunch =\n\t\t\tinteraction === 'anchor' && !hrefAttr?.trim() && Boolean(experienceId?.trim());\n\t\tconst resolvedHref = resolveThumbnailHref({\n\t\t\thref: hrefAttr ?? undefined,\n\t\t\texperienceId: experienceId ?? undefined,\n\t\t\treturnUrl: this.getAttribute('return-url') ?? undefined,\n\t\t\tplayerBaseUrl: this.getAttribute('player-base-url') ?? undefined,\n\t\t\tuseCurrentPageAsReturnUrl:\n\t\t\t\tisHostedLaunch && !this.getAttribute('return-url')?.trim()\n\t\t});\n\n\t\tlet root: HTMLElement;\n\t\tif (interaction === 'anchor' && resolvedHref) {\n\t\t\tconst a = document.createElement('a');\n\t\t\ta.href = resolvedHref;\n\t\t\tconst target = this.getAttribute('target');\n\t\t\tconst rel = this.getAttribute('rel');\n\t\t\tif (target) a.target = target;\n\t\t\tif (rel) a.rel = rel;\n\t\t\troot = a;\n\t\t} else if (interaction === 'button') {\n\t\t\tconst button = document.createElement('button');\n\t\t\tbutton.type = 'button';\n\t\t\troot = button;\n\t\t} else {\n\t\t\troot = document.createElement('div');\n\t\t\troot.setAttribute('role', 'img');\n\t\t}\n\n\t\troot.className = 'liforma-et';\n\t\troot.setAttribute('aria-label', alt);\n\t\tthis.#root = root;\n\t\tshadow.appendChild(root);\n\n\t\tconst syncClass = () => {\n\t\t\tconst hasScene = Boolean(layers.backgroundImage);\n\t\t\tconst parallaxActive = parallaxEnabled && (this.#hovering || this.#scrollParallax);\n\t\t\t// Fade placeholder as soon as any layer can paint — don't wait for the slowest plate.\n\t\t\tconst reveal =\n\t\t\t\tthis.#shouldLoad &&\n\t\t\t\t(this.#avatarReady || this.#backgroundReady || this.#foregroundReady);\n\t\t\troot.className = [\n\t\t\t\t'liforma-et',\n\t\t\t\thasScene ? 'liforma-et--scene' : '',\n\t\t\t\tthumbnailFitClass(fit),\n\t\t\t\tparallaxActive ? 'liforma-et--parallax' : '',\n\t\t\t\tthis.#scrollParallax ? 'liforma-et--scroll-parallax' : '',\n\t\t\t\tthis.#shouldLoad && !reveal ? 'liforma-et--loading' : '',\n\t\t\t\treveal ? 'liforma-et--revealed' : ''\n\t\t\t]\n\t\t\t\t.filter(Boolean)\n\t\t\t\t.join(' ');\n\t\t\troot.style.setProperty('--liforma-et-parallax-x', String(this.#parallaxX));\n\t\t\troot.style.setProperty('--liforma-et-parallax-y', String(this.#parallaxY));\n\t\t};\n\n\t\tconst placeholder = document.createElement('span');\n\t\tplaceholder.className = 'liforma-et__placeholder';\n\t\tplaceholder.setAttribute('aria-hidden', 'true');\n\t\troot.appendChild(placeholder);\n\n\t\tconst paintImages = () => {\n\t\t\tif (!this.#shouldLoad) return;\n\t\t\tconst avatarSrc =\n\t\t\t\tthis.#avatarLoadFailed && layers.fallbackImage\n\t\t\t\t\t? layers.fallbackImage\n\t\t\t\t\t: layers.avatarImage;\n\n\t\t\tif (layers.backgroundImage && !root.querySelector('.liforma-et__background')) {\n\t\t\t\tconst img = document.createElement('img');\n\t\t\t\timg.className = 'liforma-et__background liforma-et__media';\n\t\t\t\timg.alt = '';\n\t\t\t\timg.decoding = 'async';\n\t\t\t\timg.src = layers.backgroundImage;\n\t\t\t\tthis.#cleanup.push(\n\t\t\t\t\tattachImageReady(img, () => {\n\t\t\t\t\t\tthis.#backgroundReady = true;\n\t\t\t\t\t\timg.classList.add('liforma-et__media--ready');\n\t\t\t\t\t\tsyncClass();\n\t\t\t\t\t})\n\t\t\t\t);\n\t\t\t\troot.appendChild(img);\n\t\t\t}\n\n\t\t\tlet avatar = root.querySelector('.liforma-et__avatar') as HTMLImageElement | null;\n\t\t\tif (!avatar) {\n\t\t\t\tavatar = document.createElement('img');\n\t\t\t\tavatar.className = 'liforma-et__avatar liforma-et__media';\n\t\t\t\tavatar.alt = '';\n\t\t\t\tavatar.decoding = 'async';\n\t\t\t\tavatar.addEventListener('error', () => {\n\t\t\t\t\tif (layers.fallbackImage && !this.#avatarLoadFailed) {\n\t\t\t\t\t\tthis.#avatarLoadFailed = true;\n\t\t\t\t\t\tavatar!.src = layers.fallbackImage;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.#avatarReady = true;\n\t\t\t\t\t\tsyncClass();\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tthis.#cleanup.push(\n\t\t\t\t\tattachImageReady(avatar, () => {\n\t\t\t\t\t\tthis.#avatarReady = true;\n\t\t\t\t\t\tavatar!.classList.add('liforma-et__media--ready');\n\t\t\t\t\t\tsyncClass();\n\t\t\t\t\t})\n\t\t\t\t);\n\t\t\t\troot.appendChild(avatar);\n\t\t\t}\n\t\t\tif (avatar.src !== avatarSrc) avatar.src = avatarSrc;\n\n\t\t\tif (layers.foregroundImage && !root.querySelector('.liforma-et__foreground')) {\n\t\t\t\tconst img = document.createElement('img');\n\t\t\t\timg.className = 'liforma-et__foreground liforma-et__media';\n\t\t\t\timg.alt = '';\n\t\t\t\timg.decoding = 'async';\n\t\t\t\timg.src = layers.foregroundImage;\n\t\t\t\tthis.#cleanup.push(\n\t\t\t\t\tattachImageReady(img, () => {\n\t\t\t\t\t\tthis.#foregroundReady = true;\n\t\t\t\t\t\timg.classList.add('liforma-et__media--ready');\n\t\t\t\t\t\tsyncClass();\n\t\t\t\t\t})\n\t\t\t\t);\n\t\t\t\troot.appendChild(img);\n\t\t\t}\n\t\t\tsyncClass();\n\t\t};\n\n\t\tif (lazy) {\n\t\t\tthis.#cleanup.push(\n\t\t\t\tattachLazyThumb(root, {\n\t\t\t\t\tonVisibleChange: (visible) => {\n\t\t\t\t\t\tif (!visible) return;\n\t\t\t\t\t\tthis.#shouldLoad = true;\n\t\t\t\t\t\tpaintImages();\n\t\t\t\t\t\tsyncClass();\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t);\n\t\t} else {\n\t\t\tpaintImages();\n\t\t}\n\n\t\tif (parallaxEnabled) {\n\t\t\tthis.#cleanup.push(\n\t\t\t\tattachScrollLinkedParallax(root, {\n\t\t\t\t\tonParallax: (x) => {\n\t\t\t\t\t\tthis.#scrollParallax = true;\n\t\t\t\t\t\tif (!this.#pointerDown) {\n\t\t\t\t\t\t\tthis.#parallaxX = x;\n\t\t\t\t\t\t\tthis.#parallaxY = 0;\n\t\t\t\t\t\t\tsyncClass();\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\tpointerActive: () => this.#pointerDown\n\t\t\t\t})\n\t\t\t);\n\n\t\t\tconst onPointerMove = (event: PointerEvent) => {\n\t\t\t\tif (this.#scrollParallax) return;\n\t\t\t\tconst rect = root.getBoundingClientRect();\n\t\t\t\tif (rect.width <= 0 || rect.height <= 0) return;\n\t\t\t\tthis.#parallaxX = ((event.clientX - rect.left) / rect.width - 0.5) * 2;\n\t\t\t\tthis.#parallaxY = ((event.clientY - rect.top) / rect.height - 0.5) * 2;\n\t\t\t\tsyncClass();\n\t\t\t};\n\t\t\tconst onPointerDown = (event: PointerEvent) => {\n\t\t\t\tthis.#pointerDown = true;\n\t\t\t\tif (event.pointerType === 'mouse') this.#hovering = true;\n\t\t\t\tsyncClass();\n\t\t\t};\n\t\t\tconst onPointerUp = () => {\n\t\t\t\tconst wasDown = this.#pointerDown;\n\t\t\t\tthis.#pointerDown = false;\n\t\t\t\tthis.#hovering = false;\n\t\t\t\tif (this.#scrollParallax && wasDown) {\n\t\t\t\t\tthis.#parallaxX = horizontalParallaxFromViewportY(\n\t\t\t\t\t\troot.getBoundingClientRect(),\n\t\t\t\t\t\twindow.innerHeight\n\t\t\t\t\t);\n\t\t\t\t\tthis.#parallaxY = 0;\n\t\t\t\t} else if (!this.#scrollParallax) {\n\t\t\t\t\tthis.#parallaxX = 0;\n\t\t\t\t\tthis.#parallaxY = 0;\n\t\t\t\t}\n\t\t\t\tsyncClass();\n\t\t\t};\n\t\t\troot.addEventListener('pointermove', onPointerMove);\n\t\t\troot.addEventListener('pointerdown', onPointerDown);\n\t\t\troot.addEventListener('pointerup', onPointerUp);\n\t\t\troot.addEventListener('pointercancel', onPointerUp);\n\t\t\troot.addEventListener('pointerenter', (event: PointerEvent) => {\n\t\t\t\tif (event.pointerType === 'mouse') {\n\t\t\t\t\tthis.#hovering = true;\n\t\t\t\t\tsyncClass();\n\t\t\t\t}\n\t\t\t});\n\t\t\troot.addEventListener('pointerleave', () => {\n\t\t\t\tif (!this.#pointerDown) {\n\t\t\t\t\tthis.#hovering = false;\n\t\t\t\t\tif (!this.#scrollParallax) {\n\t\t\t\t\t\tthis.#parallaxX = 0;\n\t\t\t\t\t\tthis.#parallaxY = 0;\n\t\t\t\t\t}\n\t\t\t\t\tsyncClass();\n\t\t\t\t}\n\t\t\t});\n\t\t\tthis.#cleanup.push(() => {\n\t\t\t\troot.removeEventListener('pointermove', onPointerMove);\n\t\t\t\troot.removeEventListener('pointerdown', onPointerDown);\n\t\t\t\troot.removeEventListener('pointerup', onPointerUp);\n\t\t\t\troot.removeEventListener('pointercancel', onPointerUp);\n\t\t\t});\n\t\t}\n\n\t\troot.addEventListener('click', (event: MouseEvent) => {\n\t\t\tif (isHostedLaunch && !this.getAttribute('return-url')?.trim()) {\n\t\t\t\tconst next = resolveThumbnailHref({\n\t\t\t\t\texperienceId: experienceId ?? undefined,\n\t\t\t\t\tplayerBaseUrl: this.getAttribute('player-base-url') ?? undefined,\n\t\t\t\t\tuseCurrentPageAsReturnUrl: true\n\t\t\t\t});\n\t\t\t\tif (\n\t\t\t\t\tnext &&\n\t\t\t\t\tevent.button === 0 &&\n\t\t\t\t\t!event.metaKey &&\n\t\t\t\t\t!event.ctrlKey &&\n\t\t\t\t\t!event.shiftKey\n\t\t\t\t) {\n\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\twindow.location.assign(next);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tsyncClass();\n\t}\n}\n\nif (!customElements.get(ELEMENT_NAME)) {\n\tcustomElements.define(ELEMENT_NAME, LiformaExperienceThumbnailElement);\n}\n\nexport { LiformaExperienceThumbnailElement };\n","import { resolvePublicApiBaseUrl } from '../../shims/apiBaseUrl';\nimport type { GalleryThumb } from '../../thumbnail/types';\n\nexport type ExperiencePreview = {\n\treadonly experienceId: string;\n\treadonly title: string;\n\treadonly galleryThumb: GalleryThumb;\n};\n\nfunction parseGalleryThumb(raw: unknown): GalleryThumb | null {\n\tif (!raw || typeof raw !== 'object') return null;\n\tconst thumb = raw as Record<string, unknown>;\n\tconst avatarImage = typeof thumb.avatarImage === 'string' ? thumb.avatarImage.trim() : '';\n\tif (!avatarImage) return null;\n\treturn {\n\t\tavatarImage,\n\t\t...(typeof thumb.backgroundImage === 'string' && thumb.backgroundImage.trim()\n\t\t\t? { backgroundImage: thumb.backgroundImage.trim() }\n\t\t\t: {}),\n\t\t...(typeof thumb.foregroundImage === 'string' && thumb.foregroundImage.trim()\n\t\t\t? { foregroundImage: thumb.foregroundImage.trim() }\n\t\t\t: {})\n\t};\n}\n\n/**\n * Fetch public widget preview (title + gallery thumb plates) by experience id.\n * No API key / origin allowlist — safe for collapsed FAB paint before mint.\n */\nexport async function fetchExperiencePreview(\n\texperienceId: string,\n\toptions?: {\n\t\treadonly apiBaseUrl?: string;\n\t\treadonly signal?: AbortSignal;\n\t}\n): Promise<ExperiencePreview> {\n\tconst trimmed = experienceId.trim();\n\tif (!trimmed) {\n\t\tthrow new Error('Experience preview requires experienceId.');\n\t}\n\n\tconst apiBaseUrl = (options?.apiBaseUrl ?? resolvePublicApiBaseUrl()).replace(/\\/$/, '');\n\tconst res = await fetch(\n\t\t`${apiBaseUrl}/v1/experiences/${encodeURIComponent(trimmed)}/preview`,\n\t\t{\n\t\t\tmethod: 'GET',\n\t\t\theaders: { Accept: 'application/json' },\n\t\t\tsignal: options?.signal\n\t\t}\n\t);\n\n\tif (!res.ok) {\n\t\tlet message = `Experience preview failed (${res.status})`;\n\t\ttry {\n\t\t\tconst body = (await res.json()) as { message?: string };\n\t\t\tif (body.message?.trim()) message = body.message.trim();\n\t\t} catch {\n\t\t\t/* keep */\n\t\t}\n\t\tthrow new Error(message);\n\t}\n\n\tlet raw: unknown;\n\ttry {\n\t\traw = await res.json();\n\t} catch {\n\t\tthrow new Error('Experience preview returned invalid JSON.');\n\t}\n\n\tif (!raw || typeof raw !== 'object') {\n\t\tthrow new Error('Experience preview returned an invalid payload.');\n\t}\n\tconst payload = raw as Record<string, unknown>;\n\tconst id =\n\t\ttypeof payload.experienceId === 'string' ? payload.experienceId.trim() : trimmed;\n\tconst title = typeof payload.title === 'string' ? payload.title.trim() : '';\n\tconst galleryThumb = parseGalleryThumb(payload.galleryThumb);\n\tif (!galleryThumb) {\n\t\tthrow new Error('Experience preview is missing gallery thumb images.');\n\t}\n\n\treturn {\n\t\texperienceId: id || trimmed,\n\t\ttitle: title || id || trimmed,\n\t\tgalleryThumb\n\t};\n}\n","/** Desktop breakpoint for reduce / dock chrome (matches CSS). */\nexport const WIDGET_DESKTOP_MEDIA = '(min-width: 768px)';\n\nconst DOCK_MARGIN = 24;\n/** Fraction of the device screen height (not the browser chrome box). */\nexport const DOCK_HEIGHT_FRAC = 0.5;\nexport const DOCK_HEIGHT_MAX = 1200;\n/** Never taller than this share of the browser viewport (keeps margins). */\nconst DOCK_VIEWPORT_FRAC = 0.85;\n/** Portrait width / height. */\nexport const DOCK_ASPECT = 9 / 16;\n\nexport type DockSize = {\n\treadonly width: number;\n\treadonly height: number;\n};\n\nexport type DockPoint = {\n\treadonly left: number;\n\treadonly top: number;\n};\n\nfunction readScreenHeight(): number {\n\tif (typeof window === 'undefined') return 800;\n\tconst avail = window.screen?.availHeight;\n\treturn typeof avail === 'number' && avail > 0 ? avail : window.innerHeight;\n}\n\n/**\n * Docked panel size: ~50% of device screen height (cap 1200), 9:16 portrait,\n * then clamped to fit the browser viewport with margin.\n */\nexport function computeDockSize(\n\tviewportWidth = typeof window !== 'undefined' ? window.innerWidth : 1280,\n\tviewportHeight = typeof window !== 'undefined' ? window.innerHeight : 800,\n\tmargin = DOCK_MARGIN,\n\tscreenHeight = typeof window !== 'undefined' ? readScreenHeight() : 800\n): DockSize {\n\tconst maxH = Math.max(\n\t\t160,\n\t\tMath.min(viewportHeight - margin * 2, viewportHeight * DOCK_VIEWPORT_FRAC)\n\t);\n\tconst maxW = Math.max(120, viewportWidth - margin * 2);\n\tlet height = Math.min(screenHeight * DOCK_HEIGHT_FRAC, DOCK_HEIGHT_MAX, maxH);\n\tlet width = height * DOCK_ASPECT;\n\tif (width > maxW) {\n\t\twidth = maxW;\n\t\theight = width / DOCK_ASPECT;\n\t\tif (height > maxH) {\n\t\t\theight = maxH;\n\t\t\twidth = height * DOCK_ASPECT;\n\t\t}\n\t}\n\treturn {\n\t\twidth: Math.round(width),\n\t\theight: Math.round(height)\n\t};\n}\n\n/** Default bottom-right placement for a docked panel. */\nexport function defaultDockOrigin(\n\tsize: DockSize,\n\tviewportWidth = typeof window !== 'undefined' ? window.innerWidth : 1280,\n\tviewportHeight = typeof window !== 'undefined' ? window.innerHeight : 800,\n\tmargin = DOCK_MARGIN\n): DockPoint {\n\treturn {\n\t\tleft: Math.max(margin, viewportWidth - size.width - margin),\n\t\ttop: Math.max(margin, viewportHeight - size.height - margin)\n\t};\n}\n\n/** Keep a docked panel fully inside the viewport. */\nexport function clampDockOrigin(\n\tleft: number,\n\ttop: number,\n\tsize: DockSize,\n\tviewportWidth = typeof window !== 'undefined' ? window.innerWidth : 1280,\n\tviewportHeight = typeof window !== 'undefined' ? window.innerHeight : 800,\n\tmargin = DOCK_MARGIN\n): DockPoint {\n\tconst maxLeft = Math.max(margin, viewportWidth - size.width - margin);\n\tconst maxTop = Math.max(margin, viewportHeight - size.height - margin);\n\treturn {\n\t\tleft: Math.min(Math.max(margin, left), maxLeft),\n\t\ttop: Math.min(Math.max(margin, top), maxTop)\n\t};\n}\n\nexport function isDesktopViewport(\n\tmatchMediaFn: (query: string) => MediaQueryList = typeof window !== 'undefined'\n\t\t? window.matchMedia.bind(window)\n\t\t: () => ({ matches: true }) as MediaQueryList\n): boolean {\n\treturn matchMediaFn(WIDGET_DESKTOP_MEDIA).matches;\n}\n","/** Styles for ExperienceWidget (class prefix `liforma-ew`). */\nexport const EXPERIENCE_WIDGET_CSS = `\n.liforma-ew {\n\tbox-sizing: border-box;\n\tposition: relative;\n\tdisplay: block;\n\twidth: 100%;\n\theight: 100%;\n\tmin-height: 0;\n\tmargin: 0;\n\tpadding: 0;\n\tborder: 0;\n}\n\n/* Optional self-positioned FAB (position=bottom-right|bottom-left).\n * Cap at 50vmin so small phones don't get an oversized thumb (desktop 224px stays). */\n.liforma-ew[data-position='bottom-right'],\n.liforma-ew[data-position='bottom-left'] {\n\tposition: fixed;\n\twidth: min(var(--liforma-ew-fab-size, 224px), 50vmin);\n\theight: min(var(--liforma-ew-fab-size, 224px), 50vmin);\n\tbottom: var(--liforma-ew-offset, 16px);\n\tz-index: 2147482900;\n}\n\n.liforma-ew[data-position='bottom-right'] {\n\tright: var(--liforma-ew-offset, 16px);\n\tleft: auto;\n}\n\n.liforma-ew[data-position='bottom-left'] {\n\tleft: var(--liforma-ew-offset, 16px);\n\tright: auto;\n}\n\n/* While open, collapse the FAB box so a corner hole / thumb never remains. */\n.liforma-ew[data-position='bottom-right'][data-state='expanded'],\n.liforma-ew[data-position='bottom-right'][data-state='docked'],\n.liforma-ew[data-position='bottom-left'][data-state='expanded'],\n.liforma-ew[data-position='bottom-left'][data-state='docked'] {\n\twidth: 0;\n\theight: 0;\n\tmin-width: 0;\n\tmin-height: 0;\n\toverflow: visible;\n\tpointer-events: none;\n}\n\n.liforma-ew[data-state='expanded'] .liforma-ew__warm,\n.liforma-ew[data-state='docked'] .liforma-ew__warm,\n.liforma-ew[data-state='expanded'] .liforma-ew__overlay,\n.liforma-ew[data-state='expanded'] .liforma-ew__toolbar,\n.liforma-ew[data-state='docked'] .liforma-ew__toolbar {\n\tpointer-events: auto;\n}\n\n.liforma-ew[data-state='docked'] .liforma-ew__overlay {\n\tpointer-events: none;\n}\n\n.liforma-ew[data-state='docked'] .liforma-ew__panel {\n\tpointer-events: auto;\n}\n\n.liforma-ew__hit {\n\tbox-sizing: border-box;\n\tdisplay: block;\n\twidth: 100%;\n\theight: 100%;\n\tmargin: 0;\n\tpadding: 0;\n\tborder: 0;\n\tbackground: transparent;\n\tcursor: pointer;\n\tfont: inherit;\n\tappearance: none;\n\t-webkit-tap-highlight-color: transparent;\n\ttransform: scale(1);\n\ttransform-origin: center center;\n\ttransition:\n\t\ttransform 180ms ease,\n\t\tfilter 180ms ease;\n\twill-change: transform;\n}\n\n/* Author display must not defeat the HTML hidden attribute. */\n.liforma-ew__hit[hidden] {\n\tdisplay: none !important;\n}\n\n.liforma-ew__hit:focus-visible {\n\toutline: 2px solid #5b8def;\n\toutline-offset: 3px;\n\tborder-radius: 0.75rem;\n}\n\n@media (hover: hover) and (pointer: fine) {\n\t/* Scale the thumb whenever the collapsed FAB is hovered — including via the warm iframe hit layer. */\n\t.liforma-ew[data-state='collapsed'] .liforma-ew__hit:not(:disabled):hover,\n\t.liforma-ew[data-state='collapsed']:has(.liforma-ew__warm:hover) .liforma-ew__hit {\n\t\ttransform: scale(1.06);\n\t}\n\n\t.liforma-ew[data-state='collapsed'] .liforma-ew__hit:not(:disabled):hover .liforma-et,\n\t.liforma-ew[data-state='collapsed']:has(.liforma-ew__warm:hover) .liforma-et {\n\t\tbox-shadow: 0 12px 36px rgba(0, 0, 0, 0.34);\n\t}\n}\n\n.liforma-ew__face {\n\twidth: 100%;\n\theight: 100%;\n\tmin-height: 0;\n\tposition: relative;\n}\n\n.liforma-ew__face-status {\n\tposition: absolute;\n\tinset: 0;\n\tz-index: 1;\n\tdisplay: grid;\n\tplace-items: center;\n\tpadding: 0.5rem;\n\ttext-align: center;\n\tfont: 600 0.75rem/1.3 system-ui, sans-serif;\n\tcolor: #e8ecf4;\n\tbackground: rgba(10, 12, 18, 0.72);\n\tborder-radius: 0.75rem;\n\tpointer-events: none;\n}\n\n.liforma-ew__face-status[hidden] {\n\tdisplay: none !important;\n}\n\n.liforma-ew__hit:disabled {\n\tcursor: default;\n\topacity: 0.85;\n}\n\n.liforma-ew__face .liforma-et {\n\tborder-radius: 0.75rem;\n\tbox-shadow: 0 8px 28px rgba(0, 0, 0, 0.28);\n\ttransition: box-shadow 180ms ease;\n}\n\n.liforma-ew__warm {\n\tbox-sizing: border-box;\n\tposition: absolute;\n\tinset: 0;\n\tz-index: 2;\n\tborder-radius: 0.75rem;\n\toverflow: hidden;\n\tbackground: transparent;\n\tpointer-events: none;\n\topacity: 1;\n\ttransition: opacity 1000ms ease;\n}\n\n/*\n * Collapsed: keep the warm iframe as an invisible hit target only.\n * Painting it over the thumb causes a “ghost enlarge” (face scales under a static iframe).\n */\n.liforma-ew[data-state='collapsed'] .liforma-ew__warm {\n\topacity: 0;\n\ttransition: none;\n}\n\n.liforma-ew[data-warm='true'] .liforma-ew__warm {\n\tpointer-events: auto;\n}\n\n.liforma-ew[data-warm='true']:not([data-live='true']) .liforma-ew__hit {\n\tpointer-events: none;\n}\n\n.liforma-ew[data-live='true'][data-state='collapsed'] .liforma-ew__hit {\n\tpointer-events: auto;\n}\n\n.liforma-ew[data-live='true'] .liforma-ew__warm,\n.liforma-ew[data-live='true'] .liforma-ew__stage {\n\t/* Hidden under the thumb / until post-resize fade-in (matches cold avatar reveal). */\n\topacity: 0;\n}\n\n.liforma-ew[data-live='true'].liforma-ew--session-revealed[data-state='expanded'] .liforma-ew__warm,\n.liforma-ew[data-live='true'].liforma-ew--session-revealed[data-state='expanded'] .liforma-ew__stage,\n.liforma-ew[data-live='true'].liforma-ew--session-revealed[data-state='docked'] .liforma-ew__warm,\n.liforma-ew[data-live='true'].liforma-ew--session-revealed[data-state='docked'] .liforma-ew__stage {\n\topacity: 1;\n}\n\n.liforma-ew--session-concealing .liforma-ew__warm,\n.liforma-ew--session-concealing .liforma-ew__stage {\n\ttransition-duration: 280ms;\n}\n\n/* Instant hide while dock/maximize geometry settles (player morph resize ~160ms). */\n.liforma-ew--layout-settling .liforma-ew__warm,\n.liforma-ew--layout-settling .liforma-ew__stage {\n\topacity: 0 !important;\n\ttransition: none !important;\n}\n\n.liforma-ew[data-live='true'][data-state='collapsed'] .liforma-ew__warm {\n\t/* Keep the live iframe under the thumb; FAB click resumes the shell. */\n\tpointer-events: none;\n\topacity: 0;\n\t/* Instant hide when collapsed so the FAB never shows a resized live mesh. */\n\ttransition: none;\n}\n\n.liforma-ew[data-state='expanded'] .liforma-ew__warm {\n\tposition: fixed;\n\t/* Match overlay panel inset so the warm iframe fills the conversation shell. */\n\tinset: clamp(12px, 3vw, 28px);\n\tz-index: 2147483001;\n\twidth: auto;\n\theight: auto;\n\tborder-radius: 16px;\n\tpointer-events: auto;\n}\n\n.liforma-ew[data-state='docked'] .liforma-ew__warm {\n\tposition: fixed;\n\tinset: auto;\n\tz-index: 2147483001;\n\tborder-radius: 16px;\n\tpointer-events: auto;\n\t/* left/top/width/height set inline from dock geometry */\n}\n\n.liforma-ew__toolbar {\n\tdisplay: none;\n\tposition: fixed;\n\tz-index: 2147483002;\n\tpointer-events: auto;\n\tbox-sizing: border-box;\n\talign-items: center;\n\tgap: 0.35rem;\n\tpadding: 0.35rem 0.4rem;\n\tborder-radius: 999px;\n\tbackground: rgba(12, 14, 20, 0.72);\n\tbackdrop-filter: blur(8px);\n\t-webkit-backdrop-filter: blur(8px);\n\tbox-shadow: 0 8px 24px rgba(0, 0, 0, 0.28);\n}\n\n.liforma-ew[data-state='expanded'] .liforma-ew__toolbar {\n\tdisplay: flex;\n\ttop: calc(clamp(12px, 3vw, 28px) + 10px);\n\tright: calc(clamp(12px, 3vw, 28px) + 10px);\n\tleft: auto;\n\tcursor: default;\n}\n\n.liforma-ew[data-state='docked'] .liforma-ew__toolbar {\n\tdisplay: flex;\n\t/* left/top set inline to track the panel; strip remains a drag handle */\n\tcursor: grab;\n\ttouch-action: none;\n\tuser-select: none;\n}\n\n.liforma-ew[data-state='docked'] .liforma-ew__panel,\n.liforma-ew[data-state='docked'] .liforma-ew__warm,\n.liforma-ew[data-state='docked'] .liforma-ew__stage {\n\t/* Player also reports surface drags via postMessage when docked. */\n\tcursor: grab;\n\ttouch-action: none;\n}\n\n.liforma-ew[data-state='docked'] .liforma-ew__toolbar.liforma-ew__toolbar--dragging,\n.liforma-ew--dock-dragging,\n.liforma-ew--dock-dragging .liforma-ew__toolbar,\n.liforma-ew--dock-dragging .liforma-ew__panel,\n.liforma-ew--dock-dragging .liforma-ew__warm,\n.liforma-ew--dock-dragging .liforma-ew__stage {\n\tcursor: grabbing;\n}\n\n\n.liforma-ew__toolbar[hidden] {\n\tdisplay: none !important;\n}\n\n.liforma-ew__icon-btn {\n\tappearance: none;\n\tbox-sizing: border-box;\n\tdisplay: inline-grid;\n\tplace-items: center;\n\twidth: 2rem;\n\theight: 2rem;\n\tmargin: 0;\n\tpadding: 0;\n\tborder: 0;\n\tborder-radius: 999px;\n\tcolor: #f5f7fb;\n\tbackground: transparent;\n\tcursor: pointer;\n}\n\n.liforma-ew__icon-btn:hover {\n\tbackground: rgba(255, 255, 255, 0.12);\n}\n\n.liforma-ew__icon-btn[aria-pressed='true'] {\n\tbackground: rgba(255, 255, 255, 0.2);\n}\n\n.liforma-ew__icon-btn:focus-visible {\n\toutline: 2px solid #5b8def;\n\toutline-offset: 2px;\n}\n\n.liforma-ew__icon-btn svg {\n\twidth: 1.05rem;\n\theight: 1.05rem;\n\tdisplay: block;\n}\n\n.liforma-ew__drag-grip {\n\tdisplay: none;\n\twidth: 0.85rem;\n\theight: 1.25rem;\n\tmargin: 0 0.15rem 0 0.25rem;\n\tbackground:\n\t\tradial-gradient(circle, currentColor 1.1px, transparent 1.2px) 0 0 / 0.35rem 0.35rem,\n\t\tradial-gradient(circle, currentColor 1.1px, transparent 1.2px) 0.35rem 0 / 0.35rem 0.35rem;\n\tbackground-repeat: repeat-y;\n\topacity: 0.55;\n\tcolor: #f5f7fb;\n\tpointer-events: none;\n}\n\n.liforma-ew[data-state='docked'] .liforma-ew__drag-grip {\n\tdisplay: block;\n}\n\n.liforma-ew__dock-toggle[hidden] {\n\tdisplay: none !important;\n}\n\n.liforma-ew__overlay {\n\tposition: fixed;\n\tinset: 0;\n\tz-index: 2147483000;\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\tpadding: clamp(12px, 3vw, 28px);\n\tbox-sizing: border-box;\n}\n\n.liforma-ew[data-state='docked'] .liforma-ew__overlay {\n\tpadding: 0;\n\talign-items: stretch;\n\tjustify-content: stretch;\n\tpointer-events: none;\n\tbackground: transparent;\n}\n\n.liforma-ew__overlay[hidden] {\n\tdisplay: none !important;\n}\n\n.liforma-ew__backdrop {\n\tposition: absolute;\n\tinset: 0;\n\tborder: 0;\n\tpadding: 0;\n\tmargin: 0;\n\tbackground: rgba(6, 8, 14, 0.62);\n\tcursor: pointer;\n}\n\n.liforma-ew[data-state='docked'] .liforma-ew__backdrop {\n\tdisplay: none !important;\n}\n\n.liforma-ew__panel {\n\tposition: relative;\n\tz-index: 1;\n\tdisplay: flex;\n\tflex-direction: column;\n\t/* Fill the overlay so player loading / start UI centers in the visible window. */\n\twidth: var(--liforma-ew-max-w, 100%);\n\theight: var(--liforma-ew-max-h, 100%);\n\tmax-width: 100%;\n\tmax-height: 100%;\n\tborder-radius: 16px;\n\toverflow: hidden;\n\tbackground: #030508;\n\tbox-shadow: 0 24px 80px rgba(0, 0, 0, 0.45);\n\t/* Warm iframe sits above as a sibling; keep panel as chrome shell. */\n\tpointer-events: none;\n}\n\n.liforma-ew[data-state='docked'] .liforma-ew__panel {\n\tposition: fixed;\n\tinset: auto;\n\tmax-width: none;\n\tmax-height: none;\n\tpointer-events: auto;\n\t/* left/top/width/height set inline from dock geometry */\n}\n\n.liforma-ew:not([data-warm='true']) .liforma-ew__panel {\n\tpointer-events: auto;\n}\n\n.liforma-ew__stage {\n\tposition: relative;\n\tflex: 1;\n\tmin-height: 0;\n\twidth: 100%;\n\theight: 100%;\n\tbackground: #030508;\n\topacity: 1;\n\ttransition: opacity 1000ms ease;\n}\n\n.liforma-ew__status {\n\tposition: absolute;\n\tinset: 0;\n\tz-index: 1;\n\tdisplay: grid;\n\tplace-items: center;\n\tpadding: 1.5rem;\n\ttext-align: center;\n\tcolor: #d7dde8;\n\tfont: 500 0.9375rem/1.45 system-ui, sans-serif;\n\tbackground: rgba(3, 5, 8, 0.92);\n\tpointer-events: none;\n}\n\n.liforma-ew__status[hidden] {\n\tdisplay: none !important;\n}\n\n/* Mobile expanded: ~90% viewport, no reduce control. */\n@media (max-width: 767px) {\n\t.liforma-ew[data-state='expanded'] .liforma-ew__overlay {\n\t\tpadding: 5vh 5vw;\n\t}\n\n\t.liforma-ew[data-state='expanded'] .liforma-ew__panel {\n\t\twidth: 90vw;\n\t\theight: 90vh;\n\t\tmax-width: 90vw;\n\t\tmax-height: 90vh;\n\t}\n\n\t.liforma-ew[data-state='expanded'] .liforma-ew__warm {\n\t\tinset: 5vh 5vw;\n\t}\n\n\t.liforma-ew[data-state='expanded'] .liforma-ew__toolbar {\n\t\ttop: calc(5vh + 10px);\n\t\tright: calc(5vw + 10px);\n\t}\n\n\t.liforma-ew__dock-toggle {\n\t\tdisplay: none !important;\n\t}\n}\n\n@media (prefers-reduced-motion: reduce) {\n\t.liforma-ew__hit {\n\t\ttransition: none;\n\t\twill-change: auto;\n\t}\n\n\t.liforma-ew[data-state='collapsed'] .liforma-ew__hit:not(:disabled):hover,\n\t.liforma-ew[data-state='collapsed']:has(.liforma-ew__warm:hover) .liforma-ew__hit {\n\t\ttransform: none;\n\t}\n\n\t.liforma-ew__face .liforma-et {\n\t\tbox-shadow: none;\n\t\ttransition: none;\n\t}\n\n\t.liforma-ew__warm,\n\t.liforma-ew__stage {\n\t\ttransition: none;\n\t}\n}\n`;\n\nconst STYLE_ID = 'liforma-experience-widget-styles';\n\nexport function ensureExperienceWidgetStyles(css: string = EXPERIENCE_WIDGET_CSS): void {\n\tif (typeof document === 'undefined') return;\n\tconst existing = document.getElementById(STYLE_ID);\n\tif (existing) {\n\t\t/* Keep injected CSS current across local HMR / preview reloads. */\n\t\texisting.textContent = css;\n\t\treturn;\n\t}\n\tconst style = document.createElement('style');\n\tstyle.id = STYLE_ID;\n\tstyle.textContent = css;\n\tdocument.head.append(style);\n}\n","import { fetchExperiencePreview } from '../v2/api/fetchExperiencePreview';\nimport { Experience } from '../v2/experience';\nimport type { StartButtonOptions } from '../v2/types/sessionInteraction';\nimport {\n\tattachThumbnailParallax,\n\tensureExperienceThumbnailStyles,\n\tEXPERIENCE_THUMBNAIL_CSS,\n\tresolveThumbnailLayers\n} from '../thumbnail/index';\nimport type { GalleryThumb } from '../thumbnail/types';\nimport {\n\tclampDockOrigin,\n\tcomputeDockSize,\n\tdefaultDockOrigin,\n\tisDesktopViewport,\n\tWIDGET_DESKTOP_MEDIA\n} from './dockGeometry';\nimport { ensureExperienceWidgetStyles, EXPERIENCE_WIDGET_CSS } from './styles';\nimport type {\n\tExperienceWidgetController,\n\tExperienceWidgetOptions,\n\tExperienceWidgetPosition,\n\tExperienceWidgetPrefetch\n} from './types';\n\nfunction resolvePosition(value: ExperienceWidgetPosition | undefined): ExperienceWidgetPosition {\n\treturn value === 'bottom-right' || value === 'bottom-left' ? value : 'static';\n}\n\n/** Normalize offset to a CSS length (default 16px). */\nexport function resolveWidgetOffsetCss(offset?: number | string): string {\n\tif (typeof offset === 'number' && Number.isFinite(offset)) {\n\t\treturn `${offset}px`;\n\t}\n\tconst raw = typeof offset === 'string' ? offset.trim() : '';\n\tif (!raw) return '16px';\n\tif (/^\\d+(\\.\\d+)?$/.test(raw)) return `${raw}px`;\n\treturn raw;\n}\n\ntype FaceLayers = {\n\treadonly galleryThumb?: GalleryThumb;\n\treadonly avatarImage?: string;\n\treadonly backgroundImage?: string;\n\treadonly foregroundImage?: string;\n\treadonly fallbackImage?: string;\n};\n\ntype ShellState = 'collapsed' | 'expanded' | 'docked';\n\nfunction isOpenShell(state: string | undefined): state is 'expanded' | 'docked' {\n\treturn state === 'expanded' || state === 'docked';\n}\n\n/** Tabler `settings` — audio device panel. */\nconst ICON_SETTINGS = `<svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 0 0 2.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 0 0 1.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 0 0-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 0 0-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 0 0-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 0 0-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 0 0 1.066-2.573c-.94-1.543.826-3.31 2.37-2.37c1 .608 2.296.07 2.572-1.065\"/><path d=\"M9 12a3 3 0 1 0 6 0a3 3 0 0 0-6 0\"/></svg>`;\n\nconst ICON_CLOSE = `<svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\"><path fill=\"currentColor\" d=\"M18.3 5.71a1 1 0 0 0-1.41 0L12 10.59 7.11 5.7A1 1 0 0 0 5.7 7.11L10.59 12l-4.9 4.89a1 1 0 1 0 1.42 1.42L12 13.41l4.89 4.9a1 1 0 0 0 1.42-1.42L13.41 12l4.9-4.89a1 1 0 0 0-.01-1.4Z\"/></svg>`;\n\n/** Tabler `arrows-diagonal-minimize-2` — reduce / dock. */\nconst ICON_REDUCE = `<svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M18 10h-4V6m6-2l-6 6m-8 4h4v4m0-4l-6 6\"/></svg>`;\n\n/** Tabler `arrows-diagonal` — maximize (pair for minimize-2). */\nconst ICON_MAXIMIZE = `<svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M16 4h4v4m-6 2l6-6M8 20H4v-4m0 4l6-6\"/></svg>`;\n\nfunction renderFace(\n\tcontainer: HTMLElement,\n\tlayersInput: FaceLayers,\n\toptions?: { readonly parallax?: boolean }\n): (() => void) | null {\n\tensureExperienceThumbnailStyles(EXPERIENCE_THUMBNAIL_CSS);\n\tconst layers = resolveThumbnailLayers({\n\t\tgalleryThumb: layersInput.galleryThumb,\n\t\tavatarImage: layersInput.avatarImage,\n\t\tbackgroundImage: layersInput.backgroundImage,\n\t\tforegroundImage: layersInput.foregroundImage,\n\t\tfallbackImage: layersInput.fallbackImage\n\t});\n\tif (!layers) {\n\t\tcontainer.replaceChildren();\n\t\treturn null;\n\t}\n\n\tconst root = document.createElement('div');\n\troot.className = `liforma-et${layers.backgroundImage ? ' liforma-et--scene' : ''} liforma-et--revealed`;\n\troot.setAttribute('aria-hidden', 'true');\n\n\tconst placeholder = document.createElement('span');\n\tplaceholder.className = 'liforma-et__placeholder';\n\tplaceholder.setAttribute('aria-hidden', 'true');\n\troot.append(placeholder);\n\n\tconst markReady = (img: HTMLImageElement) => {\n\t\timg.classList.add('liforma-et__media--ready');\n\t};\n\n\tif (layers.backgroundImage) {\n\t\tconst bg = document.createElement('img');\n\t\tbg.className = 'liforma-et__background liforma-et__media';\n\t\tbg.alt = '';\n\t\tbg.decoding = 'async';\n\t\tbg.src = layers.backgroundImage;\n\t\tbg.addEventListener('load', () => markReady(bg), { once: true });\n\t\troot.append(bg);\n\t}\n\n\tconst avatar = document.createElement('img');\n\tavatar.className = 'liforma-et__avatar liforma-et__media';\n\tavatar.alt = '';\n\tavatar.decoding = 'async';\n\tavatar.src = layers.avatarImage;\n\tavatar.addEventListener('load', () => markReady(avatar), { once: true });\n\tavatar.addEventListener(\n\t\t'error',\n\t\t() => {\n\t\t\tif (layers.fallbackImage) {\n\t\t\t\tavatar.src = layers.fallbackImage;\n\t\t\t}\n\t\t},\n\t\t{ once: true }\n\t);\n\troot.append(avatar);\n\n\tif (layers.foregroundImage) {\n\t\tconst fg = document.createElement('img');\n\t\tfg.className = 'liforma-et__foreground liforma-et__media';\n\t\tfg.alt = '';\n\t\tfg.decoding = 'async';\n\t\tfg.src = layers.foregroundImage;\n\t\tfg.addEventListener('load', () => markReady(fg), { once: true });\n\t\troot.append(fg);\n\t}\n\n\tcontainer.replaceChildren(root);\n\n\tif (options?.parallax === false) return () => {};\n\treturn attachThumbnailParallax(root);\n}\n\nfunction resolveStartButton(options: ExperienceWidgetOptions): StartButtonOptions {\n\tif (options.startButton) {\n\t\treturn {\n\t\t\t...options.startButton,\n\t\t\tlabel: options.startButton.label ?? options.startButtonLabel ?? 'Tap to talk',\n\t\t\tplacement: options.startButton.placement ?? 'bottom-center',\n\t\t\tvariant: options.startButton.variant ?? 'primary'\n\t\t};\n\t}\n\treturn {\n\t\tlabel: options.startButtonLabel ?? 'Tap to talk',\n\t\tplacement: 'bottom-center',\n\t\tvariant: 'primary'\n\t};\n}\n\nfunction scheduleIdle(callback: () => void): () => void {\n\tlet cancelled = false;\n\tconst run = () => {\n\t\tif (cancelled) return;\n\t\tcallback();\n\t};\n\n\tconst onLoad = () => {\n\t\tif (cancelled) return;\n\t\tconst ric = (\n\t\t\twindow as Window & {\n\t\t\t\trequestIdleCallback?: (cb: () => void, opts?: { timeout: number }) => number;\n\t\t\t\tcancelIdleCallback?: (id: number) => void;\n\t\t\t}\n\t\t).requestIdleCallback;\n\t\tif (typeof ric === 'function') {\n\t\t\tconst id = ric(run, { timeout: 3000 });\n\t\t\tcancelIdle = () => {\n\t\t\t\t(\n\t\t\t\t\twindow as Window & { cancelIdleCallback?: (id: number) => void }\n\t\t\t\t).cancelIdleCallback?.(id);\n\t\t\t};\n\t\t\treturn;\n\t\t}\n\t\tconst t = window.setTimeout(run, 1);\n\t\tcancelIdle = () => window.clearTimeout(t);\n\t};\n\n\tlet cancelIdle: (() => void) | null = null;\n\tif (document.readyState === 'complete') {\n\t\tonLoad();\n\t} else {\n\t\twindow.addEventListener('load', onLoad, { once: true });\n\t}\n\n\treturn () => {\n\t\tcancelled = true;\n\t\twindow.removeEventListener('load', onLoad);\n\t\tcancelIdle?.();\n\t};\n}\n\n/**\n * Mount a light collapsed widget that expands an Experience overlay on click.\n * Default `prefetch=\"onExpand\"` is cold (second tap unlocks audio).\n * `idle` / `eager` warm a transparent iframe so one in-iframe click unlocks + expands.\n *\n * When gallery thumb layers are not provided, fetches\n * `GET /v1/experiences/{id}/preview` (no auth / allowlist).\n */\nexport function mountExperienceWidget(\n\thost: HTMLElement,\n\toptions: ExperienceWidgetOptions\n): ExperienceWidgetController {\n\tensureExperienceWidgetStyles(EXPERIENCE_WIDGET_CSS);\n\n\tconst experienceId = options.experienceId?.trim();\n\tif (!experienceId) {\n\t\tthrow new Error('ExperienceWidget requires experienceId.');\n\t}\n\n\tconst prefetchMode: ExperienceWidgetPrefetch = options.prefetch ?? 'onExpand';\n\tconst position = resolvePosition(options.position);\n\n\tconst root = document.createElement('div');\n\troot.className = 'liforma-ew';\n\troot.dataset.state = 'collapsed';\n\troot.dataset.prefetch = prefetchMode;\n\troot.dataset.position = position;\n\troot.style.setProperty('--liforma-ew-max-w', options.expandedMaxWidth ?? '100%');\n\troot.style.setProperty('--liforma-ew-max-h', options.expandedMaxHeight ?? '100%');\n\tif (position !== 'static') {\n\t\troot.style.setProperty('--liforma-ew-offset', resolveWidgetOffsetCss(options.offset));\n\t}\n\n\tconst hit = document.createElement('button');\n\thit.type = 'button';\n\thit.className = 'liforma-ew__hit';\n\thit.setAttribute('aria-label', options.alt);\n\thit.disabled = true;\n\n\tconst face = document.createElement('div');\n\tface.className = 'liforma-ew__face';\n\thit.append(face);\n\n\tconst faceLoading = document.createElement('div');\n\tfaceLoading.className = 'liforma-ew__face-status';\n\tfaceLoading.textContent = 'Loading…';\n\tface.append(faceLoading);\n\n\tconst warmStage = document.createElement('div');\n\twarmStage.className = 'liforma-ew__warm';\n\twarmStage.hidden = true;\n\twarmStage.setAttribute('aria-hidden', 'true');\n\n\tconst overlay = document.createElement('div');\n\toverlay.className = 'liforma-ew__overlay';\n\toverlay.hidden = true;\n\toverlay.setAttribute('role', 'dialog');\n\toverlay.setAttribute('aria-modal', 'true');\n\toverlay.setAttribute('aria-label', options.alt);\n\n\tconst backdrop = document.createElement('button');\n\tbackdrop.type = 'button';\n\tbackdrop.className = 'liforma-ew__backdrop';\n\tbackdrop.setAttribute('aria-label', 'Close experience');\n\n\tconst panel = document.createElement('div');\n\tpanel.className = 'liforma-ew__panel';\n\n\tconst toolbar = document.createElement('div');\n\ttoolbar.className = 'liforma-ew__toolbar';\n\ttoolbar.hidden = true;\n\n\tconst dragGrip = document.createElement('span');\n\tdragGrip.className = 'liforma-ew__drag-grip';\n\tdragGrip.setAttribute('aria-hidden', 'true');\n\n\tconst dockToggleBtn = document.createElement('button');\n\tdockToggleBtn.type = 'button';\n\tdockToggleBtn.className = 'liforma-ew__icon-btn liforma-ew__dock-toggle';\n\tdockToggleBtn.innerHTML = ICON_REDUCE;\n\tdockToggleBtn.setAttribute('aria-label', 'Reduce');\n\n\tconst settingsBtn = document.createElement('button');\n\tsettingsBtn.type = 'button';\n\tsettingsBtn.className = 'liforma-ew__icon-btn liforma-ew__settings';\n\tsettingsBtn.innerHTML = ICON_SETTINGS;\n\tsettingsBtn.setAttribute('aria-label', 'Audio settings');\n\tsettingsBtn.setAttribute('aria-pressed', 'false');\n\n\tconst closeBtn = document.createElement('button');\n\tcloseBtn.type = 'button';\n\tcloseBtn.className = 'liforma-ew__icon-btn liforma-ew__close';\n\tcloseBtn.innerHTML = ICON_CLOSE;\n\tcloseBtn.setAttribute('aria-label', 'Close');\n\n\ttoolbar.append(dragGrip, dockToggleBtn, settingsBtn, closeBtn);\n\n\tconst stage = document.createElement('div');\n\tstage.className = 'liforma-ew__stage';\n\n\tconst status = document.createElement('div');\n\tstatus.className = 'liforma-ew__status';\n\tstatus.hidden = true;\n\n\tpanel.append(stage, status);\n\toverlay.append(backdrop, panel);\n\t/* Toolbar must be a root sibling above the warm iframe stacking context. */\n\troot.append(hit, warmStage, overlay, toolbar);\n\thost.replaceChildren(root);\n\n\tlet experience: Experience | null = null;\n\tlet expanding = false;\n\tlet destroyed = false;\n\tlet previewReady = false;\n\tlet warmReady = false;\n\tlet warmInFlight = false;\n\t/** True after iframe attach (warm or cold); retain across collapse until hard teardown. */\n\tlet shellMounted = false;\n\t/** True after the player has unlocked audio / started conversation. */\n\tlet sessionStarted = false;\n\t/** Bumped on hard teardown so in-flight expand/warm work is discarded. */\n\tlet expandEpoch = 0;\n\tlet previousOverflow = '';\n\tlet detachFaceParallax: (() => void) | null = null;\n\tlet cancelWarmSchedule: (() => void) | null = null;\n\tlet collapseRevealTimer: ReturnType<typeof setTimeout> | null = null;\n\tlet layoutSettleTimer: ReturnType<typeof setTimeout> | null = null;\n\t/** Last docked origin; null means use default bottom-right for current size. */\n\tlet dockLeft: number | null = null;\n\tlet dockTop: number | null = null;\n\tlet dragPointerId: number | null = null;\n\tlet dragOffsetX = 0;\n\tlet dragOffsetY = 0;\n\t/** `toolbar` = parent pointer capture; `player` = iframe postMessage stream. */\n\tlet dockDragSource: 'toolbar' | 'player' | null = null;\n\tlet dockDragWindowBound = false;\n\tconst previewAbort = new AbortController();\n\tconst parallaxEnabled = options.parallax !== false;\n\tconst SESSION_CONCEAL_MS = 280;\n\t/**\n\t * Past FaceSessionView resize debounce (160ms) + morph restart so dock/maximize\n\t * never flashes a wrong-aspect canvas stretched into the new box.\n\t */\n\tconst LAYOUT_SETTLE_MS = 220;\n\tconst TOOLBAR_GAP = 10;\n\n\tconst shellState = (): ShellState => {\n\t\tconst state = root.dataset.state;\n\t\tif (state === 'expanded' || state === 'docked' || state === 'collapsed') return state;\n\t\treturn 'collapsed';\n\t};\n\n\tconst clearCollapseRevealTimer = () => {\n\t\tif (collapseRevealTimer !== null) {\n\t\t\tclearTimeout(collapseRevealTimer);\n\t\t\tcollapseRevealTimer = null;\n\t\t}\n\t};\n\n\tconst clearLayoutSettleTimer = () => {\n\t\tif (layoutSettleTimer !== null) {\n\t\t\tclearTimeout(layoutSettleTimer);\n\t\t\tlayoutSettleTimer = null;\n\t\t}\n\t\troot.classList.remove('liforma-ew--layout-settling');\n\t};\n\n\tconst clearExpandedLayoutInline = () => {\n\t\tfor (const el of [panel, warmStage, toolbar]) {\n\t\t\tel.style.removeProperty('left');\n\t\t\tel.style.removeProperty('top');\n\t\t\tel.style.removeProperty('width');\n\t\t\tel.style.removeProperty('height');\n\t\t\tel.style.removeProperty('right');\n\t\t\tel.style.removeProperty('bottom');\n\t\t}\n\t\ttoolbar.classList.remove('liforma-ew__toolbar--dragging');\n\t\troot.classList.remove('liforma-ew--dock-dragging');\n\t};\n\n\tconst applyDockedGeometry = (left: number, top: number, width: number, height: number) => {\n\t\tconst box = (el: HTMLElement) => {\n\t\t\tel.style.left = `${left}px`;\n\t\t\tel.style.top = `${top}px`;\n\t\t\tel.style.width = `${width}px`;\n\t\t\tel.style.height = `${height}px`;\n\t\t\tel.style.right = 'auto';\n\t\t\tel.style.bottom = 'auto';\n\t\t};\n\t\tbox(panel);\n\t\tbox(warmStage);\n\t\tconst toolbarWidth = toolbar.offsetWidth || 120;\n\t\ttoolbar.style.left = `${left + width - toolbarWidth - TOOLBAR_GAP}px`;\n\t\ttoolbar.style.top = `${top + TOOLBAR_GAP}px`;\n\t\ttoolbar.style.right = 'auto';\n\t\ttoolbar.style.bottom = 'auto';\n\t\ttoolbar.style.width = 'auto';\n\t\ttoolbar.style.height = 'auto';\n\t};\n\n\tconst applyDockDragPosition = (clientX: number, clientY: number) => {\n\t\tif (shellState() !== 'docked') return;\n\t\tconst size = computeDockSize();\n\t\tconst origin = clampDockOrigin(clientX - dragOffsetX, clientY - dragOffsetY, size);\n\t\tdockLeft = origin.left;\n\t\tdockTop = origin.top;\n\t\tapplyDockedGeometry(origin.left, origin.top, size.width, size.height);\n\t};\n\n\tconst applyDockDragDelta = (dx: number, dy: number) => {\n\t\tif (shellState() !== 'docked') return;\n\t\tif (!Number.isFinite(dx) || !Number.isFinite(dy)) return;\n\t\tif (dx === 0 && dy === 0) return;\n\t\tconst size = computeDockSize();\n\t\tconst currentLeft = dockLeft ?? panel.getBoundingClientRect().left;\n\t\tconst currentTop = dockTop ?? panel.getBoundingClientRect().top;\n\t\tconst origin = clampDockOrigin(currentLeft + dx, currentTop + dy, size);\n\t\tdockLeft = origin.left;\n\t\tdockTop = origin.top;\n\t\tapplyDockedGeometry(origin.left, origin.top, size.width, size.height);\n\t};\n\n\tconst onToolbarDockPointerMove = (event: PointerEvent) => {\n\t\tif (dockDragSource !== 'toolbar') return;\n\t\tif (dragPointerId === null || event.pointerId !== dragPointerId) return;\n\t\tevent.preventDefault();\n\t\tapplyDockDragPosition(event.clientX, event.clientY);\n\t};\n\n\tconst endDockDrag = (event?: PointerEvent) => {\n\t\tif (dragPointerId === null) return;\n\t\tif (event && event.pointerId !== dragPointerId) return;\n\t\tconst source = dockDragSource;\n\t\tdragPointerId = null;\n\t\tdockDragSource = null;\n\t\ttoolbar.classList.remove('liforma-ew__toolbar--dragging');\n\t\troot.classList.remove('liforma-ew--dock-dragging');\n\t\tif (source === 'toolbar' && dockDragWindowBound) {\n\t\t\tdockDragWindowBound = false;\n\t\t\twindow.removeEventListener('pointermove', onToolbarDockPointerMove);\n\t\t\twindow.removeEventListener('pointerup', endDockDrag);\n\t\t\twindow.removeEventListener('pointercancel', endDockDrag);\n\t\t}\n\t};\n\n\tconst beginToolbarDockDrag = (clientX: number, clientY: number, pointerId: number) => {\n\t\tif (destroyed) return;\n\t\tif (shellState() !== 'docked') return;\n\t\tif (!isDesktopViewport()) return;\n\t\tif (dragPointerId !== null) return;\n\t\tdragPointerId = pointerId;\n\t\tdockDragSource = 'toolbar';\n\t\ttoolbar.classList.add('liforma-ew__toolbar--dragging');\n\t\troot.classList.add('liforma-ew--dock-dragging');\n\t\tconst panelRect = panel.getBoundingClientRect();\n\t\tdragOffsetX = clientX - panelRect.left;\n\t\tdragOffsetY = clientY - panelRect.top;\n\t\tif (!dockDragWindowBound) {\n\t\t\tdockDragWindowBound = true;\n\t\t\twindow.addEventListener('pointermove', onToolbarDockPointerMove);\n\t\t\twindow.addEventListener('pointerup', endDockDrag);\n\t\t\twindow.addEventListener('pointercancel', endDockDrag);\n\t\t}\n\t};\n\n\tconst beginDockDragFromPlayer = (event: { readonly pointerId: number }) => {\n\t\tif (destroyed) return;\n\t\tif (shellState() !== 'docked') return;\n\t\tif (!isDesktopViewport()) return;\n\t\tif (dragPointerId !== null) return;\n\t\tdragPointerId = event.pointerId;\n\t\tdockDragSource = 'player';\n\t\ttoolbar.classList.add('liforma-ew__toolbar--dragging');\n\t\troot.classList.add('liforma-ew--dock-dragging');\n\t\t/*\n\t\t * Delta-based player drags: keep dockLeft/dockTop as the live origin.\n\t\t * Do not convert iframe client coordinates (async layout feedback jumps).\n\t\t */\n\t\tconst panelRect = panel.getBoundingClientRect();\n\t\tdockLeft = panelRect.left;\n\t\tdockTop = panelRect.top;\n\t};\n\n\tconst moveDockDragFromPlayer = (event: {\n\t\treadonly pointerId: number;\n\t\treadonly dx: number;\n\t\treadonly dy: number;\n\t}) => {\n\t\tif (dockDragSource !== 'player') return;\n\t\tif (dragPointerId === null || event.pointerId !== dragPointerId) return;\n\t\tapplyDockDragDelta(event.dx, event.dy);\n\t};\n\n\tconst endDockDragFromPlayer = (event: { readonly pointerId: number }) => {\n\t\tif (dockDragSource !== 'player') return;\n\t\tif (dragPointerId === null || event.pointerId !== dragPointerId) return;\n\t\tendDockDrag();\n\t};\n\n\tconst syncDockToggleChrome = () => {\n\t\tconst desktop = isDesktopViewport();\n\t\tconst state = shellState();\n\t\tif (!desktop || (state !== 'expanded' && state !== 'docked')) {\n\t\t\tdockToggleBtn.hidden = true;\n\t\t\treturn;\n\t\t}\n\t\tdockToggleBtn.hidden = false;\n\t\tif (state === 'docked') {\n\t\t\tdockToggleBtn.innerHTML = ICON_MAXIMIZE;\n\t\t\tdockToggleBtn.setAttribute('aria-label', 'Maximize');\n\t\t} else {\n\t\t\tdockToggleBtn.innerHTML = ICON_REDUCE;\n\t\t\tdockToggleBtn.setAttribute('aria-label', 'Reduce');\n\t\t}\n\t};\n\n\tconst layoutDockedShell = (preserveOrigin: boolean) => {\n\t\tconst size = computeDockSize();\n\t\tlet origin =\n\t\t\tpreserveOrigin && dockLeft !== null && dockTop !== null\n\t\t\t\t? clampDockOrigin(dockLeft, dockTop, size)\n\t\t\t\t: defaultDockOrigin(size);\n\t\torigin = clampDockOrigin(origin.left, origin.top, size);\n\t\tdockLeft = origin.left;\n\t\tdockTop = origin.top;\n\t\tapplyDockedGeometry(origin.left, origin.top, size.width, size.height);\n\t\tsyncDockToggleChrome();\n\t};\n\n\t/** Fade the live session in after layout/resize has settled (matches cold avatar reveal). */\n\tconst revealLiveSession = () => {\n\t\troot.classList.remove('liforma-ew--session-revealed');\n\t\trequestAnimationFrame(() => {\n\t\t\trequestAnimationFrame(() => {\n\t\t\t\tif (destroyed || !isOpenShell(root.dataset.state)) return;\n\t\t\t\troot.classList.add('liforma-ew--session-revealed');\n\t\t\t});\n\t\t});\n\t};\n\n\t/**\n\t * Hide the live surface instantly, apply a layout change, then reveal after the\n\t * player has had time to rebuild the morph canvas for the new aspect.\n\t */\n\tconst settleLayoutThenReveal = (applyLayout: () => void) => {\n\t\tclearLayoutSettleTimer();\n\t\troot.classList.remove('liforma-ew--session-revealed');\n\t\troot.classList.add('liforma-ew--layout-settling');\n\t\tapplyLayout();\n\t\tif (!sessionStarted) {\n\t\t\troot.classList.remove('liforma-ew--layout-settling');\n\t\t\treturn;\n\t\t}\n\t\tlayoutSettleTimer = setTimeout(() => {\n\t\t\tlayoutSettleTimer = null;\n\t\t\tif (destroyed || !isOpenShell(root.dataset.state)) {\n\t\t\t\troot.classList.remove('liforma-ew--layout-settling');\n\t\t\t\treturn;\n\t\t\t}\n\t\t\troot.classList.remove('liforma-ew--layout-settling');\n\t\t\trevealLiveSession();\n\t\t}, LAYOUT_SETTLE_MS);\n\t};\n\n\tconst concealLiveSession = (): Promise<void> => {\n\t\troot.classList.add('liforma-ew--session-concealing');\n\t\troot.classList.remove('liforma-ew--session-revealed');\n\t\treturn new Promise((resolve) => {\n\t\t\tclearCollapseRevealTimer();\n\t\t\tcollapseRevealTimer = setTimeout(() => {\n\t\t\t\tcollapseRevealTimer = null;\n\t\t\t\troot.classList.remove('liforma-ew--session-concealing');\n\t\t\t\tresolve();\n\t\t\t}, SESSION_CONCEAL_MS);\n\t\t});\n\t};\n\n\tconst initialLayers: FaceLayers = {\n\t\tgalleryThumb: options.galleryThumb,\n\t\tavatarImage: options.avatarImage,\n\t\tbackgroundImage: options.backgroundImage,\n\t\tforegroundImage: options.foregroundImage,\n\t\tfallbackImage: options.fallbackImage\n\t};\n\n\tconst setStatus = (text: string | null) => {\n\t\tif (!text) {\n\t\t\tstatus.hidden = true;\n\t\t\tstatus.textContent = '';\n\t\t\treturn;\n\t\t}\n\t\tstatus.hidden = false;\n\t\tstatus.textContent = text;\n\t};\n\n\tconst setFaceStatus = (text: string | null) => {\n\t\tif (!text) {\n\t\t\tfaceLoading.hidden = true;\n\t\t\tfaceLoading.textContent = '';\n\t\t\treturn;\n\t\t}\n\t\tfaceLoading.hidden = false;\n\t\tfaceLoading.textContent = text;\n\t\tif (!face.contains(faceLoading)) {\n\t\t\tface.append(faceLoading);\n\t\t}\n\t};\n\n\tconst markPreviewReady = () => {\n\t\tpreviewReady = true;\n\t\thit.disabled = false;\n\t\tsetFaceStatus(null);\n\t\tif (prefetchMode === 'onExpand') {\n\t\t\toptions.onPrefetchReady?.();\n\t\t}\n\t};\n\n\tconst armWarmHit = () => {\n\t\twarmReady = true;\n\t\troot.dataset.warm = 'true';\n\t\twarmStage.hidden = false;\n\t\tif (!sessionStarted) {\n\t\t\thit.tabIndex = -1;\n\t\t\thit.setAttribute('aria-hidden', 'true');\n\t\t}\n\t\toptions.onPrefetchReady?.();\n\t};\n\n\tconst paintFace = (layers: FaceLayers): boolean => {\n\t\tdetachFaceParallax?.();\n\t\tdetachFaceParallax = null;\n\t\tconst detach = renderFace(face, layers, { parallax: parallaxEnabled });\n\t\tif (!detach) return false;\n\t\tdetachFaceParallax = detach;\n\t\treturn true;\n\t};\n\n\tconst paintOrFetchPreview = () => {\n\t\tif (paintFace(initialLayers)) {\n\t\t\tmarkPreviewReady();\n\t\t\treturn;\n\t\t}\n\n\t\tsetFaceStatus('Loading…');\n\t\tvoid fetchExperiencePreview(experienceId, { signal: previewAbort.signal })\n\t\t\t.then((preview) => {\n\t\t\t\tif (destroyed) return;\n\t\t\t\tif (!paintFace({ galleryThumb: preview.galleryThumb })) {\n\t\t\t\t\tthrow new Error('Experience preview is missing gallery thumb images.');\n\t\t\t\t}\n\t\t\t\tmarkPreviewReady();\n\t\t\t})\n\t\t\t.catch((error) => {\n\t\t\t\tif (destroyed || previewAbort.signal.aborted) return;\n\t\t\t\tconst err = error instanceof Error ? error : new Error(String(error));\n\t\t\t\tsetFaceStatus('Preview unavailable');\n\t\t\t\thit.disabled = true;\n\t\t\t\toptions.onError?.(err);\n\t\t\t\tconsole.error('[ExperienceWidget]', err);\n\t\t\t});\n\t};\n\n\tconst sessionLaunchOptions = () => ({\n\t\texperienceId,\n\t\tui: 'widget' as const,\n\t\t...(options.sessionEndpoint ? { sessionEndpoint: options.sessionEndpoint } : {}),\n\t\t...(options.locale ? { locale: options.locale } : {}),\n\t\t...(options.language ? { language: options.language } : {}),\n\t\t...(options.mode ? { mode: options.mode } : {}),\n\t\t...(options.speechInputMode ? { speechInputMode: options.speechInputMode } : {}),\n\t\t...(options.speechOnly !== undefined ? { speechOnly: options.speechOnly } : {}),\n\t\t...(options.embedBaseUrl ? { embedBaseUrl: options.embedBaseUrl } : {})\n\t});\n\n\tconst sessionAttachPresentation = () => ({\n\t\t...(options.fit ? { fit: options.fit } : {}),\n\t\tstartButton: resolveStartButton(options),\n\t\t...(options.showFeedback !== undefined ? { showFeedback: options.showFeedback } : {})\n\t});\n\n\tconst showExpandedShell = (announce = true) => {\n\t\thit.hidden = true;\n\t\toverlay.hidden = false;\n\t\ttoolbar.hidden = false;\n\t\toverlay.setAttribute('aria-modal', 'true');\n\t\tclearExpandedLayoutInline();\n\t\troot.dataset.state = 'expanded';\n\t\tsetStatus(null);\n\t\tif (typeof document !== 'undefined') {\n\t\t\tpreviousOverflow = document.body.style.overflow;\n\t\t\tdocument.body.style.overflow = 'hidden';\n\t\t}\n\t\tsyncDockToggleChrome();\n\t\tif (announce) {\n\t\t\toptions.onExpanded?.();\n\t\t}\n\t};\n\n\tconst showDockedShell = () => {\n\t\thit.hidden = true;\n\t\toverlay.hidden = false;\n\t\ttoolbar.hidden = false;\n\t\toverlay.setAttribute('aria-modal', 'false');\n\t\troot.dataset.state = 'docked';\n\t\tsetStatus(null);\n\t\tif (typeof document !== 'undefined') {\n\t\t\tdocument.body.style.overflow = previousOverflow;\n\t\t}\n\t\tlayoutDockedShell(true);\n\t\tsyncDockToggleChrome();\n\t\texperience?.setWidgetChrome('docked');\n\t};\n\n\tconst bindSessionLifecycle = (session: Experience) => {\n\t\tsession.on('started', () => {\n\t\t\tif (destroyed) return;\n\t\t\tsessionStarted = true;\n\t\t\troot.dataset.live = 'true';\n\t\t\tsetStatus(null);\n\t\t\toptions.onStarted?.();\n\t\t\tif (isOpenShell(root.dataset.state)) {\n\t\t\t\trevealLiveSession();\n\t\t\t}\n\t\t});\n\t\tsession.on('feedback', (event) => {\n\t\t\tif (destroyed) return;\n\t\t\toptions.onFeedback?.(event);\n\t\t});\n\t\tsession.on('feedbackFailed', (event) => {\n\t\t\tif (destroyed) return;\n\t\t\toptions.onFeedbackFailed?.(event);\n\t\t});\n\t};\n\n\tconst restoreCollapsedChrome = () => {\n\t\tclearLayoutSettleTimer();\n\t\toverlay.hidden = true;\n\t\ttoolbar.hidden = true;\n\t\tclearExpandedLayoutInline();\n\t\troot.dataset.state = 'collapsed';\n\t\troot.classList.remove('liforma-ew--session-revealed');\n\t\troot.classList.remove('liforma-ew--session-concealing');\n\t\tsettingsBtn.setAttribute('aria-pressed', 'false');\n\t\thit.hidden = false;\n\t\thit.tabIndex = 0;\n\t\thit.removeAttribute('aria-hidden');\n\t\tif (previewReady) hit.disabled = false;\n\t\tsetStatus(null);\n\t\tif (typeof document !== 'undefined') {\n\t\t\tdocument.body.style.overflow = previousOverflow;\n\t\t}\n\t\tsyncDockToggleChrome();\n\t};\n\n\tconst hardTeardownSession = () => {\n\t\tclearCollapseRevealTimer();\n\t\tclearLayoutSettleTimer();\n\t\texpandEpoch += 1;\n\t\texpanding = false;\n\t\twarmReady = false;\n\t\twarmInFlight = false;\n\t\tshellMounted = false;\n\t\tsessionStarted = false;\n\t\tconst session = experience;\n\t\texperience = null;\n\t\tsession?.destroy();\n\t\tstage.replaceChildren();\n\t\twarmStage.replaceChildren();\n\t\twarmStage.hidden = true;\n\t\tdelete root.dataset.warm;\n\t\tdelete root.dataset.live;\n\t\troot.classList.remove('liforma-ew--session-revealed');\n\t};\n\n\tconst collapse = () => {\n\t\tif (destroyed) return;\n\t\tif (!isOpenShell(root.dataset.state)) return;\n\t\tendDockDrag();\n\t\texpanding = false;\n\n\t\t/*\n\t\t * Keep any attached session (warm or cold, started or waiting on Tap to talk).\n\t\t * Only CSS-collapse / pause — never remint on the next open.\n\t\t */\n\t\tif (experience && shellMounted) {\n\t\t\tconst session = experience;\n\t\t\tvoid (async () => {\n\t\t\t\tif (sessionStarted) {\n\t\t\t\t\tawait concealLiveSession();\n\t\t\t\t\tif (destroyed || experience !== session) return;\n\t\t\t\t}\n\t\t\t\tif (warmReady) {\n\t\t\t\t\tsession.setWidgetChrome('collapsed');\n\t\t\t\t}\n\t\t\t\trestoreCollapsedChrome();\n\t\t\t\tif (sessionStarted) {\n\t\t\t\t\troot.dataset.live = 'true';\n\t\t\t\t\t/* Face receives the next click; warm iframe stays hidden under the thumb. */\n\t\t\t\t\thit.tabIndex = 0;\n\t\t\t\t\thit.removeAttribute('aria-hidden');\n\t\t\t\t\thit.disabled = false;\n\t\t\t\t} else if (warmReady) {\n\t\t\t\t\tarmWarmHit();\n\t\t\t\t}\n\t\t\t\toptions.onCollapsed?.();\n\t\t\t\toptions.onClose?.();\n\t\t\t})();\n\t\t\treturn;\n\t\t}\n\n\t\thardTeardownSession();\n\t\trestoreCollapsedChrome();\n\t\toptions.onCollapsed?.();\n\t\toptions.onClose?.();\n\t};\n\n\tconst dock = () => {\n\t\tif (destroyed) return;\n\t\tif (!isDesktopViewport()) return;\n\t\tif (shellState() !== 'expanded') return;\n\t\tsettleLayoutThenReveal(() => {\n\t\t\tshowDockedShell();\n\t\t});\n\t};\n\n\tconst maximize = () => {\n\t\tif (destroyed) return;\n\t\tif (shellState() !== 'docked') return;\n\t\tendDockDrag();\n\t\tsettleLayoutThenReveal(() => {\n\t\t\tclearExpandedLayoutInline();\n\t\t\tshowExpandedShell(false);\n\t\t\texperience?.setWidgetChrome('expanded');\n\t\t});\n\t};\n\n\tconst resumeLiveSession = () => {\n\t\tif (!experience || !sessionStarted) return;\n\t\tclearCollapseRevealTimer();\n\t\troot.classList.remove('liforma-ew--session-revealed');\n\t\tshowExpandedShell();\n\t\texperience.setWidgetChrome('expanded');\n\t\texpanding = false;\n\t\trevealLiveSession();\n\t};\n\n\t/**\n\t * Re-open an attached session that has not unlocked yet.\n\t * Warm path should click the iframe hit (one-gesture start). This is a fallback when\n\t * the parent FAB receives the click instead — open the shell only; do not force\n\t * Tap-to-start chrome (that fights the warm “start talking immediately” contract).\n\t */\n\tconst resumeMountedSession = () => {\n\t\tif (!experience || !shellMounted || sessionStarted) return;\n\t\tclearCollapseRevealTimer();\n\t\troot.classList.remove('liforma-ew--session-revealed');\n\t\tshowExpandedShell();\n\t\texpanding = false;\n\t\tif (!warmReady) {\n\t\t\t/* Cold / still warming: need an in-player Start once ready. */\n\t\t\texperience.setWidgetChrome('expanded');\n\t\t}\n\t};\n\n\t/** Warm iframe hit unlocked audio and is starting — parent only CSS-expands. */\n\tconst onWarmExpand = (epoch: number) => {\n\t\tif (destroyed) return;\n\t\tif (epoch !== expandEpoch && !sessionStarted) return;\n\t\tclearCollapseRevealTimer();\n\t\troot.classList.remove('liforma-ew--session-revealed');\n\t\tshowExpandedShell();\n\t\texpanding = false;\n\t\t/*\n\t\t * Do not post setWidgetChrome('expanded'): the player already flipped chrome and\n\t\t * began startSession in the click turn. Echoing expanded causes a face fade +\n\t\t * Tap-to-start flash before speech.\n\t\t */\n\t\tif (sessionStarted) {\n\t\t\trevealLiveSession();\n\t\t}\n\t};\n\n\tconst beginWarm = () => {\n\t\tif (\n\t\t\tdestroyed ||\n\t\t\twarmInFlight ||\n\t\t\twarmReady ||\n\t\t\texperience ||\n\t\t\tisOpenShell(root.dataset.state)\n\t\t) {\n\t\t\treturn;\n\t\t}\n\t\twarmInFlight = true;\n\t\tconst epoch = expandEpoch;\n\t\twarmStage.hidden = false;\n\t\tvoid (async () => {\n\t\t\ttry {\n\t\t\t\tconst session = await Experience.startSession(sessionLaunchOptions());\n\t\t\t\tif (destroyed || epoch !== expandEpoch) {\n\t\t\t\t\tsession.destroy();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\texperience = session;\n\t\t\t\tbindSessionLifecycle(session);\n\t\t\t\tconst player = await session.attach({\n\t\t\t\t\tcontainer: warmStage,\n\t\t\t\t\t...sessionAttachPresentation(),\n\t\t\t\t\tonWidgetReady: () => {\n\t\t\t\t\t\tif (destroyed || epoch !== expandEpoch) return;\n\t\t\t\t\t\twarmInFlight = false;\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t * Mark mounted only when the iframe hit is armed. Setting this earlier\n\t\t\t\t\t\t * let the FAB steal clicks (no data-warm yet) and show Tap-to-start\n\t\t\t\t\t\t * instead of one-gesture warm start.\n\t\t\t\t\t\t */\n\t\t\t\t\t\tshellMounted = true;\n\t\t\t\t\t\tif (isOpenShell(root.dataset.state)) {\n\t\t\t\t\t\t\t/* Opened while warming — missed iframe gesture; need Tap to start. */\n\t\t\t\t\t\t\tsession.setWidgetChrome('expanded');\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tarmWarmHit();\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\tonWidgetExpand: () => {\n\t\t\t\t\t\tonWarmExpand(epoch);\n\t\t\t\t\t},\n\t\t\t\t\tonWidgetAudioSettings: (open) => {\n\t\t\t\t\t\tif (!destroyed) syncSettingsPressed(open);\n\t\t\t\t\t},\n\t\t\t\t\tonWidgetDragStart: (event) => {\n\t\t\t\t\t\tif (!destroyed) beginDockDragFromPlayer(event);\n\t\t\t\t\t},\n\t\t\t\t\tonWidgetDragMove: (event) => {\n\t\t\t\t\t\tif (!destroyed) moveDockDragFromPlayer(event);\n\t\t\t\t\t},\n\t\t\t\t\tonWidgetDragEnd: (event) => {\n\t\t\t\t\t\tif (!destroyed) endDockDragFromPlayer(event);\n\t\t\t\t\t},\n\t\t\t\t\tonPlayerStatusChange: (state) => {\n\t\t\t\t\t\tif (destroyed) return;\n\t\t\t\t\t\tif (state === 'error' && isOpenShell(root.dataset.state)) {\n\t\t\t\t\t\t\tsetStatus('Could not load the experience.');\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tif (destroyed || epoch !== expandEpoch) {\n\t\t\t\t\tsession.destroy();\n\t\t\t\t\tif (experience === session) experience = null;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tplayer.on('close', () => {\n\t\t\t\t\tif (destroyed) return;\n\t\t\t\t\tcollapse();\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tif (destroyed || epoch !== expandEpoch) return;\n\t\t\t\twarmInFlight = false;\n\t\t\t\twarmStage.hidden = true;\n\t\t\t\twarmStage.replaceChildren();\n\t\t\t\texperience = null;\n\t\t\t\tconst err = error instanceof Error ? error : new Error(String(error));\n\t\t\t\toptions.onError?.(err);\n\t\t\t\tconsole.error('[ExperienceWidget] warm prefetch failed', err);\n\t\t\t}\n\t\t})();\n\t};\n\n\tconst expandCold = () => {\n\t\tif (destroyed || expanding || !previewReady || isOpenShell(root.dataset.state)) return;\n\t\tif (experience && sessionStarted) {\n\t\t\tresumeLiveSession();\n\t\t\treturn;\n\t\t}\n\t\tif (experience && shellMounted) {\n\t\t\tresumeMountedSession();\n\t\t\treturn;\n\t\t}\n\t\tconst epoch = ++expandEpoch;\n\t\texpanding = true;\n\t\twarmReady = false;\n\t\twarmInFlight = false;\n\t\tshellMounted = false;\n\t\tsessionStarted = false;\n\t\tdelete root.dataset.warm;\n\t\tdelete root.dataset.live;\n\t\twarmStage.hidden = true;\n\t\twarmStage.replaceChildren();\n\t\tif (experience) {\n\t\t\texperience.destroy();\n\t\t\texperience = null;\n\t\t}\n\t\tvoid (async () => {\n\t\t\ttry {\n\t\t\t\tshowExpandedShell();\n\t\t\t\tconst session = await Experience.startSession(sessionLaunchOptions());\n\t\t\t\tif (destroyed || epoch !== expandEpoch) {\n\t\t\t\t\tsession.destroy();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\texperience = session;\n\t\t\t\tbindSessionLifecycle(session);\n\t\t\t\tconst player = await session.attach({\n\t\t\t\t\tcontainer: stage,\n\t\t\t\t\t...sessionAttachPresentation(),\n\t\t\t\t\tonWidgetAudioSettings: (open) => {\n\t\t\t\t\t\tif (!destroyed) syncSettingsPressed(open);\n\t\t\t\t\t},\n\t\t\t\t\tonWidgetDragStart: (event) => {\n\t\t\t\t\t\tif (!destroyed) beginDockDragFromPlayer(event);\n\t\t\t\t\t},\n\t\t\t\t\tonWidgetDragMove: (event) => {\n\t\t\t\t\t\tif (!destroyed) moveDockDragFromPlayer(event);\n\t\t\t\t\t},\n\t\t\t\t\tonWidgetDragEnd: (event) => {\n\t\t\t\t\t\tif (!destroyed) endDockDragFromPlayer(event);\n\t\t\t\t\t},\n\t\t\t\t\tonPlayerStatusChange: (state) => {\n\t\t\t\t\t\tif (destroyed) return;\n\t\t\t\t\t\tif (state === 'error') {\n\t\t\t\t\t\t\tsetStatus('Could not load the experience.');\n\t\t\t\t\t\t} else if (isOpenShell(root.dataset.state)) {\n\t\t\t\t\t\t\tsetStatus(null);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tif (destroyed || epoch !== expandEpoch) {\n\t\t\t\t\tsession.destroy();\n\t\t\t\t\tif (experience === session) experience = null;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tshellMounted = true;\n\t\t\t\t/* Cold expand: second gesture unlocks audio via in-player Start. */\n\t\t\t\tsession.setWidgetChrome('expanded');\n\t\t\t\tplayer.on('close', () => {\n\t\t\t\t\tif (destroyed) return;\n\t\t\t\t\tcollapse();\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tif (destroyed || epoch !== expandEpoch) return;\n\t\t\t\tconst err = error instanceof Error ? error : new Error(String(error));\n\t\t\t\tsetStatus(err.message);\n\t\t\t\toptions.onError?.(err);\n\t\t\t\tconsole.error('[ExperienceWidget]', err);\n\t\t\t} finally {\n\t\t\t\tif (epoch === expandEpoch) {\n\t\t\t\t\texpanding = false;\n\t\t\t\t}\n\t\t\t}\n\t\t})();\n\t};\n\n\tconst expand = () => {\n\t\tif (destroyed || isOpenShell(root.dataset.state)) return;\n\t\tif (experience && sessionStarted) {\n\t\t\tresumeLiveSession();\n\t\t\treturn;\n\t\t}\n\t\tif (experience && shellMounted) {\n\t\t\t/*\n\t\t\t * Warm path: clicks should hit the transparent iframe (data-warm). If the FAB\n\t\t\t * still receives the event, open the shell without reminting or forcing Start.\n\t\t\t */\n\t\t\tresumeMountedSession();\n\t\t\treturn;\n\t\t}\n\t\t/*\n\t\t * Warm prefetch still attaching: open the shell and wait for onWidgetReady.\n\t\t * Do not expandCold — that destroys the in-flight warm session.\n\t\t */\n\t\tif (warmInFlight || experience) {\n\t\t\tshowExpandedShell();\n\t\t\treturn;\n\t\t}\n\t\texpandCold();\n\t};\n\n\tconst onDockToggleClick = (event: MouseEvent) => {\n\t\tevent.stopPropagation();\n\t\tif (shellState() === 'expanded') {\n\t\t\tdock();\n\t\t\treturn;\n\t\t}\n\t\tif (shellState() === 'docked') {\n\t\t\tmaximize();\n\t\t}\n\t};\n\n\tconst syncSettingsPressed = (open: boolean) => {\n\t\tsettingsBtn.setAttribute('aria-pressed', open ? 'true' : 'false');\n\t};\n\n\tconst onSettingsClick = (event: MouseEvent) => {\n\t\tevent.stopPropagation();\n\t\tif (!isOpenShell(root.dataset.state)) return;\n\t\texperience?.toggleAudioSettings();\n\t};\n\n\tconst onToolbarPointerDown = (event: PointerEvent) => {\n\t\tif (shellState() !== 'docked') return;\n\t\tif (!isDesktopViewport()) return;\n\t\tconst target = event.target as HTMLElement | null;\n\t\tif (target?.closest('button')) return;\n\t\tevent.preventDefault();\n\t\tbeginToolbarDockDrag(event.clientX, event.clientY, event.pointerId);\n\t};\n\n\tconst onKeyDown = (event: KeyboardEvent) => {\n\t\tif (event.key === 'Escape' && isOpenShell(root.dataset.state)) {\n\t\t\tevent.preventDefault();\n\t\t\tcollapse();\n\t\t}\n\t};\n\n\tconst onWindowResize = () => {\n\t\tif (destroyed) return;\n\t\tif (shellState() === 'docked') {\n\t\t\tif (!isDesktopViewport()) {\n\t\t\t\tmaximize();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (sessionStarted) {\n\t\t\t\tsettleLayoutThenReveal(() => {\n\t\t\t\t\tlayoutDockedShell(true);\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tlayoutDockedShell(true);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tsyncDockToggleChrome();\n\t};\n\n\tconst desktopMq =\n\t\ttypeof window !== 'undefined' ? window.matchMedia(WIDGET_DESKTOP_MEDIA) : null;\n\tconst onDesktopMqChange = () => {\n\t\tif (destroyed) return;\n\t\tif (shellState() === 'docked' && !isDesktopViewport()) {\n\t\t\tmaximize();\n\t\t\treturn;\n\t\t}\n\t\tsyncDockToggleChrome();\n\t};\n\n\thit.addEventListener('click', expand);\n\tbackdrop.addEventListener('click', collapse);\n\tcloseBtn.addEventListener('click', (event) => {\n\t\tevent.stopPropagation();\n\t\tcollapse();\n\t});\n\tdockToggleBtn.addEventListener('click', onDockToggleClick);\n\tsettingsBtn.addEventListener('click', onSettingsClick);\n\ttoolbar.addEventListener('pointerdown', onToolbarPointerDown);\n\twindow.addEventListener('keydown', onKeyDown);\n\twindow.addEventListener('resize', onWindowResize);\n\tdesktopMq?.addEventListener('change', onDesktopMqChange);\n\n\tpaintOrFetchPreview();\n\tsyncDockToggleChrome();\n\n\tif (prefetchMode === 'eager') {\n\t\tqueueMicrotask(() => {\n\t\t\tif (!destroyed) beginWarm();\n\t\t});\n\t} else if (prefetchMode === 'idle') {\n\t\tcancelWarmSchedule = scheduleIdle(() => {\n\t\t\tif (!destroyed) beginWarm();\n\t\t});\n\t}\n\n\treturn {\n\t\troot,\n\t\texpand,\n\t\tcollapse,\n\t\tdock,\n\t\tmaximize,\n\t\tprefetch() {\n\t\t\tif (prefetchMode === 'onExpand') {\n\t\t\t\tbeginWarm();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tbeginWarm();\n\t\t},\n\t\tdestroy() {\n\t\t\tif (destroyed) return;\n\t\t\tdestroyed = true;\n\t\t\tendDockDrag();\n\t\t\tcancelWarmSchedule?.();\n\t\t\tcancelWarmSchedule = null;\n\t\t\tpreviewAbort.abort();\n\t\t\tdetachFaceParallax?.();\n\t\t\tdetachFaceParallax = null;\n\t\t\twindow.removeEventListener('keydown', onKeyDown);\n\t\t\twindow.removeEventListener('resize', onWindowResize);\n\t\t\tdesktopMq?.removeEventListener('change', onDesktopMqChange);\n\t\t\thardTeardownSession();\n\t\t\trestoreCollapsedChrome();\n\t\t\thost.replaceChildren();\n\t\t}\n\t};\n}\n","import { mountExperienceWidget } from '../widget/mountExperienceWidget';\nimport type {\n\tExperienceWidgetController,\n\tExperienceWidgetPosition\n} from '../widget/types';\nimport type { GalleryThumb } from '../thumbnail/types';\nimport {\n\tparseExperienceFit,\n\ttype ExperienceFit,\n\ttype ExperienceMode,\n\ttype SpeechInputMode\n} from './types/sessionInteraction';\n\nconst ELEMENT_NAME = 'liforma-experience-widget';\n\nfunction parseGalleryThumb(raw: string | null): GalleryThumb | undefined {\n\tif (!raw?.trim()) return undefined;\n\ttry {\n\t\tconst parsed = JSON.parse(raw) as unknown;\n\t\tif (parsed && typeof parsed === 'object' && typeof (parsed as GalleryThumb).avatarImage === 'string') {\n\t\t\treturn parsed as GalleryThumb;\n\t\t}\n\t} catch {\n\t\t/* ignore */\n\t}\n\treturn undefined;\n}\n\nfunction parseMode(value: string | null): ExperienceMode | undefined {\n\tconst trimmed = value?.trim();\n\treturn trimmed === 'conversation' || trimmed === 'presenter' ? trimmed : undefined;\n}\n\nfunction parseSpeechInputMode(value: string | null): SpeechInputMode | undefined {\n\tconst trimmed = value?.trim();\n\treturn trimmed === 'auto' || trimmed === 'manual' || trimmed === 'off' ? trimmed : undefined;\n}\n\nfunction parseFit(value: string | null): ExperienceFit | undefined {\n\treturn parseExperienceFit(value);\n}\n\nfunction parseLanguage(value: string | null): 'en' | 'es' | undefined {\n\tconst trimmed = value?.trim();\n\treturn trimmed === 'en' || trimmed === 'es' ? trimmed : undefined;\n}\n\nfunction parsePosition(value: string | null): ExperienceWidgetPosition {\n\tconst trimmed = value?.trim();\n\treturn trimmed === 'bottom-right' || trimmed === 'bottom-left' ? trimmed : 'static';\n}\n\nexport class LiformaExperienceWidgetElement extends HTMLElement {\n\tstatic get observedAttributes() {\n\t\treturn [\n\t\t\t'experience-id',\n\t\t\t'alt',\n\t\t\t'gallery-thumb',\n\t\t\t'avatar-image',\n\t\t\t'background-image',\n\t\t\t'foreground-image',\n\t\t\t'fallback-image',\n\t\t\t'prefetch',\n\t\t\t'parallax',\n\t\t\t'position',\n\t\t\t'offset',\n\t\t\t'session-endpoint',\n\t\t\t'locale',\n\t\t\t'secondary-locale',\n\t\t\t'learning-locale',\n\t\t\t'language',\n\t\t\t'mode',\n\t\t\t'speech-input-mode',\n\t\t\t'speech-only',\n\t\t\t'fit',\n\t\t\t'expanded-max-width',\n\t\t\t'expanded-max-height',\n\t\t\t'start-button-label'\n\t\t];\n\t}\n\n\t#controller: ExperienceWidgetController | null = null;\n\t#host: HTMLDivElement | null = null;\n\n\tconnectedCallback() {\n\t\tthis.#applyHostBox();\n\t\tthis.#mount();\n\t}\n\n\tdisconnectedCallback() {\n\t\tthis.#controller?.destroy();\n\t\tthis.#controller = null;\n\t}\n\n\tattributeChangedCallback() {\n\t\tif (!this.isConnected) return;\n\t\tthis.#applyHostBox();\n\t\tthis.#mount();\n\t}\n\n\t#applyHostBox() {\n\t\tconst position = parsePosition(this.getAttribute('position'));\n\t\tif (position !== 'static') {\n\t\t\t// Fixed FAB lives on `.liforma-ew`; collapse CE + host so they leave no layout hole.\n\t\t\tthis.style.display = 'contents';\n\t\t\tthis.style.removeProperty('width');\n\t\t\tthis.style.removeProperty('height');\n\t\t\tthis.style.removeProperty('min-height');\n\t\t\treturn;\n\t\t}\n\n\t\tthis.style.display = 'block';\n\t\tthis.style.width ||= '100%';\n\t\tthis.style.height ||= '100%';\n\t\tif (!this.style.minHeight) {\n\t\t\tconst min = getComputedStyle(this).minHeight;\n\t\t\tif (min === '0px' || min === 'auto') {\n\t\t\t\tthis.style.minHeight = '224px';\n\t\t\t}\n\t\t}\n\t}\n\n\t#mount() {\n\t\tthis.#controller?.destroy();\n\t\tthis.#controller = null;\n\n\t\tconst experienceId = this.getAttribute('experience-id')?.trim();\n\t\tconst alt = this.getAttribute('alt')?.trim() || 'Open experience';\n\t\tif (!experienceId) return;\n\n\t\tconst position = parsePosition(this.getAttribute('position'));\n\n\t\tif (!this.#host) {\n\t\t\tthis.#host = document.createElement('div');\n\t\t\tthis.replaceChildren(this.#host);\n\t\t}\n\t\tthis.#host.style.cssText =\n\t\t\tposition === 'static'\n\t\t\t\t? 'width:100%;height:100%;min-height:inherit;'\n\t\t\t\t: 'display:contents;';\n\n\t\tconst prefetchAttr = this.getAttribute('prefetch')?.trim();\n\t\tconst prefetch =\n\t\t\tprefetchAttr === 'idle' || prefetchAttr === 'eager' || prefetchAttr === 'onExpand'\n\t\t\t\t? prefetchAttr\n\t\t\t\t: 'onExpand';\n\n\t\tconst speechOnlyAttr = this.getAttribute('speech-only');\n\t\tconst offsetAttr = this.getAttribute('offset');\n\n\t\ttry {\n\t\t\tthis.#controller = mountExperienceWidget(this.#host, {\n\t\t\t\texperienceId,\n\t\t\t\talt,\n\t\t\t\tgalleryThumb: parseGalleryThumb(this.getAttribute('gallery-thumb')),\n\t\t\t\tavatarImage: this.getAttribute('avatar-image') ?? undefined,\n\t\t\t\tbackgroundImage: this.getAttribute('background-image') ?? undefined,\n\t\t\t\tforegroundImage: this.getAttribute('foreground-image') ?? undefined,\n\t\t\t\tfallbackImage: this.getAttribute('fallback-image') ?? undefined,\n\t\t\t\tprefetch,\n\t\t\t\tparallax: this.getAttribute('parallax') !== 'false',\n\t\t\t\tposition,\n\t\t\t\toffset: offsetAttr === null ? undefined : offsetAttr,\n\t\t\t\tsessionEndpoint: this.getAttribute('session-endpoint') ?? undefined,\n\t\t\t\tlocale: this.getAttribute('locale')?.trim() || undefined,\n\t\t\t\tsecondaryLocale: this.getAttribute('secondary-locale')?.trim() || undefined,\n\t\t\t\tlearningLocale: this.getAttribute('learning-locale')?.trim() || undefined,\n\t\t\t\tlanguage: parseLanguage(this.getAttribute('language')),\n\t\t\t\tmode: parseMode(this.getAttribute('mode')),\n\t\t\t\tspeechInputMode: parseSpeechInputMode(this.getAttribute('speech-input-mode')),\n\t\t\t\tspeechOnly:\n\t\t\t\t\tspeechOnlyAttr === null\n\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t: speechOnlyAttr === '' || speechOnlyAttr === 'true',\n\t\t\t\tfit: parseFit(this.getAttribute('fit')),\n\t\t\t\texpandedMaxWidth: this.getAttribute('expanded-max-width') ?? undefined,\n\t\t\t\texpandedMaxHeight: this.getAttribute('expanded-max-height') ?? undefined,\n\t\t\t\tstartButtonLabel: this.getAttribute('start-button-label') ?? undefined,\n\t\t\t\tonExpanded: () => {\n\t\t\t\t\tthis.dispatchEvent(new CustomEvent('expanded', { bubbles: true }));\n\t\t\t\t},\n\t\t\t\tonCollapsed: () => {\n\t\t\t\t\tthis.dispatchEvent(new CustomEvent('collapsed', { bubbles: true }));\n\t\t\t\t},\n\t\t\t\tonStarted: () => {\n\t\t\t\t\tthis.dispatchEvent(new CustomEvent('started', { bubbles: true }));\n\t\t\t\t},\n\t\t\t\tonClose: () => {\n\t\t\t\t\tthis.dispatchEvent(new CustomEvent('close', { bubbles: true }));\n\t\t\t\t},\n\t\t\t\tonError: (error) => {\n\t\t\t\t\tthis.dispatchEvent(\n\t\t\t\t\t\tnew CustomEvent('error', { detail: error, bubbles: true })\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tconsole.error('[liforma-experience-widget]', error);\n\t\t}\n\t}\n}\n\nif (typeof customElements !== 'undefined' && !customElements.get(ELEMENT_NAME)) {\n\tcustomElements.define(ELEMENT_NAME, LiformaExperienceWidgetElement);\n}\n","import './liforma-experience';\nimport './liforma-experience-thumbnail';\nimport './liforma-experience-widget';\n\nexport { Experience, type ExperienceSession } from './experience';\nexport { LiformaError, liformaErrorFromResponse } from './errors/LiformaError';\nexport type {\n\tAttachOptions,\n\tCloseEvent,\n\tEmbedState,\n\tLiformaPlayer,\n\tReadyEvent,\n\tStartSessionOptions\n} from './experience';\nexport type { SessionManifest, SessionManifestCharacter } from './types/sessionManifest';\nexport type {\n\tCloseButtonOptions,\n\tLocaleVoiceFallback,\n\tPlayerStatus,\n\tSession,\n\tSessionActivity,\n\tSessionCapabilities,\n\tSessionLaunchResponse,\n\tSessionMode\n} from './types/sessionLaunch';\nexport { warnLocaleVoiceFallbacks } from './types/sessionLaunch';\nexport type { ExperienceEventName, ExperienceEvents, ExperienceEventEnvelope, FeedbackResult, FeedbackFailedEvent } from './experienceEvents';\nexport type {\n\tCharacterSpeechEvent,\n\tCharacterSpeechSource,\n\tCharacterSpeechTerminalReason,\n\tConversationMessage,\n\tSpeakOptions,\n\tTextSpeakResult,\n\tSpeechResult as LegacySpeakResult,\n\tUserTranscriptUpdate,\n\tUtteranceResult,\n\tExperienceFit,\n\tExperienceMode,\n\tResponseMode,\n\tSpeechInputMode,\n\tStartButtonAppearance,\n\tStartButtonOptions,\n\tStartButtonPlacement,\n\tStartButtonShadow,\n\tStartButtonSize,\n\tStartButtonVariant,\n\tStartButtonIconPosition,\n\tPlayerTheme,\n\tPlayerThemeButton,\n\tPlayerThemeColors,\n\tPlayerThemeTypography,\n\tStartedEvent,\n} from './types/sessionInteraction';\nexport type {\n\tSpeechAudioFormat,\n\tSpeechController,\n\tSpeechCreateUtteranceOptions,\n\tSpeechInterruptOptions,\n\tSpeechPlayAudio,\n\tSpeechPlayEncodedAudio,\n\tSpeechPlayOptions,\n\tSpeechPlayPcmAudio,\n\tSpeechPlayUrlAudio,\n\tSpeechResult,\n\tSpeechSpeakOptions,\n\tSpeechUtterance\n} from './speech';\nexport {\n\tSPEECH_LIMITS,\n\tSpeechBufferTimeoutError,\n\tSpeechRuntimeError,\n\tSpeechUtteranceClosedError,\n\tUnsupportedSpeechOperationError\n} from './speech';\n\nimport { Experience } from './experience';\n\ndeclare global {\n\tinterface Window {\n\t\tLiforma?: {\n\t\t\tExperience: typeof Experience;\n\t\t\tsdkVersion: 'v2';\n\t\t\treadonly features: {\n\t\t\t\treadonly speakApi: boolean;\n\t\t\t\treadonly speechAudioApi: boolean;\n\t\t\t};\n\t\t};\n\t}\n}\n\nwindow.Liforma = {\n\tExperience,\n\tsdkVersion: 'v2',\n\tfeatures: {\n\t\tspeakApi: true,\n\t\tspeechAudioApi: true\n\t}\n};\n"],"mappings":"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"}
|