@qaiddev/quests-embed 1.4.0 → 1.5.0

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.
@@ -1 +1 @@
1
- {"version":3,"file":"qaid-quests.umd.cjs","sources":["../src/themes/index.ts","../src/styles.ts","../src/inputs.ts","../src/visibility.ts","../src/overflow.ts","../src/a11y.ts","../src/embed.ts","../src/index.ts"],"sourcesContent":["/**\n * Built-in theme presets. Each preset is a CSS string of token\n * overrides (Tier 3) and is injected into the shadow root after the\n * base stylesheet but before the host's `css` config string — so a\n * host always wins.\n */\n\nimport defaultCss from \"./default.css?inline\";\nimport minimalCss from \"./minimal.css?inline\";\nimport pillCss from \"./pill.css?inline\";\nimport denseCss from \"./dense.css?inline\";\n\nexport type PresetName = \"default\" | \"minimal\" | \"pill\" | \"dense\";\n\nconst PRESETS: Record<PresetName, string> = {\n default: defaultCss,\n minimal: minimalCss,\n pill: pillCss,\n dense: denseCss,\n};\n\nexport function getPresetCss(name: PresetName): string {\n return PRESETS[name] ?? \"\";\n}\n","/**\n * CSS variable + style helpers for the questionnaire embed.\n *\n * Color vars are intentionally NAMESPACED away from @qaiddev/thumbs-embed\n * so a host that uses both embeds can theme them independently:\n *\n * Quests own: --qaid-q-accent, --qaid-q-error, --qaid-q-focus\n * Shared: --qaid-modal-width, --qaid-backdrop-opacity,\n * --qaid-font-family, --qaid-font-size\n *\n * The legacy thumbs names (--qaid-positive / --qaid-negative /\n * --qaid-marker) are NOT applied here anymore.\n */\n\nimport shadowStyles from \"./styles-shadow.css?inline\";\nimport { getPresetCss as getPresetCssImpl, type PresetName } from \"./themes\";\n\nexport interface BuildCssVarsOptions {\n /** Primary CTA / progress fill / selected option ring */\n accentColor?: string;\n /** Validation error text */\n errorColor?: string;\n /** Focus ring */\n focusColor?: string;\n modalWidth?: number;\n backdropOpacity?: number;\n fontFamily?: string;\n fontSize?: number;\n}\n\nfunction getLuminance(r: number, g: number, b: number): number {\n const [rs, gs, bs] = [r, g, b].map((c) => {\n c = c / 255;\n return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);\n });\n return 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;\n}\n\nfunction parseColor(color: string): { r: number; g: number; b: number } | null {\n if (color.startsWith(\"#\")) {\n const hex = color.slice(1);\n const fullHex =\n hex.length === 3\n ? hex\n .split(\"\")\n .map((c) => c + c)\n .join(\"\")\n : hex;\n const num = parseInt(fullHex, 16);\n return { r: (num >> 16) & 255, g: (num >> 8) & 255, b: num & 255 };\n }\n const match = color.match(/rgb\\((\\d+),\\s*(\\d+),\\s*(\\d+)\\)/);\n if (match) {\n return { r: parseInt(match[1]), g: parseInt(match[2]), b: parseInt(match[3]) };\n }\n return null;\n}\n\nfunction getContrastTextColor(bgColor: string): string {\n const rgb = parseColor(bgColor);\n if (!rgb) return \"white\";\n return getLuminance(rgb.r, rgb.g, rgb.b) > 0.4 ? \"black\" : \"white\";\n}\n\nexport function buildCssVars(\n options: BuildCssVarsOptions = {},\n): Record<string, string> {\n // Only emit a var when the host explicitly passed a value. The\n // stylesheet's `var(--token, <fallback>)` already handles unset\n // values, and a theme document loaded via `themeUrl` writes its\n // own `:where(.qaid-q-root)` block — inlining defaults here would\n // beat that block on the cascade and silently override the theme.\n const out: Record<string, string> = {};\n if (options.accentColor !== undefined) {\n out[\"--qaid-q-accent\"] = options.accentColor;\n out[\"--qaid-q-accent-text\"] = getContrastTextColor(options.accentColor);\n }\n if (options.errorColor !== undefined) {\n out[\"--qaid-q-error\"] = options.errorColor;\n }\n if (options.focusColor !== undefined) {\n out[\"--qaid-q-focus\"] = options.focusColor;\n out[\"--qaid-q-focus-text\"] = getContrastTextColor(options.focusColor);\n }\n if (options.modalWidth !== undefined) {\n out[\"--qaid-modal-width\"] = `${options.modalWidth}px`;\n }\n if (options.backdropOpacity !== undefined) {\n out[\"--qaid-backdrop-opacity\"] = String(options.backdropOpacity);\n }\n if (options.fontFamily !== undefined) {\n out[\"--qaid-font-family\"] = options.fontFamily;\n }\n if (options.fontSize !== undefined) {\n out[\"--qaid-font-size\"] = `${options.fontSize}px`;\n }\n return out;\n}\n\nexport function applyCssVars(el: HTMLElement, vars: Record<string, string>): void {\n for (const [name, value] of Object.entries(vars)) {\n el.style.setProperty(name, value);\n }\n}\n\n/** CSS string injected into every shadow root */\nexport function getEmbedStyles(): string {\n return shadowStyles;\n}\n\n/** CSS for a built-in theme preset, or \"\" for unknown names. */\nexport function getPresetCss(name: PresetName): string {\n return getPresetCssImpl(name);\n}\n\nexport type { PresetName };\n","/**\n * Input renderers for each question type.\n *\n * Each renderer returns a small object with an element to mount, a\n * focus helper, a synchronous current-value reader, and a validator.\n *\n * Per-keystroke autosave is wired by the embed: the renderer just\n * calls back into onChange whenever its underlying value changes.\n * Renderers also call onSubmit when the user presses Enter (or\n * onAutoAdvance after a discrete selection that should auto-advance).\n */\n\nimport type {\n AnswerValue,\n CurrencyQuestion,\n DateQuestion,\n MultipleChoiceQuestion,\n Question,\n RangeQuestion,\n TextQuestion,\n} from \"./types\";\n\nexport interface QuestionInput {\n element: HTMLElement;\n focus(): void;\n getValue(): AnswerValue;\n isValid(): boolean;\n /**\n * Mark the control invalid: sets aria-invalid=\"true\" and links the\n * error text (identified by `errorId`) via aria-describedby so a\n * screen reader reads the error as the field's description. Do not\n * rely on colour alone (WCAG 3.3.1 / 1.3.1 / 4.1.2).\n */\n setInvalid(errorId: string): void;\n /** Clear aria-invalid and unlink the error text once the field is corrected. */\n clearInvalid(): void;\n}\n\n/**\n * Wire aria-invalid + aria-describedby error association onto a control.\n * setInvalid(id) marks it invalid and appends the error id to\n * aria-describedby (preserving any existing tokens); clearInvalid()\n * removes aria-invalid and only the token it added.\n */\nfunction createInvalidState(\n control: HTMLElement,\n): Pick<QuestionInput, \"setInvalid\" | \"clearInvalid\"> {\n let linkedId: string | null = null;\n return {\n setInvalid(errorId: string): void {\n control.setAttribute(\"aria-invalid\", \"true\");\n const ids = (control.getAttribute(\"aria-describedby\") ?? \"\")\n .split(/\\s+/)\n .filter(Boolean);\n if (!ids.includes(errorId)) ids.push(errorId);\n control.setAttribute(\"aria-describedby\", ids.join(\" \"));\n linkedId = errorId;\n },\n clearInvalid(): void {\n control.removeAttribute(\"aria-invalid\");\n if (linkedId) {\n const ids = (control.getAttribute(\"aria-describedby\") ?? \"\")\n .split(/\\s+/)\n .filter(Boolean)\n .filter((id) => id !== linkedId);\n if (ids.length) control.setAttribute(\"aria-describedby\", ids.join(\" \"));\n else control.removeAttribute(\"aria-describedby\");\n linkedId = null;\n }\n },\n };\n}\n\nexport interface QuestionInputOptions {\n question: Question;\n initialValue: AnswerValue;\n /** Called any time the value changes (every keystroke for text). */\n onChange: (value: AnswerValue) => void;\n /** Called when the user presses Enter to advance. */\n onSubmit: () => void;\n /**\n * Called after a discrete selection that should auto-advance\n * (e.g. picking a single-choice option when autoAdvance is on).\n */\n onAutoAdvance: () => void;\n /** Whether the embed is configured to auto-advance after discrete selections */\n autoAdvance: boolean;\n}\n\nexport function createInput(options: QuestionInputOptions): QuestionInput {\n switch (options.question.type) {\n case \"text\":\n return createTextInput(options.question, options);\n case \"currency\":\n return createCurrencyInput(options.question, options);\n case \"range\":\n return createRangeInput(options.question, options);\n case \"date\":\n return createDateInput(options.question, options);\n case \"multiple-choice\":\n return createMultipleChoiceInput(options.question, options);\n }\n}\n\n// ====================================================================\n// Text\n// ====================================================================\n\nfunction createTextInput(\n q: TextQuestion,\n opts: QuestionInputOptions,\n): QuestionInput {\n const initial = typeof opts.initialValue === \"string\" ? opts.initialValue : \"\";\n\n let el: HTMLInputElement | HTMLTextAreaElement;\n\n if (q.multiline) {\n const textarea = document.createElement(\"textarea\");\n textarea.className = \"qaid-q-textarea\";\n textarea.value = initial;\n if (q.placeholder) textarea.placeholder = q.placeholder;\n if (q.maxLength) textarea.maxLength = q.maxLength;\n el = textarea;\n } else {\n const input = document.createElement(\"input\");\n input.className = \"qaid-q-input\";\n input.type = q.inputType ?? \"text\";\n input.value = initial;\n if (q.placeholder) input.placeholder = q.placeholder;\n if (q.maxLength) input.maxLength = q.maxLength;\n input.setAttribute(\"autocomplete\", inputAutocomplete(q.inputType));\n el = input;\n }\n\n el.setAttribute(\"aria-label\", q.label);\n if (q.required) el.setAttribute(\"aria-required\", \"true\");\n\n el.addEventListener(\"input\", () => {\n opts.onChange(el.value);\n });\n\n el.addEventListener(\"keydown\", ((e: KeyboardEvent) => {\n if (e.key !== \"Enter\") return;\n if (q.multiline) {\n // In multiline, Enter inserts newline; Cmd/Ctrl+Enter submits.\n if (e.metaKey || e.ctrlKey) {\n e.preventDefault();\n opts.onSubmit();\n }\n return;\n }\n // Single-line: Enter submits, but allow IME composition to finish first.\n if (e.isComposing) return;\n e.preventDefault();\n opts.onSubmit();\n }) as EventListener);\n\n return {\n element: el,\n focus: () => {\n el.focus();\n // Place cursor at end so user can keep typing.\n const len = el.value.length;\n try {\n el.setSelectionRange(len, len);\n } catch {\n // input types like email may not support selectionRange\n }\n },\n getValue: () => el.value,\n isValid: () => {\n if (!q.required) return true;\n const v = el.value.trim();\n if (v.length === 0) return false;\n if (q.minLength && v.length < q.minLength) return false;\n return true;\n },\n ...createInvalidState(el),\n };\n}\n\nfunction inputAutocomplete(t?: TextQuestion[\"inputType\"]): string {\n switch (t) {\n case \"email\":\n return \"email\";\n case \"tel\":\n return \"tel\";\n case \"url\":\n return \"url\";\n default:\n return \"off\";\n }\n}\n\n// ====================================================================\n// Currency\n// ====================================================================\n\nfunction createCurrencyInput(\n q: CurrencyQuestion,\n opts: QuestionInputOptions,\n): QuestionInput {\n const wrap = document.createElement(\"div\");\n wrap.className = \"qaid-q-currency\";\n\n const prefix = document.createElement(\"span\");\n prefix.className = \"qaid-q-currency-prefix\";\n prefix.textContent = currencySymbol(q.currency ?? \"USD\", q.locale);\n wrap.appendChild(prefix);\n\n const input = document.createElement(\"input\");\n input.className = \"qaid-q-input\";\n // type=\"number\" gives native numeric keyboard + min/max validation hooks.\n input.type = \"number\";\n input.inputMode = \"decimal\";\n input.step = \"0.01\";\n if (typeof q.min === \"number\") input.min = String(q.min);\n if (typeof q.max === \"number\") input.max = String(q.max);\n if (q.placeholder) input.placeholder = q.placeholder;\n if (typeof opts.initialValue === \"number\") input.value = String(opts.initialValue);\n else if (typeof opts.initialValue === \"string\" && opts.initialValue !== \"\")\n input.value = opts.initialValue;\n input.setAttribute(\"aria-label\", q.label);\n if (q.required) input.setAttribute(\"aria-required\", \"true\");\n\n input.addEventListener(\"input\", () => {\n const raw = input.value;\n if (raw === \"\") {\n opts.onChange(null);\n } else {\n const num = Number(raw);\n opts.onChange(Number.isNaN(num) ? null : num);\n }\n });\n\n input.addEventListener(\"keydown\", (e) => {\n if (e.key === \"Enter\" && !(e as KeyboardEvent).isComposing) {\n e.preventDefault();\n opts.onSubmit();\n }\n });\n\n wrap.appendChild(input);\n\n return {\n element: wrap,\n focus: () => input.focus(),\n getValue: () => {\n if (input.value === \"\") return null;\n const num = Number(input.value);\n return Number.isNaN(num) ? null : num;\n },\n isValid: () => {\n const v = input.value === \"\" ? null : Number(input.value);\n if (q.required && (v === null || Number.isNaN(v as number))) return false;\n if (v !== null && !Number.isNaN(v as number)) {\n if (typeof q.min === \"number\" && (v as number) < q.min) return false;\n if (typeof q.max === \"number\" && (v as number) > q.max) return false;\n }\n return true;\n },\n ...createInvalidState(input),\n };\n}\n\nfunction currencySymbol(currency: string, locale?: string): string {\n try {\n const fmt = new Intl.NumberFormat(locale, {\n style: \"currency\",\n currency,\n currencyDisplay: \"narrowSymbol\",\n maximumFractionDigits: 0,\n });\n // Pull the symbol out of a formatted zero\n const parts = fmt.formatToParts(0);\n const sym = parts.find((p) => p.type === \"currency\");\n return sym?.value ?? currency;\n } catch {\n return currency;\n }\n}\n\n// ====================================================================\n// Range\n// ====================================================================\n\nfunction createRangeInput(\n q: RangeQuestion,\n opts: QuestionInputOptions,\n): QuestionInput {\n const wrap = document.createElement(\"div\");\n wrap.className = \"qaid-q-range-wrap\";\n\n const valueDisplay = document.createElement(\"div\");\n valueDisplay.className = \"qaid-q-range-value\";\n\n const valueText = document.createElement(\"span\");\n valueDisplay.appendChild(valueText);\n\n if (q.unit) {\n const unit = document.createElement(\"span\");\n unit.className = \"qaid-q-range-unit\";\n unit.textContent = q.unit;\n valueDisplay.appendChild(unit);\n }\n\n const initial =\n typeof opts.initialValue === \"number\"\n ? opts.initialValue\n : (q.defaultValue ?? q.min);\n\n const input = document.createElement(\"input\");\n input.className = \"qaid-q-range\";\n input.type = \"range\";\n input.min = String(q.min);\n input.max = String(q.max);\n input.step = String(q.step ?? 1);\n input.value = String(initial);\n input.setAttribute(\"aria-label\", q.label);\n input.setAttribute(\"aria-valuemin\", String(q.min));\n input.setAttribute(\"aria-valuemax\", String(q.max));\n\n const updateDisplay = () => {\n valueText.textContent = input.value;\n input.setAttribute(\"aria-valuenow\", input.value);\n };\n updateDisplay();\n\n input.addEventListener(\"input\", () => {\n updateDisplay();\n opts.onChange(Number(input.value));\n });\n\n input.addEventListener(\"keydown\", (e) => {\n if (e.key === \"Enter\") {\n e.preventDefault();\n opts.onSubmit();\n }\n });\n\n const bounds = document.createElement(\"div\");\n bounds.className = \"qaid-q-range-bounds\";\n const lower = document.createElement(\"span\");\n lower.textContent = `${q.min}${q.unit ?? \"\"}`;\n const upper = document.createElement(\"span\");\n upper.textContent = `${q.max}${q.unit ?? \"\"}`;\n bounds.appendChild(lower);\n bounds.appendChild(upper);\n\n wrap.appendChild(valueDisplay);\n wrap.appendChild(input);\n wrap.appendChild(bounds);\n\n // Push the initial value to onChange so first interaction is recorded\n // even if the user presses Enter without dragging.\n // (Defer to the next tick so the embed has wired up its handler.)\n queueMicrotask(() => opts.onChange(Number(input.value)));\n\n return {\n element: wrap,\n focus: () => input.focus(),\n getValue: () => Number(input.value),\n isValid: () => true,\n ...createInvalidState(input),\n };\n}\n\n// ====================================================================\n// Date\n// ====================================================================\n\nfunction createDateInput(q: DateQuestion, opts: QuestionInputOptions): QuestionInput {\n const input = document.createElement(\"input\");\n input.className = \"qaid-q-input\";\n input.type = \"date\";\n if (q.min) input.min = q.min;\n if (q.max) input.max = q.max;\n if (typeof opts.initialValue === \"string\") input.value = opts.initialValue;\n input.setAttribute(\"aria-label\", q.label);\n if (q.required) input.setAttribute(\"aria-required\", \"true\");\n\n input.addEventListener(\"input\", () => {\n opts.onChange(input.value || null);\n });\n\n input.addEventListener(\"keydown\", (e) => {\n if (e.key === \"Enter\" && !(e as KeyboardEvent).isComposing) {\n e.preventDefault();\n opts.onSubmit();\n }\n });\n\n return {\n element: input,\n focus: () => input.focus(),\n getValue: () => input.value || null,\n isValid: () => {\n if (!q.required) return true;\n return input.value !== \"\";\n },\n ...createInvalidState(input),\n };\n}\n\n// ====================================================================\n// Multiple choice\n// ====================================================================\n\nfunction createMultipleChoiceInput(\n q: MultipleChoiceQuestion,\n opts: QuestionInputOptions,\n): QuestionInput {\n const wrap = document.createElement(\"div\");\n const alignment = q.imageAlignment ?? \"horizontal\";\n const hasAnyImage = q.options.some((o) => !!o.image);\n const layoutClass = hasAnyImage\n ? alignment === \"vertical\"\n ? \" qaid-q-options--image-vertical\"\n : \" qaid-q-options--image-horizontal\"\n : \"\";\n wrap.className = `qaid-q-options${layoutClass}`;\n wrap.setAttribute(\"role\", q.multiple ? \"group\" : \"radiogroup\");\n wrap.setAttribute(\"aria-label\", q.label);\n if (q.required) wrap.setAttribute(\"aria-required\", \"true\");\n\n // Normalize initial selection to a Set for both modes\n const selected = new Set<string>();\n if (q.multiple && Array.isArray(opts.initialValue)) {\n for (const v of opts.initialValue) selected.add(v);\n } else if (!q.multiple && typeof opts.initialValue === \"string\") {\n selected.add(opts.initialValue);\n }\n\n const buttons: HTMLButtonElement[] = [];\n\n q.options.forEach((opt, idx) => {\n const btn = document.createElement(\"button\");\n btn.type = \"button\";\n btn.className = `qaid-q-option${q.multiple ? \" qaid-q-multi\" : \"\"}`;\n btn.dataset.value = opt.value;\n btn.setAttribute(\"role\", q.multiple ? \"checkbox\" : \"radio\");\n btn.setAttribute(\"aria-checked\", selected.has(opt.value) ? \"true\" : \"false\");\n if (selected.has(opt.value)) btn.classList.add(\"qaid-q-selected\");\n\n const marker = document.createElement(\"span\");\n marker.className = \"qaid-q-option-marker\";\n marker.setAttribute(\"aria-hidden\", \"true\");\n\n const body = document.createElement(\"span\");\n body.className = \"qaid-q-option-body\";\n\n if (opt.image) {\n const img = document.createElement(\"img\");\n img.className = \"qaid-q-option-image\";\n img.src = opt.image;\n img.alt = \"\";\n img.loading = \"lazy\";\n img.decoding = \"async\";\n body.appendChild(img);\n }\n\n const label = document.createElement(\"span\");\n label.className = \"qaid-q-option-label\";\n label.textContent = opt.label;\n body.appendChild(label);\n\n if (opt.description) {\n const desc = document.createElement(\"span\");\n desc.className = \"qaid-q-option-desc\";\n desc.textContent = opt.description;\n body.appendChild(desc);\n }\n\n btn.appendChild(marker);\n btn.appendChild(body);\n\n if (idx < 9) {\n const key = document.createElement(\"span\");\n key.className = \"qaid-q-option-key\";\n key.textContent = String(idx + 1);\n key.setAttribute(\"aria-hidden\", \"true\");\n btn.appendChild(key);\n }\n\n btn.addEventListener(\"click\", () => toggle(idx));\n\n btn.addEventListener(\"keydown\", (e) => {\n // Native button handles Space/Enter; we add arrow-key roving focus\n // and Enter-to-advance for single-choice.\n if (e.key === \"ArrowDown\" || e.key === \"ArrowRight\") {\n e.preventDefault();\n focusOption((idx + 1) % buttons.length);\n return;\n }\n if (e.key === \"ArrowUp\" || e.key === \"ArrowLeft\") {\n e.preventDefault();\n focusOption((idx - 1 + buttons.length) % buttons.length);\n return;\n }\n if (!q.multiple && e.key === \"Enter\") {\n // If something is already selected, advance instead of toggling\n if (selected.size > 0) {\n e.preventDefault();\n opts.onSubmit();\n }\n }\n });\n\n buttons.push(btn);\n wrap.appendChild(btn);\n });\n\n // Number-key shortcuts (1-9) at the wrap level\n wrap.addEventListener(\"keydown\", (e) => {\n const k = e.key;\n if (k.length === 1 && k >= \"1\" && k <= \"9\") {\n const idx = parseInt(k, 10) - 1;\n if (idx < buttons.length) {\n e.preventDefault();\n toggle(idx);\n focusOption(idx);\n }\n }\n });\n\n function focusOption(idx: number): void {\n buttons[idx]?.focus();\n }\n\n function toggle(idx: number): void {\n const value = q.options[idx].value;\n if (q.multiple) {\n if (selected.has(value)) selected.delete(value);\n else selected.add(value);\n } else {\n selected.clear();\n selected.add(value);\n }\n // Update DOM state\n buttons.forEach((b) => {\n const v = b.dataset.value!;\n const on = selected.has(v);\n b.classList.toggle(\"qaid-q-selected\", on);\n b.setAttribute(\"aria-checked\", on ? \"true\" : \"false\");\n });\n opts.onChange(currentValue());\n if (!q.multiple && opts.autoAdvance) {\n // Tiny delay so the user sees the selection animation before advancing.\n setTimeout(() => opts.onAutoAdvance(), 180);\n }\n }\n\n function currentValue(): AnswerValue {\n if (q.multiple) return Array.from(selected);\n const it = selected.values().next();\n return it.done ? null : it.value;\n }\n\n return {\n element: wrap,\n focus: () => {\n // Focus the first selected option, or the first option overall.\n const firstSelected = buttons.find((b) => b.classList.contains(\"qaid-q-selected\"));\n (firstSelected ?? buttons[0])?.focus();\n },\n getValue: () => currentValue(),\n isValid: () => {\n if (!q.required) return true;\n return selected.size > 0;\n },\n ...createInvalidState(wrap),\n };\n}\n","/**\n * Pure helpers for evaluating per-question visibility predicates.\n *\n * `getVisibleQuestions` filters a questionnaire's flat question array down\n * to those whose `visibleIf` predicate currently evaluates to true. A\n * question with no `visibleIf` is always visible.\n *\n * Predicates are evaluated against the answers map (the same shape the\n * embed maintains internally and posts on submit). They never throw — a\n * predicate referencing an unknown questionId resolves as if that\n * question is unanswered.\n */\n\nimport type {\n AnswerValue,\n Answers,\n Question,\n Questionnaire,\n VisibilityRule,\n} from \"./types\";\n\nexport function getVisibleQuestions(\n q: Questionnaire,\n answers: Answers,\n): Question[] {\n return q.questions.filter((qn) =>\n qn.visibleIf ? evaluateRule(qn.visibleIf, answers) : true,\n );\n}\n\nexport function evaluateRule(\n rule: VisibilityRule,\n answers: Answers,\n): boolean {\n if (\"allOf\" in rule) {\n // Empty allOf is vacuously true; the validator rejects empty arrays\n // at authoring time, so this branch only matters for inline /\n // unvalidated questionnaires.\n for (const r of rule.allOf) {\n if (!evaluateRule(r, answers)) return false;\n }\n return true;\n }\n if (\"anyOf\" in rule) {\n for (const r of rule.anyOf) {\n if (evaluateRule(r, answers)) return true;\n }\n return false;\n }\n\n const v = answers[rule.questionId];\n // Empty string and empty array count as \"not answered\" so a follow-up\n // doesn't flash visible the moment a text input is focused.\n const isAnswered =\n v !== null &&\n v !== undefined &&\n !(typeof v === \"string\" && v.trim() === \"\") &&\n !(Array.isArray(v) && v.length === 0);\n\n if (\"answered\" in rule) return rule.answered ? isAnswered : !isAnswered;\n\n // Value-comparison atoms against an unanswered question return false\n // — including notEquals, so a \"show this when X is not Y\" follow-up\n // doesn't reveal itself before X has been answered at all.\n if (!isAnswered) return false;\n\n if (\"equals\" in rule) return matchesAtomic(v, rule.equals);\n if (\"notEquals\" in rule) return !matchesAtomic(v, rule.notEquals);\n if (\"in\" in rule) return rule.in.some((x) => matchesAtomic(v, x));\n return false;\n}\n\nfunction matchesAtomic(\n answer: AnswerValue,\n target: string | number,\n): boolean {\n // Multi-select multiple-choice answers are arrays; treat equality as\n // \"target is one of the selected values.\"\n if (Array.isArray(answer)) return answer.some((a) => a === target);\n return answer === target;\n}\n","/**\n * Keeping a step readable inside whatever box the host gives us.\n *\n * The card body is the scroll container. Two things were missing: it never\n * adapted its own content to the height available (option tiles have width\n * rules only, so a short container just clipped them), and when content did\n * overflow there was no sign of it at all — the step simply looked cut off.\n *\n * This module owns both. It is DOM-only and free of embed state so it can be\n * driven directly in tests.\n */\n\n/** Where the scroll position currently sits. */\nexport type ScrollEdge = \"none\" | \"top\" | \"middle\" | \"bottom\";\n\n/**\n * Below this many pixels of available height, a step switches to its compact\n * layout: smaller image tiles, tighter gaps. Chosen so the common 2-3 option\n * step fits without scrolling in a ~360px tall container.\n */\nexport const COMPACT_HEIGHT = 360;\n\n/** Ignore sub-pixel rounding when deciding whether we are at an edge. */\nconst EDGE_SLOP = 2;\n\n/** Which edges of `el` have content beyond them. */\nexport function scrollEdgeOf(el: {\n scrollTop: number;\n scrollHeight: number;\n clientHeight: number;\n}): ScrollEdge {\n const overflow = el.scrollHeight - el.clientHeight;\n if (overflow <= EDGE_SLOP) return \"none\";\n if (el.scrollTop <= EDGE_SLOP) return \"top\";\n if (el.scrollTop >= overflow - EDGE_SLOP) return \"bottom\";\n return \"middle\";\n}\n\n/**\n * Reflect the scroll state onto the body as `data-qaid-scroll`, which the\n * stylesheet turns into a fade mask, and onto the cue element's hidden state.\n */\nexport function applyScrollState(body: HTMLElement, cue: HTMLElement | null): ScrollEdge {\n const edge = scrollEdgeOf(body);\n body.setAttribute(\"data-qaid-scroll\", edge);\n if (cue) {\n // The cue only ever means \"there is more below\".\n const more = edge === \"top\" || edge === \"middle\";\n cue.hidden = !more;\n }\n return edge;\n}\n\n/** Reflect the available height onto the card as `data-qaid-fit`. */\nexport function applyFitState(card: HTMLElement, availableHeight: number): \"compact\" | \"roomy\" {\n const fit = availableHeight > 0 && availableHeight < COMPACT_HEIGHT ? \"compact\" : \"roomy\";\n card.setAttribute(\"data-qaid-fit\", fit);\n return fit;\n}\n\nexport interface OverflowWatcher {\n /** Re-measure now — call after rendering a step. */\n refresh(): void;\n /** Detach every listener and observer. */\n destroy(): void;\n}\n\n/**\n * Watch `body` for scrolling and resizing, keeping the scroll and fit state\n * attributes current.\n *\n * A ResizeObserver rather than a CSS container query on height: a container\n * query needs `container-type: size`, which requires a definite height on both\n * axes, and this card is deliberately content-sized when the host does not\n * constrain it. Declaring it would collapse the body to nothing in exactly the\n * inline case the embed supports.\n */\nexport function watchOverflow(\n body: HTMLElement,\n card: HTMLElement,\n cue: HTMLElement | null\n): OverflowWatcher {\n const refresh = () => {\n applyScrollState(body, cue);\n applyFitState(card, body.clientHeight);\n };\n\n body.addEventListener(\"scroll\", refresh, { passive: true });\n\n let observer: ResizeObserver | null = null;\n if (typeof ResizeObserver === \"function\") {\n observer = new ResizeObserver(refresh);\n observer.observe(body);\n }\n\n refresh();\n\n return {\n refresh,\n destroy() {\n body.removeEventListener(\"scroll\", refresh);\n observer?.disconnect();\n },\n };\n}\n","/**\n * Shared accessibility primitives for the qaid embeds.\n *\n * These helpers are intentionally framework-free and self-contained (no CSS\n * dependency) so they can be dropped into any open shadow root. They cover the\n * cross-cutting WCAG 2.2 AA infrastructure called for in the embed\n * accessibility plan: live-region announcements, focus management (trap,\n * save/restore), dialog semantics, and background isolation.\n *\n * The API is deliberately identical to @qaiddev/thumbs-embed's a11y module so\n * both embeds consume one shared surface. In quests-embed specifically:\n * - {@link announce} is a GENERAL-PURPOSE announcer for step-change,\n * loading, and thank-you messages. It is separate from the pre-existing\n * per-step `.qaid-q-error` region (aria-live=\"polite\") in embed.ts — that\n * region stays the owner of field-validation errors. Callers should route\n * step/status text through announce()'s polite region and only use the\n * assertive region for load failures, so the two never clobber each other.\n */\n\n/** Host for the live regions: either a shadow root or a plain element. */\nexport type AnnounceRoot = ShadowRoot | HTMLElement;\n\n/** Options accepted by {@link announce}. */\nexport interface AnnounceOptions {\n /** Route the message through the assertive (role=\"alert\") region. */\n assertive?: boolean;\n}\n\n/** Options accepted by {@link applyDialog}. */\nexport interface ApplyDialogOptions {\n /** id of the element that labels the dialog (wired via aria-labelledby). */\n labelledbyId?: string;\n /** id of the element that describes the dialog (wired via aria-describedby). */\n describedbyId?: string;\n /** Fallback accessible name when no labelling element exists. */\n label?: string;\n}\n\n/** Handle returned by {@link createFocusTrap}. */\nexport interface FocusTrap {\n /** Remove the trap's key handler and clean up any tabindex it added. */\n release(): void;\n}\n\nconst LIVE_ATTR = \"data-qaid-a11y-live\";\n\nconst VISUALLY_HIDDEN_CSS =\n \"position:absolute;width:1px;height:1px;margin:-1px;padding:0;\" +\n \"border:0;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);\" +\n \"white-space:nowrap;\";\n\n/**\n * Selector for natively focusable / author-focusable elements. Elements that\n * opt out with tabindex=\"-1\" are excluded below in {@link getFocusable}.\n */\nconst FOCUSABLE_SELECTOR = [\n \"a[href]\",\n \"button\",\n \"input\",\n \"textarea\",\n \"select\",\n \"[tabindex]\",\n].join(\",\");\n\nfunction ownerDoc(root: AnnounceRoot): Document {\n return root.ownerDocument || document;\n}\n\nfunction getLiveRegion(root: AnnounceRoot, assertive: boolean): HTMLElement {\n const kind = assertive ? \"assertive\" : \"polite\";\n const existing = root.querySelector<HTMLElement>(\n `[${LIVE_ATTR}=\"${kind}\"]`,\n );\n if (existing) return existing;\n\n const doc = ownerDoc(root);\n const region = doc.createElement(\"div\");\n region.setAttribute(LIVE_ATTR, kind);\n region.setAttribute(\"role\", assertive ? \"alert\" : \"status\");\n region.setAttribute(\"aria-live\", assertive ? \"assertive\" : \"polite\");\n region.setAttribute(\"aria-atomic\", \"true\");\n region.style.cssText = VISUALLY_HIDDEN_CSS;\n root.appendChild(region);\n return region;\n}\n\n/**\n * Announce a message to assistive technology via a visually-hidden live region\n * mounted inside `root`. The two regions (polite role=\"status\" and assertive\n * role=\"alert\") are created lazily on first use and reused thereafter.\n *\n * The target region is cleared before the new text is written so that repeated\n * identical messages are re-announced rather than coalesced.\n */\nexport function announce(\n root: AnnounceRoot,\n message: string,\n opts: AnnounceOptions = {},\n): void {\n const region = getLiveRegion(root, !!opts.assertive);\n // Clear-then-set so an identical repeat is still perceived as a change.\n region.textContent = \"\";\n region.textContent = message;\n}\n\nfunction isDisabled(el: HTMLElement): boolean {\n if (el.hasAttribute(\"disabled\")) return true;\n const asInput = el as HTMLElement & { disabled?: boolean };\n return asInput.disabled === true;\n}\n\nfunction isHidden(el: HTMLElement): boolean {\n let node: HTMLElement | null = el;\n while (node) {\n if (node.hasAttribute(\"hidden\")) return true;\n const style = node.style;\n if (\n style &&\n (style.display === \"none\" ||\n style.visibility === \"hidden\" ||\n style.visibility === \"collapse\")\n ) {\n return true;\n }\n node = node.parentElement;\n }\n return false;\n}\n\n/**\n * Collect the focusable descendants of `container` in DOM order, excluding\n * disabled controls, elements opted out with tabindex=\"-1\", and elements that\n * are hidden (via the hidden attribute or an inline display/visibility rule on\n * themselves or an ancestor up to the document root).\n */\nexport function getFocusable(container: HTMLElement): HTMLElement[] {\n const nodes = Array.from(\n container.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR),\n );\n return nodes.filter((el) => {\n if (el.getAttribute(\"tabindex\") === \"-1\") return false;\n if (el instanceof HTMLInputElement && el.type === \"hidden\") return false;\n if (el instanceof HTMLAnchorElement && !el.getAttribute(\"href\")) {\n return false;\n }\n if (isDisabled(el)) return false;\n if (isHidden(el)) return false;\n return true;\n });\n}\n\n/** Resolve the deepest active element, drilling through open shadow roots. */\nfunction getDeepActiveElement(doc: Document): HTMLElement | null {\n let active: Element | null = doc.activeElement;\n while (active && active.shadowRoot && active.shadowRoot.activeElement) {\n active = active.shadowRoot.activeElement;\n }\n return active instanceof HTMLElement ? active : null;\n}\n\n/**\n * Trap keyboard focus inside `container`: Tab / Shift+Tab cycle between the\n * first and last focusable descendants and can never leave the container. On\n * creation focus moves to the first focusable element, or to the container\n * itself (made programmatically focusable) when it has no focusable children.\n *\n * Call {@link FocusTrap.release} to remove the handler; pair it with\n * {@link restoreFocus} to return focus to the invoking control.\n */\nexport function createFocusTrap(container: HTMLElement): FocusTrap {\n const doc = container.ownerDocument || document;\n let addedTabindex = false;\n\n const onKeydown = (event: KeyboardEvent): void => {\n if (event.key !== \"Tab\") return;\n\n const focusable = getFocusable(container);\n if (focusable.length === 0) {\n // Nothing to move to — keep focus pinned on the container.\n event.preventDefault();\n container.focus();\n return;\n }\n\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n const active = getDeepActiveElement(doc);\n const withinTrap = active ? focusable.indexOf(active) !== -1 : false;\n\n if (event.shiftKey) {\n if (!withinTrap || active === first) {\n event.preventDefault();\n last.focus();\n }\n } else {\n if (!withinTrap || active === last) {\n event.preventDefault();\n first.focus();\n }\n }\n };\n\n container.addEventListener(\"keydown\", onKeydown);\n\n const initial = getFocusable(container);\n if (initial.length > 0) {\n initial[0].focus();\n } else {\n if (!container.hasAttribute(\"tabindex\")) {\n container.setAttribute(\"tabindex\", \"-1\");\n addedTabindex = true;\n }\n container.focus();\n }\n\n return {\n release(): void {\n container.removeEventListener(\"keydown\", onKeydown);\n if (addedTabindex) {\n container.removeAttribute(\"tabindex\");\n addedTabindex = false;\n }\n },\n };\n}\n\n/**\n * Capture the currently focused element (drilling through shadow roots) so it\n * can be restored later with {@link restoreFocus}. Returns null when focus is\n * on nothing focusable.\n */\nexport function saveFocus(): HTMLElement | null {\n return getDeepActiveElement(document);\n}\n\n/**\n * Restore focus to a previously saved element. Safe to call with null or an\n * element that has since been detached (a missing/throwing focus is ignored).\n */\nexport function restoreFocus(el: HTMLElement | null): void {\n if (!el || typeof el.focus !== \"function\") return;\n try {\n el.focus();\n } catch {\n // The element may have been removed; nothing more to do.\n }\n}\n\n/**\n * Apply dialog semantics to `el`: role=\"dialog\", aria-modal=\"true\", and the\n * naming/description wiring described by `opts`. Prefer aria-labelledby /\n * aria-describedby pointing at visible title/subtitle elements; fall back to\n * aria-label when no visible label element exists.\n */\nexport function applyDialog(\n el: HTMLElement,\n opts: ApplyDialogOptions = {},\n): void {\n el.setAttribute(\"role\", \"dialog\");\n el.setAttribute(\"aria-modal\", \"true\");\n if (opts.labelledbyId) {\n el.setAttribute(\"aria-labelledby\", opts.labelledbyId);\n }\n if (opts.describedbyId) {\n el.setAttribute(\"aria-describedby\", opts.describedbyId);\n }\n if (opts.label) {\n el.setAttribute(\"aria-label\", opts.label);\n }\n}\n\n/** Walk up to the body-level ancestor of `el`, crossing shadow boundaries. */\nfunction topLevelAncestor(\n el: HTMLElement,\n body: HTMLElement,\n): HTMLElement | null {\n let node: Node | null = el;\n while (node && node !== body) {\n const parent: Node | null = node.parentNode;\n if (parent === body) return node as HTMLElement;\n if (parent && (parent as ShadowRoot).host) {\n node = (parent as ShadowRoot).host;\n continue;\n }\n if (!parent) return null;\n node = parent;\n }\n return null;\n}\n\n/**\n * Isolate the background from assistive technology while a dialog is open by\n * marking every top-level sibling of the dialog inert (with an aria-hidden\n * fallback). `except` is the open dialog (or any element inside it); the\n * top-level element that contains it is left interactive.\n *\n * Returns a restore function that reverts every attribute this call changed,\n * leaving elements that were already inert/hidden untouched.\n */\nexport function setBackgroundInert(except: HTMLElement): () => void {\n const doc = except.ownerDocument || document;\n const body = doc.body;\n if (!body) return () => {};\n\n const keep = topLevelAncestor(except, body);\n const changed: Array<{\n el: HTMLElement;\n prevInert: boolean;\n prevAriaHidden: string | null;\n }> = [];\n\n Array.from(body.children).forEach((child) => {\n if (!(child instanceof HTMLElement)) return;\n if (keep && child === keep) return;\n\n const prevInert = child.inert === true;\n const prevAriaHidden = child.getAttribute(\"aria-hidden\");\n // Only record (and later revert) elements we actually change.\n if (prevInert && prevAriaHidden === \"true\") return;\n\n child.inert = true;\n child.setAttribute(\"aria-hidden\", \"true\");\n changed.push({ el: child, prevInert, prevAriaHidden });\n });\n\n return function restore(): void {\n while (changed.length) {\n const entry = changed.pop();\n if (!entry) continue;\n entry.el.inert = entry.prevInert;\n if (entry.prevAriaHidden === null) {\n entry.el.removeAttribute(\"aria-hidden\");\n } else {\n entry.el.setAttribute(\"aria-hidden\", entry.prevAriaHidden);\n }\n }\n };\n}\n","import type {\n Answers,\n AnswerValue,\n CreateResponseResult,\n Question,\n Questionnaire,\n QuestsConfig,\n ResolvedQuestsConfig,\n ResolvedQuestTheme,\n} from \"./types\";\nimport { applyCssVars, buildCssVars, getEmbedStyles, getPresetCss } from \"./styles\";\nimport { createInput, type QuestionInput } from \"./inputs\";\nimport { getVisibleQuestions } from \"./visibility\";\nimport { watchOverflow, type OverflowWatcher } from \"./overflow\";\nimport {\n announce,\n applyDialog,\n createFocusTrap,\n restoreFocus,\n saveFocus,\n setBackgroundInert,\n type FocusTrap,\n} from \"./a11y\";\n\nconst VISITOR_ID_KEY = \"qaid_visitor_id\";\n\ntype EmbedState = \"LOADING\" | \"READY\" | \"DONE\" | \"ERROR\";\n\nfunction getOrCreateVisitorId(): string {\n try {\n let id = localStorage.getItem(VISITOR_ID_KEY);\n if (!id) {\n id = crypto.randomUUID();\n localStorage.setItem(VISITOR_ID_KEY, id);\n }\n return id;\n } catch {\n return crypto.randomUUID();\n }\n}\n\nconst CLOSE_ICON = `<svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" aria-hidden=\"true\"><path d=\"M2 2l10 10M12 2L2 12\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" fill=\"none\"/></svg>`;\nconst CHECK_ICON = `<svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"M5 12l4 4L19 7\" stroke=\"currentColor\" stroke-width=\"3\" stroke-linecap=\"round\" stroke-linejoin=\"round\" fill=\"none\"/></svg>`;\n\nexport class QaidQuests {\n private config: ResolvedQuestsConfig;\n private questionnaire: Questionnaire | null = null;\n private inlineQuestionnaire: Questionnaire | undefined;\n private configUrl: string | undefined;\n\n private state: EmbedState = \"LOADING\";\n private stepIndex = 0;\n // Cached subset of questionnaire.questions that are currently visible\n // given the answers so far. Recomputed on every answer change so\n // stepIndex always indexes into THIS list, not the raw question array.\n private visibleQuestions: Question[] = [];\n // Latched goToStep request when called before init finishes. Applied\n // once the questionnaire + visible list are ready.\n private pendingGoToStep: string | null = null;\n // True after the first step has rendered. Used so the focus-on-step\n // logic in renderStep() can distinguish initial mount (driven by the\n // `autoFocus` config) from subsequent step changes (always focused so\n // keyboard + screen-reader users land on the active input after Next\n // / Back).\n private hasRenderedStep = false;\n private answers: Answers = {};\n private responseId: string | number | null = null;\n private visitorId: string;\n\n // Per-question pending autosave (text/currency are debounced)\n private pendingSaveTimer: ReturnType<typeof setTimeout> | null = null;\n private pendingSaveQuestionId: string | null = null;\n private pendingSaveValue: AnswerValue = null;\n private inflightSaves = 0;\n\n // Shadow DOM\n private shadowHost: HTMLDivElement | null = null;\n private shadowRoot: ShadowRoot | null = null;\n private isUserContainer = false;\n\n // Mounted UI elements\n private rootEl: HTMLDivElement | null = null;\n private cardEl: HTMLDivElement | null = null;\n private titleEl: HTMLHeadingElement | null = null;\n private stepCounterEl: HTMLDivElement | null = null;\n private progressEl: HTMLDivElement | null = null;\n private progressFillEl: HTMLDivElement | null = null;\n private bodyEl: HTMLDivElement | null = null;\n private scrollCueEl: HTMLDivElement | null = null;\n private overflow: OverflowWatcher | null = null;\n private footerEl: HTMLDivElement | null = null;\n private savingEl: HTMLDivElement | null = null;\n private backdropEl: HTMLDivElement | null = null;\n private currentInput: QuestionInput | null = null;\n private boundKeyDown: (e: KeyboardEvent) => void;\n\n // Modal-mode a11y plumbing (never set in inline/container mode, which\n // keeps normal page tab flow and never traps or restores focus).\n private savedOpener: HTMLElement | null = null;\n private focusTrap: FocusTrap | null = null;\n private inertRestore: (() => void) | null = null;\n\n private cssVars: Record<string, string> = {};\n\n // Track which theme-related fields the host explicitly set so the\n // theme document can fill the rest. A `themeUrl` / `themeDocument`\n // theme is layered between the embed's defaults and the host's\n // explicit overrides, so the host always wins.\n private hostThemeOverrides: {\n preset: boolean;\n mode: boolean;\n unstyled: boolean;\n css: boolean;\n };\n // Raw, host-provided values for the tokens we inline as inline-style\n // CSS vars on rootEl. Anything `undefined` here is left out of the\n // inline write so theme tokens (which arrive as `:where(...) { ... }`\n // rules) can win the cascade.\n private hostInlineVars: {\n accentColor?: string;\n errorColor?: string;\n focusColor?: string;\n modalWidth?: number;\n backdropOpacity?: number;\n fontFamily?: string;\n fontSize?: number;\n };\n private themeUrl: string | undefined;\n private themeDocument: ResolvedQuestTheme | undefined;\n private hostCss: string;\n\n // Host integration hooks (see QuestsConfig). Kept off the resolved\n // value-config since they're behaviour, not render tokens.\n private metadata: Record<string, unknown> | undefined;\n private onCompleteCb: ((answers: Answers) => void) | undefined;\n private onCloseCb: (() => void) | undefined;\n // Guards onClose to fire exactly once even if destroy() runs twice.\n private closed = false;\n\n constructor(config: QuestsConfig) {\n this.config = {\n endpoint: config.endpoint,\n apiKey: config.apiKey ?? \"\",\n container: config.container ?? \"\",\n zIndex: config.zIndex ?? 50,\n // Prefer the new quest-only keys; fall back to the deprecated\n // thumbs-style keys (positive/negative/marker) so existing\n // integrations keep rendering until they migrate.\n colors: {\n accent: config.colors?.accent ?? config.colors?.positive ?? \"#10b981\",\n error: config.colors?.error ?? config.colors?.negative ?? \"#ef4444\",\n focus: config.colors?.focus ?? config.colors?.marker ?? \"#6366f1\",\n },\n modalWidth: config.modalWidth ?? 480,\n backdropOpacity: config.backdropOpacity ?? 0.4,\n fontFamily: config.fontFamily ?? \"system-ui, -apple-system, sans-serif\",\n fontSize: config.fontSize ?? 16,\n css: config.css ?? \"\",\n autoAdvance: config.autoAdvance ?? false,\n saveDebounceMs: config.saveDebounceMs ?? 500,\n autoFocus: config.autoFocus ?? true,\n animate: config.animate ?? true,\n // Don't default here — we want to fall through to the\n // questionnaire's `progressPosition` when the host didn't\n // explicitly set one. effectiveProgressPosition() handles the\n // final fallback to \"top\".\n progressPosition: config.progressPosition,\n theme: config.theme ?? \"auto\",\n unstyled: config.unstyled ?? false,\n preset: config.preset ?? \"default\",\n };\n\n this.hostThemeOverrides = {\n preset: config.preset !== undefined,\n mode: config.theme !== undefined,\n unstyled: config.unstyled !== undefined,\n css: config.css !== undefined,\n };\n this.hostInlineVars = {\n accentColor: config.colors?.accent ?? config.colors?.positive,\n errorColor: config.colors?.error ?? config.colors?.negative,\n focusColor: config.colors?.focus ?? config.colors?.marker,\n modalWidth: config.modalWidth,\n backdropOpacity: config.backdropOpacity,\n fontFamily: config.fontFamily,\n fontSize: config.fontSize,\n };\n this.themeUrl = config.themeUrl;\n this.themeDocument = config.themeDocument;\n this.hostCss = config.css ?? \"\";\n\n this.metadata = config.metadata;\n this.onCompleteCb = config.onComplete;\n this.onCloseCb = config.onClose;\n\n this.inlineQuestionnaire = config.questionnaire;\n this.configUrl = config.configUrl;\n this.visitorId = getOrCreateVisitorId();\n this.boundKeyDown = this.handleKeyDown.bind(this);\n\n this.init();\n }\n\n private async init(): Promise<void> {\n // Inline only the vars the host explicitly passed, so theme\n // documents can supply the rest via the cascade.\n this.cssVars = buildCssVars(this.hostInlineVars);\n\n this.mountShell();\n this.renderLoading();\n\n try {\n // Fetch the questionnaire and the (optional) theme document in\n // parallel so the theme never blocks the form. A failed theme\n // fetch is non-fatal — we just render with defaults.\n const [q, themeDoc] = await Promise.all([\n this.loadQuestionnaire(),\n this.loadTheme(),\n ]);\n if (!q || !q.questions || q.questions.length === 0) {\n throw new Error(\"Questionnaire is empty\");\n }\n this.questionnaire = q;\n this.applyResolvedTheme(themeDoc);\n this.recomputeVisible();\n // Apply any goToStep request that came in before init finished.\n if (this.pendingGoToStep) {\n const idx = this.visibleQuestions.findIndex(\n (qn) => qn.id === this.pendingGoToStep,\n );\n if (idx !== -1) this.stepIndex = idx;\n this.pendingGoToStep = null;\n }\n // Fire-and-forget create. The form is usable immediately;\n // saves will queue until the response id arrives.\n this.createResponse();\n this.state = \"READY\";\n this.renderHeader();\n this.renderStep();\n // Arm the modal focus trap + background isolation once the first\n // step exists. Done synchronously after renderStep so the trap's\n // initial focus lands first and renderStep's own rAF(input.focus)\n // then wins, leaving the intended field focused.\n this.setupModalA11y();\n } catch (err) {\n this.state = \"ERROR\";\n this.renderError(err);\n }\n }\n\n // ------------------------------------------------------------------\n // Theme document loading + merging\n // ------------------------------------------------------------------\n\n private async loadTheme(): Promise<ResolvedQuestTheme | null> {\n if (this.themeDocument) return this.themeDocument;\n if (!this.themeUrl) return null;\n try {\n const res = await fetch(this.themeUrl, {\n headers: { Accept: \"application/json\" },\n });\n if (!res.ok) {\n console.warn(\n `[quests-embed] theme fetch failed (${res.status}) — rendering with defaults`,\n );\n return null;\n }\n return (await res.json()) as ResolvedQuestTheme;\n } catch (err) {\n console.warn(\"[quests-embed] theme fetch error — rendering with defaults\", err);\n return null;\n }\n }\n\n /**\n * Merge a fetched theme document into the live config, then inject\n * the resulting CSS layers + class toggles into the shadow root.\n *\n * Precedence (highest first):\n * 1. Host's explicit `QuestsConfig` fields (preset/theme/unstyled/css)\n * 2. Theme document fields (preset/mode/unstyled/tokens/css)\n * 3. Embed defaults\n *\n * CSS layer order (later wins for same-specificity rules):\n * base stylesheet → theme.tokens block → preset CSS → theme.css → host css\n */\n private applyResolvedTheme(themeDoc: ResolvedQuestTheme | null): void {\n if (!this.shadowRoot || !this.rootEl) return;\n\n const finalPreset = this.hostThemeOverrides.preset\n ? this.config.preset\n : (themeDoc?.preset ?? this.config.preset);\n const finalMode = this.hostThemeOverrides.mode\n ? this.config.theme\n : (themeDoc?.mode ?? this.config.theme);\n const finalUnstyled = this.hostThemeOverrides.unstyled\n ? this.config.unstyled\n : (themeDoc?.unstyled ?? this.config.unstyled);\n\n // Toggle the theme/unstyled classes on rootEl. We don't touch\n // qaid-q-inline / qaid-q-no-motion which were set in mountShell.\n this.rootEl.classList.toggle(\n \"qaid-q-theme-light\",\n finalMode === \"light\",\n );\n this.rootEl.classList.toggle(\n \"qaid-q-theme-dark\",\n finalMode === \"dark\",\n );\n this.rootEl.classList.toggle(\"qaid-q-unstyled\", !!finalUnstyled);\n\n // Build the merged stylesheet. Each layer is appended only when\n // there's something to add so the shadow root stays clean.\n const layers: string[] = [];\n\n if (themeDoc?.tokens && Object.keys(themeDoc.tokens).length > 0) {\n const body = Object.entries(themeDoc.tokens)\n .filter(([, v]) => typeof v === \"string\" && v.trim() !== \"\")\n .map(([k, v]) => ` ${k}: ${v};`)\n .join(\"\\n\");\n if (body) layers.push(`:where(.qaid-q-root) {\\n${body}\\n}`);\n }\n\n if (finalPreset && finalPreset !== \"default\") {\n const presetCss = getPresetCss(finalPreset);\n if (presetCss) layers.push(presetCss);\n }\n\n if (themeDoc?.css && themeDoc.css.trim() !== \"\") {\n layers.push(themeDoc.css);\n }\n\n if (this.hostCss && this.hostCss.trim() !== \"\") {\n layers.push(this.hostCss);\n }\n\n if (layers.length > 0) {\n const themeStyle = document.createElement(\"style\");\n themeStyle.setAttribute(\"data-qaid-q-theme\", \"\");\n themeStyle.textContent = layers.join(\"\\n\\n\");\n this.shadowRoot.appendChild(themeStyle);\n }\n }\n\n // ------------------------------------------------------------------\n // Loading the questionnaire JSON\n // ------------------------------------------------------------------\n\n private async loadQuestionnaire(): Promise<Questionnaire> {\n if (this.inlineQuestionnaire) return this.inlineQuestionnaire;\n if (!this.configUrl) {\n throw new Error(\"No questionnaire or configUrl provided\");\n }\n const res = await fetch(this.configUrl, {\n headers: { Accept: \"application/json\" },\n });\n if (!res.ok) {\n throw new Error(`Failed to load questionnaire (${res.status})`);\n }\n return (await res.json()) as Questionnaire;\n }\n\n // ------------------------------------------------------------------\n // Shell (shadow host + card chrome that's stable across steps)\n // ------------------------------------------------------------------\n\n private mountShell(): void {\n this.shadowHost = document.createElement(\"div\");\n this.shadowHost.setAttribute(\"data-qaid-quests\", \"\");\n\n const userContainer = this.config.container\n ? document.querySelector<HTMLElement>(this.config.container)\n : null;\n\n if (userContainer) {\n this.shadowHost.style.display = \"block\";\n // Fill the user-supplied container vertically so the card can\n // flex/shrink-to-fit when the host has a definite height. When\n // the container is content-sized (no fixed/min height), this is\n // a no-op — the chain just grows naturally as before.\n this.shadowHost.style.height = \"100%\";\n this.shadowHost.style.minHeight = \"0\";\n userContainer.appendChild(this.shadowHost);\n this.isUserContainer = true;\n } else {\n // Modal mode: remember the control that opened us so focus can be\n // restored on close/destroy (WCAG 2.4.3).\n this.savedOpener = saveFocus();\n // Modal mode: shadow host is fixed and full-viewport.\n this.shadowHost.style.position = \"fixed\";\n this.shadowHost.style.inset = \"0\";\n this.shadowHost.style.zIndex = String(this.config.zIndex);\n this.shadowHost.style.pointerEvents = \"none\";\n document.body.appendChild(this.shadowHost);\n }\n\n this.shadowRoot = this.shadowHost.attachShadow({ mode: \"open\" });\n\n const baseStyle = document.createElement(\"style\");\n baseStyle.textContent = getEmbedStyles();\n this.shadowRoot.appendChild(baseStyle);\n\n // Theme-derived styling (preset CSS, tokens block, theme.css,\n // host css) and theme/unstyled classes are deferred to\n // applyResolvedTheme() — that runs after the parallel theme +\n // questionnaire fetch resolves so a remote theme document can\n // contribute. Until then we render with embed defaults.\n\n this.rootEl = document.createElement(\"div\");\n this.rootEl.className = [\n \"qaid-q-root\",\n this.isUserContainer ? \"qaid-q-inline\" : \"\",\n this.config.animate ? \"\" : \"qaid-q-no-motion\",\n ]\n .filter(Boolean)\n .join(\" \");\n applyCssVars(this.rootEl, this.cssVars);\n\n if (!this.isUserContainer) {\n this.backdropEl = document.createElement(\"div\");\n this.backdropEl.className = \"qaid-q-backdrop\";\n this.backdropEl.addEventListener(\"click\", () => this.close());\n this.rootEl.appendChild(this.backdropEl);\n\n const modal = document.createElement(\"div\");\n modal.className = \"qaid-q-modal\";\n this.cardEl = document.createElement(\"div\");\n this.cardEl.className = \"qaid-q-card\";\n modal.appendChild(this.cardEl);\n this.rootEl.appendChild(modal);\n } else {\n this.cardEl = document.createElement(\"div\");\n this.cardEl.className = \"qaid-q-card\";\n this.rootEl.appendChild(this.cardEl);\n }\n\n this.shadowRoot.appendChild(this.rootEl);\n\n // Modal mode needs pointer-events on host so the modal itself is clickable.\n if (!this.isUserContainer && this.shadowHost) {\n this.shadowHost.style.pointerEvents = \"auto\";\n }\n\n // Modal mode is a dialog; inline (container) mode stays a plain\n // region. The accessible name (aria-labelledby / aria-label) is\n // wired in renderHeader once the title element exists.\n if (!this.isUserContainer && this.cardEl) {\n applyDialog(this.cardEl);\n }\n\n document.addEventListener(\"keydown\", this.boundKeyDown);\n }\n\n private renderLoading(): void {\n if (!this.cardEl) return;\n this.cardEl.replaceChildren();\n const msg = document.createElement(\"p\");\n msg.className = \"qaid-q-description\";\n msg.textContent = \"Loading…\";\n this.cardEl.appendChild(msg);\n if (this.shadowRoot) announce(this.shadowRoot, \"Loading\");\n }\n\n private renderError(err: unknown): void {\n if (!this.cardEl) return;\n this.cardEl.replaceChildren();\n const heading = document.createElement(\"h3\");\n heading.className = \"qaid-q-title\";\n heading.textContent = \"Couldn't load form\";\n const detail = err instanceof Error ? err.message : String(err);\n const msg = document.createElement(\"p\");\n msg.className = \"qaid-q-description\";\n msg.textContent = detail;\n this.cardEl.appendChild(heading);\n this.cardEl.appendChild(msg);\n if (this.shadowRoot) {\n announce(this.shadowRoot, `Couldn't load form. ${detail}`, {\n assertive: true,\n });\n }\n }\n\n private effectiveProgressPosition(): \"top\" | \"bottom\" {\n return (\n this.config.progressPosition ??\n this.questionnaire?.progressPosition ??\n \"top\"\n );\n }\n\n private renderHeader(): void {\n if (!this.cardEl || !this.questionnaire) return;\n this.cardEl.replaceChildren();\n\n // A single-question form has nothing meaningful to track, so we\n // suppress the progress chrome whether or not the author asked for\n // it explicitly via `hideProgress`. Branching forms with one\n // *currently visible* question still show progress because more\n // questions can appear after the gate is answered.\n const hideProgress =\n !!this.questionnaire.hideProgress ||\n this.questionnaire.questions.length <= 1;\n const isBottom =\n !hideProgress && this.effectiveProgressPosition() === \"bottom\";\n // `hideTitle` is the dashboard's \"Hide Header\" toggle — it hides\n // both the title row and the description, since they live together\n // in the shared header area.\n const headerHidden = !!this.questionnaire.hideTitle;\n const hasTitle = !!this.questionnaire.title && !headerHidden;\n const hasDescription = !!this.questionnaire.description && !headerHidden;\n const hasCloseBtn = !this.isUserContainer;\n // Only render content into the right-hand side of the header when\n // we'd actually have something to put there.\n const headerHasRightContent =\n hasCloseBtn || (!isBottom && !hideProgress);\n\n // These three are created up front and reused across step renders.\n // In top mode they slot into the header / above the body; in bottom\n // mode the footer in renderStep() pulls them into its own row.\n this.savingEl = document.createElement(\"div\");\n this.savingEl.className = \"qaid-q-saving\";\n const dot = document.createElement(\"span\");\n dot.className = \"qaid-q-saving-dot\";\n const savingText = document.createElement(\"span\");\n savingText.textContent = \"Saving…\";\n this.savingEl.appendChild(dot);\n this.savingEl.appendChild(savingText);\n\n this.stepCounterEl = document.createElement(\"div\");\n this.stepCounterEl.className = \"qaid-q-step-counter\";\n\n this.progressEl = document.createElement(\"div\");\n this.progressEl.className = isBottom\n ? \"qaid-q-progress qaid-q-progress--inline\"\n : \"qaid-q-progress\";\n this.progressFillEl = document.createElement(\"div\");\n this.progressFillEl.className = \"qaid-q-progress-fill\";\n this.progressEl.appendChild(this.progressFillEl);\n\n // Skip the header row entirely when there's nothing to show in it\n // — keeps the form flush at the top of inline embeds with no\n // title/description, hidden progress, or bottom-progress.\n const showHeader = hasTitle || headerHasRightContent;\n if (showHeader) {\n const header = document.createElement(\"div\");\n header.className = \"qaid-q-header\";\n\n const titleWrap = document.createElement(\"div\");\n if (hasTitle) {\n this.titleEl = document.createElement(\"h2\");\n this.titleEl.className = \"qaid-q-title\";\n this.titleEl.textContent = this.questionnaire.title!;\n titleWrap.appendChild(this.titleEl);\n }\n\n const right = document.createElement(\"div\");\n right.style.display = \"flex\";\n right.style.alignItems = \"center\";\n right.style.gap = \"0.5rem\";\n\n // Saving + step counter live in the header in top mode. In bottom\n // mode the footer owns them. When progress is hidden, the step\n // counter is suppressed but the saving indicator still surfaces.\n if (!isBottom) {\n right.appendChild(this.savingEl);\n if (!hideProgress) right.appendChild(this.stepCounterEl);\n }\n\n if (hasCloseBtn) {\n const closeBtn = document.createElement(\"button\");\n closeBtn.type = \"button\";\n closeBtn.className = \"qaid-q-close\";\n closeBtn.setAttribute(\"aria-label\", \"Close form\");\n closeBtn.innerHTML = CLOSE_ICON;\n closeBtn.addEventListener(\"click\", () => this.close());\n right.appendChild(closeBtn);\n }\n\n header.appendChild(titleWrap);\n header.appendChild(right);\n this.cardEl.appendChild(header);\n }\n\n // Give the modal dialog an accessible name: reference the visible\n // title when shown, otherwise fall back to the questionnaire title.\n // Inline mode is not a dialog, so it is skipped.\n if (!this.isUserContainer && this.cardEl) {\n if (this.titleEl) {\n this.titleEl.id = this.titleEl.id || \"qaid-q-dialog-title\";\n this.cardEl.setAttribute(\"aria-labelledby\", this.titleEl.id);\n this.cardEl.removeAttribute(\"aria-label\");\n } else {\n this.cardEl.removeAttribute(\"aria-labelledby\");\n this.cardEl.setAttribute(\n \"aria-label\",\n this.questionnaire.title ?? \"Questionnaire\",\n );\n }\n }\n\n if (hasDescription) {\n const desc = document.createElement(\"p\");\n desc.className = \"qaid-q-description\";\n desc.textContent = this.questionnaire.description!;\n desc.style.marginTop = showHeader ? \"-0.5rem\" : \"0\";\n desc.style.marginBottom = \"0.75rem\";\n this.cardEl.appendChild(desc);\n }\n\n if (!isBottom && !hideProgress) {\n this.cardEl.appendChild(this.progressEl);\n }\n\n this.bodyEl = document.createElement(\"div\");\n this.bodyEl.className = \"qaid-q-body\";\n this.cardEl.appendChild(this.bodyEl);\n\n this.footerEl = document.createElement(\"div\");\n this.footerEl.className = \"qaid-q-footer\";\n this.cardEl.appendChild(this.footerEl);\n\n // \"More below\" cue. Decorative: the body is the scroll region and the\n // footer's Next button is the real affordance, so keep it out of the\n // accessibility tree entirely.\n this.scrollCueEl = document.createElement(\"div\");\n this.scrollCueEl.className = \"qaid-q-scroll-cue\";\n this.scrollCueEl.setAttribute(\"aria-hidden\", \"true\");\n this.scrollCueEl.hidden = true;\n this.scrollCueEl.innerHTML =\n '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\"' +\n ' stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M6 9l6 6 6-6\"/></svg>';\n this.bodyEl.appendChild(this.scrollCueEl);\n\n this.overflow = watchOverflow(this.bodyEl, this.cardEl, this.scrollCueEl);\n }\n\n // ------------------------------------------------------------------\n // Step rendering\n // ------------------------------------------------------------------\n\n private renderStep(): void {\n if (!this.questionnaire || !this.bodyEl || !this.footerEl) return;\n\n const total = this.visibleQuestions.length;\n if (total === 0) {\n // No visible questions — should be unreachable when the validator\n // forbids visibleIf on the first question, but guard against\n // misauthored inline questionnaires that gate everything.\n void this.submit();\n return;\n }\n if (this.stepIndex >= total) {\n this.stepIndex = total - 1;\n }\n const idx = this.stepIndex;\n const question = this.visibleQuestions[idx];\n\n if (this.stepCounterEl) {\n this.stepCounterEl.textContent = `${idx + 1} / ${total}`;\n }\n if (this.progressFillEl) {\n const pct = ((idx + 1) / total) * 100;\n this.progressFillEl.style.width = `${pct}%`;\n }\n // Expose progress semantics to assistive tech. valuemax is the count\n // of currently-visible questions (branching can change it), valuenow\n // is 1-based so the ratio matches the visual fill (idx+1)/total.\n if (this.progressEl) {\n this.progressEl.setAttribute(\"role\", \"progressbar\");\n this.progressEl.setAttribute(\"aria-label\", \"Progress\");\n this.progressEl.setAttribute(\"aria-valuemin\", \"0\");\n this.progressEl.setAttribute(\"aria-valuemax\", String(total));\n this.progressEl.setAttribute(\"aria-valuenow\", String(idx + 1));\n this.progressEl.setAttribute(\n \"aria-valuetext\",\n `Question ${idx + 1} of ${total}`,\n );\n }\n // Announce the step change through the shared polite region. Kept\n // distinct from the per-step validation error region below so the\n // two never clobber each other. Skipped for single-question forms\n // where there is no step to move between.\n if (this.shadowRoot && total > 1) {\n announce(this.shadowRoot, `Step ${idx + 1} of ${total}`);\n }\n\n // Build step body\n const step = document.createElement(\"div\");\n step.className = \"qaid-q-step\";\n step.setAttribute(\"role\", \"group\");\n step.setAttribute(\"aria-labelledby\", `qaid-q-label-${idx}`);\n\n const label = document.createElement(\"label\");\n label.id = `qaid-q-label-${idx}`;\n label.className = \"qaid-q-label\";\n label.textContent = question.label;\n if (question.required) {\n const star = document.createElement(\"span\");\n star.className = \"qaid-q-required\";\n star.textContent = \"*\";\n star.setAttribute(\"aria-label\", \"required\");\n label.appendChild(star);\n }\n step.appendChild(label);\n\n if (question.description) {\n const desc = document.createElement(\"p\");\n desc.className = \"qaid-q-description\";\n desc.textContent = question.description;\n step.appendChild(desc);\n }\n\n const errorEl = document.createElement(\"p\");\n errorEl.className = \"qaid-q-error\";\n errorEl.id = `qaid-q-error-${idx}`;\n errorEl.setAttribute(\"aria-live\", \"polite\");\n\n const initialValue = this.answers[question.id] ?? null;\n const isLast = idx === total - 1;\n\n const input = createInput({\n question,\n initialValue,\n onChange: (value) => {\n this.handleAnswerChange(question, value);\n // clearing error if the user starts typing again\n if (errorEl.textContent) {\n errorEl.textContent = \"\";\n this.currentInput?.clearInvalid();\n }\n },\n onSubmit: () => this.advance(question, errorEl),\n onAutoAdvance: () => this.advance(question, errorEl),\n autoAdvance: this.config.autoAdvance,\n });\n this.currentInput = input;\n\n step.appendChild(input.element);\n step.appendChild(errorEl);\n\n this.bodyEl.replaceChildren(step);\n // replaceChildren drops the cue, so put it back and re-measure the new\n // step. The ResizeObserver watches the body's box, which a step with more\n // options does not change — only its scrollHeight — so refresh explicitly.\n if (this.scrollCueEl) this.bodyEl.appendChild(this.scrollCueEl);\n this.bodyEl.scrollTop = 0;\n this.overflow?.refresh();\n\n // Footer\n this.footerEl.replaceChildren();\n const hideProgress =\n !!this.questionnaire.hideProgress ||\n this.questionnaire.questions.length <= 1;\n const isBottomProgress =\n !hideProgress && this.effectiveProgressPosition() === \"bottom\";\n\n const back =\n idx > 0\n ? (() => {\n const b = document.createElement(\"button\");\n b.type = \"button\";\n b.className = \"qaid-q-btn qaid-q-btn-secondary\";\n b.textContent = this.questionnaire!.backLabel ?? \"Back\";\n b.addEventListener(\"click\", () => this.back());\n return b;\n })()\n : null;\n\n const next = document.createElement(\"button\");\n next.type = \"button\";\n next.className = \"qaid-q-btn qaid-q-btn-primary\";\n next.textContent = isLast\n ? (this.questionnaire.submitLabel ?? \"Submit\")\n : (this.questionnaire.nextLabel ?? \"Next\");\n next.addEventListener(\"click\", () => this.advance(question, errorEl));\n\n if (isBottomProgress) {\n // Single-row footer: [back] [progress fills] [savingEl][stepCounter] [next]\n this.footerEl.classList.add(\"qaid-q-footer--inline-progress\");\n if (back) this.footerEl.appendChild(back);\n if (this.progressEl) this.footerEl.appendChild(this.progressEl);\n if (this.savingEl) this.footerEl.appendChild(this.savingEl);\n if (this.stepCounterEl) this.footerEl.appendChild(this.stepCounterEl);\n this.footerEl.appendChild(next);\n } else {\n this.footerEl.classList.remove(\"qaid-q-footer--inline-progress\");\n const left = document.createElement(\"div\");\n left.className = \"qaid-q-footer-left\";\n const right = document.createElement(\"div\");\n right.className = \"qaid-q-footer-right\";\n\n if (back) left.appendChild(back);\n\n right.appendChild(next);\n\n this.footerEl.appendChild(left);\n this.footerEl.appendChild(right);\n }\n\n // Focus policy:\n // - Initial render: only focus when `autoFocus` is enabled.\n // Embedded previews and \"open in page\" usages set autoFocus:false\n // to avoid yanking focus on mount.\n // - Every subsequent step (advance / back): always focus the\n // active input. The user just performed a keyboard or click\n // action expecting to interact with the next question, and\n // screen-reader users need focus to move so the new question's\n // accessible name is announced.\n const isInitialRender = !this.hasRenderedStep;\n this.hasRenderedStep = true;\n const shouldFocus = isInitialRender ? this.config.autoFocus : true;\n if (shouldFocus) {\n requestAnimationFrame(() => input.focus());\n }\n }\n\n private renderDone(): void {\n if (!this.cardEl || !this.questionnaire) return;\n this.cardEl.replaceChildren();\n\n const done = document.createElement(\"div\");\n done.className = \"qaid-q-done\";\n\n const icon = document.createElement(\"div\");\n icon.className = \"qaid-q-done-icon\";\n icon.innerHTML = CHECK_ICON;\n done.appendChild(icon);\n\n const title = document.createElement(\"h3\");\n title.className = \"qaid-q-done-title\";\n const doneTitle = this.questionnaire.thankYouTitle ?? \"Thank you!\";\n title.textContent = doneTitle;\n // Make the heading programmatically focusable so focus can land on\n // the confirmation once the step controls are gone (WCAG 2.4.3).\n title.setAttribute(\"tabindex\", \"-1\");\n done.appendChild(title);\n\n if (this.questionnaire.thankYouMessage) {\n const msg = document.createElement(\"p\");\n msg.className = \"qaid-q-done-message\";\n msg.textContent = this.questionnaire.thankYouMessage;\n done.appendChild(msg);\n }\n\n if (!this.isUserContainer) {\n const closeBtn = document.createElement(\"button\");\n closeBtn.type = \"button\";\n closeBtn.className = \"qaid-q-btn qaid-q-btn-primary\";\n closeBtn.textContent = \"Close\";\n closeBtn.style.marginTop = \"1rem\";\n closeBtn.addEventListener(\"click\", () => this.close());\n done.appendChild(closeBtn);\n }\n\n this.cardEl.appendChild(done);\n\n // Move focus to the thank-you heading and announce the confirmation\n // (the submit button that had focus was just removed).\n requestAnimationFrame(() => title.focus());\n if (this.shadowRoot) {\n const message = this.questionnaire.thankYouMessage\n ? `${doneTitle}. ${this.questionnaire.thankYouMessage}`\n : doneTitle;\n announce(this.shadowRoot, message);\n }\n }\n\n // ------------------------------------------------------------------\n // Navigation\n // ------------------------------------------------------------------\n\n private advance(question: Question, errorEl: HTMLElement): void {\n if (!this.currentInput || !this.questionnaire) return;\n if (!this.currentInput.isValid()) {\n errorEl.textContent = validationMessage(question);\n // Signal invalidity on the control itself and tie the error text to\n // it via aria-describedby so it is not conveyed by colour alone.\n this.currentInput.setInvalid(errorEl.id);\n this.currentInput.focus();\n return;\n }\n // Capture latest value (e.g. range may not have fired its first change).\n // handleAnswerChange already calls recomputeVisible after recording\n // the answer, so visibleQuestions is up-to-date here.\n this.handleAnswerChange(question, this.currentInput.getValue(), { immediate: true });\n\n const last = this.stepIndex === this.visibleQuestions.length - 1;\n if (last) {\n void this.submit();\n return;\n }\n this.stepIndex++;\n this.renderStep();\n }\n\n private back(): void {\n // Flush any pending save before navigating\n this.flushPendingSave();\n if (this.stepIndex > 0) {\n this.stepIndex--;\n // Defensive: a previous answer may have been changed in a way that\n // shrinks the visible tail. Recompute and clamp.\n this.recomputeVisible();\n if (this.stepIndex >= this.visibleQuestions.length) {\n this.stepIndex = Math.max(0, this.visibleQuestions.length - 1);\n }\n this.renderStep();\n }\n }\n\n // ------------------------------------------------------------------\n // Answer handling + autosave\n // ------------------------------------------------------------------\n\n private recomputeVisible(): void {\n if (!this.questionnaire) return;\n this.visibleQuestions = getVisibleQuestions(this.questionnaire, this.answers);\n }\n\n private handleAnswerChange(\n question: Question,\n value: AnswerValue,\n opts: { immediate?: boolean } = {},\n ): void {\n this.answers[question.id] = value;\n // Visibility may flip on follow-up questions when this answer\n // changes. Recompute synchronously so advance() / back() see the\n // up-to-date list. We don't re-render here — the user is mid-edit.\n this.recomputeVisible();\n\n const debounced =\n question.type === \"text\" ||\n question.type === \"currency\" ||\n question.type === \"range\";\n\n if (opts.immediate || !debounced) {\n this.flushPendingSave();\n this.saveAnswer(question.id, value);\n return;\n }\n\n // Debounce — collapse rapid keystrokes into one PATCH.\n this.pendingSaveQuestionId = question.id;\n this.pendingSaveValue = value;\n if (this.pendingSaveTimer) clearTimeout(this.pendingSaveTimer);\n this.pendingSaveTimer = setTimeout(() => {\n this.flushPendingSave();\n }, this.config.saveDebounceMs);\n }\n\n private flushPendingSave(): void {\n if (this.pendingSaveTimer) {\n clearTimeout(this.pendingSaveTimer);\n this.pendingSaveTimer = null;\n }\n if (this.pendingSaveQuestionId !== null) {\n const id = this.pendingSaveQuestionId;\n const value = this.pendingSaveValue;\n this.pendingSaveQuestionId = null;\n this.pendingSaveValue = null;\n this.saveAnswer(id, value);\n }\n }\n\n private saveAnswer(questionId: string, value: AnswerValue): void {\n this.showSaving(true);\n this.inflightSaves++;\n void this.doSave(questionId, value).finally(() => {\n this.inflightSaves--;\n if (this.inflightSaves === 0) this.showSaving(false);\n });\n }\n\n private async doSave(questionId: string, value: AnswerValue): Promise<void> {\n // Wait for response id to be assigned before sending updates.\n // If creation is still in flight, queue tightly via microtask polling.\n let waited = 0;\n while (this.responseId === null && waited < 5000) {\n await new Promise((r) => setTimeout(r, 50));\n waited += 50;\n }\n if (this.responseId === null) return;\n\n try {\n await fetch(`${this.config.endpoint}/${this.responseId}`, {\n method: \"PATCH\",\n headers: this.jsonHeaders(),\n body: JSON.stringify({ questionId, value }),\n });\n } catch (err) {\n console.error(\"[quests-embed] failed to save answer:\", err);\n }\n }\n\n private async createResponse(): Promise<void> {\n try {\n const res = await fetch(this.config.endpoint, {\n method: \"POST\",\n headers: this.jsonHeaders(),\n body: JSON.stringify({\n apiKey: this.config.apiKey || undefined,\n questId: this.questionnaire?.id,\n pageUrl: window.location.href,\n visitorId: this.visitorId,\n userAgent: navigator.userAgent,\n metadata: this.metadata,\n }),\n });\n if (res.ok) {\n const data = (await res.json()) as CreateResponseResult;\n this.responseId = data.id;\n }\n } catch (err) {\n console.error(\"[quests-embed] failed to create response:\", err);\n }\n }\n\n private async submit(): Promise<void> {\n this.flushPendingSave();\n // Wait for any in-flight saves so the server sees the final state.\n while (this.inflightSaves > 0) {\n await new Promise((r) => setTimeout(r, 50));\n }\n\n if (this.responseId !== null) {\n try {\n await fetch(`${this.config.endpoint}/${this.responseId}/submit`, {\n method: \"POST\",\n headers: this.jsonHeaders(),\n body: JSON.stringify({ answers: this.answers }),\n });\n } catch (err) {\n console.error(\"[quests-embed] failed to submit:\", err);\n }\n }\n\n this.state = \"DONE\";\n this.renderDone();\n\n // Notify the host after the thank-you screen is up. A copy so a\n // callback can't mutate our answer store. Never let a throwing\n // handler break the completed flow.\n if (this.onCompleteCb) {\n try {\n this.onCompleteCb({ ...this.answers });\n } catch (err) {\n console.error(\"[quests-embed] onComplete handler threw:\", err);\n }\n }\n }\n\n private jsonHeaders(): Record<string, string> {\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n };\n if (this.config.apiKey) headers[\"X-API-Key\"] = this.config.apiKey;\n return headers;\n }\n\n private showSaving(visible: boolean): void {\n if (!this.savingEl) return;\n this.savingEl.classList.toggle(\"qaid-q-visible\", visible);\n }\n\n // ------------------------------------------------------------------\n // Keyboard / lifecycle\n // ------------------------------------------------------------------\n\n private handleKeyDown(e: KeyboardEvent): void {\n if (e.key === \"Escape\" && !this.isUserContainer && this.state !== \"DONE\") {\n this.close();\n }\n }\n\n /**\n * Modal mode only: trap Tab within the card and mark the rest of the\n * page inert while the dialog is open. Runs once (guarded) after the\n * first step renders. Inline mode keeps normal page tab flow and is\n * never trapped or isolated.\n */\n private setupModalA11y(): void {\n if (this.isUserContainer || !this.cardEl) return;\n if (this.focusTrap) return;\n this.focusTrap = createFocusTrap(this.cardEl);\n this.inertRestore = setBackgroundInert(this.cardEl);\n }\n\n private close(): void {\n this.flushPendingSave();\n this.destroy();\n }\n\n /** Destroy the embed and clean up all resources */\n public destroy(): void {\n document.removeEventListener(\"keydown\", this.boundKeyDown);\n if (this.pendingSaveTimer) {\n clearTimeout(this.pendingSaveTimer);\n this.pendingSaveTimer = null;\n }\n // Tear down modal-mode a11y plumbing before removing the host:\n // release the focus trap, un-inert the background, then return focus\n // to the control that opened the dialog. All no-ops in inline mode.\n if (this.focusTrap) {\n this.focusTrap.release();\n this.focusTrap = null;\n }\n if (this.inertRestore) {\n this.inertRestore();\n this.inertRestore = null;\n }\n if (!this.isUserContainer && this.savedOpener) {\n restoreFocus(this.savedOpener);\n }\n this.savedOpener = null;\n if (this.shadowHost) {\n this.shadowHost.remove();\n this.shadowHost = null;\n this.shadowRoot = null;\n }\n this.rootEl = null;\n this.cardEl = null;\n this.overflow?.destroy();\n this.overflow = null;\n this.bodyEl = null;\n this.scrollCueEl = null;\n this.footerEl = null;\n this.titleEl = null;\n this.stepCounterEl = null;\n this.progressFillEl = null;\n this.savingEl = null;\n this.backdropEl = null;\n this.currentInput = null;\n\n // Notify the host exactly once that the embed is gone, whatever the\n // teardown path (visitor close, completion, or host-driven destroy).\n if (!this.closed) {\n this.closed = true;\n if (this.onCloseCb) {\n try {\n this.onCloseCb();\n } catch (err) {\n console.error(\"[quests-embed] onClose handler threw:\", err);\n }\n }\n }\n }\n\n /** Read-only snapshot of current answers */\n public getAnswers(): Answers {\n return { ...this.answers };\n }\n\n /**\n * Id of the question currently rendered, or `null` if the embed\n * isn't on a question (still loading, on the thank-you screen, or\n * unmounted). Useful for editor previews that want to restore the\n * user's place after a forced re-mount.\n */\n public getCurrentQuestionId(): string | null {\n if (this.state !== \"READY\") return null;\n const q = this.visibleQuestions[this.stepIndex];\n return q ? q.id : null;\n }\n\n /**\n * Jump to the visible question with the given id. Returns true if\n * the question is currently visible (and the embed navigated to\n * it), false if it's hidden by an unmet `visibleIf` predicate or\n * unknown. If the embed is still initializing, the request is\n * latched and applied as soon as the questionnaire is ready.\n *\n * Intended for editor previews and other host-driven step control.\n */\n public goToStep(questionId: string): boolean {\n if (this.state !== \"READY\" || !this.questionnaire) {\n this.pendingGoToStep = questionId;\n return false;\n }\n const idx = this.visibleQuestions.findIndex((q) => q.id === questionId);\n if (idx === -1) return false;\n if (idx === this.stepIndex) return true;\n this.flushPendingSave();\n this.stepIndex = idx;\n this.renderStep();\n return true;\n }\n}\n\nfunction validationMessage(q: Question): string {\n switch (q.type) {\n case \"text\":\n if (q.minLength) return `Please enter at least ${q.minLength} characters.`;\n return \"This field is required.\";\n case \"currency\":\n return \"Please enter a valid amount.\";\n case \"date\":\n return \"Please pick a date.\";\n case \"multiple-choice\":\n return q.multiple ? \"Please select at least one option.\" : \"Please select an option.\";\n case \"range\":\n return \"Please choose a value.\";\n }\n}\n","/**\n * @qaiddev/quests-embed\n *\n * Step-by-step JSON-defined form embed.\n *\n * Usage as ES module:\n * ```ts\n * import { QaidQuests } from \"@qaiddev/quests-embed\";\n *\n * new QaidQuests({\n * endpoint: \"/api/responses\",\n * configUrl: \"/forms/intake.json\",\n * container: \"#form\",\n * });\n * ```\n *\n * Usage via script tag (auto-init):\n * ```html\n * <script src=\"qaid-quests.umd.cjs\"\n * data-endpoint=\"/api/responses\"\n * data-config-url=\"/forms/intake.json\"\n * data-container=\"#form\"></script>\n * ```\n *\n * Or with a JSON config tag:\n * ```html\n * <script type=\"application/json\" data-quests-config>\n * { \"endpoint\": \"/api/responses\", \"configUrl\": \"/forms/intake.json\" }\n * </script>\n * <script src=\"qaid-quests.umd.cjs\"></script>\n * ```\n */\n\nimport { QaidQuests } from \"./embed\";\nimport type { QuestsConfig } from \"./types\";\n\nexport { QaidQuests };\nexport { getVisibleQuestions, evaluateRule } from \"./visibility\";\nexport type {\n QuestsConfig,\n ResolvedQuestsConfig,\n Questionnaire,\n Question,\n TextQuestion,\n CurrencyQuestion,\n RangeQuestion,\n DateQuestion,\n MultipleChoiceQuestion,\n MultipleChoiceOption,\n Answers,\n AnswerValue,\n VisibilityRule,\n CreateResponsePayload,\n CreateResponseResult,\n UpdateAnswerPayload,\n SubmitPayload,\n} from \"./types\";\n\nfunction findCssFromSelector(selector: string): string {\n const el = document.querySelector(selector);\n return el?.textContent?.trim() ?? \"\";\n}\n\nfunction parseJsonConfig(): Partial<QuestsConfig> | null {\n const tag = document.querySelector(\n 'script[type=\"application/json\"][data-quests-config]',\n );\n if (!tag) return null;\n const raw = tag.textContent?.trim();\n if (!raw) return null;\n try {\n const parsed = JSON.parse(raw) as Partial<QuestsConfig> & {\n cssSelector?: string;\n };\n if (parsed.cssSelector && !parsed.css) {\n parsed.css = findCssFromSelector(parsed.cssSelector);\n delete parsed.cssSelector;\n }\n return parsed;\n } catch {\n return null;\n }\n}\n\nfunction parseDataAttributes(\n script: HTMLScriptElement,\n): Partial<QuestsConfig> | null {\n const endpoint = script.getAttribute(\"data-endpoint\");\n if (!endpoint) return null;\n\n const configUrl = script.getAttribute(\"data-config-url\");\n const apiKey = script.getAttribute(\"data-api-key\");\n const container = script.getAttribute(\"data-container\");\n const zIndex = script.getAttribute(\"data-zindex\");\n const positiveColor = script.getAttribute(\"data-positive-color\");\n const negativeColor = script.getAttribute(\"data-negative-color\");\n const markerColor = script.getAttribute(\"data-marker-color\");\n const modalWidth = script.getAttribute(\"data-modal-width\");\n const backdropOpacity = script.getAttribute(\"data-backdrop-opacity\");\n const fontFamily = script.getAttribute(\"data-font-family\");\n const fontSize = script.getAttribute(\"data-font-size\");\n const cssSelector = script.getAttribute(\"data-css-selector\");\n const autoAdvance = script.getAttribute(\"data-auto-advance\");\n const saveDebounceMs = script.getAttribute(\"data-save-debounce-ms\");\n const themeUrl = script.getAttribute(\"data-theme-url\");\n const preset = script.getAttribute(\"data-preset\");\n const themeMode = script.getAttribute(\"data-theme\");\n const unstyled = script.getAttribute(\"data-unstyled\");\n\n return {\n endpoint,\n configUrl: configUrl ?? undefined,\n apiKey: apiKey ?? undefined,\n container: container ?? undefined,\n zIndex: zIndex ? parseInt(zIndex, 10) : undefined,\n colors: {\n positive: positiveColor ?? undefined,\n negative: negativeColor ?? undefined,\n marker: markerColor ?? undefined,\n },\n modalWidth: modalWidth ? parseInt(modalWidth, 10) : undefined,\n backdropOpacity: backdropOpacity ? parseFloat(backdropOpacity) : undefined,\n fontFamily: fontFamily ?? undefined,\n fontSize: fontSize ? parseInt(fontSize, 10) : undefined,\n css: cssSelector ? findCssFromSelector(cssSelector) : undefined,\n autoAdvance: autoAdvance === \"true\" ? true : undefined,\n saveDebounceMs: saveDebounceMs ? parseInt(saveDebounceMs, 10) : undefined,\n themeUrl: themeUrl ?? undefined,\n preset: preset ? (preset as \"default\" | \"minimal\" | \"pill\" | \"dense\") : undefined,\n theme: themeMode ? (themeMode as \"light\" | \"dark\" | \"auto\") : undefined,\n unstyled: unstyled === \"true\" ? true : undefined,\n };\n}\n\nif (typeof document !== \"undefined\") {\n const initFromScript = (): void => {\n const script = document.currentScript as HTMLScriptElement | null;\n const jsonConfig = parseJsonConfig();\n const dataConfig = script ? parseDataAttributes(script) : null;\n const config = jsonConfig ?? dataConfig;\n if (config?.endpoint && (config.configUrl || config.questionnaire)) {\n new QaidQuests(config as QuestsConfig);\n }\n };\n\n if (document.readyState === \"loading\") {\n document.addEventListener(\"DOMContentLoaded\", initFromScript);\n } else {\n initFromScript();\n }\n}\n"],"names":["PRESETS","getPresetCss","name","getLuminance","r","g","b","rs","gs","bs","c","parseColor","color","hex","fullHex","num","match","getContrastTextColor","bgColor","rgb","buildCssVars","options","out","applyCssVars","el","vars","value","getEmbedStyles","shadowStyles","getPresetCssImpl","createInvalidState","control","linkedId","errorId","ids","id","createInput","createTextInput","createCurrencyInput","createRangeInput","createDateInput","createMultipleChoiceInput","q","opts","initial","textarea","input","inputAutocomplete","e","len","v","wrap","prefix","currencySymbol","raw","currency","locale","p","valueDisplay","valueText","unit","updateDisplay","bounds","lower","upper","alignment","layoutClass","o","selected","buttons","opt","idx","btn","marker","body","img","label","desc","key","toggle","focusOption","k","on","currentValue","it","getVisibleQuestions","answers","qn","evaluateRule","rule","isAnswered","matchesAtomic","x","answer","target","a","COMPACT_HEIGHT","EDGE_SLOP","scrollEdgeOf","overflow","applyScrollState","cue","edge","more","applyFitState","card","availableHeight","fit","watchOverflow","refresh","observer","LIVE_ATTR","VISUALLY_HIDDEN_CSS","FOCUSABLE_SELECTOR","ownerDoc","root","getLiveRegion","assertive","kind","existing","region","announce","message","isDisabled","isHidden","node","style","getFocusable","container","getDeepActiveElement","doc","active","createFocusTrap","addedTabindex","onKeydown","event","focusable","first","last","withinTrap","saveFocus","restoreFocus","applyDialog","topLevelAncestor","parent","setBackgroundInert","except","keep","changed","child","prevInert","prevAriaHidden","entry","VISITOR_ID_KEY","getOrCreateVisitorId","CLOSE_ICON","CHECK_ICON","QaidQuests","config","themeDoc","err","res","finalPreset","finalMode","finalUnstyled","layers","presetCss","themeStyle","userContainer","baseStyle","modal","msg","heading","detail","hideProgress","isBottom","headerHidden","hasTitle","hasDescription","hasCloseBtn","headerHasRightContent","dot","savingText","showHeader","header","titleWrap","right","closeBtn","total","question","pct","step","star","errorEl","initialValue","isLast","isBottomProgress","back","next","left","isInitialRender","done","icon","title","doneTitle","validationMessage","debounced","questionId","waited","data","headers","visible","findCssFromSelector","selector","parseJsonConfig","tag","parsed","parseDataAttributes","script","endpoint","configUrl","apiKey","zIndex","positiveColor","negativeColor","markerColor","modalWidth","backdropOpacity","fontFamily","fontSize","cssSelector","autoAdvance","saveDebounceMs","themeUrl","preset","themeMode","unstyled","initFromScript","jsonConfig","dataConfig"],"mappings":"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"}
1
+ {"version":3,"file":"qaid-quests.umd.cjs","sources":["../src/themes/index.ts","../src/styles.ts","../src/inputs.ts","../src/visibility.ts","../src/overflow.ts","../src/a11y.ts","../src/embed.ts","../src/index.ts"],"sourcesContent":["/**\n * Built-in theme presets. Each preset is a CSS string of token\n * overrides (Tier 3) and is injected into the shadow root after the\n * base stylesheet but before the host's `css` config string — so a\n * host always wins.\n */\n\nimport defaultCss from \"./default.css?inline\";\nimport minimalCss from \"./minimal.css?inline\";\nimport pillCss from \"./pill.css?inline\";\nimport denseCss from \"./dense.css?inline\";\n\nexport type PresetName = \"default\" | \"minimal\" | \"pill\" | \"dense\";\n\nconst PRESETS: Record<PresetName, string> = {\n default: defaultCss,\n minimal: minimalCss,\n pill: pillCss,\n dense: denseCss,\n};\n\nexport function getPresetCss(name: PresetName): string {\n return PRESETS[name] ?? \"\";\n}\n","/**\n * CSS variable + style helpers for the questionnaire embed.\n *\n * Color vars are intentionally NAMESPACED away from @qaiddev/thumbs-embed\n * so a host that uses both embeds can theme them independently:\n *\n * Quests own: --qaid-q-accent, --qaid-q-error, --qaid-q-focus\n * Shared: --qaid-modal-width, --qaid-backdrop-opacity,\n * --qaid-font-family, --qaid-font-size\n *\n * The legacy thumbs names (--qaid-positive / --qaid-negative /\n * --qaid-marker) are NOT applied here anymore.\n */\n\nimport shadowStyles from \"./styles-shadow.css?inline\";\nimport { getPresetCss as getPresetCssImpl, type PresetName } from \"./themes\";\n\nexport interface BuildCssVarsOptions {\n /** Primary CTA / progress fill / selected option ring */\n accentColor?: string;\n /** Validation error text */\n errorColor?: string;\n /** Focus ring */\n focusColor?: string;\n modalWidth?: number;\n backdropOpacity?: number;\n fontFamily?: string;\n fontSize?: number;\n}\n\nfunction getLuminance(r: number, g: number, b: number): number {\n const [rs, gs, bs] = [r, g, b].map((c) => {\n c = c / 255;\n return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);\n });\n return 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;\n}\n\nfunction parseColor(color: string): { r: number; g: number; b: number } | null {\n if (color.startsWith(\"#\")) {\n const hex = color.slice(1);\n const fullHex =\n hex.length === 3\n ? hex\n .split(\"\")\n .map((c) => c + c)\n .join(\"\")\n : hex;\n const num = parseInt(fullHex, 16);\n return { r: (num >> 16) & 255, g: (num >> 8) & 255, b: num & 255 };\n }\n const match = color.match(/rgb\\((\\d+),\\s*(\\d+),\\s*(\\d+)\\)/);\n if (match) {\n return { r: parseInt(match[1]), g: parseInt(match[2]), b: parseInt(match[3]) };\n }\n return null;\n}\n\nfunction getContrastTextColor(bgColor: string): string {\n const rgb = parseColor(bgColor);\n if (!rgb) return \"white\";\n return getLuminance(rgb.r, rgb.g, rgb.b) > 0.4 ? \"black\" : \"white\";\n}\n\nexport function buildCssVars(\n options: BuildCssVarsOptions = {},\n): Record<string, string> {\n // Only emit a var when the host explicitly passed a value. The\n // stylesheet's `var(--token, <fallback>)` already handles unset\n // values, and a theme document loaded via `themeUrl` writes its\n // own `:where(.qaid-q-root)` block — inlining defaults here would\n // beat that block on the cascade and silently override the theme.\n const out: Record<string, string> = {};\n if (options.accentColor !== undefined) {\n out[\"--qaid-q-accent\"] = options.accentColor;\n out[\"--qaid-q-accent-text\"] = getContrastTextColor(options.accentColor);\n }\n if (options.errorColor !== undefined) {\n out[\"--qaid-q-error\"] = options.errorColor;\n }\n if (options.focusColor !== undefined) {\n out[\"--qaid-q-focus\"] = options.focusColor;\n out[\"--qaid-q-focus-text\"] = getContrastTextColor(options.focusColor);\n }\n if (options.modalWidth !== undefined) {\n out[\"--qaid-modal-width\"] = `${options.modalWidth}px`;\n }\n if (options.backdropOpacity !== undefined) {\n out[\"--qaid-backdrop-opacity\"] = String(options.backdropOpacity);\n }\n if (options.fontFamily !== undefined) {\n out[\"--qaid-font-family\"] = options.fontFamily;\n }\n if (options.fontSize !== undefined) {\n out[\"--qaid-font-size\"] = `${options.fontSize}px`;\n }\n return out;\n}\n\nexport function applyCssVars(el: HTMLElement, vars: Record<string, string>): void {\n for (const [name, value] of Object.entries(vars)) {\n el.style.setProperty(name, value);\n }\n}\n\n/** CSS string injected into every shadow root */\nexport function getEmbedStyles(): string {\n return shadowStyles;\n}\n\n/** CSS for a built-in theme preset, or \"\" for unknown names. */\nexport function getPresetCss(name: PresetName): string {\n return getPresetCssImpl(name);\n}\n\nexport type { PresetName };\n","/**\n * Input renderers for each question type.\n *\n * Each renderer returns a small object with an element to mount, a\n * focus helper, a synchronous current-value reader, and a validator.\n *\n * Per-keystroke autosave is wired by the embed: the renderer just\n * calls back into onChange whenever its underlying value changes.\n * Renderers also call onSubmit when the user presses Enter (or\n * onAutoAdvance after a discrete selection that should auto-advance).\n */\n\nimport type {\n AnswerValue,\n CurrencyQuestion,\n DateQuestion,\n MultipleChoiceQuestion,\n Question,\n RangeQuestion,\n TextQuestion,\n} from \"./types\";\n\nexport interface QuestionInput {\n element: HTMLElement;\n focus(): void;\n getValue(): AnswerValue;\n isValid(): boolean;\n /**\n * Mark the control invalid: sets aria-invalid=\"true\" and links the\n * error text (identified by `errorId`) via aria-describedby so a\n * screen reader reads the error as the field's description. Do not\n * rely on colour alone (WCAG 3.3.1 / 1.3.1 / 4.1.2).\n */\n setInvalid(errorId: string): void;\n /** Clear aria-invalid and unlink the error text once the field is corrected. */\n clearInvalid(): void;\n}\n\n/**\n * Wire aria-invalid + aria-describedby error association onto a control.\n * setInvalid(id) marks it invalid and appends the error id to\n * aria-describedby (preserving any existing tokens); clearInvalid()\n * removes aria-invalid and only the token it added.\n */\nfunction createInvalidState(\n control: HTMLElement,\n): Pick<QuestionInput, \"setInvalid\" | \"clearInvalid\"> {\n let linkedId: string | null = null;\n return {\n setInvalid(errorId: string): void {\n control.setAttribute(\"aria-invalid\", \"true\");\n const ids = (control.getAttribute(\"aria-describedby\") ?? \"\")\n .split(/\\s+/)\n .filter(Boolean);\n if (!ids.includes(errorId)) ids.push(errorId);\n control.setAttribute(\"aria-describedby\", ids.join(\" \"));\n linkedId = errorId;\n },\n clearInvalid(): void {\n control.removeAttribute(\"aria-invalid\");\n if (linkedId) {\n const ids = (control.getAttribute(\"aria-describedby\") ?? \"\")\n .split(/\\s+/)\n .filter(Boolean)\n .filter((id) => id !== linkedId);\n if (ids.length) control.setAttribute(\"aria-describedby\", ids.join(\" \"));\n else control.removeAttribute(\"aria-describedby\");\n linkedId = null;\n }\n },\n };\n}\n\nexport interface QuestionInputOptions {\n question: Question;\n initialValue: AnswerValue;\n /** Called any time the value changes (every keystroke for text). */\n onChange: (value: AnswerValue) => void;\n /** Called when the user presses Enter to advance. */\n onSubmit: () => void;\n /**\n * Called after a discrete selection that should auto-advance\n * (e.g. picking a single-choice option when autoAdvance is on).\n */\n onAutoAdvance: () => void;\n /** Whether the embed is configured to auto-advance after discrete selections */\n autoAdvance: boolean;\n}\n\nexport function createInput(options: QuestionInputOptions): QuestionInput {\n switch (options.question.type) {\n case \"text\":\n return createTextInput(options.question, options);\n case \"currency\":\n return createCurrencyInput(options.question, options);\n case \"range\":\n return createRangeInput(options.question, options);\n case \"date\":\n return createDateInput(options.question, options);\n case \"multiple-choice\":\n return createMultipleChoiceInput(options.question, options);\n }\n}\n\n// ====================================================================\n// Text\n// ====================================================================\n\nfunction createTextInput(\n q: TextQuestion,\n opts: QuestionInputOptions,\n): QuestionInput {\n const initial = typeof opts.initialValue === \"string\" ? opts.initialValue : \"\";\n\n let el: HTMLInputElement | HTMLTextAreaElement;\n\n if (q.multiline) {\n const textarea = document.createElement(\"textarea\");\n textarea.className = \"qaid-q-textarea\";\n textarea.value = initial;\n if (q.placeholder) textarea.placeholder = q.placeholder;\n if (q.maxLength) textarea.maxLength = q.maxLength;\n el = textarea;\n } else {\n const input = document.createElement(\"input\");\n input.className = \"qaid-q-input\";\n input.type = q.inputType ?? \"text\";\n input.value = initial;\n if (q.placeholder) input.placeholder = q.placeholder;\n if (q.maxLength) input.maxLength = q.maxLength;\n input.setAttribute(\"autocomplete\", inputAutocomplete(q.inputType));\n el = input;\n }\n\n el.setAttribute(\"aria-label\", q.label);\n if (q.required) el.setAttribute(\"aria-required\", \"true\");\n\n el.addEventListener(\"input\", () => {\n opts.onChange(el.value);\n });\n\n el.addEventListener(\"keydown\", ((e: KeyboardEvent) => {\n if (e.key !== \"Enter\") return;\n if (q.multiline) {\n // In multiline, Enter inserts newline; Cmd/Ctrl+Enter submits.\n if (e.metaKey || e.ctrlKey) {\n e.preventDefault();\n opts.onSubmit();\n }\n return;\n }\n // Single-line: Enter submits, but allow IME composition to finish first.\n if (e.isComposing) return;\n e.preventDefault();\n opts.onSubmit();\n }) as EventListener);\n\n return {\n element: el,\n focus: () => {\n el.focus();\n // Place cursor at end so user can keep typing.\n const len = el.value.length;\n try {\n el.setSelectionRange(len, len);\n } catch {\n // input types like email may not support selectionRange\n }\n },\n getValue: () => el.value,\n isValid: () => {\n if (!q.required) return true;\n const v = el.value.trim();\n if (v.length === 0) return false;\n if (q.minLength && v.length < q.minLength) return false;\n return true;\n },\n ...createInvalidState(el),\n };\n}\n\nfunction inputAutocomplete(t?: TextQuestion[\"inputType\"]): string {\n switch (t) {\n case \"email\":\n return \"email\";\n case \"tel\":\n return \"tel\";\n case \"url\":\n return \"url\";\n default:\n return \"off\";\n }\n}\n\n// ====================================================================\n// Currency\n// ====================================================================\n\nfunction createCurrencyInput(\n q: CurrencyQuestion,\n opts: QuestionInputOptions,\n): QuestionInput {\n const wrap = document.createElement(\"div\");\n wrap.className = \"qaid-q-currency\";\n\n const prefix = document.createElement(\"span\");\n prefix.className = \"qaid-q-currency-prefix\";\n prefix.textContent = currencySymbol(q.currency ?? \"USD\", q.locale);\n wrap.appendChild(prefix);\n\n const input = document.createElement(\"input\");\n input.className = \"qaid-q-input\";\n // type=\"number\" gives native numeric keyboard + min/max validation hooks.\n input.type = \"number\";\n input.inputMode = \"decimal\";\n input.step = \"0.01\";\n if (typeof q.min === \"number\") input.min = String(q.min);\n if (typeof q.max === \"number\") input.max = String(q.max);\n if (q.placeholder) input.placeholder = q.placeholder;\n if (typeof opts.initialValue === \"number\") input.value = String(opts.initialValue);\n else if (typeof opts.initialValue === \"string\" && opts.initialValue !== \"\")\n input.value = opts.initialValue;\n input.setAttribute(\"aria-label\", q.label);\n if (q.required) input.setAttribute(\"aria-required\", \"true\");\n\n input.addEventListener(\"input\", () => {\n const raw = input.value;\n if (raw === \"\") {\n opts.onChange(null);\n } else {\n const num = Number(raw);\n opts.onChange(Number.isNaN(num) ? null : num);\n }\n });\n\n input.addEventListener(\"keydown\", (e) => {\n if (e.key === \"Enter\" && !(e as KeyboardEvent).isComposing) {\n e.preventDefault();\n opts.onSubmit();\n }\n });\n\n wrap.appendChild(input);\n\n return {\n element: wrap,\n focus: () => input.focus(),\n getValue: () => {\n if (input.value === \"\") return null;\n const num = Number(input.value);\n return Number.isNaN(num) ? null : num;\n },\n isValid: () => {\n const v = input.value === \"\" ? null : Number(input.value);\n if (q.required && (v === null || Number.isNaN(v as number))) return false;\n if (v !== null && !Number.isNaN(v as number)) {\n if (typeof q.min === \"number\" && (v as number) < q.min) return false;\n if (typeof q.max === \"number\" && (v as number) > q.max) return false;\n }\n return true;\n },\n ...createInvalidState(input),\n };\n}\n\nfunction currencySymbol(currency: string, locale?: string): string {\n try {\n const fmt = new Intl.NumberFormat(locale, {\n style: \"currency\",\n currency,\n currencyDisplay: \"narrowSymbol\",\n maximumFractionDigits: 0,\n });\n // Pull the symbol out of a formatted zero\n const parts = fmt.formatToParts(0);\n const sym = parts.find((p) => p.type === \"currency\");\n return sym?.value ?? currency;\n } catch {\n return currency;\n }\n}\n\n// ====================================================================\n// Range\n// ====================================================================\n\nfunction createRangeInput(\n q: RangeQuestion,\n opts: QuestionInputOptions,\n): QuestionInput {\n const wrap = document.createElement(\"div\");\n wrap.className = \"qaid-q-range-wrap\";\n\n const valueDisplay = document.createElement(\"div\");\n valueDisplay.className = \"qaid-q-range-value\";\n\n const valueText = document.createElement(\"span\");\n valueDisplay.appendChild(valueText);\n\n if (q.unit) {\n const unit = document.createElement(\"span\");\n unit.className = \"qaid-q-range-unit\";\n unit.textContent = q.unit;\n valueDisplay.appendChild(unit);\n }\n\n const initial =\n typeof opts.initialValue === \"number\"\n ? opts.initialValue\n : (q.defaultValue ?? q.min);\n\n const input = document.createElement(\"input\");\n input.className = \"qaid-q-range\";\n input.type = \"range\";\n input.min = String(q.min);\n input.max = String(q.max);\n input.step = String(q.step ?? 1);\n input.value = String(initial);\n input.setAttribute(\"aria-label\", q.label);\n input.setAttribute(\"aria-valuemin\", String(q.min));\n input.setAttribute(\"aria-valuemax\", String(q.max));\n\n const updateDisplay = () => {\n valueText.textContent = input.value;\n input.setAttribute(\"aria-valuenow\", input.value);\n };\n updateDisplay();\n\n input.addEventListener(\"input\", () => {\n updateDisplay();\n opts.onChange(Number(input.value));\n });\n\n input.addEventListener(\"keydown\", (e) => {\n if (e.key === \"Enter\") {\n e.preventDefault();\n opts.onSubmit();\n }\n });\n\n const bounds = document.createElement(\"div\");\n bounds.className = \"qaid-q-range-bounds\";\n const lower = document.createElement(\"span\");\n lower.textContent = `${q.min}${q.unit ?? \"\"}`;\n const upper = document.createElement(\"span\");\n upper.textContent = `${q.max}${q.unit ?? \"\"}`;\n bounds.appendChild(lower);\n bounds.appendChild(upper);\n\n wrap.appendChild(valueDisplay);\n wrap.appendChild(input);\n wrap.appendChild(bounds);\n\n // Push the initial value to onChange so first interaction is recorded\n // even if the user presses Enter without dragging.\n // (Defer to the next tick so the embed has wired up its handler.)\n queueMicrotask(() => opts.onChange(Number(input.value)));\n\n return {\n element: wrap,\n focus: () => input.focus(),\n getValue: () => Number(input.value),\n isValid: () => true,\n ...createInvalidState(input),\n };\n}\n\n// ====================================================================\n// Date\n// ====================================================================\n\nfunction createDateInput(q: DateQuestion, opts: QuestionInputOptions): QuestionInput {\n const input = document.createElement(\"input\");\n input.className = \"qaid-q-input\";\n input.type = \"date\";\n if (q.min) input.min = q.min;\n if (q.max) input.max = q.max;\n if (typeof opts.initialValue === \"string\") input.value = opts.initialValue;\n input.setAttribute(\"aria-label\", q.label);\n if (q.required) input.setAttribute(\"aria-required\", \"true\");\n\n input.addEventListener(\"input\", () => {\n opts.onChange(input.value || null);\n });\n\n input.addEventListener(\"keydown\", (e) => {\n if (e.key === \"Enter\" && !(e as KeyboardEvent).isComposing) {\n e.preventDefault();\n opts.onSubmit();\n }\n });\n\n return {\n element: input,\n focus: () => input.focus(),\n getValue: () => input.value || null,\n isValid: () => {\n if (!q.required) return true;\n return input.value !== \"\";\n },\n ...createInvalidState(input),\n };\n}\n\n// ====================================================================\n// Multiple choice\n// ====================================================================\n\nfunction createMultipleChoiceInput(\n q: MultipleChoiceQuestion,\n opts: QuestionInputOptions,\n): QuestionInput {\n const wrap = document.createElement(\"div\");\n const alignment = q.imageAlignment ?? \"horizontal\";\n const hasAnyImage = q.options.some((o) => !!o.image);\n const layoutClass = hasAnyImage\n ? alignment === \"vertical\"\n ? \" qaid-q-options--image-vertical\"\n : \" qaid-q-options--image-horizontal\"\n : \"\";\n wrap.className = `qaid-q-options${layoutClass}`;\n wrap.setAttribute(\"role\", q.multiple ? \"group\" : \"radiogroup\");\n wrap.setAttribute(\"aria-label\", q.label);\n if (q.required) wrap.setAttribute(\"aria-required\", \"true\");\n\n // Normalize initial selection to a Set for both modes\n const selected = new Set<string>();\n if (q.multiple && Array.isArray(opts.initialValue)) {\n for (const v of opts.initialValue) selected.add(v);\n } else if (!q.multiple && typeof opts.initialValue === \"string\") {\n selected.add(opts.initialValue);\n }\n\n const buttons: HTMLButtonElement[] = [];\n\n q.options.forEach((opt, idx) => {\n const btn = document.createElement(\"button\");\n btn.type = \"button\";\n btn.className = `qaid-q-option${q.multiple ? \" qaid-q-multi\" : \"\"}`;\n btn.dataset.value = opt.value;\n btn.setAttribute(\"role\", q.multiple ? \"checkbox\" : \"radio\");\n btn.setAttribute(\"aria-checked\", selected.has(opt.value) ? \"true\" : \"false\");\n if (selected.has(opt.value)) btn.classList.add(\"qaid-q-selected\");\n\n const marker = document.createElement(\"span\");\n marker.className = \"qaid-q-option-marker\";\n marker.setAttribute(\"aria-hidden\", \"true\");\n\n const body = document.createElement(\"span\");\n body.className = \"qaid-q-option-body\";\n\n if (opt.image) {\n const img = document.createElement(\"img\");\n img.className = \"qaid-q-option-image\";\n img.src = opt.image;\n img.alt = \"\";\n img.loading = \"lazy\";\n img.decoding = \"async\";\n body.appendChild(img);\n }\n\n const label = document.createElement(\"span\");\n label.className = \"qaid-q-option-label\";\n label.textContent = opt.label;\n body.appendChild(label);\n\n if (opt.description) {\n const desc = document.createElement(\"span\");\n desc.className = \"qaid-q-option-desc\";\n desc.textContent = opt.description;\n body.appendChild(desc);\n }\n\n btn.appendChild(marker);\n btn.appendChild(body);\n\n if (idx < 9) {\n const key = document.createElement(\"span\");\n key.className = \"qaid-q-option-key\";\n key.textContent = String(idx + 1);\n key.setAttribute(\"aria-hidden\", \"true\");\n btn.appendChild(key);\n }\n\n btn.addEventListener(\"click\", () => toggle(idx));\n\n btn.addEventListener(\"keydown\", (e) => {\n // Native button handles Space/Enter; we add arrow-key roving focus\n // and Enter-to-advance for single-choice.\n if (e.key === \"ArrowDown\" || e.key === \"ArrowRight\") {\n e.preventDefault();\n focusOption((idx + 1) % buttons.length);\n return;\n }\n if (e.key === \"ArrowUp\" || e.key === \"ArrowLeft\") {\n e.preventDefault();\n focusOption((idx - 1 + buttons.length) % buttons.length);\n return;\n }\n if (!q.multiple && e.key === \"Enter\") {\n // If something is already selected, advance instead of toggling\n if (selected.size > 0) {\n e.preventDefault();\n opts.onSubmit();\n }\n }\n });\n\n buttons.push(btn);\n wrap.appendChild(btn);\n });\n\n // Number-key shortcuts (1-9) at the wrap level\n wrap.addEventListener(\"keydown\", (e) => {\n const k = e.key;\n if (k.length === 1 && k >= \"1\" && k <= \"9\") {\n const idx = parseInt(k, 10) - 1;\n if (idx < buttons.length) {\n e.preventDefault();\n toggle(idx);\n focusOption(idx);\n }\n }\n });\n\n function focusOption(idx: number): void {\n buttons[idx]?.focus();\n }\n\n function toggle(idx: number): void {\n const value = q.options[idx].value;\n if (q.multiple) {\n if (selected.has(value)) selected.delete(value);\n else selected.add(value);\n } else {\n selected.clear();\n selected.add(value);\n }\n // Update DOM state\n buttons.forEach((b) => {\n const v = b.dataset.value!;\n const on = selected.has(v);\n b.classList.toggle(\"qaid-q-selected\", on);\n b.setAttribute(\"aria-checked\", on ? \"true\" : \"false\");\n });\n opts.onChange(currentValue());\n if (!q.multiple && opts.autoAdvance) {\n // Tiny delay so the user sees the selection animation before advancing.\n setTimeout(() => opts.onAutoAdvance(), 180);\n }\n }\n\n function currentValue(): AnswerValue {\n if (q.multiple) return Array.from(selected);\n const it = selected.values().next();\n return it.done ? null : it.value;\n }\n\n return {\n element: wrap,\n focus: () => {\n // Focus the first selected option, or the first option overall.\n const firstSelected = buttons.find((b) => b.classList.contains(\"qaid-q-selected\"));\n (firstSelected ?? buttons[0])?.focus();\n },\n getValue: () => currentValue(),\n isValid: () => {\n if (!q.required) return true;\n return selected.size > 0;\n },\n ...createInvalidState(wrap),\n };\n}\n","/**\n * Pure helpers for evaluating per-question visibility predicates.\n *\n * `getVisibleQuestions` filters a questionnaire's flat question array down\n * to those whose `visibleIf` predicate currently evaluates to true. A\n * question with no `visibleIf` is always visible.\n *\n * Predicates are evaluated against the answers map (the same shape the\n * embed maintains internally and posts on submit). They never throw — a\n * predicate referencing an unknown questionId resolves as if that\n * question is unanswered.\n */\n\nimport type {\n AnswerValue,\n Answers,\n Question,\n Questionnaire,\n VisibilityRule,\n} from \"./types\";\n\nexport function getVisibleQuestions(\n q: Questionnaire,\n answers: Answers,\n): Question[] {\n return q.questions.filter((qn) =>\n qn.visibleIf ? evaluateRule(qn.visibleIf, answers) : true,\n );\n}\n\nexport function evaluateRule(\n rule: VisibilityRule,\n answers: Answers,\n): boolean {\n if (\"allOf\" in rule) {\n // Empty allOf is vacuously true; the validator rejects empty arrays\n // at authoring time, so this branch only matters for inline /\n // unvalidated questionnaires.\n for (const r of rule.allOf) {\n if (!evaluateRule(r, answers)) return false;\n }\n return true;\n }\n if (\"anyOf\" in rule) {\n for (const r of rule.anyOf) {\n if (evaluateRule(r, answers)) return true;\n }\n return false;\n }\n\n const v = answers[rule.questionId];\n // Empty string and empty array count as \"not answered\" so a follow-up\n // doesn't flash visible the moment a text input is focused.\n const isAnswered =\n v !== null &&\n v !== undefined &&\n !(typeof v === \"string\" && v.trim() === \"\") &&\n !(Array.isArray(v) && v.length === 0);\n\n if (\"answered\" in rule) return rule.answered ? isAnswered : !isAnswered;\n\n // Value-comparison atoms against an unanswered question return false\n // — including notEquals, so a \"show this when X is not Y\" follow-up\n // doesn't reveal itself before X has been answered at all.\n if (!isAnswered) return false;\n\n if (\"equals\" in rule) return matchesAtomic(v, rule.equals);\n if (\"notEquals\" in rule) return !matchesAtomic(v, rule.notEquals);\n if (\"in\" in rule) return rule.in.some((x) => matchesAtomic(v, x));\n return false;\n}\n\nfunction matchesAtomic(\n answer: AnswerValue,\n target: string | number,\n): boolean {\n // Multi-select multiple-choice answers are arrays; treat equality as\n // \"target is one of the selected values.\"\n if (Array.isArray(answer)) return answer.some((a) => a === target);\n return answer === target;\n}\n","/**\n * Keeping a step readable inside whatever box the host gives us.\n *\n * The card body is the scroll container. Two things were missing: it never\n * adapted its own content to the height available (option tiles have width\n * rules only, so a short container just clipped them), and when content did\n * overflow there was no sign of it at all — the step simply looked cut off.\n *\n * This module owns both. It is DOM-only and free of embed state so it can be\n * driven directly in tests.\n */\n\n/** Where the scroll position currently sits. */\nexport type ScrollEdge = \"none\" | \"top\" | \"middle\" | \"bottom\";\n\n/**\n * Below this many pixels of available height, a step switches to its compact\n * layout: smaller image tiles, tighter gaps. Chosen so the common 2-3 option\n * step fits without scrolling in a ~360px tall container.\n */\nexport const COMPACT_HEIGHT = 360;\n\n/** Ignore sub-pixel rounding when deciding whether we are at an edge. */\nconst EDGE_SLOP = 2;\n\n/** Which edges of `el` have content beyond them. */\nexport function scrollEdgeOf(el: {\n scrollTop: number;\n scrollHeight: number;\n clientHeight: number;\n}): ScrollEdge {\n const overflow = el.scrollHeight - el.clientHeight;\n if (overflow <= EDGE_SLOP) return \"none\";\n if (el.scrollTop <= EDGE_SLOP) return \"top\";\n if (el.scrollTop >= overflow - EDGE_SLOP) return \"bottom\";\n return \"middle\";\n}\n\n/**\n * Reflect the scroll state onto the body as `data-qaid-scroll`, which the\n * stylesheet turns into a fade mask, and onto the cue element's hidden state.\n */\nexport function applyScrollState(body: HTMLElement, cue: HTMLElement | null): ScrollEdge {\n const edge = scrollEdgeOf(body);\n body.setAttribute(\"data-qaid-scroll\", edge);\n if (cue) {\n // The cue only ever means \"there is more below\".\n const more = edge === \"top\" || edge === \"middle\";\n cue.hidden = !more;\n }\n return edge;\n}\n\n/** Reflect the available height onto the card as `data-qaid-fit`. */\nexport function applyFitState(card: HTMLElement, availableHeight: number): \"compact\" | \"roomy\" {\n const fit = availableHeight > 0 && availableHeight < COMPACT_HEIGHT ? \"compact\" : \"roomy\";\n card.setAttribute(\"data-qaid-fit\", fit);\n return fit;\n}\n\nexport interface OverflowWatcher {\n /** Re-measure now — call after rendering a step. */\n refresh(): void;\n /** Detach every listener and observer. */\n destroy(): void;\n}\n\n/**\n * Watch `body` for scrolling and resizing, keeping the scroll and fit state\n * attributes current.\n *\n * A ResizeObserver rather than a CSS container query on height: a container\n * query needs `container-type: size`, which requires a definite height on both\n * axes, and this card is deliberately content-sized when the host does not\n * constrain it. Declaring it would collapse the body to nothing in exactly the\n * inline case the embed supports.\n */\nexport function watchOverflow(\n body: HTMLElement,\n card: HTMLElement,\n cue: HTMLElement | null\n): OverflowWatcher {\n const refresh = () => {\n applyScrollState(body, cue);\n applyFitState(card, body.clientHeight);\n };\n\n body.addEventListener(\"scroll\", refresh, { passive: true });\n\n let observer: ResizeObserver | null = null;\n if (typeof ResizeObserver === \"function\") {\n observer = new ResizeObserver(refresh);\n observer.observe(body);\n }\n\n refresh();\n\n return {\n refresh,\n destroy() {\n body.removeEventListener(\"scroll\", refresh);\n observer?.disconnect();\n },\n };\n}\n","/**\n * Shared accessibility primitives for the qaid embeds.\n *\n * These helpers are intentionally framework-free and self-contained (no CSS\n * dependency) so they can be dropped into any open shadow root. They cover the\n * cross-cutting WCAG 2.2 AA infrastructure called for in the embed\n * accessibility plan: live-region announcements, focus management (trap,\n * save/restore), dialog semantics, and background isolation.\n *\n * The API is deliberately identical to @qaiddev/thumbs-embed's a11y module so\n * both embeds consume one shared surface. In quests-embed specifically:\n * - {@link announce} is a GENERAL-PURPOSE announcer for step-change,\n * loading, and thank-you messages. It is separate from the pre-existing\n * per-step `.qaid-q-error` region (aria-live=\"polite\") in embed.ts — that\n * region stays the owner of field-validation errors. Callers should route\n * step/status text through announce()'s polite region and only use the\n * assertive region for load failures, so the two never clobber each other.\n */\n\n/** Host for the live regions: either a shadow root or a plain element. */\nexport type AnnounceRoot = ShadowRoot | HTMLElement;\n\n/** Options accepted by {@link announce}. */\nexport interface AnnounceOptions {\n /** Route the message through the assertive (role=\"alert\") region. */\n assertive?: boolean;\n}\n\n/** Options accepted by {@link applyDialog}. */\nexport interface ApplyDialogOptions {\n /** id of the element that labels the dialog (wired via aria-labelledby). */\n labelledbyId?: string;\n /** id of the element that describes the dialog (wired via aria-describedby). */\n describedbyId?: string;\n /** Fallback accessible name when no labelling element exists. */\n label?: string;\n}\n\n/** Handle returned by {@link createFocusTrap}. */\nexport interface FocusTrap {\n /** Remove the trap's key handler and clean up any tabindex it added. */\n release(): void;\n}\n\nconst LIVE_ATTR = \"data-qaid-a11y-live\";\n\nconst VISUALLY_HIDDEN_CSS =\n \"position:absolute;width:1px;height:1px;margin:-1px;padding:0;\" +\n \"border:0;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);\" +\n \"white-space:nowrap;\";\n\n/**\n * Selector for natively focusable / author-focusable elements. Elements that\n * opt out with tabindex=\"-1\" are excluded below in {@link getFocusable}.\n */\nconst FOCUSABLE_SELECTOR = [\n \"a[href]\",\n \"button\",\n \"input\",\n \"textarea\",\n \"select\",\n \"[tabindex]\",\n].join(\",\");\n\nfunction ownerDoc(root: AnnounceRoot): Document {\n return root.ownerDocument || document;\n}\n\nfunction getLiveRegion(root: AnnounceRoot, assertive: boolean): HTMLElement {\n const kind = assertive ? \"assertive\" : \"polite\";\n const existing = root.querySelector<HTMLElement>(\n `[${LIVE_ATTR}=\"${kind}\"]`,\n );\n if (existing) return existing;\n\n const doc = ownerDoc(root);\n const region = doc.createElement(\"div\");\n region.setAttribute(LIVE_ATTR, kind);\n region.setAttribute(\"role\", assertive ? \"alert\" : \"status\");\n region.setAttribute(\"aria-live\", assertive ? \"assertive\" : \"polite\");\n region.setAttribute(\"aria-atomic\", \"true\");\n region.style.cssText = VISUALLY_HIDDEN_CSS;\n root.appendChild(region);\n return region;\n}\n\n/**\n * Announce a message to assistive technology via a visually-hidden live region\n * mounted inside `root`. The two regions (polite role=\"status\" and assertive\n * role=\"alert\") are created lazily on first use and reused thereafter.\n *\n * The target region is cleared before the new text is written so that repeated\n * identical messages are re-announced rather than coalesced.\n */\nexport function announce(\n root: AnnounceRoot,\n message: string,\n opts: AnnounceOptions = {},\n): void {\n const region = getLiveRegion(root, !!opts.assertive);\n // Clear-then-set so an identical repeat is still perceived as a change.\n region.textContent = \"\";\n region.textContent = message;\n}\n\nfunction isDisabled(el: HTMLElement): boolean {\n if (el.hasAttribute(\"disabled\")) return true;\n const asInput = el as HTMLElement & { disabled?: boolean };\n return asInput.disabled === true;\n}\n\nfunction isHidden(el: HTMLElement): boolean {\n let node: HTMLElement | null = el;\n while (node) {\n if (node.hasAttribute(\"hidden\")) return true;\n const style = node.style;\n if (\n style &&\n (style.display === \"none\" ||\n style.visibility === \"hidden\" ||\n style.visibility === \"collapse\")\n ) {\n return true;\n }\n node = node.parentElement;\n }\n return false;\n}\n\n/**\n * Collect the focusable descendants of `container` in DOM order, excluding\n * disabled controls, elements opted out with tabindex=\"-1\", and elements that\n * are hidden (via the hidden attribute or an inline display/visibility rule on\n * themselves or an ancestor up to the document root).\n */\nexport function getFocusable(container: HTMLElement): HTMLElement[] {\n const nodes = Array.from(\n container.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR),\n );\n return nodes.filter((el) => {\n if (el.getAttribute(\"tabindex\") === \"-1\") return false;\n if (el instanceof HTMLInputElement && el.type === \"hidden\") return false;\n if (el instanceof HTMLAnchorElement && !el.getAttribute(\"href\")) {\n return false;\n }\n if (isDisabled(el)) return false;\n if (isHidden(el)) return false;\n return true;\n });\n}\n\n/** Resolve the deepest active element, drilling through open shadow roots. */\nfunction getDeepActiveElement(doc: Document): HTMLElement | null {\n let active: Element | null = doc.activeElement;\n while (active && active.shadowRoot && active.shadowRoot.activeElement) {\n active = active.shadowRoot.activeElement;\n }\n return active instanceof HTMLElement ? active : null;\n}\n\n/**\n * Trap keyboard focus inside `container`: Tab / Shift+Tab cycle between the\n * first and last focusable descendants and can never leave the container. On\n * creation focus moves to the first focusable element, or to the container\n * itself (made programmatically focusable) when it has no focusable children.\n *\n * Call {@link FocusTrap.release} to remove the handler; pair it with\n * {@link restoreFocus} to return focus to the invoking control.\n */\nexport function createFocusTrap(container: HTMLElement): FocusTrap {\n const doc = container.ownerDocument || document;\n let addedTabindex = false;\n\n const onKeydown = (event: KeyboardEvent): void => {\n if (event.key !== \"Tab\") return;\n\n const focusable = getFocusable(container);\n if (focusable.length === 0) {\n // Nothing to move to — keep focus pinned on the container.\n event.preventDefault();\n container.focus();\n return;\n }\n\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n const active = getDeepActiveElement(doc);\n const withinTrap = active ? focusable.indexOf(active) !== -1 : false;\n\n if (event.shiftKey) {\n if (!withinTrap || active === first) {\n event.preventDefault();\n last.focus();\n }\n } else {\n if (!withinTrap || active === last) {\n event.preventDefault();\n first.focus();\n }\n }\n };\n\n container.addEventListener(\"keydown\", onKeydown);\n\n const initial = getFocusable(container);\n if (initial.length > 0) {\n initial[0].focus();\n } else {\n if (!container.hasAttribute(\"tabindex\")) {\n container.setAttribute(\"tabindex\", \"-1\");\n addedTabindex = true;\n }\n container.focus();\n }\n\n return {\n release(): void {\n container.removeEventListener(\"keydown\", onKeydown);\n if (addedTabindex) {\n container.removeAttribute(\"tabindex\");\n addedTabindex = false;\n }\n },\n };\n}\n\n/**\n * Capture the currently focused element (drilling through shadow roots) so it\n * can be restored later with {@link restoreFocus}. Returns null when focus is\n * on nothing focusable.\n */\nexport function saveFocus(): HTMLElement | null {\n return getDeepActiveElement(document);\n}\n\n/**\n * Restore focus to a previously saved element. Safe to call with null or an\n * element that has since been detached (a missing/throwing focus is ignored).\n */\nexport function restoreFocus(el: HTMLElement | null): void {\n if (!el || typeof el.focus !== \"function\") return;\n try {\n el.focus();\n } catch {\n // The element may have been removed; nothing more to do.\n }\n}\n\n/**\n * Apply dialog semantics to `el`: role=\"dialog\", aria-modal=\"true\", and the\n * naming/description wiring described by `opts`. Prefer aria-labelledby /\n * aria-describedby pointing at visible title/subtitle elements; fall back to\n * aria-label when no visible label element exists.\n */\nexport function applyDialog(\n el: HTMLElement,\n opts: ApplyDialogOptions = {},\n): void {\n el.setAttribute(\"role\", \"dialog\");\n el.setAttribute(\"aria-modal\", \"true\");\n if (opts.labelledbyId) {\n el.setAttribute(\"aria-labelledby\", opts.labelledbyId);\n }\n if (opts.describedbyId) {\n el.setAttribute(\"aria-describedby\", opts.describedbyId);\n }\n if (opts.label) {\n el.setAttribute(\"aria-label\", opts.label);\n }\n}\n\n/** Walk up to the body-level ancestor of `el`, crossing shadow boundaries. */\nfunction topLevelAncestor(\n el: HTMLElement,\n body: HTMLElement,\n): HTMLElement | null {\n let node: Node | null = el;\n while (node && node !== body) {\n const parent: Node | null = node.parentNode;\n if (parent === body) return node as HTMLElement;\n if (parent && (parent as ShadowRoot).host) {\n node = (parent as ShadowRoot).host;\n continue;\n }\n if (!parent) return null;\n node = parent;\n }\n return null;\n}\n\n/**\n * Isolate the background from assistive technology while a dialog is open by\n * marking every top-level sibling of the dialog inert (with an aria-hidden\n * fallback). `except` is the open dialog (or any element inside it); the\n * top-level element that contains it is left interactive.\n *\n * Returns a restore function that reverts every attribute this call changed,\n * leaving elements that were already inert/hidden untouched.\n */\nexport function setBackgroundInert(except: HTMLElement): () => void {\n const doc = except.ownerDocument || document;\n const body = doc.body;\n if (!body) return () => {};\n\n const keep = topLevelAncestor(except, body);\n const changed: Array<{\n el: HTMLElement;\n prevInert: boolean;\n prevAriaHidden: string | null;\n }> = [];\n\n Array.from(body.children).forEach((child) => {\n if (!(child instanceof HTMLElement)) return;\n if (keep && child === keep) return;\n\n const prevInert = child.inert === true;\n const prevAriaHidden = child.getAttribute(\"aria-hidden\");\n // Only record (and later revert) elements we actually change.\n if (prevInert && prevAriaHidden === \"true\") return;\n\n child.inert = true;\n child.setAttribute(\"aria-hidden\", \"true\");\n changed.push({ el: child, prevInert, prevAriaHidden });\n });\n\n return function restore(): void {\n while (changed.length) {\n const entry = changed.pop();\n if (!entry) continue;\n entry.el.inert = entry.prevInert;\n if (entry.prevAriaHidden === null) {\n entry.el.removeAttribute(\"aria-hidden\");\n } else {\n entry.el.setAttribute(\"aria-hidden\", entry.prevAriaHidden);\n }\n }\n };\n}\n","import type {\n Answers,\n AnswerValue,\n CreateResponseResult,\n Question,\n Questionnaire,\n QuestsConfig,\n ResolvedQuestsConfig,\n ResolvedQuestTheme,\n} from \"./types\";\nimport { applyCssVars, buildCssVars, getEmbedStyles, getPresetCss } from \"./styles\";\nimport { createInput, type QuestionInput } from \"./inputs\";\nimport { getVisibleQuestions } from \"./visibility\";\nimport { watchOverflow, type OverflowWatcher } from \"./overflow\";\nimport {\n announce,\n applyDialog,\n createFocusTrap,\n restoreFocus,\n saveFocus,\n setBackgroundInert,\n type FocusTrap,\n} from \"./a11y\";\n\nconst VISITOR_ID_KEY = \"qaid_visitor_id\";\n\ntype EmbedState = \"LOADING\" | \"READY\" | \"DONE\" | \"ERROR\";\n\nfunction getOrCreateVisitorId(): string {\n try {\n let id = localStorage.getItem(VISITOR_ID_KEY);\n if (!id) {\n id = crypto.randomUUID();\n localStorage.setItem(VISITOR_ID_KEY, id);\n }\n return id;\n } catch {\n return crypto.randomUUID();\n }\n}\n\nconst CLOSE_ICON = `<svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" aria-hidden=\"true\"><path d=\"M2 2l10 10M12 2L2 12\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" fill=\"none\"/></svg>`;\nconst CHECK_ICON = `<svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"M5 12l4 4L19 7\" stroke=\"currentColor\" stroke-width=\"3\" stroke-linecap=\"round\" stroke-linejoin=\"round\" fill=\"none\"/></svg>`;\n\nexport class QaidQuests {\n private config: ResolvedQuestsConfig;\n private questionnaire: Questionnaire | null = null;\n private inlineQuestionnaire: Questionnaire | undefined;\n private configUrl: string | undefined;\n\n private state: EmbedState = \"LOADING\";\n private stepIndex = 0;\n // Cached subset of questionnaire.questions that are currently visible\n // given the answers so far. Recomputed on every answer change so\n // stepIndex always indexes into THIS list, not the raw question array.\n private visibleQuestions: Question[] = [];\n // Latched goToStep request when called before init finishes. Applied\n // once the questionnaire + visible list are ready.\n private pendingGoToStep: string | null = null;\n // Latched update() request, for the same reason: a host that swaps the\n // questionnaire while init is still in flight would otherwise have its\n // questionnaire overwritten by the one init is already loading.\n private pendingUpdate: Questionnaire | null = null;\n // Consumed by the next renderStep(). Set by update(), because a host\n // swapping the questionnaire is not the reader navigating: pulling focus\n // into the form would take it off whatever they are actually using — in\n // an editor preview, the field they are typing the questionnaire into.\n private suppressStepFocus = false;\n // True after the first step has rendered. Used so the focus-on-step\n // logic in renderStep() can distinguish initial mount (driven by the\n // `autoFocus` config) from subsequent step changes (always focused so\n // keyboard + screen-reader users land on the active input after Next\n // / Back).\n private hasRenderedStep = false;\n private answers: Answers = {};\n private responseId: string | number | null = null;\n private visitorId: string;\n\n // Per-question pending autosave (text/currency are debounced)\n private pendingSaveTimer: ReturnType<typeof setTimeout> | null = null;\n private pendingSaveQuestionId: string | null = null;\n private pendingSaveValue: AnswerValue = null;\n private inflightSaves = 0;\n\n // Shadow DOM\n private shadowHost: HTMLDivElement | null = null;\n private shadowRoot: ShadowRoot | null = null;\n private isUserContainer = false;\n\n // Mounted UI elements\n private rootEl: HTMLDivElement | null = null;\n private cardEl: HTMLDivElement | null = null;\n private titleEl: HTMLHeadingElement | null = null;\n private stepCounterEl: HTMLDivElement | null = null;\n private progressEl: HTMLDivElement | null = null;\n private progressFillEl: HTMLDivElement | null = null;\n private bodyEl: HTMLDivElement | null = null;\n private scrollCueEl: HTMLDivElement | null = null;\n private overflow: OverflowWatcher | null = null;\n private footerEl: HTMLDivElement | null = null;\n private savingEl: HTMLDivElement | null = null;\n private backdropEl: HTMLDivElement | null = null;\n private currentInput: QuestionInput | null = null;\n private boundKeyDown: (e: KeyboardEvent) => void;\n\n // Modal-mode a11y plumbing (never set in inline/container mode, which\n // keeps normal page tab flow and never traps or restores focus).\n private savedOpener: HTMLElement | null = null;\n private focusTrap: FocusTrap | null = null;\n private inertRestore: (() => void) | null = null;\n\n private cssVars: Record<string, string> = {};\n\n // Track which theme-related fields the host explicitly set so the\n // theme document can fill the rest. A `themeUrl` / `themeDocument`\n // theme is layered between the embed's defaults and the host's\n // explicit overrides, so the host always wins.\n private hostThemeOverrides: {\n preset: boolean;\n mode: boolean;\n unstyled: boolean;\n css: boolean;\n };\n // Raw, host-provided values for the tokens we inline as inline-style\n // CSS vars on rootEl. Anything `undefined` here is left out of the\n // inline write so theme tokens (which arrive as `:where(...) { ... }`\n // rules) can win the cascade.\n private hostInlineVars: {\n accentColor?: string;\n errorColor?: string;\n focusColor?: string;\n modalWidth?: number;\n backdropOpacity?: number;\n fontFamily?: string;\n fontSize?: number;\n };\n private themeUrl: string | undefined;\n private themeDocument: ResolvedQuestTheme | undefined;\n private hostCss: string;\n\n // Host integration hooks (see QuestsConfig). Kept off the resolved\n // value-config since they're behaviour, not render tokens.\n private metadata: Record<string, unknown> | undefined;\n private onCompleteCb: ((answers: Answers) => void) | undefined;\n private onCloseCb: (() => void) | undefined;\n // Guards onClose to fire exactly once even if destroy() runs twice.\n private closed = false;\n\n constructor(config: QuestsConfig) {\n this.config = {\n endpoint: config.endpoint,\n apiKey: config.apiKey ?? \"\",\n container: config.container ?? \"\",\n zIndex: config.zIndex ?? 50,\n // Prefer the new quest-only keys; fall back to the deprecated\n // thumbs-style keys (positive/negative/marker) so existing\n // integrations keep rendering until they migrate.\n colors: {\n accent: config.colors?.accent ?? config.colors?.positive ?? \"#10b981\",\n error: config.colors?.error ?? config.colors?.negative ?? \"#ef4444\",\n focus: config.colors?.focus ?? config.colors?.marker ?? \"#6366f1\",\n },\n modalWidth: config.modalWidth ?? 480,\n backdropOpacity: config.backdropOpacity ?? 0.4,\n fontFamily: config.fontFamily ?? \"system-ui, -apple-system, sans-serif\",\n fontSize: config.fontSize ?? 16,\n css: config.css ?? \"\",\n autoAdvance: config.autoAdvance ?? false,\n saveDebounceMs: config.saveDebounceMs ?? 500,\n autoFocus: config.autoFocus ?? true,\n animate: config.animate ?? true,\n // Don't default here — we want to fall through to the\n // questionnaire's `progressPosition` when the host didn't\n // explicitly set one. effectiveProgressPosition() handles the\n // final fallback to \"top\".\n progressPosition: config.progressPosition,\n theme: config.theme ?? \"auto\",\n unstyled: config.unstyled ?? false,\n preset: config.preset ?? \"default\",\n };\n\n this.hostThemeOverrides = {\n preset: config.preset !== undefined,\n mode: config.theme !== undefined,\n unstyled: config.unstyled !== undefined,\n css: config.css !== undefined,\n };\n this.hostInlineVars = {\n accentColor: config.colors?.accent ?? config.colors?.positive,\n errorColor: config.colors?.error ?? config.colors?.negative,\n focusColor: config.colors?.focus ?? config.colors?.marker,\n modalWidth: config.modalWidth,\n backdropOpacity: config.backdropOpacity,\n fontFamily: config.fontFamily,\n fontSize: config.fontSize,\n };\n this.themeUrl = config.themeUrl;\n this.themeDocument = config.themeDocument;\n this.hostCss = config.css ?? \"\";\n\n this.metadata = config.metadata;\n this.onCompleteCb = config.onComplete;\n this.onCloseCb = config.onClose;\n\n this.inlineQuestionnaire = config.questionnaire;\n this.configUrl = config.configUrl;\n this.visitorId = getOrCreateVisitorId();\n this.boundKeyDown = this.handleKeyDown.bind(this);\n\n this.init();\n }\n\n private async init(): Promise<void> {\n // Inline only the vars the host explicitly passed, so theme\n // documents can supply the rest via the cascade.\n this.cssVars = buildCssVars(this.hostInlineVars);\n\n this.mountShell();\n this.renderLoading();\n\n try {\n // Fetch the questionnaire and the (optional) theme document in\n // parallel so the theme never blocks the form. A failed theme\n // fetch is non-fatal — we just render with defaults.\n const [q, themeDoc] = await Promise.all([\n this.loadQuestionnaire(),\n this.loadTheme(),\n ]);\n if (!q || !q.questions || q.questions.length === 0) {\n throw new Error(\"Questionnaire is empty\");\n }\n // A questionnaire handed to update() while this was in flight wins:\n // it is newer than the one we were already loading.\n this.questionnaire = this.pendingUpdate ?? q;\n this.pendingUpdate = null;\n this.applyResolvedTheme(themeDoc);\n this.recomputeVisible();\n // Apply any goToStep request that came in before init finished.\n if (this.pendingGoToStep) {\n const idx = this.visibleQuestions.findIndex(\n (qn) => qn.id === this.pendingGoToStep,\n );\n if (idx !== -1) this.stepIndex = idx;\n this.pendingGoToStep = null;\n }\n // Fire-and-forget create. The form is usable immediately;\n // saves will queue until the response id arrives.\n this.createResponse();\n this.state = \"READY\";\n this.renderHeader();\n this.renderStep();\n // Arm the modal focus trap + background isolation once the first\n // step exists. Done synchronously after renderStep so the trap's\n // initial focus lands first and renderStep's own rAF(input.focus)\n // then wins, leaving the intended field focused.\n this.setupModalA11y();\n } catch (err) {\n this.state = \"ERROR\";\n this.renderError(err);\n }\n }\n\n // ------------------------------------------------------------------\n // Theme document loading + merging\n // ------------------------------------------------------------------\n\n private async loadTheme(): Promise<ResolvedQuestTheme | null> {\n if (this.themeDocument) return this.themeDocument;\n if (!this.themeUrl) return null;\n try {\n const res = await fetch(this.themeUrl, {\n headers: { Accept: \"application/json\" },\n });\n if (!res.ok) {\n console.warn(\n `[quests-embed] theme fetch failed (${res.status}) — rendering with defaults`,\n );\n return null;\n }\n return (await res.json()) as ResolvedQuestTheme;\n } catch (err) {\n console.warn(\"[quests-embed] theme fetch error — rendering with defaults\", err);\n return null;\n }\n }\n\n /**\n * Merge a fetched theme document into the live config, then inject\n * the resulting CSS layers + class toggles into the shadow root.\n *\n * Precedence (highest first):\n * 1. Host's explicit `QuestsConfig` fields (preset/theme/unstyled/css)\n * 2. Theme document fields (preset/mode/unstyled/tokens/css)\n * 3. Embed defaults\n *\n * CSS layer order (later wins for same-specificity rules):\n * base stylesheet → theme.tokens block → preset CSS → theme.css → host css\n */\n private applyResolvedTheme(themeDoc: ResolvedQuestTheme | null): void {\n if (!this.shadowRoot || !this.rootEl) return;\n\n const finalPreset = this.hostThemeOverrides.preset\n ? this.config.preset\n : (themeDoc?.preset ?? this.config.preset);\n const finalMode = this.hostThemeOverrides.mode\n ? this.config.theme\n : (themeDoc?.mode ?? this.config.theme);\n const finalUnstyled = this.hostThemeOverrides.unstyled\n ? this.config.unstyled\n : (themeDoc?.unstyled ?? this.config.unstyled);\n\n // Toggle the theme/unstyled classes on rootEl. We don't touch\n // qaid-q-inline / qaid-q-no-motion which were set in mountShell.\n this.rootEl.classList.toggle(\n \"qaid-q-theme-light\",\n finalMode === \"light\",\n );\n this.rootEl.classList.toggle(\n \"qaid-q-theme-dark\",\n finalMode === \"dark\",\n );\n this.rootEl.classList.toggle(\"qaid-q-unstyled\", !!finalUnstyled);\n\n // Build the merged stylesheet. Each layer is appended only when\n // there's something to add so the shadow root stays clean.\n const layers: string[] = [];\n\n if (themeDoc?.tokens && Object.keys(themeDoc.tokens).length > 0) {\n const body = Object.entries(themeDoc.tokens)\n .filter(([, v]) => typeof v === \"string\" && v.trim() !== \"\")\n .map(([k, v]) => ` ${k}: ${v};`)\n .join(\"\\n\");\n if (body) layers.push(`:where(.qaid-q-root) {\\n${body}\\n}`);\n }\n\n if (finalPreset && finalPreset !== \"default\") {\n const presetCss = getPresetCss(finalPreset);\n if (presetCss) layers.push(presetCss);\n }\n\n if (themeDoc?.css && themeDoc.css.trim() !== \"\") {\n layers.push(themeDoc.css);\n }\n\n if (this.hostCss && this.hostCss.trim() !== \"\") {\n layers.push(this.hostCss);\n }\n\n if (layers.length > 0) {\n const themeStyle = document.createElement(\"style\");\n themeStyle.setAttribute(\"data-qaid-q-theme\", \"\");\n themeStyle.textContent = layers.join(\"\\n\\n\");\n this.shadowRoot.appendChild(themeStyle);\n }\n }\n\n // ------------------------------------------------------------------\n // Loading the questionnaire JSON\n // ------------------------------------------------------------------\n\n private async loadQuestionnaire(): Promise<Questionnaire> {\n if (this.inlineQuestionnaire) return this.inlineQuestionnaire;\n if (!this.configUrl) {\n throw new Error(\"No questionnaire or configUrl provided\");\n }\n const res = await fetch(this.configUrl, {\n headers: { Accept: \"application/json\" },\n });\n if (!res.ok) {\n throw new Error(`Failed to load questionnaire (${res.status})`);\n }\n return (await res.json()) as Questionnaire;\n }\n\n // ------------------------------------------------------------------\n // Shell (shadow host + card chrome that's stable across steps)\n // ------------------------------------------------------------------\n\n private mountShell(): void {\n this.shadowHost = document.createElement(\"div\");\n this.shadowHost.setAttribute(\"data-qaid-quests\", \"\");\n\n const userContainer = this.config.container\n ? document.querySelector<HTMLElement>(this.config.container)\n : null;\n\n if (userContainer) {\n this.shadowHost.style.display = \"block\";\n // Fill the user-supplied container vertically so the card can\n // flex/shrink-to-fit when the host has a definite height. When\n // the container is content-sized (no fixed/min height), this is\n // a no-op — the chain just grows naturally as before.\n this.shadowHost.style.height = \"100%\";\n this.shadowHost.style.minHeight = \"0\";\n userContainer.appendChild(this.shadowHost);\n this.isUserContainer = true;\n } else {\n // Modal mode: remember the control that opened us so focus can be\n // restored on close/destroy (WCAG 2.4.3).\n this.savedOpener = saveFocus();\n // Modal mode: shadow host is fixed and full-viewport.\n this.shadowHost.style.position = \"fixed\";\n this.shadowHost.style.inset = \"0\";\n this.shadowHost.style.zIndex = String(this.config.zIndex);\n this.shadowHost.style.pointerEvents = \"none\";\n document.body.appendChild(this.shadowHost);\n }\n\n this.shadowRoot = this.shadowHost.attachShadow({ mode: \"open\" });\n\n const baseStyle = document.createElement(\"style\");\n baseStyle.textContent = getEmbedStyles();\n this.shadowRoot.appendChild(baseStyle);\n\n // Theme-derived styling (preset CSS, tokens block, theme.css,\n // host css) and theme/unstyled classes are deferred to\n // applyResolvedTheme() — that runs after the parallel theme +\n // questionnaire fetch resolves so a remote theme document can\n // contribute. Until then we render with embed defaults.\n\n this.rootEl = document.createElement(\"div\");\n this.rootEl.className = [\n \"qaid-q-root\",\n this.isUserContainer ? \"qaid-q-inline\" : \"\",\n this.config.animate ? \"\" : \"qaid-q-no-motion\",\n ]\n .filter(Boolean)\n .join(\" \");\n applyCssVars(this.rootEl, this.cssVars);\n\n if (!this.isUserContainer) {\n this.backdropEl = document.createElement(\"div\");\n this.backdropEl.className = \"qaid-q-backdrop\";\n this.backdropEl.addEventListener(\"click\", () => this.close());\n this.rootEl.appendChild(this.backdropEl);\n\n const modal = document.createElement(\"div\");\n modal.className = \"qaid-q-modal\";\n this.cardEl = document.createElement(\"div\");\n this.cardEl.className = \"qaid-q-card\";\n modal.appendChild(this.cardEl);\n this.rootEl.appendChild(modal);\n } else {\n this.cardEl = document.createElement(\"div\");\n this.cardEl.className = \"qaid-q-card\";\n this.rootEl.appendChild(this.cardEl);\n }\n\n this.shadowRoot.appendChild(this.rootEl);\n\n // Modal mode needs pointer-events on host so the modal itself is clickable.\n if (!this.isUserContainer && this.shadowHost) {\n this.shadowHost.style.pointerEvents = \"auto\";\n }\n\n // Modal mode is a dialog; inline (container) mode stays a plain\n // region. The accessible name (aria-labelledby / aria-label) is\n // wired in renderHeader once the title element exists.\n if (!this.isUserContainer && this.cardEl) {\n applyDialog(this.cardEl);\n }\n\n document.addEventListener(\"keydown\", this.boundKeyDown);\n }\n\n private renderLoading(): void {\n if (!this.cardEl) return;\n this.cardEl.replaceChildren();\n const msg = document.createElement(\"p\");\n msg.className = \"qaid-q-description\";\n msg.textContent = \"Loading…\";\n this.cardEl.appendChild(msg);\n if (this.shadowRoot) announce(this.shadowRoot, \"Loading\");\n }\n\n private renderError(err: unknown): void {\n if (!this.cardEl) return;\n this.cardEl.replaceChildren();\n const heading = document.createElement(\"h3\");\n heading.className = \"qaid-q-title\";\n heading.textContent = \"Couldn't load form\";\n const detail = err instanceof Error ? err.message : String(err);\n const msg = document.createElement(\"p\");\n msg.className = \"qaid-q-description\";\n msg.textContent = detail;\n this.cardEl.appendChild(heading);\n this.cardEl.appendChild(msg);\n if (this.shadowRoot) {\n announce(this.shadowRoot, `Couldn't load form. ${detail}`, {\n assertive: true,\n });\n }\n }\n\n private effectiveProgressPosition(): \"top\" | \"bottom\" {\n return (\n this.config.progressPosition ??\n this.questionnaire?.progressPosition ??\n \"top\"\n );\n }\n\n private renderHeader(): void {\n if (!this.cardEl || !this.questionnaire) return;\n this.cardEl.replaceChildren();\n\n // A single-question form has nothing meaningful to track, so we\n // suppress the progress chrome whether or not the author asked for\n // it explicitly via `hideProgress`. Branching forms with one\n // *currently visible* question still show progress because more\n // questions can appear after the gate is answered.\n const hideProgress =\n !!this.questionnaire.hideProgress ||\n this.questionnaire.questions.length <= 1;\n const isBottom =\n !hideProgress && this.effectiveProgressPosition() === \"bottom\";\n // `hideTitle` is the dashboard's \"Hide Header\" toggle — it hides\n // both the title row and the description, since they live together\n // in the shared header area.\n const headerHidden = !!this.questionnaire.hideTitle;\n const hasTitle = !!this.questionnaire.title && !headerHidden;\n const hasDescription = !!this.questionnaire.description && !headerHidden;\n const hasCloseBtn = !this.isUserContainer;\n // Only render content into the right-hand side of the header when\n // we'd actually have something to put there.\n const headerHasRightContent =\n hasCloseBtn || (!isBottom && !hideProgress);\n\n // These three are created up front and reused across step renders.\n // In top mode they slot into the header / above the body; in bottom\n // mode the footer in renderStep() pulls them into its own row.\n this.savingEl = document.createElement(\"div\");\n this.savingEl.className = \"qaid-q-saving\";\n const dot = document.createElement(\"span\");\n dot.className = \"qaid-q-saving-dot\";\n const savingText = document.createElement(\"span\");\n savingText.textContent = \"Saving…\";\n this.savingEl.appendChild(dot);\n this.savingEl.appendChild(savingText);\n\n this.stepCounterEl = document.createElement(\"div\");\n this.stepCounterEl.className = \"qaid-q-step-counter\";\n\n this.progressEl = document.createElement(\"div\");\n this.progressEl.className = isBottom\n ? \"qaid-q-progress qaid-q-progress--inline\"\n : \"qaid-q-progress\";\n this.progressFillEl = document.createElement(\"div\");\n this.progressFillEl.className = \"qaid-q-progress-fill\";\n this.progressEl.appendChild(this.progressFillEl);\n\n // Skip the header row entirely when there's nothing to show in it\n // — keeps the form flush at the top of inline embeds with no\n // title/description, hidden progress, or bottom-progress.\n const showHeader = hasTitle || headerHasRightContent;\n if (showHeader) {\n const header = document.createElement(\"div\");\n header.className = \"qaid-q-header\";\n\n const titleWrap = document.createElement(\"div\");\n if (hasTitle) {\n this.titleEl = document.createElement(\"h2\");\n this.titleEl.className = \"qaid-q-title\";\n this.titleEl.textContent = this.questionnaire.title!;\n titleWrap.appendChild(this.titleEl);\n }\n\n const right = document.createElement(\"div\");\n right.style.display = \"flex\";\n right.style.alignItems = \"center\";\n right.style.gap = \"0.5rem\";\n\n // Saving + step counter live in the header in top mode. In bottom\n // mode the footer owns them. When progress is hidden, the step\n // counter is suppressed but the saving indicator still surfaces.\n if (!isBottom) {\n right.appendChild(this.savingEl);\n if (!hideProgress) right.appendChild(this.stepCounterEl);\n }\n\n if (hasCloseBtn) {\n const closeBtn = document.createElement(\"button\");\n closeBtn.type = \"button\";\n closeBtn.className = \"qaid-q-close\";\n closeBtn.setAttribute(\"aria-label\", \"Close form\");\n closeBtn.innerHTML = CLOSE_ICON;\n closeBtn.addEventListener(\"click\", () => this.close());\n right.appendChild(closeBtn);\n }\n\n header.appendChild(titleWrap);\n header.appendChild(right);\n this.cardEl.appendChild(header);\n }\n\n // Give the modal dialog an accessible name: reference the visible\n // title when shown, otherwise fall back to the questionnaire title.\n // Inline mode is not a dialog, so it is skipped.\n if (!this.isUserContainer && this.cardEl) {\n if (this.titleEl) {\n this.titleEl.id = this.titleEl.id || \"qaid-q-dialog-title\";\n this.cardEl.setAttribute(\"aria-labelledby\", this.titleEl.id);\n this.cardEl.removeAttribute(\"aria-label\");\n } else {\n this.cardEl.removeAttribute(\"aria-labelledby\");\n this.cardEl.setAttribute(\n \"aria-label\",\n this.questionnaire.title ?? \"Questionnaire\",\n );\n }\n }\n\n if (hasDescription) {\n const desc = document.createElement(\"p\");\n desc.className = \"qaid-q-description\";\n desc.textContent = this.questionnaire.description!;\n desc.style.marginTop = showHeader ? \"-0.5rem\" : \"0\";\n desc.style.marginBottom = \"0.75rem\";\n this.cardEl.appendChild(desc);\n }\n\n if (!isBottom && !hideProgress) {\n this.cardEl.appendChild(this.progressEl);\n }\n\n this.bodyEl = document.createElement(\"div\");\n this.bodyEl.className = \"qaid-q-body\";\n this.cardEl.appendChild(this.bodyEl);\n\n this.footerEl = document.createElement(\"div\");\n this.footerEl.className = \"qaid-q-footer\";\n this.cardEl.appendChild(this.footerEl);\n\n // \"More below\" cue. Decorative: the body is the scroll region and the\n // footer's Next button is the real affordance, so keep it out of the\n // accessibility tree entirely.\n this.scrollCueEl = document.createElement(\"div\");\n this.scrollCueEl.className = \"qaid-q-scroll-cue\";\n this.scrollCueEl.setAttribute(\"aria-hidden\", \"true\");\n this.scrollCueEl.hidden = true;\n this.scrollCueEl.innerHTML =\n '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\"' +\n ' stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M6 9l6 6 6-6\"/></svg>';\n this.bodyEl.appendChild(this.scrollCueEl);\n\n this.overflow = watchOverflow(this.bodyEl, this.cardEl, this.scrollCueEl);\n }\n\n // ------------------------------------------------------------------\n // Step rendering\n // ------------------------------------------------------------------\n\n private renderStep(): void {\n // Read before the guards so a suppressed render that bails early can't\n // leave the flag set for the next, unrelated one.\n const suppressFocus = this.suppressStepFocus;\n this.suppressStepFocus = false;\n if (!this.questionnaire || !this.bodyEl || !this.footerEl) return;\n\n const total = this.visibleQuestions.length;\n if (total === 0) {\n // No visible questions — should be unreachable when the validator\n // forbids visibleIf on the first question, but guard against\n // misauthored inline questionnaires that gate everything.\n void this.submit();\n return;\n }\n if (this.stepIndex >= total) {\n this.stepIndex = total - 1;\n }\n const idx = this.stepIndex;\n const question = this.visibleQuestions[idx];\n\n if (this.stepCounterEl) {\n this.stepCounterEl.textContent = `${idx + 1} / ${total}`;\n }\n if (this.progressFillEl) {\n const pct = ((idx + 1) / total) * 100;\n this.progressFillEl.style.width = `${pct}%`;\n }\n // Expose progress semantics to assistive tech. valuemax is the count\n // of currently-visible questions (branching can change it), valuenow\n // is 1-based so the ratio matches the visual fill (idx+1)/total.\n if (this.progressEl) {\n this.progressEl.setAttribute(\"role\", \"progressbar\");\n this.progressEl.setAttribute(\"aria-label\", \"Progress\");\n this.progressEl.setAttribute(\"aria-valuemin\", \"0\");\n this.progressEl.setAttribute(\"aria-valuemax\", String(total));\n this.progressEl.setAttribute(\"aria-valuenow\", String(idx + 1));\n this.progressEl.setAttribute(\n \"aria-valuetext\",\n `Question ${idx + 1} of ${total}`,\n );\n }\n // Announce the step change through the shared polite region. Kept\n // distinct from the per-step validation error region below so the\n // two never clobber each other. Skipped for single-question forms\n // where there is no step to move between.\n if (this.shadowRoot && total > 1) {\n announce(this.shadowRoot, `Step ${idx + 1} of ${total}`);\n }\n\n // Build step body\n const step = document.createElement(\"div\");\n step.className = \"qaid-q-step\";\n step.setAttribute(\"role\", \"group\");\n step.setAttribute(\"aria-labelledby\", `qaid-q-label-${idx}`);\n\n const label = document.createElement(\"label\");\n label.id = `qaid-q-label-${idx}`;\n label.className = \"qaid-q-label\";\n label.textContent = question.label;\n if (question.required) {\n const star = document.createElement(\"span\");\n star.className = \"qaid-q-required\";\n star.textContent = \"*\";\n star.setAttribute(\"aria-label\", \"required\");\n label.appendChild(star);\n }\n step.appendChild(label);\n\n if (question.description) {\n const desc = document.createElement(\"p\");\n desc.className = \"qaid-q-description\";\n desc.textContent = question.description;\n step.appendChild(desc);\n }\n\n const errorEl = document.createElement(\"p\");\n errorEl.className = \"qaid-q-error\";\n errorEl.id = `qaid-q-error-${idx}`;\n errorEl.setAttribute(\"aria-live\", \"polite\");\n\n const initialValue = this.answers[question.id] ?? null;\n const isLast = idx === total - 1;\n\n const input = createInput({\n question,\n initialValue,\n onChange: (value) => {\n this.handleAnswerChange(question, value);\n // clearing error if the user starts typing again\n if (errorEl.textContent) {\n errorEl.textContent = \"\";\n this.currentInput?.clearInvalid();\n }\n },\n onSubmit: () => this.advance(question, errorEl),\n onAutoAdvance: () => this.advance(question, errorEl),\n autoAdvance: this.config.autoAdvance,\n });\n this.currentInput = input;\n\n step.appendChild(input.element);\n step.appendChild(errorEl);\n\n this.bodyEl.replaceChildren(step);\n // replaceChildren drops the cue, so put it back and re-measure the new\n // step. The ResizeObserver watches the body's box, which a step with more\n // options does not change — only its scrollHeight — so refresh explicitly.\n if (this.scrollCueEl) this.bodyEl.appendChild(this.scrollCueEl);\n this.bodyEl.scrollTop = 0;\n this.overflow?.refresh();\n\n // Footer\n this.footerEl.replaceChildren();\n const hideProgress =\n !!this.questionnaire.hideProgress ||\n this.questionnaire.questions.length <= 1;\n const isBottomProgress =\n !hideProgress && this.effectiveProgressPosition() === \"bottom\";\n\n const back =\n idx > 0\n ? (() => {\n const b = document.createElement(\"button\");\n b.type = \"button\";\n b.className = \"qaid-q-btn qaid-q-btn-secondary\";\n b.textContent = this.questionnaire!.backLabel ?? \"Back\";\n b.addEventListener(\"click\", () => this.back());\n return b;\n })()\n : null;\n\n const next = document.createElement(\"button\");\n next.type = \"button\";\n next.className = \"qaid-q-btn qaid-q-btn-primary\";\n next.textContent = isLast\n ? (this.questionnaire.submitLabel ?? \"Submit\")\n : (this.questionnaire.nextLabel ?? \"Next\");\n next.addEventListener(\"click\", () => this.advance(question, errorEl));\n\n if (isBottomProgress) {\n // Single-row footer: [back] [progress fills] [savingEl][stepCounter] [next]\n this.footerEl.classList.add(\"qaid-q-footer--inline-progress\");\n if (back) this.footerEl.appendChild(back);\n if (this.progressEl) this.footerEl.appendChild(this.progressEl);\n if (this.savingEl) this.footerEl.appendChild(this.savingEl);\n if (this.stepCounterEl) this.footerEl.appendChild(this.stepCounterEl);\n this.footerEl.appendChild(next);\n } else {\n this.footerEl.classList.remove(\"qaid-q-footer--inline-progress\");\n const left = document.createElement(\"div\");\n left.className = \"qaid-q-footer-left\";\n const right = document.createElement(\"div\");\n right.className = \"qaid-q-footer-right\";\n\n if (back) left.appendChild(back);\n\n right.appendChild(next);\n\n this.footerEl.appendChild(left);\n this.footerEl.appendChild(right);\n }\n\n // Focus policy:\n // - Initial render: only focus when `autoFocus` is enabled.\n // Embedded previews and \"open in page\" usages set autoFocus:false\n // to avoid yanking focus on mount.\n // - Every subsequent step (advance / back): always focus the\n // active input. The user just performed a keyboard or click\n // action expecting to interact with the next question, and\n // screen-reader users need focus to move so the new question's\n // accessible name is announced.\n // - A host-driven `update()`: never, see `suppressStepFocus`.\n const isInitialRender = !this.hasRenderedStep;\n this.hasRenderedStep = true;\n const shouldFocus = suppressFocus\n ? false\n : isInitialRender\n ? this.config.autoFocus\n : true;\n if (shouldFocus) {\n requestAnimationFrame(() => input.focus());\n }\n }\n\n private renderDone(): void {\n if (!this.cardEl || !this.questionnaire) return;\n this.cardEl.replaceChildren();\n\n const done = document.createElement(\"div\");\n done.className = \"qaid-q-done\";\n\n const icon = document.createElement(\"div\");\n icon.className = \"qaid-q-done-icon\";\n icon.innerHTML = CHECK_ICON;\n done.appendChild(icon);\n\n const title = document.createElement(\"h3\");\n title.className = \"qaid-q-done-title\";\n const doneTitle = this.questionnaire.thankYouTitle ?? \"Thank you!\";\n title.textContent = doneTitle;\n // Make the heading programmatically focusable so focus can land on\n // the confirmation once the step controls are gone (WCAG 2.4.3).\n title.setAttribute(\"tabindex\", \"-1\");\n done.appendChild(title);\n\n if (this.questionnaire.thankYouMessage) {\n const msg = document.createElement(\"p\");\n msg.className = \"qaid-q-done-message\";\n msg.textContent = this.questionnaire.thankYouMessage;\n done.appendChild(msg);\n }\n\n if (!this.isUserContainer) {\n const closeBtn = document.createElement(\"button\");\n closeBtn.type = \"button\";\n closeBtn.className = \"qaid-q-btn qaid-q-btn-primary\";\n closeBtn.textContent = \"Close\";\n closeBtn.style.marginTop = \"1rem\";\n closeBtn.addEventListener(\"click\", () => this.close());\n done.appendChild(closeBtn);\n }\n\n this.cardEl.appendChild(done);\n\n // Move focus to the thank-you heading and announce the confirmation\n // (the submit button that had focus was just removed).\n requestAnimationFrame(() => title.focus());\n if (this.shadowRoot) {\n const message = this.questionnaire.thankYouMessage\n ? `${doneTitle}. ${this.questionnaire.thankYouMessage}`\n : doneTitle;\n announce(this.shadowRoot, message);\n }\n }\n\n // ------------------------------------------------------------------\n // Navigation\n // ------------------------------------------------------------------\n\n private advance(question: Question, errorEl: HTMLElement): void {\n if (!this.currentInput || !this.questionnaire) return;\n if (!this.currentInput.isValid()) {\n errorEl.textContent = validationMessage(question);\n // Signal invalidity on the control itself and tie the error text to\n // it via aria-describedby so it is not conveyed by colour alone.\n this.currentInput.setInvalid(errorEl.id);\n this.currentInput.focus();\n return;\n }\n // Capture latest value (e.g. range may not have fired its first change).\n // handleAnswerChange already calls recomputeVisible after recording\n // the answer, so visibleQuestions is up-to-date here.\n this.handleAnswerChange(question, this.currentInput.getValue(), { immediate: true });\n\n const last = this.stepIndex === this.visibleQuestions.length - 1;\n if (last) {\n void this.submit();\n return;\n }\n this.stepIndex++;\n this.renderStep();\n }\n\n private back(): void {\n // Flush any pending save before navigating\n this.flushPendingSave();\n if (this.stepIndex > 0) {\n this.stepIndex--;\n // Defensive: a previous answer may have been changed in a way that\n // shrinks the visible tail. Recompute and clamp.\n this.recomputeVisible();\n if (this.stepIndex >= this.visibleQuestions.length) {\n this.stepIndex = Math.max(0, this.visibleQuestions.length - 1);\n }\n this.renderStep();\n }\n }\n\n // ------------------------------------------------------------------\n // Answer handling + autosave\n // ------------------------------------------------------------------\n\n private recomputeVisible(): void {\n if (!this.questionnaire) return;\n this.visibleQuestions = getVisibleQuestions(this.questionnaire, this.answers);\n }\n\n private handleAnswerChange(\n question: Question,\n value: AnswerValue,\n opts: { immediate?: boolean } = {},\n ): void {\n this.answers[question.id] = value;\n // Visibility may flip on follow-up questions when this answer\n // changes. Recompute synchronously so advance() / back() see the\n // up-to-date list. We don't re-render here — the user is mid-edit.\n this.recomputeVisible();\n\n const debounced =\n question.type === \"text\" ||\n question.type === \"currency\" ||\n question.type === \"range\";\n\n if (opts.immediate || !debounced) {\n this.flushPendingSave();\n this.saveAnswer(question.id, value);\n return;\n }\n\n // Debounce — collapse rapid keystrokes into one PATCH.\n this.pendingSaveQuestionId = question.id;\n this.pendingSaveValue = value;\n if (this.pendingSaveTimer) clearTimeout(this.pendingSaveTimer);\n this.pendingSaveTimer = setTimeout(() => {\n this.flushPendingSave();\n }, this.config.saveDebounceMs);\n }\n\n private flushPendingSave(): void {\n if (this.pendingSaveTimer) {\n clearTimeout(this.pendingSaveTimer);\n this.pendingSaveTimer = null;\n }\n if (this.pendingSaveQuestionId !== null) {\n const id = this.pendingSaveQuestionId;\n const value = this.pendingSaveValue;\n this.pendingSaveQuestionId = null;\n this.pendingSaveValue = null;\n this.saveAnswer(id, value);\n }\n }\n\n private saveAnswer(questionId: string, value: AnswerValue): void {\n this.showSaving(true);\n this.inflightSaves++;\n void this.doSave(questionId, value).finally(() => {\n this.inflightSaves--;\n if (this.inflightSaves === 0) this.showSaving(false);\n });\n }\n\n private async doSave(questionId: string, value: AnswerValue): Promise<void> {\n // Wait for response id to be assigned before sending updates.\n // If creation is still in flight, queue tightly via microtask polling.\n let waited = 0;\n while (this.responseId === null && waited < 5000) {\n await new Promise((r) => setTimeout(r, 50));\n waited += 50;\n }\n if (this.responseId === null) return;\n\n try {\n await fetch(`${this.config.endpoint}/${this.responseId}`, {\n method: \"PATCH\",\n headers: this.jsonHeaders(),\n body: JSON.stringify({ questionId, value }),\n });\n } catch (err) {\n console.error(\"[quests-embed] failed to save answer:\", err);\n }\n }\n\n private async createResponse(): Promise<void> {\n try {\n const res = await fetch(this.config.endpoint, {\n method: \"POST\",\n headers: this.jsonHeaders(),\n body: JSON.stringify({\n apiKey: this.config.apiKey || undefined,\n questId: this.questionnaire?.id,\n pageUrl: window.location.href,\n visitorId: this.visitorId,\n userAgent: navigator.userAgent,\n metadata: this.metadata,\n }),\n });\n if (res.ok) {\n const data = (await res.json()) as CreateResponseResult;\n this.responseId = data.id;\n }\n } catch (err) {\n console.error(\"[quests-embed] failed to create response:\", err);\n }\n }\n\n private async submit(): Promise<void> {\n this.flushPendingSave();\n // Wait for any in-flight saves so the server sees the final state.\n while (this.inflightSaves > 0) {\n await new Promise((r) => setTimeout(r, 50));\n }\n\n if (this.responseId !== null) {\n try {\n await fetch(`${this.config.endpoint}/${this.responseId}/submit`, {\n method: \"POST\",\n headers: this.jsonHeaders(),\n body: JSON.stringify({ answers: this.answers }),\n });\n } catch (err) {\n console.error(\"[quests-embed] failed to submit:\", err);\n }\n }\n\n this.state = \"DONE\";\n this.renderDone();\n\n // Notify the host after the thank-you screen is up. A copy so a\n // callback can't mutate our answer store. Never let a throwing\n // handler break the completed flow.\n if (this.onCompleteCb) {\n try {\n this.onCompleteCb({ ...this.answers });\n } catch (err) {\n console.error(\"[quests-embed] onComplete handler threw:\", err);\n }\n }\n }\n\n private jsonHeaders(): Record<string, string> {\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n };\n if (this.config.apiKey) headers[\"X-API-Key\"] = this.config.apiKey;\n return headers;\n }\n\n private showSaving(visible: boolean): void {\n if (!this.savingEl) return;\n this.savingEl.classList.toggle(\"qaid-q-visible\", visible);\n }\n\n // ------------------------------------------------------------------\n // Keyboard / lifecycle\n // ------------------------------------------------------------------\n\n private handleKeyDown(e: KeyboardEvent): void {\n if (e.key === \"Escape\" && !this.isUserContainer && this.state !== \"DONE\") {\n this.close();\n }\n }\n\n /**\n * Modal mode only: trap Tab within the card and mark the rest of the\n * page inert while the dialog is open. Runs once (guarded) after the\n * first step renders. Inline mode keeps normal page tab flow and is\n * never trapped or isolated.\n */\n private setupModalA11y(): void {\n if (this.isUserContainer || !this.cardEl) return;\n if (this.focusTrap) return;\n this.focusTrap = createFocusTrap(this.cardEl);\n this.inertRestore = setBackgroundInert(this.cardEl);\n }\n\n private close(): void {\n this.flushPendingSave();\n this.destroy();\n }\n\n /** Destroy the embed and clean up all resources */\n public destroy(): void {\n document.removeEventListener(\"keydown\", this.boundKeyDown);\n if (this.pendingSaveTimer) {\n clearTimeout(this.pendingSaveTimer);\n this.pendingSaveTimer = null;\n }\n // Tear down modal-mode a11y plumbing before removing the host:\n // release the focus trap, un-inert the background, then return focus\n // to the control that opened the dialog. All no-ops in inline mode.\n if (this.focusTrap) {\n this.focusTrap.release();\n this.focusTrap = null;\n }\n if (this.inertRestore) {\n this.inertRestore();\n this.inertRestore = null;\n }\n if (!this.isUserContainer && this.savedOpener) {\n restoreFocus(this.savedOpener);\n }\n this.savedOpener = null;\n if (this.shadowHost) {\n this.shadowHost.remove();\n this.shadowHost = null;\n this.shadowRoot = null;\n }\n this.rootEl = null;\n this.cardEl = null;\n this.overflow?.destroy();\n this.overflow = null;\n this.bodyEl = null;\n this.scrollCueEl = null;\n this.footerEl = null;\n this.titleEl = null;\n this.stepCounterEl = null;\n this.progressFillEl = null;\n this.savingEl = null;\n this.backdropEl = null;\n this.currentInput = null;\n\n // Notify the host exactly once that the embed is gone, whatever the\n // teardown path (visitor close, completion, or host-driven destroy).\n if (!this.closed) {\n this.closed = true;\n if (this.onCloseCb) {\n try {\n this.onCloseCb();\n } catch (err) {\n console.error(\"[quests-embed] onClose handler threw:\", err);\n }\n }\n }\n }\n\n /** Read-only snapshot of current answers */\n public getAnswers(): Answers {\n return { ...this.answers };\n }\n\n /**\n * Id of the question currently rendered, or `null` if the embed\n * isn't on a question (still loading, on the thank-you screen, or\n * unmounted). Useful for editor previews that want to restore the\n * user's place after a forced re-mount.\n */\n public getCurrentQuestionId(): string | null {\n if (this.state !== \"READY\") return null;\n const q = this.visibleQuestions[this.stepIndex];\n return q ? q.id : null;\n }\n\n /**\n * Swap in a new questionnaire without tearing the embed down.\n *\n * Built for editor previews, where the alternative — `destroy()` plus a\n * fresh construction on every edit — re-mounts the shadow root, repaints\n * the loading state, and re-runs both the theme fetch and the create\n * call. Because that path awaits the network it always paints a blank\n * frame first, which is what makes a live preview strobe while the author\n * types. This re-renders the header and the current step and nothing\n * else, synchronously: the shadow root, the resolved theme, the response\n * id and the answers so far all survive.\n *\n * Answers are kept for questions that still exist and dropped for ones\n * that don't, so `getAnswers()` never reports an id the questionnaire has\n * no question for. The reader's place is kept the same way: if the\n * question on screen is still present and visible, the embed stays on it,\n * otherwise the step index is clamped into range.\n *\n * Returns whether the update was applied. It is refused, leaving the\n * embed exactly as it was, when the questionnaire has no questions, or\n * once the reader has completed the form — resuming a submitted response\n * is not something this can decide on the host's behalf, so a host that\n * wants the new form there should rebuild. Called before the embed has\n * finished initializing, the update is latched and applied as soon as it\n * is ready (and reported as applied), the same way `goToStep` is.\n */\n public update(questionnaire: Questionnaire): boolean {\n if (!questionnaire?.questions || questionnaire.questions.length === 0) {\n return false;\n }\n if (this.state === \"DONE\") return false;\n if (this.state === \"LOADING\") {\n this.pendingUpdate = questionnaire;\n return true;\n }\n\n // Resolve the incoming questionnaire fully before committing to it, so a\n // refusal below leaves the embed untouched rather than half-swapped.\n const live = new Set(questionnaire.questions.map((q) => q.id));\n const nextAnswers: Answers = {};\n for (const [id, value] of Object.entries(this.answers)) {\n if (live.has(id)) nextAnswers[id] = value;\n }\n const nextVisible = getVisibleQuestions(questionnaire, nextAnswers);\n // Every question gated behind an unmet `visibleIf` leaves nothing to\n // render, and renderStep() reads \"no visible questions\" as \"the reader\n // finished\" and submits. Refuse instead — a host mid-edit has simply not\n // written a reachable first question yet.\n if (nextVisible.length === 0) return false;\n\n // Don't let a debounced PATCH for the outgoing questionnaire land after\n // the incoming one is on screen.\n this.flushPendingSave();\n\n const currentId = this.getCurrentQuestionId();\n this.questionnaire = questionnaire;\n // Keep the inline copy in step, or a later re-init would resurrect the\n // questionnaire this one replaced.\n if (this.inlineQuestionnaire) this.inlineQuestionnaire = questionnaire;\n this.state = \"READY\";\n this.answers = nextAnswers;\n this.visibleQuestions = nextVisible;\n\n const idx = currentId\n ? this.visibleQuestions.findIndex((q) => q.id === currentId)\n : -1;\n this.stepIndex =\n idx !== -1\n ? idx\n : Math.min(this.stepIndex, Math.max(0, this.visibleQuestions.length - 1));\n\n this.suppressStepFocus = true;\n this.renderHeader();\n this.renderStep();\n return true;\n }\n\n /**\n * Jump to the visible question with the given id. Returns true if\n * the question is currently visible (and the embed navigated to\n * it), false if it's hidden by an unmet `visibleIf` predicate or\n * unknown. If the embed is still initializing, the request is\n * latched and applied as soon as the questionnaire is ready.\n *\n * Intended for editor previews and other host-driven step control.\n */\n public goToStep(questionId: string): boolean {\n if (this.state !== \"READY\" || !this.questionnaire) {\n this.pendingGoToStep = questionId;\n return false;\n }\n const idx = this.visibleQuestions.findIndex((q) => q.id === questionId);\n if (idx === -1) return false;\n if (idx === this.stepIndex) return true;\n this.flushPendingSave();\n this.stepIndex = idx;\n this.renderStep();\n return true;\n }\n}\n\nfunction validationMessage(q: Question): string {\n switch (q.type) {\n case \"text\":\n if (q.minLength) return `Please enter at least ${q.minLength} characters.`;\n return \"This field is required.\";\n case \"currency\":\n return \"Please enter a valid amount.\";\n case \"date\":\n return \"Please pick a date.\";\n case \"multiple-choice\":\n return q.multiple ? \"Please select at least one option.\" : \"Please select an option.\";\n case \"range\":\n return \"Please choose a value.\";\n }\n}\n","/**\n * @qaiddev/quests-embed\n *\n * Step-by-step JSON-defined form embed.\n *\n * Usage as ES module:\n * ```ts\n * import { QaidQuests } from \"@qaiddev/quests-embed\";\n *\n * new QaidQuests({\n * endpoint: \"/api/responses\",\n * configUrl: \"/forms/intake.json\",\n * container: \"#form\",\n * });\n * ```\n *\n * Usage via script tag (auto-init):\n * ```html\n * <script src=\"qaid-quests.umd.cjs\"\n * data-endpoint=\"/api/responses\"\n * data-config-url=\"/forms/intake.json\"\n * data-container=\"#form\"></script>\n * ```\n *\n * Or with a JSON config tag:\n * ```html\n * <script type=\"application/json\" data-quests-config>\n * { \"endpoint\": \"/api/responses\", \"configUrl\": \"/forms/intake.json\" }\n * </script>\n * <script src=\"qaid-quests.umd.cjs\"></script>\n * ```\n */\n\nimport { QaidQuests } from \"./embed\";\nimport type { QuestsConfig } from \"./types\";\n\nexport { QaidQuests };\nexport { getVisibleQuestions, evaluateRule } from \"./visibility\";\nexport type {\n QuestsConfig,\n ResolvedQuestsConfig,\n Questionnaire,\n Question,\n TextQuestion,\n CurrencyQuestion,\n RangeQuestion,\n DateQuestion,\n MultipleChoiceQuestion,\n MultipleChoiceOption,\n Answers,\n AnswerValue,\n VisibilityRule,\n CreateResponsePayload,\n CreateResponseResult,\n UpdateAnswerPayload,\n SubmitPayload,\n} from \"./types\";\n\nfunction findCssFromSelector(selector: string): string {\n const el = document.querySelector(selector);\n return el?.textContent?.trim() ?? \"\";\n}\n\nfunction parseJsonConfig(): Partial<QuestsConfig> | null {\n const tag = document.querySelector(\n 'script[type=\"application/json\"][data-quests-config]',\n );\n if (!tag) return null;\n const raw = tag.textContent?.trim();\n if (!raw) return null;\n try {\n const parsed = JSON.parse(raw) as Partial<QuestsConfig> & {\n cssSelector?: string;\n };\n if (parsed.cssSelector && !parsed.css) {\n parsed.css = findCssFromSelector(parsed.cssSelector);\n delete parsed.cssSelector;\n }\n return parsed;\n } catch {\n return null;\n }\n}\n\nfunction parseDataAttributes(\n script: HTMLScriptElement,\n): Partial<QuestsConfig> | null {\n const endpoint = script.getAttribute(\"data-endpoint\");\n if (!endpoint) return null;\n\n const configUrl = script.getAttribute(\"data-config-url\");\n const apiKey = script.getAttribute(\"data-api-key\");\n const container = script.getAttribute(\"data-container\");\n const zIndex = script.getAttribute(\"data-zindex\");\n const positiveColor = script.getAttribute(\"data-positive-color\");\n const negativeColor = script.getAttribute(\"data-negative-color\");\n const markerColor = script.getAttribute(\"data-marker-color\");\n const modalWidth = script.getAttribute(\"data-modal-width\");\n const backdropOpacity = script.getAttribute(\"data-backdrop-opacity\");\n const fontFamily = script.getAttribute(\"data-font-family\");\n const fontSize = script.getAttribute(\"data-font-size\");\n const cssSelector = script.getAttribute(\"data-css-selector\");\n const autoAdvance = script.getAttribute(\"data-auto-advance\");\n const saveDebounceMs = script.getAttribute(\"data-save-debounce-ms\");\n const themeUrl = script.getAttribute(\"data-theme-url\");\n const preset = script.getAttribute(\"data-preset\");\n const themeMode = script.getAttribute(\"data-theme\");\n const unstyled = script.getAttribute(\"data-unstyled\");\n\n return {\n endpoint,\n configUrl: configUrl ?? undefined,\n apiKey: apiKey ?? undefined,\n container: container ?? undefined,\n zIndex: zIndex ? parseInt(zIndex, 10) : undefined,\n colors: {\n positive: positiveColor ?? undefined,\n negative: negativeColor ?? undefined,\n marker: markerColor ?? undefined,\n },\n modalWidth: modalWidth ? parseInt(modalWidth, 10) : undefined,\n backdropOpacity: backdropOpacity ? parseFloat(backdropOpacity) : undefined,\n fontFamily: fontFamily ?? undefined,\n fontSize: fontSize ? parseInt(fontSize, 10) : undefined,\n css: cssSelector ? findCssFromSelector(cssSelector) : undefined,\n autoAdvance: autoAdvance === \"true\" ? true : undefined,\n saveDebounceMs: saveDebounceMs ? parseInt(saveDebounceMs, 10) : undefined,\n themeUrl: themeUrl ?? undefined,\n preset: preset ? (preset as \"default\" | \"minimal\" | \"pill\" | \"dense\") : undefined,\n theme: themeMode ? (themeMode as \"light\" | \"dark\" | \"auto\") : undefined,\n unstyled: unstyled === \"true\" ? true : undefined,\n };\n}\n\nif (typeof document !== \"undefined\") {\n const initFromScript = (): void => {\n const script = document.currentScript as HTMLScriptElement | null;\n const jsonConfig = parseJsonConfig();\n const dataConfig = script ? parseDataAttributes(script) : null;\n const config = jsonConfig ?? dataConfig;\n if (config?.endpoint && (config.configUrl || config.questionnaire)) {\n new QaidQuests(config as QuestsConfig);\n }\n };\n\n if (document.readyState === \"loading\") {\n document.addEventListener(\"DOMContentLoaded\", initFromScript);\n } else {\n initFromScript();\n }\n}\n"],"names":["PRESETS","getPresetCss","name","getLuminance","r","g","b","rs","gs","bs","c","parseColor","color","hex","fullHex","num","match","getContrastTextColor","bgColor","rgb","buildCssVars","options","out","applyCssVars","el","vars","value","getEmbedStyles","shadowStyles","getPresetCssImpl","createInvalidState","control","linkedId","errorId","ids","id","createInput","createTextInput","createCurrencyInput","createRangeInput","createDateInput","createMultipleChoiceInput","q","opts","initial","textarea","input","inputAutocomplete","e","len","v","wrap","prefix","currencySymbol","raw","currency","locale","p","valueDisplay","valueText","unit","updateDisplay","bounds","lower","upper","alignment","layoutClass","o","selected","buttons","opt","idx","btn","marker","body","img","label","desc","key","toggle","focusOption","k","on","currentValue","it","getVisibleQuestions","answers","qn","evaluateRule","rule","isAnswered","matchesAtomic","x","answer","target","a","COMPACT_HEIGHT","EDGE_SLOP","scrollEdgeOf","overflow","applyScrollState","cue","edge","more","applyFitState","card","availableHeight","fit","watchOverflow","refresh","observer","LIVE_ATTR","VISUALLY_HIDDEN_CSS","FOCUSABLE_SELECTOR","ownerDoc","root","getLiveRegion","assertive","kind","existing","region","announce","message","isDisabled","isHidden","node","style","getFocusable","container","getDeepActiveElement","doc","active","createFocusTrap","addedTabindex","onKeydown","event","focusable","first","last","withinTrap","saveFocus","restoreFocus","applyDialog","topLevelAncestor","parent","setBackgroundInert","except","keep","changed","child","prevInert","prevAriaHidden","entry","VISITOR_ID_KEY","getOrCreateVisitorId","CLOSE_ICON","CHECK_ICON","QaidQuests","config","themeDoc","err","res","finalPreset","finalMode","finalUnstyled","layers","presetCss","themeStyle","userContainer","baseStyle","modal","msg","heading","detail","hideProgress","isBottom","headerHidden","hasTitle","hasDescription","hasCloseBtn","headerHasRightContent","dot","savingText","showHeader","header","titleWrap","right","closeBtn","suppressFocus","total","question","pct","step","star","errorEl","initialValue","isLast","isBottomProgress","back","next","left","isInitialRender","done","icon","title","doneTitle","validationMessage","debounced","questionId","waited","data","headers","visible","questionnaire","live","nextAnswers","nextVisible","currentId","findCssFromSelector","selector","parseJsonConfig","tag","parsed","parseDataAttributes","script","endpoint","configUrl","apiKey","zIndex","positiveColor","negativeColor","markerColor","modalWidth","backdropOpacity","fontFamily","fontSize","cssSelector","autoAdvance","saveDebounceMs","themeUrl","preset","themeMode","unstyled","initFromScript","jsonConfig","dataConfig"],"mappings":"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"}