@syntrologie/adapt-faq 2.8.0-canary.416 → 2.8.0-canary.417
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/faq-styles.d.ts +7 -4
- package/dist/faq-styles.d.ts.map +1 -1
- package/dist/runtime.js +26 -6
- package/dist/runtime.js.map +2 -2
- package/package.json +1 -1
package/dist/faq-styles.d.ts
CHANGED
|
@@ -105,7 +105,7 @@ export declare const baseStyles: {
|
|
|
105
105
|
readonly flexDirection: "column";
|
|
106
106
|
readonly gap: "0";
|
|
107
107
|
readonly minHeight: "0";
|
|
108
|
-
readonly padding: "var(--sc-content-item-padding,
|
|
108
|
+
readonly padding: "var(--sc-content-item-padding, 10px 16px)";
|
|
109
109
|
};
|
|
110
110
|
readonly detailBack: {
|
|
111
111
|
readonly display: "inline-flex";
|
|
@@ -115,7 +115,7 @@ export declare const baseStyles: {
|
|
|
115
115
|
readonly background: "none";
|
|
116
116
|
readonly border: "none";
|
|
117
117
|
readonly padding: "4px 0";
|
|
118
|
-
readonly marginBottom: "
|
|
118
|
+
readonly marginBottom: "6px";
|
|
119
119
|
readonly cursor: "pointer";
|
|
120
120
|
readonly fontSize: "13px";
|
|
121
121
|
readonly fontWeight: 600;
|
|
@@ -138,16 +138,19 @@ export declare const baseStyles: {
|
|
|
138
138
|
readonly detailAnswerClamp: {
|
|
139
139
|
readonly margin: "0";
|
|
140
140
|
readonly display: "-webkit-box";
|
|
141
|
-
readonly webkitLineClamp: "var(--sc-faq-answer-clamp,
|
|
141
|
+
readonly webkitLineClamp: "var(--sc-faq-answer-clamp, 3)";
|
|
142
142
|
readonly webkitBoxOrient: "vertical";
|
|
143
143
|
readonly overflow: "hidden";
|
|
144
|
+
readonly flex: "1 1 auto";
|
|
145
|
+
readonly minHeight: "0";
|
|
144
146
|
};
|
|
145
147
|
readonly detailFooter: {
|
|
146
148
|
readonly display: "flex";
|
|
147
149
|
readonly alignItems: "center";
|
|
148
150
|
readonly justifyContent: "flex-end";
|
|
149
151
|
readonly gap: "8px";
|
|
150
|
-
readonly marginTop: "
|
|
152
|
+
readonly marginTop: "6px";
|
|
153
|
+
readonly flexShrink: 0;
|
|
151
154
|
};
|
|
152
155
|
readonly detailDiveDeeper: {
|
|
153
156
|
readonly flex: "0 0 auto";
|
package/dist/faq-styles.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"faq-styles.d.ts","sourceRoot":"","sources":["../src/faq-styles.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAQH,eAAO,MAAM,UAAU
|
|
1
|
+
{"version":3,"file":"faq-styles.d.ts","sourceRoot":"","sources":["../src/faq-styles.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAQH,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2Mb,CAAC;AAMX,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqFvB,CAAC"}
|
package/dist/runtime.js
CHANGED
|
@@ -228,7 +228,11 @@ var baseStyles = {
|
|
|
228
228
|
minHeight: "0",
|
|
229
229
|
// Inset the master→detail takeover to match the list questions' padding so
|
|
230
230
|
// a single Q&A taking over the tile doesn't render flush to the edges.
|
|
231
|
-
|
|
231
|
+
// Vertical inset is tightened (12px→10px default) because the takeover lives
|
|
232
|
+
// in the fixed-height velvet deck band, where every px of chrome pushes the
|
|
233
|
+
// "Dive deeper" footer toward the card's overflow:hidden clip edge
|
|
234
|
+
// (BUG-1783496844). Horizontal stays 16px to keep alignment with the list.
|
|
235
|
+
padding: "var(--sc-content-item-padding, 10px 16px)"
|
|
232
236
|
},
|
|
233
237
|
detailBack: {
|
|
234
238
|
display: "inline-flex",
|
|
@@ -238,8 +242,10 @@ var baseStyles = {
|
|
|
238
242
|
background: "none",
|
|
239
243
|
border: "none",
|
|
240
244
|
padding: "4px 0",
|
|
241
|
-
// Clear space below the back control (was the container gap).
|
|
242
|
-
|
|
245
|
+
// Clear space below the back control (was the container gap). Tightened
|
|
246
|
+
// 10px→6px to reclaim vertical room in the bounded deck band so the
|
|
247
|
+
// "Dive deeper" footer stays clear of the card clip edge (BUG-1783496844).
|
|
248
|
+
marginBottom: "6px",
|
|
243
249
|
cursor: "pointer",
|
|
244
250
|
fontSize: "13px",
|
|
245
251
|
fontWeight: 600,
|
|
@@ -270,9 +276,20 @@ var baseStyles = {
|
|
|
270
276
|
detailAnswerClamp: {
|
|
271
277
|
margin: "0",
|
|
272
278
|
display: "-webkit-box",
|
|
273
|
-
|
|
279
|
+
// Default cap lowered 4→3 lines: the takeover renders inside the
|
|
280
|
+
// fixed-height velvet deck card (overflow:hidden), so a 4-line preview
|
|
281
|
+
// plus chrome pushed the "Dive deeper" footer past the clip edge on
|
|
282
|
+
// smaller decks / two-line questions (BUG-1783496844). 3 lines is a
|
|
283
|
+
// compact preview; "Dive deeper" carries the full answer to chat. Roomy
|
|
284
|
+
// surfaces can raise it via the token.
|
|
285
|
+
webkitLineClamp: "var(--sc-faq-answer-clamp, 3)",
|
|
274
286
|
webkitBoxOrient: "vertical",
|
|
275
|
-
overflow: "hidden"
|
|
287
|
+
overflow: "hidden",
|
|
288
|
+
// Shrink-ready: as a flex child of detailAnswer, yield space first so that
|
|
289
|
+
// WHEN a host bounds the tile height (definite deck-card height) the answer
|
|
290
|
+
// gives up lines before the pinned footer is ever clipped.
|
|
291
|
+
flex: "1 1 auto",
|
|
292
|
+
minHeight: "0"
|
|
276
293
|
},
|
|
277
294
|
// Footer row under the answer: the "Dive deeper" chip sits bottom-right.
|
|
278
295
|
detailFooter: {
|
|
@@ -280,7 +297,10 @@ var baseStyles = {
|
|
|
280
297
|
alignItems: "center",
|
|
281
298
|
justifyContent: "flex-end",
|
|
282
299
|
gap: "8px",
|
|
283
|
-
|
|
300
|
+
// Tightened 8px→6px to reclaim deck-band vertical room (BUG-1783496844).
|
|
301
|
+
marginTop: "6px",
|
|
302
|
+
// Never shrink or clip the footer — the answer above yields space first.
|
|
303
|
+
flexShrink: 0
|
|
284
304
|
},
|
|
285
305
|
detailDiveDeeper: {
|
|
286
306
|
flex: "0 0 auto",
|
package/dist/runtime.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/executors.ts", "../src/FAQWidgetLit.ts", "../src/faq-styles.ts", "../src/renderHealth.ts", "../src/runtime.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Adaptive FAQ - Action Executors\n *\n * Three executors that operate on the FAQStore:\n * - executeScrollToFaq: scroll to a FAQ item and optionally expand it\n * - executeToggleFaqItem: open / close / toggle a FAQ item\n * - executeUpdateFaq: add, remove, reorder, or replace FAQ items\n */\n\nimport type { FAQStore } from './state';\nimport type {\n ExecutorContext,\n ExecutorResult,\n FAQQuestionAction,\n ScrollToFaqAction,\n ToggleFaqItemAction,\n UpdateFaqAction,\n} from './types';\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n/**\n * Resolve a FAQ item by direct ID or by fuzzy question text match.\n * Throws if neither yields a result.\n */\nfunction resolveItem(store: FAQStore, itemId?: string, itemQuestion?: string): FAQQuestionAction {\n if (itemId) {\n const found = store.getState().items.find((i) => i.config.id === itemId);\n if (found) return found;\n }\n\n if (itemQuestion) {\n const found = store.findByQuestion(itemQuestion);\n if (found) return found;\n }\n\n throw new Error('FAQ item not found');\n}\n\n// ============================================================================\n// executeScrollToFaq\n// ============================================================================\n\n/**\n * Scroll to a FAQ item in the DOM and optionally expand it.\n *\n * Looks up the item by `itemId` or `itemQuestion`, scrolls the matching\n * `[data-faq-item-id]` element into view, and expands it unless\n * `expand` is explicitly set to `false`.\n */\nexport async function executeScrollToFaq(\n action: ScrollToFaqAction,\n context: ExecutorContext,\n store: FAQStore\n): Promise<ExecutorResult> {\n const item = resolveItem(store, action.itemId, action.itemQuestion);\n const { id } = item.config;\n\n // Expand the item unless explicitly told not to\n if (action.expand !== false) {\n store.expand(id);\n }\n\n // Scroll the DOM element into view (may be absent in test environments)\n const el = document.querySelector(`[data-faq-item-id=\"${id}\"]`);\n if (el) {\n el.scrollIntoView({\n behavior: action.behavior ?? 'smooth',\n });\n }\n\n // Publish analytics event\n context.publishEvent('faq:scroll_to', { itemId: id });\n\n return {\n cleanup: () => {\n // Optionally collapse on revert\n },\n };\n}\n\n// ============================================================================\n// executeToggleFaqItem\n// ============================================================================\n\n/**\n * Open, close, or toggle a FAQ item's expanded state.\n */\nexport async function executeToggleFaqItem(\n action: ToggleFaqItemAction,\n context: ExecutorContext,\n store: FAQStore\n): Promise<ExecutorResult> {\n const item = resolveItem(store, action.itemId, action.itemQuestion);\n const { id } = item.config;\n const desiredState = action.state ?? 'toggle';\n\n let newState: 'open' | 'closed';\n\n switch (desiredState) {\n case 'open':\n store.expand(id);\n newState = 'open';\n break;\n case 'closed':\n store.collapse(id);\n newState = 'closed';\n break;\n default: {\n const wasExpanded = store.getState().expandedItems.has(id);\n store.toggle(id);\n newState = wasExpanded ? 'closed' : 'open';\n break;\n }\n }\n\n context.publishEvent('faq:toggle', { itemId: id, newState });\n\n return {\n cleanup: () => {\n // Revert toggle on cleanup\n },\n };\n}\n\n// ============================================================================\n// executeUpdateFaq\n// ============================================================================\n\n/**\n * Add, remove, reorder, or replace FAQ items in the store.\n */\nexport async function executeUpdateFaq(\n action: UpdateFaqAction,\n context: ExecutorContext,\n store: FAQStore\n): Promise<ExecutorResult> {\n switch (action.operation) {\n case 'add': {\n const items = action.items ?? [];\n const position = action.position === 'prepend' ? 'prepend' : 'append';\n store.addItems(items, position);\n break;\n }\n\n case 'remove': {\n if (!action.itemId) {\n throw new Error('FAQ item not found');\n }\n // Verify the item exists before removing\n const exists = store.getState().items.some((i) => i.config.id === action.itemId);\n if (!exists) {\n throw new Error('FAQ item not found');\n }\n store.removeItem(action.itemId);\n break;\n }\n\n case 'reorder': {\n const order = action.order ?? [];\n store.reorderItems(order);\n break;\n }\n\n case 'replace': {\n const items = action.items ?? [];\n store.replaceItems(items);\n break;\n }\n }\n\n context.publishEvent('faq:update', { operation: action.operation });\n\n return {\n cleanup: () => {\n // Could snapshot previous state for undo\n },\n };\n}\n\n// ============================================================================\n// Executor Definitions for Registration\n// ============================================================================\n\n/**\n * All executors provided by adaptive-faq.\n * These are registered with the runtime's ExecutorRegistry.\n */\nexport const executorDefinitions = [\n { kind: 'faq:scroll_to', executor: executeScrollToFaq },\n { kind: 'faq:toggle_item', executor: executeToggleFaqItem },\n { kind: 'faq:update', executor: executeUpdateFaq },\n] as const;\n", "/**\n * Adaptive FAQ - FAQWidgetLit\n *\n * Lit web component equivalent of FAQWidget.tsx.\n * Renders a collapsible Q&A accordion with search, category grouping,\n * feedback, and markdown rendering \u2014 all as a custom element with no\n * Shadow DOM (light DOM via createRenderRoot).\n *\n * Tag name: <syntro-faq-accordion>\n *\n * Decorator-free: uses `static override properties` (tsconfig has no\n * experimentalDecorators).\n */\n\nimport { purple } from '@syntro/design-system/tokens';\nimport { dispatchDiveDeeper } from '@syntrologie/sdk-contracts';\nimport { html, LitElement, nothing } from 'lit';\nimport { styleMap } from 'lit/directives/style-map.js';\nimport { unsafeHTML } from 'lit/directives/unsafe-html.js';\nimport { getAnswerText, renderAnswerHtml } from './answerRendering';\nimport { baseStyles, themeStyles } from './faq-styles';\nimport type { FAQWidgetRuntime } from './faq-types';\nimport { RenderHealthController, type RenderHealthProbe } from './renderHealth';\nimport type {\n FAQAnswer,\n FAQConfig,\n FAQQuestionAction,\n FeedbackConfig,\n FeedbackValue,\n} from './types';\n\n// ============================================================================\n// Utility \u2014 styleMap accepts Record<string, string | number | undefined>\n// but its TypeScript signature is overly narrow in some Lit versions.\n// Cast through unknown to avoid false positives when style values include\n// numeric CSS properties (fontWeight, lineHeight, etc.).\n// ============================================================================\n\nfunction sm(styles: Record<string, unknown>): Record<string, string> {\n return styles as unknown as Record<string, string>;\n}\n\nfunction resolveFeedbackConfig(\n feedback: boolean | FeedbackConfig | undefined\n): FeedbackConfig | null {\n if (!feedback) return null;\n if (feedback === true) return { style: 'thumbs' };\n return feedback;\n}\n\nfunction getFeedbackPrompt(feedbackConfig: FeedbackConfig): string {\n return feedbackConfig.prompt ?? 'Was this helpful?';\n}\n\n/**\n * Small deterministic string hash (djb2, base36) for content-derived atomic\n * FAQ row ids \u2014 stable per question across re-mounts so expand/feedback state\n * keyed by id sticks to the right row even if the list reorders.\n */\nfunction hashFaqId(text: string): string {\n let h = 5381;\n for (let i = 0; i < text.length; i++) {\n h = (Math.imul(h, 33) ^ text.charCodeAt(i)) >>> 0;\n }\n return h.toString(36);\n}\n\n/**\n * Escape a value for safe use inside a DOUBLE-QUOTED CSS attribute selector\n * (`[attr=\"\u2026\"]`). Question ids are content-derived (hashed) or LLM-supplied, so\n * a stray double-quote or backslash would break `querySelector`. We escape only\n * the two characters that are special inside a double-quoted string \u2014 NOT via\n * `CSS.escape`, which escapes for an *identifier* context (unquoted) and would\n * over-escape here.\n */\nfunction cssAttrEscape(value: string): string {\n return value.replace(/[\"\\\\]/g, '\\\\$&');\n}\n\nfunction resolveTheme(theme: FAQConfig['theme'] | undefined): 'light' | 'dark' {\n if (theme && theme !== 'auto') return theme;\n if (typeof window !== 'undefined') {\n return window.matchMedia?.('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';\n }\n return 'light';\n}\n\n/** Whether the visitor has asked for reduced motion \u2014 the flip is disabled and\n * the takeover swaps instantly instead of rotating. */\nfunction prefersReducedMotion(): boolean {\n if (typeof window === 'undefined') return false;\n return window.matchMedia?.('(prefers-reduced-motion: reduce)').matches ?? false;\n}\n\n// Flip transition window FALLBACK. The velvet house tokens are ~240ms; we clear\n// the open question a little after that so the back face isn't blanked before\n// the flip visually completes. This is only the fallback used when the computed\n// `--vc-transition-duration` token can't be read (e.g. no live layout). At\n// runtime we read the actual token and clamp the timer to be >= it, so a host\n// that sets a slower token never blanks the back face mid-flip (see\n// {@link FAQAccordionElement._flipDurationMs}). Kept here so the timer and the\n// CSS agree on a sane default.\nconst FLIP_TRANSITION_MS = 360;\n\n// Minimum extra time added on top of the CSS transition duration before the\n// back face is torn down, so the flip has visibly settled first.\nconst FLIP_CLEAR_BUFFER_MS = 120;\n\n// The default (shared) instanceId. Two default-id tiles would cross-scope their\n// injected <style> (both scoped to `[data-adaptive-id=\"faq-widget\"]`), so when\n// the id is left at the default we fall back to a per-element generated uid.\nconst DEFAULT_INSTANCE_ID = 'faq-widget';\n\n// Monotonic counter for generated scope uids (default-instanceId fallback).\nlet _faqUidCounter = 0;\n\n// ============================================================================\n// FAQAccordionElement \u2014 Lit element\n// ============================================================================\n\n/**\n * <syntro-faq-accordion> \u2014 light-DOM Lit web component.\n *\n * Set properties imperatively (no attribute serialisation for objects):\n * el.faqConfig = { expandBehavior: 'single', ... };\n * el.runtime = runtimeInstance;\n * el.instanceId = 'my-faq';\n */\nexport class FAQAccordionElement extends LitElement {\n // -----------------------------------------------------------------------\n // Reactive properties (no decorators \u2014 tsconfig forbids experimentalDecorators)\n // -----------------------------------------------------------------------\n\n static override properties = {\n // Public API \u2014 set from the outside\n faqConfig: { attribute: false },\n runtime: { attribute: false },\n instanceId: { type: String },\n\n // Internal reactive state (prefixed with _ to signal \"private\")\n // _openId: currently-open question id for the master\u2192detail takeover.\n // null = LIST view; a value = DETAIL view showing only that single Q&A.\n // _activeFace: which face of the flip is showing \u2014 'front' (question list)\n // or 'back' (the selected question's answer detail). Both faces are always\n // in the DOM (a flip animates between two live faces); this attribute drives\n // the CSS rotateY, mirroring the product card's `data-active-face` idiom.\n _openId: { state: true },\n _activeFace: { state: true },\n // _flipAnimating: true ONLY while a flip rotation is running. The 3D flip\n // machinery (perspective, preserve-3d, rotateY, per-face backface) exists\n // solely during this window; once the flip settles it is torn down so the\n // resting face renders FLAT (transform:none) \u2014 a persistently\n // rotateY/perspective-composited face blanks its text on some Android GPUs\n // and rasterizes it soft everywhere. _flipDir selects which keyframes run.\n _flipAnimating: { state: true },\n _flipDir: { state: true },\n _highlightId: { state: true },\n _searchQuery: { state: true },\n _feedbackState: { state: true },\n _hoveredId: { state: true },\n };\n\n // -----------------------------------------------------------------------\n // Property declarations\n // -----------------------------------------------------------------------\n\n faqConfig: FAQConfig = {\n expandBehavior: 'single',\n searchable: false,\n theme: 'auto',\n actions: [],\n };\n\n runtime: FAQWidgetRuntime | null = null;\n\n instanceId: string = DEFAULT_INSTANCE_ID;\n\n // Per-element generated scope uid, used ONLY when `instanceId` is left at the\n // shared default so two default-id tiles don't cross-scope their injected\n // <style> (both would otherwise target `[data-adaptive-id=\"faq-widget\"]`).\n // Generated lazily and once per element (see {@link _scopeId}).\n private _generatedUid: string | null = null;\n\n // Internal state\n // The currently-open question id (null = list view, value = detail view).\n // The master\u2192detail takeover swaps the whole widget to a single Q&A so a\n // long answer never overflows the bounded velvet tile.\n _openId: string | null = null;\n // Which face is showing. Both faces are always mounted so the takeover FLIPS\n // (front = question list \u2194 back = the open question's answer detail) instead\n // of instantly swapping templates \u2014 matching the product card's flip feel.\n _activeFace: 'front' | 'back' = 'front';\n // True ONLY while a flip rotation is running (see the property doc above).\n _flipAnimating = false;\n // Which keyframes the running flip uses: 'in' = front\u2192back (open),\n // 'out' = back\u2192front (close).\n _flipDir: 'in' | 'out' = 'in';\n _highlightId: string | null = null;\n _searchQuery: string = '';\n _feedbackState: Map<string, FeedbackValue> = new Map();\n _hoveredId: string | null = null;\n\n // Belt timer that force-SETTLES the flip if the container's `animationend`\n // never fires (backgrounded tab, engine quirk, reduced-motion suppression).\n // Also the point at which a flip-OUT (back\u2192front) finally clears `_openId`,\n // so the back face isn't emptied (blanked) mid-flip. The delay is\n // {@link _flipDurationMs} (>= the CSS duration). Reduced motion settles\n // synchronously instead (no animation to wait for).\n private _flipSettleTimer: ReturnType<typeof setTimeout> | null = null;\n\n // Focus management (a11y): after a flip renders, move focus to the face that\n // just became active \u2014 the \u2039 Back button on flip-to-back, and the originating\n // question row on flip-to-front \u2014 so keyboard/AT users follow the takeover.\n // Consumed (and cleared) in `updated()`. `_focusReturnId` remembers WHICH row\n // opened the detail so focus returns to that exact row, not just the first.\n private _pendingFocus: 'front' | 'back' | null = null;\n private _focusReturnId: string | null = null;\n\n // Subscription cleanup handles\n private _unsubContext: (() => void) | null = null;\n private _unsubAccumulator: (() => void) | null = null;\n private _unsubCta: (() => void) | null = null;\n private _unsubDeepLink: (() => void) | null = null;\n private _unsubSessionMetrics: (() => void) | null = null;\n private _highlightTimer: ReturnType<typeof setTimeout> | null = null;\n\n // Compositional-child wiring \u2014 faq:question items the LLM mounts into this\n // accordion's tile arrive as `element.compositional_append` events (see\n // adaptive-chatbot's ItemHandler). Without this subscription the questions\n // are published into the void and the accordion renders empty.\n private _unsubCompositional: (() => void) | null = null;\n private _tileId: string | null = null;\n /** Instance ids already appended via the compositional bus \u2014 dedups\n * the subscribe-then-replay path (and re-subscribes on runtime change). */\n private _llmAppendedIds: Set<string> = new Set();\n\n constructor() {\n super();\n // Runtime self-detection: emits app:faq_accordion:blank / :clipped after\n // paint if the mounted accordion has area but paints no content (the blank\n // FAQ tile bug). Self-registers via host.addController \u2014 no stored ref.\n new RenderHealthController(this, () => this.#healthProbe());\n }\n\n /** Measure only the SETTLED accordion (at least one visible question). An\n * empty accordion renders `nothing` (runtime) or a reassurance box (no\n * runtime) and is never \"blank\". NON-PII stateKey. */\n #healthProbe(): RenderHealthProbe | null {\n const visible = this._visibleQuestions();\n if (visible.length === 0) return null;\n const root = this.querySelector('[data-adaptive-type=\"adaptive-faq\"]');\n if (!root) return null;\n return {\n root,\n stateKey: `faq|${visible.length}|${this._activeFace}`,\n category: 'faq_accordion',\n widgetKind: 'adaptive-faq:accordion',\n };\n }\n\n // -----------------------------------------------------------------------\n // Light DOM \u2014 no Shadow DOM so CSS variables from the host page apply\n // -----------------------------------------------------------------------\n\n override createRenderRoot() {\n return this;\n }\n\n // -----------------------------------------------------------------------\n // Lifecycle\n // -----------------------------------------------------------------------\n\n override connectedCallback() {\n super.connectedCallback();\n this._subscribeAll();\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback();\n this._unsubscribeAll();\n if (this._highlightTimer !== null) {\n clearTimeout(this._highlightTimer);\n this._highlightTimer = null;\n }\n this._clearFlipSettleTimer();\n }\n\n // Re-subscribe when runtime changes (property may be set after connectedCallback)\n override updated(changedProps: Map<string, unknown>) {\n if (changedProps.has('runtime')) {\n this._unsubscribeAll();\n this._subscribeAll();\n }\n this._applyPendingFocus();\n }\n\n /**\n * Move focus to the face that just became active after a flip (a11y). On\n * flip-to-back, focus the \u2039 Back button; on flip-to-front, focus the question\n * row that originally opened the detail (so keyboard/AT users land back where\n * they were). No-op when nothing is pending or the target isn't in the DOM\n * yet (the flip-to-front row may not exist if the list changed).\n */\n private _applyPendingFocus() {\n if (this._pendingFocus === null) return;\n const target = this._pendingFocus;\n this._pendingFocus = null;\n if (target === 'back') {\n const back = this.querySelector<HTMLElement>('[data-faq-back]');\n back?.focus();\n return;\n }\n // Flip to front: return focus to the originating row's button.\n const id = this._focusReturnId;\n this._focusReturnId = null;\n if (!id) return;\n const row = this.querySelector<HTMLElement>(`[data-faq-item-id=\"${cssAttrEscape(id)}\"] button`);\n row?.focus();\n }\n\n // -----------------------------------------------------------------------\n // Subscription management\n // -----------------------------------------------------------------------\n\n private _subscribeAll() {\n if (!this.runtime) return;\n\n // Context changes \u2192 force re-render\n this._unsubContext = this.runtime.context.subscribe(() => {\n this._reevaluateOpenAndUpdate();\n });\n\n // Accumulator changes \u2192 force re-render (for event_count triggerWhen)\n if (this.runtime.accumulator?.subscribe) {\n this._unsubAccumulator = this.runtime.accumulator.subscribe(() => {\n this._reevaluateOpenAndUpdate();\n });\n }\n\n // Session metric changes \u2192 force re-render (for session_metric triggerWhen)\n if (this.runtime.sessionMetrics?.subscribe) {\n this._unsubSessionMetrics = this.runtime.sessionMetrics.subscribe(() => {\n this._reevaluateOpenAndUpdate();\n });\n }\n\n // faq:open:* events from overlay CTA clicks\n if (this.runtime.events.subscribe) {\n // Check EventBus history for pending faq:open events\n if (this.runtime.events.getRecent) {\n const recentEvents = this.runtime.events.getRecent(\n { patterns: ['^action\\\\.tooltip_cta_clicked$', '^action\\\\.modal_cta_clicked$'] },\n 10\n );\n const pendingEvent = recentEvents\n .filter((e) => {\n const actionId = e.props?.actionId;\n return typeof actionId === 'string' && actionId.startsWith('faq:open:');\n })\n .pop();\n\n if (pendingEvent && Date.now() - pendingEvent.ts < 10000) {\n const questionId = (pendingEvent.props!.actionId as string).replace('faq:open:', '');\n // Takeover: flip the widget to the DETAIL (back) face for that question\n // (previously this expanded the row inline).\n this._openToDetail(questionId);\n }\n }\n\n this._unsubCta = this.runtime.events.subscribe(\n { patterns: ['^action\\\\.tooltip_cta_clicked$', '^action\\\\.modal_cta_clicked$'] },\n (event) => {\n // Same staleness guard as the history scan above: some EventBus\n // implementations replay recent history into new subscribers. Without\n // this guard the history scan AND this live handler both fire for the\n // same old event \u2014 a spurious second `canvas.requestOpen`. Skip any\n // event older than ~10s (a fresh click always has ts\u2248now).\n const ts = (event as { ts?: number }).ts;\n if (typeof ts === 'number' && Date.now() - ts >= 10000) return;\n const actionId = event.props?.actionId;\n if (typeof actionId !== 'string' || !actionId.startsWith('faq:open:')) return;\n const questionId = actionId.replace('faq:open:', '');\n // Takeover: flip to the question's detail (back) face instead of inline expand.\n this._openToDetail(questionId);\n this.runtime?.events.publish('canvas.requestOpen');\n }\n );\n }\n\n // notification.deep_link events\n if (this.runtime.events.subscribe) {\n const handleDeepLink = (event: { name: string; props?: Record<string, unknown> }) => {\n const tileId = event.props?.tileId as string | undefined;\n const itemId = event.props?.itemId as string | undefined;\n if (tileId !== this.instanceId) return;\n if (!itemId) return;\n\n // Takeover: flip to the question's detail (back) face (previously inline expand).\n this._openToDetail(itemId);\n this._highlightId = itemId;\n\n if (this._highlightTimer !== null) clearTimeout(this._highlightTimer);\n this._highlightTimer = setTimeout(() => {\n this._highlightId = null;\n this._highlightTimer = null;\n }, 1500);\n };\n\n // Check recent events (may have fired before widget mounted)\n if (this.runtime.events.getRecent) {\n const recent = this.runtime.events.getRecent({ names: ['notification.deep_link'] }, 5);\n const pending = recent\n .filter((e) => (e.props?.tileId as string) === this.instanceId && e.props?.itemId)\n .pop();\n if (pending && Date.now() - pending.ts < 10000) {\n handleDeepLink(pending);\n }\n }\n\n this._unsubDeepLink = this.runtime.events.subscribe(\n { names: ['notification.deep_link'] },\n (event) => {\n // Mirror the history-scan staleness guard: skip replayed events older\n // than ~10s so a bus that replays history into new subscribers doesn't\n // double-handle the same deep link (history scan + live handler).\n const ts = (event as { ts?: number }).ts;\n if (typeof ts === 'number' && Date.now() - ts >= 10000) return;\n handleDeepLink(event);\n }\n );\n }\n\n // Compositional children: faq:question items mounted into this\n // accordion's tile. The ItemHandler publishes one event per kind-\n // agnostic item mount; we filter to our tile and route into\n // `faqConfig.actions`. Mirrors AdaptiveChipsStrip's CompositionalContainer.\n this._subscribeCompositional();\n }\n\n /**\n * Subscribe to `element.compositional_*` events targeting this accordion's\n * tile, and ask the element store to replay any items it already holds for\n * us (covers the case where the accordion mounts AFTER the item did \u2014 the\n * inline-slot hydration race that otherwise silently drops the question).\n */\n private _subscribeCompositional() {\n const bus = this.runtime?.events;\n if (!bus?.subscribe) return;\n this._tileId = this._resolveTileId();\n if (!this._tileId) return;\n const tileId = this._tileId;\n this._unsubCompositional = bus.subscribe((event) => {\n const props = event.props ?? {};\n if (props.tile_id !== tileId) return;\n const instanceId = String(props.instance_id ?? '');\n if (event.name === 'element.compositional_append') {\n this._insertItem(\n instanceId,\n props.item as FAQQuestionAction,\n (props.position as 'append' | 'prepend') ?? 'append'\n );\n } else if (event.name === 'element.compositional_patch') {\n this._patchItem(instanceId, props.item as FAQQuestionAction);\n } else if (event.name === 'element.compositional_remove') {\n this._removeItem(instanceId);\n }\n });\n bus.publish('element.compositional_replay_request', { tile_id: tileId });\n }\n\n /** Tile id for compositional filtering: the enclosing tile card's\n * `data-tile-id` (set by SyntroTileCard), falling back to `instanceId`\n * when the accordion is mounted outside a tile card. */\n private _resolveTileId(): string | null {\n const anchor = this.closest<HTMLElement>('[data-tile-id]');\n const fromDom = anchor?.getAttribute('data-tile-id');\n if (fromDom) return fromDom;\n return this.instanceId && this.instanceId !== DEFAULT_INSTANCE_ID ? this.instanceId : null;\n }\n\n /** Append (or prepend) a faq:question the LLM mounted. The wire shape is a\n * fully-formed `{kind, config}` envelope, so we store it verbatim. */\n private _insertItem(\n instanceId: string,\n item: FAQQuestionAction,\n position: 'append' | 'prepend'\n ): void {\n if (!instanceId || !item?.config?.id) return;\n if (this._llmAppendedIds.has(instanceId)) return; // dedup replay / re-subscribe\n this._llmAppendedIds.add(instanceId);\n const actions = this.faqConfig.actions ?? [];\n const next = position === 'prepend' ? [item, ...actions] : [...actions, item];\n // Reassign so Lit sees the change (faqConfig is attribute:false).\n this.faqConfig = { ...this.faqConfig, actions: next };\n }\n\n /** Replace an existing question's content (full replacement). */\n private _patchItem(instanceId: string, item: FAQQuestionAction): void {\n if (!instanceId || !item?.config) return;\n const actions = this.faqConfig.actions ?? [];\n const idx = actions.findIndex((a) => a.config.id === instanceId);\n if (idx < 0) return;\n const next = [...actions];\n next[idx] = item;\n // If the patch changes the open question's id, our `_openId` would point at\n // a gone id and the detail view would fall back to the list. Re-point it at\n // the replacement's new id so the open question stays open through a patch.\n if (this._openId === instanceId && item.config.id !== instanceId) {\n this._openId = item.config.id;\n }\n this.faqConfig = { ...this.faqConfig, actions: next };\n // A patch can also gate the open question OUT (e.g. a new `triggerWhen` that\n // now evaluates false, or content that drops it from the ordered list). If\n // the open id no longer resolves, reset BOTH `_openId` and `_activeFace` to\n // the front. Without resetting `_activeFace`, render() would locally force\n // the front face while leaving state on 'back' \u2014 so a later re-appearance of\n // the question would snap straight to the back face with no flip animation.\n this._resetFaceIfOpenStale();\n }\n\n /** Remove a question by instance id. */\n private _removeItem(instanceId: string): void {\n if (!instanceId) return;\n const actions = this.faqConfig.actions ?? [];\n const next = actions.filter((a) => a.config.id !== instanceId);\n if (next.length === actions.length) return;\n this._llmAppendedIds.delete(instanceId);\n // Removing the currently-open question would leave `_openId` pointing at a\n // gone id. Flip back to the list view instead of resolving to an empty\n // detail. Clear synchronously (the source question is gone \u2014 there's nothing\n // to keep mounted through a flip-out) and cancel any pending back-clear.\n if (this._openId === instanceId) {\n this._clearFlipSettleTimer();\n this._flipAnimating = false;\n this._openId = null;\n this._activeFace = 'front';\n // The open detail set _pendingFocus/_focusReturnId (a11y focus return to\n // the originating row). That row is now gone \u2014 clear the dead focus intent\n // so the next render's _applyPendingFocus doesn't chase a missing element.\n this._pendingFocus = null;\n this._focusReturnId = null;\n }\n this.faqConfig = { ...this.faqConfig, actions: next };\n }\n\n /**\n * Re-evaluate whether the open question is still visible, then re-render.\n * Called from the subscription-driven re-render paths (context / accumulator /\n * sessionMetric changes). Those subscriptions can flip an open question's\n * `triggerWhen` to false WITHOUT a compositional patch/remove \u2014 so without\n * this hook the stale-open reset (`_resetFaceIfOpenStale`, otherwise only\n * invoked on patch/remove) would never fire on the triggerWhen path, and an\n * open question gated out this way would snap straight to the back face on\n * re-appearance. `_resetFaceIfOpenStale` mutates reactive state (which itself\n * schedules an update), and we still `requestUpdate()` for the no-op case.\n */\n private _reevaluateOpenAndUpdate(): void {\n this._resetFaceIfOpenStale();\n this.requestUpdate();\n }\n\n /**\n * If `_openId` no longer resolves against the visible/ordered list (gated out\n * by triggerWhen, removed, or patched away), reset BOTH `_openId` and\n * `_activeFace` to the front (and clear the pending a11y focus intent).\n * Resolves against the SAME `ordered` list render() uses (visible + ordered,\n * NOT search-filtered). Also cancels any pending back-clear timer. Invoked on\n * compositional patch/remove AND on the subscription-driven re-render paths\n * (via {@link _reevaluateOpenAndUpdate}) so the triggerWhen-gated-out case is\n * genuinely covered. Keeping `_activeFace` on 'back' after the open id went\n * stale is the bug this guards: render() would force the front face locally\n * but leave state on 'back', so a later re-appearance snaps to the back face\n * with no animation.\n */\n private _resetFaceIfOpenStale(): void {\n if (this._openId === null) return;\n const ordered = this._orderedQuestions(this._visibleQuestions());\n const stillPresent = ordered.some((q) => q.config.id === this._openId);\n if (stillPresent) return;\n this._clearFlipSettleTimer();\n this._flipAnimating = false;\n this._openId = null;\n this._activeFace = 'front';\n // The open detail set _pendingFocus/_focusReturnId; the originating row is\n // now gated out, so clear the dead focus intent (see _removeItem).\n this._pendingFocus = null;\n this._focusReturnId = null;\n }\n\n private _unsubscribeAll() {\n this._unsubContext?.();\n this._unsubAccumulator?.();\n this._unsubSessionMetrics?.();\n this._unsubCta?.();\n this._unsubDeepLink?.();\n this._unsubCompositional?.();\n this._unsubContext = null;\n this._unsubAccumulator = null;\n this._unsubSessionMetrics = null;\n this._unsubCta = null;\n this._unsubDeepLink = null;\n this._unsubCompositional = null;\n }\n\n // -----------------------------------------------------------------------\n // Handlers\n // -----------------------------------------------------------------------\n\n /**\n * Master\u2192detail takeover: tapping a list row opens its single-Q&A detail\n * view (the whole widget swaps), so a long answer fills \u2014 and never\n * overflows \u2014 the bounded velvet tile. There is no simultaneous multi-expand;\n * one question owns the tile at a time.\n */\n private _handleOpen(id: string) {\n this._openToDetail(id);\n\n this.runtime?.events.publish('faq:toggled', {\n instanceId: this.instanceId,\n questionId: id,\n expanded: true,\n timestamp: Date.now(),\n });\n }\n\n /**\n * Put the widget into the DETAIL (back) face for `id` and flip to it. Shared\n * by the tap handler and the event-driven entry points (faq:open CTA,\n * notification.deep_link) so every \"open this question\" path flips the same\n * way. Cancels any pending back-clear timer (a re-open before the flip-out\n * finishes must keep the detail mounted). Remembers the originating question\n * id so focus can return to that exact row on flip-to-front (a11y), and moves\n * focus to the \u2039 Back button once the back face renders.\n */\n private _openToDetail(id: string) {\n // A re-open before a previous flip settled must clear the pending settle and\n // restart cleanly (interrupted flip).\n this._clearFlipSettleTimer();\n this._openId = id;\n this._activeFace = 'back';\n this._pendingFocus = 'back';\n this._focusReturnId = id;\n if (prefersReducedMotion()) {\n // No animation runs (CSS disables it) \u2192 no animationend to await. Settle\n // straight to the flat back face.\n this._flipAnimating = false;\n return;\n }\n // Turn the 3D flip machinery on for the duration of the rotation only.\n this._flipDir = 'in';\n this._flipAnimating = true;\n // Belt-and-suspenders: if animationend is dropped, force the settle so the\n // resting back face never stays on a composited perspective/rotateY layer.\n this._flipSettleTimer = setTimeout(this._settleFlip, this._flipDurationMs());\n }\n\n /** Back affordance: FLIP from the detail (back) face to the list (front) face.\n * We flip immediately but keep `_openId` (so the back face stays populated\n * through the rotation, never blank mid-flip); the id clears once the\n * transition completes. With reduced motion there's no transition, so it\n * clears synchronously.\n *\n * Timer/token coupling: the tear-down delay is {@link _flipDurationMs} \u2014 the\n * computed `--vc-transition-duration` token (clamped to be >= our fallback),\n * NOT a hardcoded constant. A host that sets the token slower than the\n * fallback would otherwise clear `_openId` (blanking the back face) before\n * the rotation finished. Focus returns to the originating question row. */\n private _handleBack() {\n const id = this._openId;\n this._activeFace = 'front';\n // Return focus to the row the visitor opened from (a11y).\n this._pendingFocus = 'front';\n\n // Interrupted flip: clear any pending settle and restart in the new\n // direction.\n this._clearFlipSettleTimer();\n if (prefersReducedMotion()) {\n // No rotation to wait for \u2014 settle synchronously and clear `_openId`.\n this._flipAnimating = false;\n this._openId = null;\n } else {\n // Flip-out: keep `_openId` (so the back face stays populated through the\n // rotation, never blank mid-flip); the settle (animationend or belt timer)\n // clears it once the flip visibly finishes.\n this._flipDir = 'out';\n this._flipAnimating = true;\n this._flipSettleTimer = setTimeout(this._settleFlip, this._flipDurationMs());\n }\n\n if (id !== null) {\n this.runtime?.events.publish('faq:toggled', {\n instanceId: this.instanceId,\n questionId: id,\n expanded: false,\n timestamp: Date.now(),\n });\n }\n }\n\n /** Tear down the flip's 3D machinery so the shown face renders FLAT. Fired by\n * the container's `animationend`, the belt timer, or synchronously under\n * reduced motion. A flip-OUT (back\u2192front) also clears `_openId` here \u2014 the\n * moment it's finally safe to empty the back face without blanking it\n * mid-rotation. */\n private _settleFlip = (): void => {\n this._clearFlipSettleTimer();\n this._flipAnimating = false;\n if (this._activeFace === 'front') {\n this._openId = null;\n }\n };\n\n /** animationend on the flip container \u2192 the rotation is done, render flat. */\n private _onFlipAnimationEnd = (e: AnimationEvent): void => {\n if (e.animationName === 'sc-faq-flip-in' || e.animationName === 'sc-faq-flip-out') {\n this._settleFlip();\n }\n };\n\n private _clearFlipSettleTimer(): void {\n if (this._flipSettleTimer !== null) {\n clearTimeout(this._flipSettleTimer);\n this._flipSettleTimer = null;\n }\n }\n\n private _handleFeedback(itemId: string, question: string, value: FeedbackValue) {\n const next = new Map(this._feedbackState);\n next.set(itemId, value);\n this._feedbackState = next;\n\n this.runtime?.events.publish('faq:feedback', { itemId, question, value });\n }\n\n // -----------------------------------------------------------------------\n // Computed helpers\n // -----------------------------------------------------------------------\n\n /**\n * Unified render list. Merges compositionally-appended rows\n * (`faqConfig.actions`, the container-then-stream path) with atomically\n * authored rows (`faqConfig.questions`, the struct_list path) normalized\n * into the same `FAQQuestionAction` shape. The atomic path is how the LLM\n * mounts a complete FAQ in one call \u2014 so it renders whole, never empty.\n */\n private _allQuestions(): FAQQuestionAction[] {\n const compositional = this.faqConfig.actions ?? [];\n // Content-derived id (not positional). Expand/feedback state is keyed by id;\n // if the LLM re-mounts the FAQ with a reordered or shortened list, a\n // positional `atomic-${i}` would re-point that state at a different\n // question. Hashing the question text keeps the id stable per question\n // across re-mounts. A `-N` suffix disambiguates same-text rows (including\n // empty strings, which all hash to the same value) so they never share\n // expand/feedback state \u2014 stable as long as their relative order is.\n const seen = new Map<string, number>();\n const atomic = (this.faqConfig.questions ?? []).map((q): FAQQuestionAction => {\n let id = `atomic-${hashFaqId(q.question)}`;\n const dup = seen.get(id) ?? 0;\n seen.set(id, dup + 1);\n if (dup > 0) id = `${id}-${dup}`;\n return {\n kind: 'faq:question',\n config: {\n id,\n question: q.question,\n answer: q.answer,\n category: q.category,\n },\n };\n });\n return [...atomic, ...compositional];\n }\n\n private _visibleQuestions(): FAQQuestionAction[] {\n return this._allQuestions().filter((q) => {\n if (!q.triggerWhen) return true;\n if (!this.runtime) return true;\n const result = this.runtime.evaluateSync<boolean>(q.triggerWhen);\n return result.value;\n });\n }\n\n private _orderedQuestions(visible: FAQQuestionAction[]): FAQQuestionAction[] {\n if (this.faqConfig.ordering === 'priority') {\n return [...visible].sort((a, b) => (b.config.priority ?? 0) - (a.config.priority ?? 0));\n }\n return visible;\n }\n\n private _filteredQuestions(ordered: FAQQuestionAction[]): FAQQuestionAction[] {\n const q = this._searchQuery.trim().toLowerCase();\n if (!this.faqConfig.searchable || !q) return ordered;\n return ordered.filter(\n (item) =>\n item.config.question.toLowerCase().includes(q) ||\n getAnswerText(item.config.answer).toLowerCase().includes(q) ||\n item.config.category?.toLowerCase().includes(q)\n );\n }\n\n private _categoryGroups(\n filtered: FAQQuestionAction[]\n ): Map<string | undefined, FAQQuestionAction[]> {\n const groups = new Map<string | undefined, FAQQuestionAction[]>();\n for (const item of filtered) {\n const cat = item.config.category;\n if (!groups.has(cat)) groups.set(cat, []);\n groups.get(cat)!.push(item);\n }\n return groups;\n }\n\n // -----------------------------------------------------------------------\n // Render helpers\n // -----------------------------------------------------------------------\n\n /**\n * The `data-adaptive-id` scope for this element's injected <style>. When\n * `instanceId` is left at the shared default (`'faq-widget'`), two tiles would\n * both scope to `[data-adaptive-id=\"faq-widget\"]` and cross-style each other.\n * In that case we fall back to a per-element generated uid so each default tile\n * gets its own scope. A caller-provided instanceId is used verbatim.\n */\n private get _scopeId(): string {\n if (this.instanceId && this.instanceId !== DEFAULT_INSTANCE_ID) return this.instanceId;\n if (this._generatedUid === null) {\n _faqUidCounter += 1;\n this._generatedUid = `faq-${Date.now().toString(36)}-${_faqUidCounter}`;\n }\n return this._generatedUid;\n }\n\n /**\n * The flip transition duration in ms, coupled to the CSS. Reads the computed\n * `--vc-transition-duration` token off this element at runtime so the JS timer\n * that tears down the back face (see {@link _handleBack}) is ALWAYS >= the CSS\n * transition \u2014 a host that sets the token slower than our hardcoded default no\n * longer blanks the back face before the flip visually finishes. Falls back to\n * {@link FLIP_TRANSITION_MS} when the token is absent/unreadable (no live\n * layout, jsdom). We add a small buffer and take the max of the token and the\n * fallback so the timer never undershoots either.\n */\n private _flipDurationMs(): number {\n let tokenMs = 0;\n if (typeof window !== 'undefined' && typeof window.getComputedStyle === 'function') {\n try {\n const raw = window\n .getComputedStyle(this)\n .getPropertyValue('--vc-transition-duration')\n .trim();\n if (raw.endsWith('ms')) tokenMs = Number.parseFloat(raw);\n else if (raw.endsWith('s')) tokenMs = Number.parseFloat(raw) * 1000;\n } catch {\n // getComputedStyle can throw on a detached element \u2014 fall through to the\n // hardcoded default below.\n }\n }\n // A malformed token (e.g. \"abcms\") makes Number.parseFloat return NaN. Left\n // unguarded, Math.max(NaN, fallback) is NaN \u2192 setTimeout(\u2026, NaN) \u2248 0, which\n // clears _openId instantly and blanks the back face mid-flip \u2014 the exact bug\n // this method exists to prevent. Treat any non-finite token as absent so we\n // fall back to FLIP_TRANSITION_MS.\n if (!Number.isFinite(tokenMs)) tokenMs = 0;\n // Guarantee the timer >= CSS transition (whichever is larger) plus a buffer,\n // so the back face is never torn down before the flip settles.\n return Math.max(tokenMs, FLIP_TRANSITION_MS) + (tokenMs > 0 ? FLIP_CLEAR_BUFFER_MS : 0);\n }\n\n /**\n * Self-injected flip CSS. The FAQ tile is a light-DOM widget mounted across\n * several canvases (velvet, default, onflow) and standalone \u2014 velvet ships a\n * GENERIC, unscoped `[data-active-face]` flip rule (for the product card), but\n * the other hosts don't. So the FAQ carries its own copy of the SAME 3D-flip\n * technique, scoped to THIS instance (`[data-adaptive-id=\"\u2026\"]`) so it can't\n * leak onto a sibling widget.\n *\n * Two deliberate departures from a naive copy of velvet's rule:\n *\n * 1. PERSPECTIVE goes on the PARENT viewport wrapper\n * (`[data-faq-flip-viewport]`), NOT on the rotating `[data-faq-flip]`\n * element. CSS `perspective` only gives depth to a DESCENDANT's transform \u2014\n * putting it on the element that itself rotates does nothing (a flat\n * squash). We ALSO fold `perspective(...)` into the\n * rotate transform itself as a belt-and-braces measure, so the flip has\n * real 3D depth even inside velvet's bounded `overflow:hidden` deck card\n * (which can otherwise flatten a parent `perspective`).\n * 2. The active-face attribute is NAMESPACED (`data-faq-active-face`), so\n * velvet's generic `[data-active-face]` rotate rule can't ALSO reach this\n * container and double-apply its (perspective-less) transform.\n *\n * It mirrors the product card / velvet house style: rotateY, preserve-3d,\n * backface-visibility, and the `--vc-transition-duration` / `--vc-transition-\n * easing` tokens (with a ~240ms fallback) so the two tiles feel consistent.\n * `prefers-reduced-motion` disables the rotation \u2014 the JS (see\n * {@link _handleBack}) clears state synchronously in that case.\n */\n private _renderFlipStyles() {\n const scope = `[data-adaptive-id=\"${this._scopeId}\"]`;\n const css = `\n ${scope} [data-faq-flip] {\n position: relative;\n }\n /* --- 3D machinery: ONLY while the flip is animating --- */\n /* Perspective (the composited GPU context) lives on the parent viewport,\n gated to the animating window so the RESTING open face never sits on a\n perspective layer (GPU/compositing hazard: rotateY+perspective at rest\n blanks text on some Android GPUs, rasterizes soft everywhere). */\n ${scope} [data-faq-flip-viewport][data-faq-flip-animating] {\n perspective: 1000px;\n }\n ${scope} [data-faq-flip][data-faq-flip-animating] {\n transform-style: preserve-3d;\n }\n ${scope} [data-faq-flip][data-faq-flip-animating] > [data-face] {\n backface-visibility: hidden;\n -webkit-backface-visibility: hidden;\n }\n ${scope} [data-faq-flip][data-faq-flip-animating] > [data-face=\"front\"] {\n position: relative;\n }\n ${scope} [data-faq-flip][data-faq-flip-animating] > [data-face=\"back\"] {\n position: absolute;\n inset: 0;\n transform: rotateY(180deg);\n }\n ${scope} [data-faq-flip][data-faq-flip-animating][data-faq-flip-dir=\"in\"] {\n animation: sc-faq-flip-in var(--vc-transition-duration, 240ms)\n var(--vc-transition-easing, ease-out) forwards;\n }\n ${scope} [data-faq-flip][data-faq-flip-animating][data-faq-flip-dir=\"out\"] {\n animation: sc-faq-flip-out var(--vc-transition-duration, 240ms)\n var(--vc-transition-easing, ease-out) forwards;\n }\n @keyframes sc-faq-flip-in {\n from { transform: perspective(1000px) rotateY(0deg); }\n to { transform: perspective(1000px) rotateY(180deg); }\n }\n @keyframes sc-faq-flip-out {\n from { transform: perspective(1000px) rotateY(180deg); }\n to { transform: perspective(1000px) rotateY(0deg); }\n }\n /* --- RESTING state: the active face is FLAT, the inactive face is gone --- */\n ${scope} [data-faq-flip]:not([data-faq-flip-animating]) {\n transform: none;\n }\n ${scope} [data-faq-flip]:not([data-faq-flip-animating]) > [data-face] {\n position: relative;\n transform: none;\n }\n ${scope} [data-faq-flip]:not([data-faq-flip-animating])[data-faq-active-face=\"front\"] > [data-face=\"back\"] {\n display: none;\n }\n ${scope} [data-faq-flip]:not([data-faq-flip-animating])[data-faq-active-face=\"back\"] > [data-face=\"front\"] {\n display: none;\n }\n @media (prefers-reduced-motion: reduce) {\n ${scope} [data-faq-flip] { animation: none; }\n }\n `;\n return html`<style>${css}</style>`;\n }\n\n private _renderAnswer(answer: FAQAnswer) {\n const html_str = renderAnswerHtml(answer);\n // Item 5: cap the answer render to a few readable lines (a declared\n // line-clamp ellipsis box). A longer answer isn't scrolled in the cramped\n // takeover \u2014 \"Dive deeper\" carries the visitor to the full answer in chat.\n return html`<div style=${styleMap(sm(baseStyles.detailAnswerClamp))} data-faq-markdown=\"\">${unsafeHTML(html_str)}</div>`;\n }\n\n private _renderFeedback(\n item: FAQQuestionAction,\n feedbackConfig: FeedbackConfig,\n feedbackValue: FeedbackValue | undefined,\n theme: 'light' | 'dark'\n ) {\n const colors = themeStyles[theme];\n const feedbackStyle = { ...baseStyles.feedback, ...colors.feedbackPrompt };\n\n return html`\n <div style=${styleMap(sm(feedbackStyle))}>\n <span>${getFeedbackPrompt(feedbackConfig)}</span>\n <button\n type=\"button\"\n style=${styleMap(\n sm({\n ...baseStyles.feedbackButton,\n ...(feedbackValue === 'up' ? baseStyles.feedbackButtonSelected : {}),\n })\n )}\n aria-label=\"Thumbs up\"\n @click=${() => this._handleFeedback(item.config.id, item.config.question, 'up')}\n >\\uD83D\\uDC4D</button>\n <button\n type=\"button\"\n style=${styleMap(\n sm({\n ...baseStyles.feedbackButton,\n ...(feedbackValue === 'down' ? baseStyles.feedbackButtonSelected : {}),\n })\n )}\n aria-label=\"Thumbs down\"\n @click=${() => this._handleFeedback(item.config.id, item.config.question, 'down')}\n >\\uD83D\\uDC4E</button>\n </div>\n `;\n }\n\n /**\n * LIST-view row. A tappable question with its chevron \\u2014 but NO inline answer\n * body. Tapping it takes over the tile with the question's detail view (see\n * {@link _renderDetail}). Keeps the per-row look/markers/highlight from the\n * old accordion minus the inline-expanded answer that overflowed the tile.\n */\n private _renderItem(item: FAQQuestionAction, isLast: boolean, theme: 'light' | 'dark') {\n const colors = themeStyles[theme];\n const isHighlighted = this._highlightId === item.config.id;\n const isHovered = this._hoveredId === item.config.id;\n\n const itemStyle = {\n ...baseStyles.item,\n ...colors.item,\n ...(isHighlighted\n ? {\n boxShadow: `0 0 0 2px ${purple[4]}, 0 0 12px rgba(106, 89, 206, 0.4)`,\n transition: 'box-shadow 0.3s ease',\n }\n : {}),\n ...(!isLast ? { borderBottom: 'var(--sc-content-item-divider, none)' } : {}),\n };\n\n const questionStyle = {\n ...baseStyles.question,\n ...colors.question,\n ...(isHovered ? colors.questionHover : {}),\n };\n\n const chevronStyle = {\n ...baseStyles.chevron,\n transform: 'rotate(0deg)',\n };\n\n return html`\n <div\n style=${styleMap(sm(itemStyle))}\n data-faq-item-id=${item.config.id}\n >\n <button\n type=\"button\"\n style=${styleMap(sm(questionStyle))}\n aria-expanded=${false}\n @click=${() => this._handleOpen(item.config.id)}\n @mouseenter=${() => {\n this._hoveredId = item.config.id;\n }}\n @mouseleave=${() => {\n this._hoveredId = null;\n }}\n >\n <span>${item.config.question}</span>\n <span style=${styleMap(sm(chevronStyle))}>\\u203A</span>\n </button>\n </div>\n `;\n }\n\n private _renderItems(items: FAQQuestionAction[], theme: 'light' | 'dark') {\n return items.map((item, index) => this._renderItem(item, index === items.length - 1, theme));\n }\n\n /**\n * DETAIL view \\u2014 the master\\u2192detail takeover. Renders ONLY the open question:\n * a back affordance, the question prominently, and its full answer via the\n * SAME {@link _renderAnswer}/`renderAnswerHtml` path (all answer formats still\n * work). The answer area scrolls if extremely long, so a single answer fills\n * the bounded velvet tile and never pushes content past the `overflow:hidden`\n * clip.\n */\n private _renderDetail(\n item: FAQQuestionAction,\n theme: 'light' | 'dark',\n feedbackConfig: FeedbackConfig | null\n ) {\n const colors = themeStyles[theme];\n\n const backStyle = {\n ...baseStyles.detailBack,\n ...colors.question,\n };\n const detailQuestionStyle = {\n ...baseStyles.detailQuestion,\n ...colors.question,\n };\n const detailAnswerStyle = {\n ...baseStyles.detailAnswer,\n ...colors.answer,\n };\n\n return html`\n <div style=${styleMap(sm(baseStyles.detail))} data-faq-detail=${item.config.id}>\n <button\n type=\"button\"\n style=${styleMap(sm(backStyle))}\n data-faq-back=\"\"\n aria-label=\"Back to questions\"\n @click=${() => this._handleBack()}\n >\n \\u2039 Back to questions\n </button>\n\n <div style=${styleMap(sm(detailQuestionStyle))} data-faq-detail-question>${item.config.question}</div>\n\n <div style=${styleMap(sm(detailAnswerStyle))} aria-hidden=${false}>\n ${this._renderAnswer(item.config.answer)}\n <div style=${styleMap(sm(baseStyles.detailFooter))}>\n <button\n type=\"button\"\n style=${styleMap(sm(baseStyles.detailDiveDeeper))}\n data-faq-dive-deeper\n @click=${() => this._handleDiveDeeper(item)}\n >Dive deeper \u203A</button>\n </div>\n ${\n feedbackConfig\n ? this._renderFeedback(\n item,\n feedbackConfig,\n this._feedbackState.get(item.config.id),\n theme\n )\n : nothing\n }\n </div>\n </div>\n `;\n }\n\n /**\n * \"Dive deeper\" (items 4/5) \u2014 hand the open question's context back to the\n * CHAT as a deep-dive turn, so a clamped answer never leaves the visitor\n * stuck. Canvas-agnostic: dispatches the neutral DIVE_DEEPER_EVENT; the\n * chat-bar mountable turns it into a user-visible turn. The answer text rides\n * along as (trimmed) context so the agent grounds its fuller reply.\n */\n private _handleDiveDeeper(item: FAQQuestionAction) {\n const question = item.config.question;\n const answerText = getAnswerText(item.config.answer);\n dispatchDiveDeeper({\n prompt: `Tell me more about: ${question}`,\n title: question,\n context: answerText ? answerText.slice(0, 500) : undefined,\n });\n }\n\n // -----------------------------------------------------------------------\n // Render\n // -----------------------------------------------------------------------\n\n override render() {\n const theme = resolveTheme(this.faqConfig.theme);\n const colors = themeStyles[theme];\n const feedbackConfig = resolveFeedbackConfig(this.faqConfig.feedback);\n\n const visible = this._visibleQuestions();\n const ordered = this._orderedQuestions(visible);\n const filtered = this._filteredQuestions(ordered);\n const hasCategories = filtered.some((q) => q.config.category);\n const groups = hasCategories ? this._categoryGroups(filtered) : null;\n\n const containerStyle = {\n ...baseStyles.container,\n ...colors.container,\n };\n\n const emptyStateStyle = {\n ...baseStyles.emptyState,\n ...colors.emptyState,\n };\n\n const categoryHeaderStyle = {\n ...baseStyles.categoryHeader,\n ...colors.categoryHeader,\n };\n\n const searchInputStyle = {\n ...baseStyles.searchInput,\n ...colors.searchInput,\n };\n\n // Empty state \u2014 no visible questions at all.\n //\n // A runtime-backed FAQ is a compositional container: its `faq:question`\n // items stream in as `element.compositional_append` events after mount\n // (and a replay request). Rendering the \"all set\" reassurance box while\n // we're still empty flashes an empty shell on every mount \u2014 and leaves\n // an orphaned empty box if the agent mounted a container it never filled\n // (the recurring \"empty FAQ showed up then disappeared\" bug). Render\n // nothing until the first question lands. The reassurance copy is only\n // for a standalone (runtime-less) FAQ configured directly with no items.\n if (visible.length === 0) {\n if (this.runtime) return nothing;\n return html`\n <div\n style=${styleMap(sm(containerStyle))}\n data-adaptive-id=${this._scopeId}\n data-adaptive-type=\"adaptive-faq\"\n >\n <div style=${styleMap(sm(emptyStateStyle))}>\n You're all set for now! We'll surface answers here when they're relevant to what\n you're doing.\n </div>\n </div>\n `;\n }\n\n // Two-face FLIP takeover. BOTH faces are always in the DOM (a flip animates\n // between two live faces): front = the question LIST, back = the open\n // question's DETAIL. `_activeFace` drives the CSS rotateY via\n // `data-active-face` \u2014 the SAME mechanism the product card uses.\n //\n // We resolve `_openId` against the *visible/ordered* list (`ordered`), NOT\n // the search-filtered list (`filtered`) \u2014 search is a list-view concern\n // only, so a `faq:open` for a question that doesn't match an active\n // `_searchQuery` must still open its detail. A genuinely stale id (gated out\n // via triggerWhen, or removed) is absent from `ordered`, so the back face\n // renders empty and the container stays on the front face.\n const openItem =\n this._openId !== null ? ordered.find((q) => q.config.id === this._openId) : undefined;\n // If the open id resolved to nothing (stale), fall back to the front face so\n // we never show a rotated-but-empty back face.\n const activeFace = openItem ? this._activeFace : 'front';\n const backActive = activeFace === 'back';\n const flipAnimating = this._flipAnimating;\n\n return html`\n <div\n style=${styleMap(sm(containerStyle))}\n data-adaptive-id=${this._scopeId}\n data-adaptive-type=\"adaptive-faq\"\n >\n ${this._renderFlipStyles()}\n <div data-faq-flip-viewport ?data-faq-flip-animating=${flipAnimating}>\n <div\n data-faq-flip\n data-faq-active-face=${activeFace}\n ?data-faq-flip-animating=${flipAnimating}\n data-faq-flip-dir=${flipAnimating ? this._flipDir : nothing}\n @animationend=${this._onFlipAnimationEnd}\n >\n <div data-face=\"front\" ?inert=${backActive} aria-hidden=${backActive}>\n ${this._renderListFace(\n groups,\n filtered,\n theme,\n searchInputStyle,\n categoryHeaderStyle,\n {\n ...baseStyles.noResults,\n ...colors.emptyState,\n }\n )}\n </div>\n <div data-face=\"back\" ?inert=${!backActive} aria-hidden=${!backActive}>\n ${openItem ? this._renderDetail(openItem, theme, feedbackConfig) : nothing}\n </div>\n </div>\n </div>\n </div>\n `;\n }\n\n /** The FRONT face \u2014 search box, the question list (optionally grouped by\n * category), and the no-results message. Extracted so both faces can live in\n * the DOM at once for the flip. */\n private _renderListFace(\n groups: Map<string | undefined, FAQQuestionAction[]> | null,\n filtered: FAQQuestionAction[],\n theme: 'light' | 'dark',\n searchInputStyle: Record<string, unknown>,\n categoryHeaderStyle: Record<string, unknown>,\n noResultsStyle: Record<string, unknown>\n ) {\n return html`\n ${\n this.faqConfig.searchable\n ? html`\n <div style=${styleMap(sm(baseStyles.searchWrapper))}>\n <style>\n [data-adaptive-id=\"${this._scopeId}\"] input::placeholder {\n color: var(--sc-content-search-color, inherit);\n opacity: 0.7;\n }\n </style>\n <input\n type=\"text\"\n placeholder=\"Search questions...\"\n .value=${this._searchQuery}\n style=${styleMap(sm(searchInputStyle))}\n @input=${(e: InputEvent) => {\n this._searchQuery = (e.target as HTMLInputElement).value;\n }}\n />\n </div>\n `\n : nothing\n }\n\n <div style=${styleMap(sm(baseStyles.accordion))}>\n ${\n groups\n ? Array.from(groups.entries()).map(\n ([category, items]) => html`\n ${\n category\n ? html`\n <div\n style=${styleMap(sm(categoryHeaderStyle))}\n data-category-header=${category}\n >\n ${category}\n </div>\n `\n : nothing\n }\n ${this._renderItems(items, theme)}\n `\n )\n : this._renderItems(filtered, theme)\n }\n </div>\n\n ${\n this.faqConfig.searchable && filtered.length === 0 && this._searchQuery\n ? html`\n <div style=${styleMap(sm(noResultsStyle))}>\n No questions found matching "${this._searchQuery}"\n </div>\n `\n : nothing\n }\n `;\n }\n}\n\n// ============================================================================\n// Custom element registration\n// ============================================================================\n\nif (!customElements.get('syntro-faq-accordion')) {\n customElements.define('syntro-faq-accordion', FAQAccordionElement);\n}\n\nexport default FAQAccordionElement;\n", "/**\n * Adaptive FAQ - Styles\n *\n * BaseStyles object (font, layout, colors, animations) and theme integration\n * for the FAQWidget component.\n */\n\nimport { purple, slateGrey } from '@syntro/design-system/tokens';\n\n// ============================================================================\n// Base Styles\n// ============================================================================\n\nexport const baseStyles = {\n container: {\n fontFamily: 'var(--sc-font-family, system-ui, -apple-system, sans-serif)',\n maxWidth: '800px',\n margin: '0 auto',\n },\n searchWrapper: {\n marginBottom: '8px',\n },\n searchInput: {\n width: '100%',\n padding: '12px 16px',\n borderRadius: '8px',\n fontSize: '14px',\n outline: 'none',\n transition: 'border-color 0.15s ease',\n backgroundColor: 'var(--sc-content-search-background)',\n color: 'var(--sc-content-search-color)',\n },\n accordion: {\n display: 'flex',\n flexDirection: 'column' as const,\n gap: 'var(--sc-content-item-gap, 6px)',\n },\n item: {\n borderRadius: 'var(--sc-content-item-border-radius, 10px)',\n border: 'var(--sc-content-item-border, 1px solid rgba(0, 0, 0, 0.08))',\n background: 'var(--sc-content-item-background, rgba(255, 255, 255, 0.5))',\n overflow: 'hidden',\n transition: 'box-shadow 0.15s ease',\n },\n question: {\n width: '100%',\n padding: 'var(--sc-content-item-padding, 12px 16px)',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n border: 'none',\n cursor: 'pointer',\n fontSize: 'var(--sc-content-item-font-size, 15px)',\n fontWeight: 500,\n textAlign: 'left' as const,\n transition: 'background-color 0.15s ease',\n },\n chevron: {\n fontSize: '20px',\n transition: 'transform 0.2s ease',\n color: 'var(--sc-content-chevron-color, currentColor)',\n },\n answer: {\n padding: 'var(--sc-content-body-padding, 0 16px 12px 16px)',\n fontSize: 'var(--sc-content-body-font-size, 14px)',\n lineHeight: 1.6,\n overflow: 'hidden',\n transition: 'max-height 0.2s ease, padding 0.2s ease',\n },\n category: {\n display: 'inline-block',\n fontSize: '11px',\n fontWeight: 600,\n textTransform: 'uppercase' as const,\n letterSpacing: '0.05em',\n padding: '4px 8px',\n borderRadius: '4px',\n marginBottom: '8px',\n },\n categoryHeader: {\n fontSize: 'var(--sc-content-category-font-size, 12px)',\n fontWeight: 700,\n textTransform: 'uppercase' as const,\n letterSpacing: '0.05em',\n padding: 'var(--sc-content-category-padding, 8px 4px 4px 4px)',\n marginTop: 'var(--sc-content-category-gap, 4px)',\n },\n feedback: {\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n marginTop: '12px',\n paddingTop: '10px',\n borderTop: '1px solid rgba(0, 0, 0, 0.08)',\n fontSize: '13px',\n },\n feedbackButton: {\n background: 'none',\n border: '1px solid transparent',\n cursor: 'pointer',\n fontSize: '16px',\n padding: '4px 8px',\n borderRadius: '4px',\n transition: 'background-color 0.15s ease, border-color 0.15s ease',\n },\n feedbackButtonSelected: {\n borderColor: 'rgba(0, 0, 0, 0.2)',\n backgroundColor: 'rgba(0, 0, 0, 0.04)',\n },\n // \u2500\u2500 Master\u2192detail takeover \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Item 5: the detail view is a tight stack \u2014 back, then the QUESTION with the\n // ANSWER hugging right below it (the old 12px flex gap between Q and A wasted\n // vertical space and pushed the answer down). Row spacing is now set by\n // explicit per-element margins, so gap:0 here.\n detail: {\n display: 'flex',\n flexDirection: 'column' as const,\n gap: '0',\n minHeight: '0',\n // Inset the master\u2192detail takeover to match the list questions' padding so\n // a single Q&A taking over the tile doesn't render flush to the edges.\n padding: 'var(--sc-content-item-padding, 12px 16px)',\n },\n detailBack: {\n display: 'inline-flex',\n alignItems: 'center',\n gap: '4px',\n alignSelf: 'flex-start',\n background: 'none',\n border: 'none',\n padding: '4px 0',\n // Clear space below the back control (was the container gap).\n marginBottom: '10px',\n cursor: 'pointer',\n fontSize: '13px',\n fontWeight: 600,\n opacity: 0.85,\n },\n detailQuestion: {\n fontSize: 'var(--sc-content-item-font-size, 16px)',\n fontWeight: 600,\n lineHeight: 1.4,\n // Tight to the answer \u2014 kill the wasted Q\u2194A gap.\n marginBottom: '4px',\n },\n detailAnswer: {\n fontSize: 'var(--sc-content-body-font-size, 14px)',\n lineHeight: 1.6,\n // No longer a long scroll region \u2014 the answer render is line-clamped\n // (detailAnswerClamp) and \"Dive deeper\" carries the user to the full answer\n // in chat. flex:1 keeps the footer (dive-deeper + feedback) pinned below.\n display: 'flex',\n flexDirection: 'column' as const,\n flex: '1 1 auto',\n minHeight: '0',\n },\n // The answer TEXT render, capped to a few readable lines with a visible\n // ellipsis cap (a declared -webkit-line-clamp box \u2014 the clip battery treats\n // this as an intentional ellipsis, not a hidden-content bug). The cap is\n // token-tunable so a roomy surface can raise it.\n detailAnswerClamp: {\n margin: '0',\n display: '-webkit-box',\n webkitLineClamp: 'var(--sc-faq-answer-clamp, 4)',\n webkitBoxOrient: 'vertical' as const,\n overflow: 'hidden',\n },\n // Footer row under the answer: the \"Dive deeper\" chip sits bottom-right.\n detailFooter: {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'flex-end',\n gap: '8px',\n marginTop: '8px',\n },\n detailDiveDeeper: {\n flex: '0 0 auto',\n padding: '4px 10px',\n borderRadius: '9999px',\n border: '1px solid var(--sc-color-border, rgba(0,0,0,0.14))',\n background: 'transparent',\n cursor: 'pointer',\n font: '600 12px/1.2 var(--sc-font-family, inherit)',\n color: 'var(--sc-color-primary, #3d8a5e)',\n whiteSpace: 'nowrap' as const,\n },\n emptyState: {\n textAlign: 'center' as const,\n padding: '48px 24px',\n fontSize: '14px',\n },\n noResults: {\n textAlign: 'center' as const,\n padding: '32px 16px',\n fontSize: '14px',\n },\n} as const;\n\n// ============================================================================\n// Theme Styles\n// ============================================================================\n\nexport const themeStyles = {\n light: {\n container: {\n backgroundColor: 'transparent',\n color: 'inherit',\n },\n searchInput: {\n border: `1px solid ${slateGrey[11]}`,\n },\n item: {\n backgroundColor: 'var(--sc-content-background)',\n borderTop: 'var(--sc-content-border)',\n borderRight: 'var(--sc-content-border)',\n borderBottom: 'var(--sc-content-border)',\n borderLeft: 'var(--sc-content-border)',\n },\n itemExpanded: {\n boxShadow: '0 4px 12px rgba(0, 0, 0, 0.08)',\n },\n question: {\n backgroundColor: 'transparent',\n color: 'var(--sc-content-text-color)',\n },\n questionHover: {\n backgroundColor: 'var(--sc-content-background-hover)',\n },\n answer: {\n color: 'var(--sc-content-text-secondary-color)',\n },\n category: {\n backgroundColor: purple[8],\n color: purple[2],\n },\n categoryHeader: {\n color: slateGrey[7],\n },\n emptyState: {\n color: slateGrey[8],\n },\n feedbackPrompt: {\n color: slateGrey[7],\n },\n },\n dark: {\n container: {\n backgroundColor: 'transparent',\n color: 'inherit',\n },\n searchInput: {\n border: `1px solid ${slateGrey[5]}`,\n },\n item: {\n backgroundColor: 'var(--sc-content-background)',\n borderTop: 'var(--sc-content-border)',\n borderRight: 'var(--sc-content-border)',\n borderBottom: 'var(--sc-content-border)',\n borderLeft: 'var(--sc-content-border)',\n },\n itemExpanded: {\n boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)',\n },\n question: {\n backgroundColor: 'transparent',\n color: 'var(--sc-content-text-color)',\n },\n questionHover: {\n backgroundColor: 'var(--sc-content-background-hover)',\n },\n answer: {\n color: 'var(--sc-content-text-secondary-color)',\n },\n category: {\n backgroundColor: purple[0],\n color: purple[6],\n },\n categoryHeader: {\n color: slateGrey[8],\n },\n emptyState: {\n color: slateGrey[7],\n },\n feedbackPrompt: {\n color: slateGrey[8],\n },\n },\n};\n", "/**\n * renderHealth \u2014 runtime self-detection of blank render and undeclared clipping\n * for the FAQ accordion.\n *\n * A tile can look correct in tests yet paint blank on a real host (a device\n * compositing quirk, a font that measures wider, a broken image CDN). After the\n * accordion paints, this module re-measures it and emits a PostHog signal via\n * the ONE consent-gated door (`window.SynOS.runtime.events.emit`) when it finds a\n * blank tile or an undeclared clip. Field-RUM for \"the FAQ broke on a customer's\n * page\".\n *\n * This is a lean, package-LOCAL copy of the pattern adaptive-product owns:\n * `@syntrologie/adapt-faq` does not (and should not) depend on\n * `@syntrologie/adapt-product`, and the two share no runtime-util package. The\n * blank check is identical; the clip check is the generic text-overflow subset\n * (the FAQ has no `[data-critical-text]` identity nodes, so the product card's\n * Tier-1 identity rule does not apply here).\n * Follow-up: extract a shared runtime-util package so both adaptives import one\n * copy.\n *\n * COE-051 lesson: `publish()` only notifies in-process canvas subscribers \u2014 it\n * NEVER reaches PostHog. `emit()` is the sanctioned capture path (the EventBus\n * forwards emit()'d events to its consent-gated posthogCapture sink). The event\n * name MUST satisfy validateEventName (`app:{category}:{action}`, colon segments)\n * or the bus silently drops it.\n *\n * NON-PII discipline: only element tag/class PATHS and pixel numbers ever leave\n * the widget \u2014 never text content, never customer copy.\n */\nimport type { ReactiveController, ReactiveControllerHost } from 'lit';\n\nexport interface ClipFinding {\n /** Structural path to the offending node (NON-PII), e.g. `div.faq>button`. */\n path: string;\n /** Horizontal overflow in px, rounded. */\n over_px: number;\n}\n\nexport interface RenderHealth {\n /** Undeclared clips found inside the tile (empty = clean). */\n clips: ClipFinding[];\n /** True when the tile occupies real area but painted no text and no complete\n * image (a blank tile). */\n blank: boolean;\n}\n\n/** Minimum tile area (px\u00B2) below which \"blank\" is not meaningful. */\nconst BLANK_MIN_AREA = 60 * 60;\n\n/** Short NON-PII path: up to 3 ancestors of `tag.class` joined by `>`. */\nfunction elementPath(el: Element, stopAt: Element): string {\n const seg = (e: Element): string => {\n const tag = e.tagName.toLowerCase();\n const cls =\n typeof e.className === 'string' && e.className.trim()\n ? `.${e.className.trim().split(/\\s+/)[0]}`\n : '';\n return `${tag}${cls}`;\n };\n const parts: string[] = [seg(el)];\n let cur: Element | null = el.parentElement;\n let hops = 0;\n while (cur && cur !== stopAt && hops < 3) {\n parts.unshift(seg(cur));\n cur = cur.parentElement;\n hops++;\n }\n return parts.join('>');\n}\n\n/** True if `el` (or an ancestor up to `root`) declares the clip is intentional. */\nfunction hasClipOkAncestor(el: Element, root: Element): boolean {\n let cur: Element | null = el;\n while (cur && cur !== root.parentElement) {\n if (cur.hasAttribute?.('data-clip-ok')) return true;\n cur = cur.parentElement;\n }\n return false;\n}\n\n/**\n * Measure the render health of a tile root. Pure DOM read \u2014 no side effects, no\n * telemetry. Safe to call any time after paint.\n * - text-clip: an element with its own text whose scrollWidth exceeds\n * clientWidth by >1px, with no textOverflow:ellipsis, not under [data-clip-ok].\n * - blank: the tile has real area but no painted text and no complete image.\n */\nexport function measureRenderHealth(root: Element): RenderHealth {\n const clips: ClipFinding[] = [];\n const seen = new Set<string>();\n const push = (f: ClipFinding): void => {\n const k = `${f.path}|${f.over_px}`;\n if (seen.has(k)) return;\n seen.add(k);\n clips.push(f);\n };\n\n let hasText = false;\n let hasCompleteImage = false;\n\n for (const el of root.querySelectorAll('*')) {\n const cs = getComputedStyle(el);\n if (cs.display === 'none' || cs.visibility === 'hidden') continue;\n const r = el.getBoundingClientRect();\n if (r.width === 0 || r.height === 0) continue;\n\n const ownText = Array.from(el.childNodes).some(\n (n) => n.nodeType === 3 && (n.textContent ?? '').trim().length > 0\n );\n if (ownText) hasText = true;\n if (el.tagName === 'IMG') {\n const img = el as HTMLImageElement;\n if (img.complete && img.naturalWidth > 0 && img.dataset.imageError !== 'true') {\n hasCompleteImage = true;\n }\n }\n\n if (\n ownText &&\n el.scrollWidth > el.clientWidth + 1 &&\n cs.textOverflow !== 'ellipsis' &&\n !hasClipOkAncestor(el, root)\n ) {\n push({ path: elementPath(el, root), over_px: el.scrollWidth - el.clientWidth });\n }\n }\n\n const rect = root.getBoundingClientRect();\n const area = rect.width * rect.height;\n const blank = area >= BLANK_MIN_AREA && !hasText && !hasCompleteImage;\n\n return { clips, blank };\n}\n\ninterface SynOSEventsWindow {\n SynOS?: {\n runtime?: {\n events?: {\n // The ONLY transport that reaches PostHog (COE-051).\n emit?: (name: string, props: Record<string, unknown>) => unknown;\n };\n };\n };\n}\n\n/** Emit a telemetry event on the PostHog-reaching path. Never throws. */\nfunction synEmit(name: string, props: Record<string, unknown>): void {\n if (typeof window === 'undefined') return;\n try {\n const emit = (window as unknown as SynOSEventsWindow).SynOS?.runtime?.events?.emit;\n if (typeof emit === 'function') emit(name, props);\n } catch {\n // Telemetry must never crash the widget.\n }\n}\n\nexport interface RenderHealthReportOptions {\n /** Event category: emits `app:<category>:blank` / `:clipped`. */\n category: string;\n /** `widget_kind` tag included in every payload. */\n widgetKind: string;\n /** Extra NON-PII context merged into every payload. */\n context?: Record<string, unknown>;\n}\n\nconst CLEAN: RenderHealth = { clips: [], blank: false };\n\n/** Measure `root` and, if it clips or paints blank, emit the self-detection\n * event via the consent-gated emit() path. Returns the measured health. Never\n * throws. */\nexport function reportRenderHealth(root: Element, opts: RenderHealthReportOptions): RenderHealth {\n try {\n const health = measureRenderHealth(root);\n if (health.clips.length === 0 && !health.blank) return health;\n const base: Record<string, unknown> = { ...(opts.context ?? {}), widget_kind: opts.widgetKind };\n if (health.blank) {\n synEmit(`app:${opts.category}:blank`, base);\n return health;\n }\n const paths = health.clips.map((c) => c.path).sort();\n const over_px = health.clips.reduce((m, c) => Math.max(m, c.over_px), 0);\n synEmit(`app:${opts.category}:clipped`, {\n paths: paths.join(','),\n over_px,\n clip_count: health.clips.length,\n ...base,\n });\n return health;\n } catch {\n return CLEAN;\n }\n}\n\n/** What the widget's probe returns for one measurement pass. */\nexport interface RenderHealthProbe extends RenderHealthReportOptions {\n /** The element to measure, or `null` to SKIP this pass (unsettled state). */\n root: Element | null;\n /** Stable key for the current visual state; a state reports AT MOST ONCE. */\n stateKey: string;\n}\n\n/**\n * Lit ReactiveController that runs a widget's render-health probe after each\n * paint settles (rAF then a 0ms timeout), at most once per visual state.\n */\nexport class RenderHealthController implements ReactiveController {\n #probe: () => RenderHealthProbe | null;\n #reported = new Set<string>();\n #raf: number | undefined;\n\n constructor(host: ReactiveControllerHost, probe: () => RenderHealthProbe | null) {\n this.#probe = probe;\n host.addController(this);\n }\n\n hostUpdated(): void {\n this.#schedule();\n }\n\n hostDisconnected(): void {\n this.#cancel();\n }\n\n #schedule(): void {\n if (typeof window === 'undefined') return;\n if (this.#raf !== undefined) return;\n const raf =\n typeof window.requestAnimationFrame === 'function'\n ? window.requestAnimationFrame.bind(window)\n : (cb: FrameRequestCallback) => window.setTimeout(() => cb(0), 0);\n this.#raf = raf(() => {\n window.setTimeout(() => {\n this.#raf = undefined;\n this.#run();\n }, 0);\n }) as unknown as number;\n }\n\n #run(): void {\n try {\n const probe = this.#probe();\n if (!probe || !probe.root) return;\n if (this.#reported.has(probe.stateKey)) return;\n this.#reported.add(probe.stateKey);\n reportRenderHealth(probe.root, probe);\n } catch {\n // Self-detection must never crash the widget.\n }\n }\n\n #cancel(): void {\n if (\n this.#raf !== undefined &&\n typeof window !== 'undefined' &&\n typeof window.cancelAnimationFrame === 'function'\n ) {\n window.cancelAnimationFrame(this.#raf);\n }\n this.#raf = undefined;\n }\n}\n", "/**\n * Adaptive FAQ - Runtime Module\n *\n * Runtime manifest for the FAQ accordion adaptive.\n * Mounts the Lit web component <syntro-faq-accordion>.\n * Provides action executors and widget registration.\n */\n\nimport { type MountPlumbing, stripMountPlumbing } from '@syntrologie/sdk-contracts';\nimport { executorDefinitions } from './executors';\nimport './FAQWidgetLit'; // registers <syntro-faq-accordion> custom element\nimport type { FAQWidgetRuntime } from './faq-types';\nimport type { FAQConfig, FAQQuestionAction } from './types';\n\n// ============================================================================\n// Lit Mountable Widget\n// ============================================================================\n\nconst DEFAULT_FAQ_CONFIG: FAQConfig = {\n expandBehavior: 'single',\n searchable: false,\n theme: 'auto',\n actions: [],\n};\n\n/**\n * Mountable widget interface for <syntro-faq-accordion> (Lit web component).\n */\nexport const FAQWidgetLitMountable = {\n mount(\n container: HTMLElement,\n config?: (FAQConfig & MountPlumbing & { runtime?: FAQWidgetRuntime }) | null\n ) {\n const incoming = config ?? null;\n const stripped = stripMountPlumbing<FAQConfig & { runtime?: FAQWidgetRuntime }>(incoming);\n // stripMountPlumbing does NOT strip the non-canonical `runtime` consumer key\n // on this widget because `runtime` is a canonical plumbing key. Pull the\n // explicit references back out for the element wiring.\n const runtime = incoming?.runtime as FAQWidgetRuntime | undefined;\n const instanceId = incoming?.instanceId ?? 'faq-widget';\n const faqConfig: FAQConfig = incoming ? (stripped as FAQConfig) : { ...DEFAULT_FAQ_CONFIG };\n\n const el = document.createElement('syntro-faq-accordion') as HTMLElement & {\n faqConfig: FAQConfig;\n runtime: FAQWidgetRuntime | null;\n instanceId: string;\n };\n\n Object.assign(el, {\n faqConfig,\n runtime: runtime ?? null,\n instanceId,\n });\n\n container.appendChild(el);\n return () => el.remove();\n },\n};\n\n// ============================================================================\n// App Runtime Manifest\n// ============================================================================\n\n/**\n * Runtime manifest for adaptive-faq.\n *\n * Provides:\n * - FAQ action executors (scroll_to, toggle_item, update)\n * - Widget-based accordion using the Lit web component\n */\nexport const runtime = {\n id: 'adaptive-faq',\n version: '2.0.0',\n name: 'FAQ Accordion',\n description:\n 'Collapsible Q&A accordion with actions, rich content, feedback, and personalization',\n\n /**\n * Action executors for programmatic FAQ interaction.\n */\n executors: executorDefinitions,\n\n /**\n * Widget definitions for the runtime's WidgetRegistry.\n */\n widgets: [\n {\n id: 'adaptive-faq:accordion',\n component: FAQWidgetLitMountable,\n metadata: {\n name: 'FAQ Accordion',\n description: 'Collapsible Q&A accordion with search, categories, and feedback',\n icon: '\u2753',\n subtitle: 'Curated just for you.',\n },\n },\n ],\n\n /**\n * Extract notify watcher entries from tile config props.\n * The runtime evaluates these continuously (even with drawer closed)\n * and publishes faq:question_revealed when triggerWhen transitions false \u2192 true.\n */\n notifyWatchers(props: Record<string, unknown>) {\n const actions = (props.actions ?? []) as FAQQuestionAction[];\n return actions\n .filter((a) => a.notify && a.triggerWhen)\n .map((a) => ({\n id: `faq:${a.config.id}`,\n strategy: a.triggerWhen!,\n eventName: 'faq:question_revealed',\n eventProps: {\n questionId: a.config.id,\n question: a.config.question,\n title: a.notify!.title,\n body: a.notify!.body,\n icon: a.notify!.icon,\n },\n }));\n },\n};\n\nexport default runtime;\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["/**\n * Adaptive FAQ - Action Executors\n *\n * Three executors that operate on the FAQStore:\n * - executeScrollToFaq: scroll to a FAQ item and optionally expand it\n * - executeToggleFaqItem: open / close / toggle a FAQ item\n * - executeUpdateFaq: add, remove, reorder, or replace FAQ items\n */\n\nimport type { FAQStore } from './state';\nimport type {\n ExecutorContext,\n ExecutorResult,\n FAQQuestionAction,\n ScrollToFaqAction,\n ToggleFaqItemAction,\n UpdateFaqAction,\n} from './types';\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n/**\n * Resolve a FAQ item by direct ID or by fuzzy question text match.\n * Throws if neither yields a result.\n */\nfunction resolveItem(store: FAQStore, itemId?: string, itemQuestion?: string): FAQQuestionAction {\n if (itemId) {\n const found = store.getState().items.find((i) => i.config.id === itemId);\n if (found) return found;\n }\n\n if (itemQuestion) {\n const found = store.findByQuestion(itemQuestion);\n if (found) return found;\n }\n\n throw new Error('FAQ item not found');\n}\n\n// ============================================================================\n// executeScrollToFaq\n// ============================================================================\n\n/**\n * Scroll to a FAQ item in the DOM and optionally expand it.\n *\n * Looks up the item by `itemId` or `itemQuestion`, scrolls the matching\n * `[data-faq-item-id]` element into view, and expands it unless\n * `expand` is explicitly set to `false`.\n */\nexport async function executeScrollToFaq(\n action: ScrollToFaqAction,\n context: ExecutorContext,\n store: FAQStore\n): Promise<ExecutorResult> {\n const item = resolveItem(store, action.itemId, action.itemQuestion);\n const { id } = item.config;\n\n // Expand the item unless explicitly told not to\n if (action.expand !== false) {\n store.expand(id);\n }\n\n // Scroll the DOM element into view (may be absent in test environments)\n const el = document.querySelector(`[data-faq-item-id=\"${id}\"]`);\n if (el) {\n el.scrollIntoView({\n behavior: action.behavior ?? 'smooth',\n });\n }\n\n // Publish analytics event\n context.publishEvent('faq:scroll_to', { itemId: id });\n\n return {\n cleanup: () => {\n // Optionally collapse on revert\n },\n };\n}\n\n// ============================================================================\n// executeToggleFaqItem\n// ============================================================================\n\n/**\n * Open, close, or toggle a FAQ item's expanded state.\n */\nexport async function executeToggleFaqItem(\n action: ToggleFaqItemAction,\n context: ExecutorContext,\n store: FAQStore\n): Promise<ExecutorResult> {\n const item = resolveItem(store, action.itemId, action.itemQuestion);\n const { id } = item.config;\n const desiredState = action.state ?? 'toggle';\n\n let newState: 'open' | 'closed';\n\n switch (desiredState) {\n case 'open':\n store.expand(id);\n newState = 'open';\n break;\n case 'closed':\n store.collapse(id);\n newState = 'closed';\n break;\n default: {\n const wasExpanded = store.getState().expandedItems.has(id);\n store.toggle(id);\n newState = wasExpanded ? 'closed' : 'open';\n break;\n }\n }\n\n context.publishEvent('faq:toggle', { itemId: id, newState });\n\n return {\n cleanup: () => {\n // Revert toggle on cleanup\n },\n };\n}\n\n// ============================================================================\n// executeUpdateFaq\n// ============================================================================\n\n/**\n * Add, remove, reorder, or replace FAQ items in the store.\n */\nexport async function executeUpdateFaq(\n action: UpdateFaqAction,\n context: ExecutorContext,\n store: FAQStore\n): Promise<ExecutorResult> {\n switch (action.operation) {\n case 'add': {\n const items = action.items ?? [];\n const position = action.position === 'prepend' ? 'prepend' : 'append';\n store.addItems(items, position);\n break;\n }\n\n case 'remove': {\n if (!action.itemId) {\n throw new Error('FAQ item not found');\n }\n // Verify the item exists before removing\n const exists = store.getState().items.some((i) => i.config.id === action.itemId);\n if (!exists) {\n throw new Error('FAQ item not found');\n }\n store.removeItem(action.itemId);\n break;\n }\n\n case 'reorder': {\n const order = action.order ?? [];\n store.reorderItems(order);\n break;\n }\n\n case 'replace': {\n const items = action.items ?? [];\n store.replaceItems(items);\n break;\n }\n }\n\n context.publishEvent('faq:update', { operation: action.operation });\n\n return {\n cleanup: () => {\n // Could snapshot previous state for undo\n },\n };\n}\n\n// ============================================================================\n// Executor Definitions for Registration\n// ============================================================================\n\n/**\n * All executors provided by adaptive-faq.\n * These are registered with the runtime's ExecutorRegistry.\n */\nexport const executorDefinitions = [\n { kind: 'faq:scroll_to', executor: executeScrollToFaq },\n { kind: 'faq:toggle_item', executor: executeToggleFaqItem },\n { kind: 'faq:update', executor: executeUpdateFaq },\n] as const;\n", "/**\n * Adaptive FAQ - FAQWidgetLit\n *\n * Lit web component equivalent of FAQWidget.tsx.\n * Renders a collapsible Q&A accordion with search, category grouping,\n * feedback, and markdown rendering \u2014 all as a custom element with no\n * Shadow DOM (light DOM via createRenderRoot).\n *\n * Tag name: <syntro-faq-accordion>\n *\n * Decorator-free: uses `static override properties` (tsconfig has no\n * experimentalDecorators).\n */\n\nimport { purple } from '@syntro/design-system/tokens';\nimport { dispatchDiveDeeper } from '@syntrologie/sdk-contracts';\nimport { html, LitElement, nothing } from 'lit';\nimport { styleMap } from 'lit/directives/style-map.js';\nimport { unsafeHTML } from 'lit/directives/unsafe-html.js';\nimport { getAnswerText, renderAnswerHtml } from './answerRendering';\nimport { baseStyles, themeStyles } from './faq-styles';\nimport type { FAQWidgetRuntime } from './faq-types';\nimport { RenderHealthController, type RenderHealthProbe } from './renderHealth';\nimport type {\n FAQAnswer,\n FAQConfig,\n FAQQuestionAction,\n FeedbackConfig,\n FeedbackValue,\n} from './types';\n\n// ============================================================================\n// Utility \u2014 styleMap accepts Record<string, string | number | undefined>\n// but its TypeScript signature is overly narrow in some Lit versions.\n// Cast through unknown to avoid false positives when style values include\n// numeric CSS properties (fontWeight, lineHeight, etc.).\n// ============================================================================\n\nfunction sm(styles: Record<string, unknown>): Record<string, string> {\n return styles as unknown as Record<string, string>;\n}\n\nfunction resolveFeedbackConfig(\n feedback: boolean | FeedbackConfig | undefined\n): FeedbackConfig | null {\n if (!feedback) return null;\n if (feedback === true) return { style: 'thumbs' };\n return feedback;\n}\n\nfunction getFeedbackPrompt(feedbackConfig: FeedbackConfig): string {\n return feedbackConfig.prompt ?? 'Was this helpful?';\n}\n\n/**\n * Small deterministic string hash (djb2, base36) for content-derived atomic\n * FAQ row ids \u2014 stable per question across re-mounts so expand/feedback state\n * keyed by id sticks to the right row even if the list reorders.\n */\nfunction hashFaqId(text: string): string {\n let h = 5381;\n for (let i = 0; i < text.length; i++) {\n h = (Math.imul(h, 33) ^ text.charCodeAt(i)) >>> 0;\n }\n return h.toString(36);\n}\n\n/**\n * Escape a value for safe use inside a DOUBLE-QUOTED CSS attribute selector\n * (`[attr=\"\u2026\"]`). Question ids are content-derived (hashed) or LLM-supplied, so\n * a stray double-quote or backslash would break `querySelector`. We escape only\n * the two characters that are special inside a double-quoted string \u2014 NOT via\n * `CSS.escape`, which escapes for an *identifier* context (unquoted) and would\n * over-escape here.\n */\nfunction cssAttrEscape(value: string): string {\n return value.replace(/[\"\\\\]/g, '\\\\$&');\n}\n\nfunction resolveTheme(theme: FAQConfig['theme'] | undefined): 'light' | 'dark' {\n if (theme && theme !== 'auto') return theme;\n if (typeof window !== 'undefined') {\n return window.matchMedia?.('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';\n }\n return 'light';\n}\n\n/** Whether the visitor has asked for reduced motion \u2014 the flip is disabled and\n * the takeover swaps instantly instead of rotating. */\nfunction prefersReducedMotion(): boolean {\n if (typeof window === 'undefined') return false;\n return window.matchMedia?.('(prefers-reduced-motion: reduce)').matches ?? false;\n}\n\n// Flip transition window FALLBACK. The velvet house tokens are ~240ms; we clear\n// the open question a little after that so the back face isn't blanked before\n// the flip visually completes. This is only the fallback used when the computed\n// `--vc-transition-duration` token can't be read (e.g. no live layout). At\n// runtime we read the actual token and clamp the timer to be >= it, so a host\n// that sets a slower token never blanks the back face mid-flip (see\n// {@link FAQAccordionElement._flipDurationMs}). Kept here so the timer and the\n// CSS agree on a sane default.\nconst FLIP_TRANSITION_MS = 360;\n\n// Minimum extra time added on top of the CSS transition duration before the\n// back face is torn down, so the flip has visibly settled first.\nconst FLIP_CLEAR_BUFFER_MS = 120;\n\n// The default (shared) instanceId. Two default-id tiles would cross-scope their\n// injected <style> (both scoped to `[data-adaptive-id=\"faq-widget\"]`), so when\n// the id is left at the default we fall back to a per-element generated uid.\nconst DEFAULT_INSTANCE_ID = 'faq-widget';\n\n// Monotonic counter for generated scope uids (default-instanceId fallback).\nlet _faqUidCounter = 0;\n\n// ============================================================================\n// FAQAccordionElement \u2014 Lit element\n// ============================================================================\n\n/**\n * <syntro-faq-accordion> \u2014 light-DOM Lit web component.\n *\n * Set properties imperatively (no attribute serialisation for objects):\n * el.faqConfig = { expandBehavior: 'single', ... };\n * el.runtime = runtimeInstance;\n * el.instanceId = 'my-faq';\n */\nexport class FAQAccordionElement extends LitElement {\n // -----------------------------------------------------------------------\n // Reactive properties (no decorators \u2014 tsconfig forbids experimentalDecorators)\n // -----------------------------------------------------------------------\n\n static override properties = {\n // Public API \u2014 set from the outside\n faqConfig: { attribute: false },\n runtime: { attribute: false },\n instanceId: { type: String },\n\n // Internal reactive state (prefixed with _ to signal \"private\")\n // _openId: currently-open question id for the master\u2192detail takeover.\n // null = LIST view; a value = DETAIL view showing only that single Q&A.\n // _activeFace: which face of the flip is showing \u2014 'front' (question list)\n // or 'back' (the selected question's answer detail). Both faces are always\n // in the DOM (a flip animates between two live faces); this attribute drives\n // the CSS rotateY, mirroring the product card's `data-active-face` idiom.\n _openId: { state: true },\n _activeFace: { state: true },\n // _flipAnimating: true ONLY while a flip rotation is running. The 3D flip\n // machinery (perspective, preserve-3d, rotateY, per-face backface) exists\n // solely during this window; once the flip settles it is torn down so the\n // resting face renders FLAT (transform:none) \u2014 a persistently\n // rotateY/perspective-composited face blanks its text on some Android GPUs\n // and rasterizes it soft everywhere. _flipDir selects which keyframes run.\n _flipAnimating: { state: true },\n _flipDir: { state: true },\n _highlightId: { state: true },\n _searchQuery: { state: true },\n _feedbackState: { state: true },\n _hoveredId: { state: true },\n };\n\n // -----------------------------------------------------------------------\n // Property declarations\n // -----------------------------------------------------------------------\n\n faqConfig: FAQConfig = {\n expandBehavior: 'single',\n searchable: false,\n theme: 'auto',\n actions: [],\n };\n\n runtime: FAQWidgetRuntime | null = null;\n\n instanceId: string = DEFAULT_INSTANCE_ID;\n\n // Per-element generated scope uid, used ONLY when `instanceId` is left at the\n // shared default so two default-id tiles don't cross-scope their injected\n // <style> (both would otherwise target `[data-adaptive-id=\"faq-widget\"]`).\n // Generated lazily and once per element (see {@link _scopeId}).\n private _generatedUid: string | null = null;\n\n // Internal state\n // The currently-open question id (null = list view, value = detail view).\n // The master\u2192detail takeover swaps the whole widget to a single Q&A so a\n // long answer never overflows the bounded velvet tile.\n _openId: string | null = null;\n // Which face is showing. Both faces are always mounted so the takeover FLIPS\n // (front = question list \u2194 back = the open question's answer detail) instead\n // of instantly swapping templates \u2014 matching the product card's flip feel.\n _activeFace: 'front' | 'back' = 'front';\n // True ONLY while a flip rotation is running (see the property doc above).\n _flipAnimating = false;\n // Which keyframes the running flip uses: 'in' = front\u2192back (open),\n // 'out' = back\u2192front (close).\n _flipDir: 'in' | 'out' = 'in';\n _highlightId: string | null = null;\n _searchQuery: string = '';\n _feedbackState: Map<string, FeedbackValue> = new Map();\n _hoveredId: string | null = null;\n\n // Belt timer that force-SETTLES the flip if the container's `animationend`\n // never fires (backgrounded tab, engine quirk, reduced-motion suppression).\n // Also the point at which a flip-OUT (back\u2192front) finally clears `_openId`,\n // so the back face isn't emptied (blanked) mid-flip. The delay is\n // {@link _flipDurationMs} (>= the CSS duration). Reduced motion settles\n // synchronously instead (no animation to wait for).\n private _flipSettleTimer: ReturnType<typeof setTimeout> | null = null;\n\n // Focus management (a11y): after a flip renders, move focus to the face that\n // just became active \u2014 the \u2039 Back button on flip-to-back, and the originating\n // question row on flip-to-front \u2014 so keyboard/AT users follow the takeover.\n // Consumed (and cleared) in `updated()`. `_focusReturnId` remembers WHICH row\n // opened the detail so focus returns to that exact row, not just the first.\n private _pendingFocus: 'front' | 'back' | null = null;\n private _focusReturnId: string | null = null;\n\n // Subscription cleanup handles\n private _unsubContext: (() => void) | null = null;\n private _unsubAccumulator: (() => void) | null = null;\n private _unsubCta: (() => void) | null = null;\n private _unsubDeepLink: (() => void) | null = null;\n private _unsubSessionMetrics: (() => void) | null = null;\n private _highlightTimer: ReturnType<typeof setTimeout> | null = null;\n\n // Compositional-child wiring \u2014 faq:question items the LLM mounts into this\n // accordion's tile arrive as `element.compositional_append` events (see\n // adaptive-chatbot's ItemHandler). Without this subscription the questions\n // are published into the void and the accordion renders empty.\n private _unsubCompositional: (() => void) | null = null;\n private _tileId: string | null = null;\n /** Instance ids already appended via the compositional bus \u2014 dedups\n * the subscribe-then-replay path (and re-subscribes on runtime change). */\n private _llmAppendedIds: Set<string> = new Set();\n\n constructor() {\n super();\n // Runtime self-detection: emits app:faq_accordion:blank / :clipped after\n // paint if the mounted accordion has area but paints no content (the blank\n // FAQ tile bug). Self-registers via host.addController \u2014 no stored ref.\n new RenderHealthController(this, () => this.#healthProbe());\n }\n\n /** Measure only the SETTLED accordion (at least one visible question). An\n * empty accordion renders `nothing` (runtime) or a reassurance box (no\n * runtime) and is never \"blank\". NON-PII stateKey. */\n #healthProbe(): RenderHealthProbe | null {\n const visible = this._visibleQuestions();\n if (visible.length === 0) return null;\n const root = this.querySelector('[data-adaptive-type=\"adaptive-faq\"]');\n if (!root) return null;\n return {\n root,\n stateKey: `faq|${visible.length}|${this._activeFace}`,\n category: 'faq_accordion',\n widgetKind: 'adaptive-faq:accordion',\n };\n }\n\n // -----------------------------------------------------------------------\n // Light DOM \u2014 no Shadow DOM so CSS variables from the host page apply\n // -----------------------------------------------------------------------\n\n override createRenderRoot() {\n return this;\n }\n\n // -----------------------------------------------------------------------\n // Lifecycle\n // -----------------------------------------------------------------------\n\n override connectedCallback() {\n super.connectedCallback();\n this._subscribeAll();\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback();\n this._unsubscribeAll();\n if (this._highlightTimer !== null) {\n clearTimeout(this._highlightTimer);\n this._highlightTimer = null;\n }\n this._clearFlipSettleTimer();\n }\n\n // Re-subscribe when runtime changes (property may be set after connectedCallback)\n override updated(changedProps: Map<string, unknown>) {\n if (changedProps.has('runtime')) {\n this._unsubscribeAll();\n this._subscribeAll();\n }\n this._applyPendingFocus();\n }\n\n /**\n * Move focus to the face that just became active after a flip (a11y). On\n * flip-to-back, focus the \u2039 Back button; on flip-to-front, focus the question\n * row that originally opened the detail (so keyboard/AT users land back where\n * they were). No-op when nothing is pending or the target isn't in the DOM\n * yet (the flip-to-front row may not exist if the list changed).\n */\n private _applyPendingFocus() {\n if (this._pendingFocus === null) return;\n const target = this._pendingFocus;\n this._pendingFocus = null;\n if (target === 'back') {\n const back = this.querySelector<HTMLElement>('[data-faq-back]');\n back?.focus();\n return;\n }\n // Flip to front: return focus to the originating row's button.\n const id = this._focusReturnId;\n this._focusReturnId = null;\n if (!id) return;\n const row = this.querySelector<HTMLElement>(`[data-faq-item-id=\"${cssAttrEscape(id)}\"] button`);\n row?.focus();\n }\n\n // -----------------------------------------------------------------------\n // Subscription management\n // -----------------------------------------------------------------------\n\n private _subscribeAll() {\n if (!this.runtime) return;\n\n // Context changes \u2192 force re-render\n this._unsubContext = this.runtime.context.subscribe(() => {\n this._reevaluateOpenAndUpdate();\n });\n\n // Accumulator changes \u2192 force re-render (for event_count triggerWhen)\n if (this.runtime.accumulator?.subscribe) {\n this._unsubAccumulator = this.runtime.accumulator.subscribe(() => {\n this._reevaluateOpenAndUpdate();\n });\n }\n\n // Session metric changes \u2192 force re-render (for session_metric triggerWhen)\n if (this.runtime.sessionMetrics?.subscribe) {\n this._unsubSessionMetrics = this.runtime.sessionMetrics.subscribe(() => {\n this._reevaluateOpenAndUpdate();\n });\n }\n\n // faq:open:* events from overlay CTA clicks\n if (this.runtime.events.subscribe) {\n // Check EventBus history for pending faq:open events\n if (this.runtime.events.getRecent) {\n const recentEvents = this.runtime.events.getRecent(\n { patterns: ['^action\\\\.tooltip_cta_clicked$', '^action\\\\.modal_cta_clicked$'] },\n 10\n );\n const pendingEvent = recentEvents\n .filter((e) => {\n const actionId = e.props?.actionId;\n return typeof actionId === 'string' && actionId.startsWith('faq:open:');\n })\n .pop();\n\n if (pendingEvent && Date.now() - pendingEvent.ts < 10000) {\n const questionId = (pendingEvent.props!.actionId as string).replace('faq:open:', '');\n // Takeover: flip the widget to the DETAIL (back) face for that question\n // (previously this expanded the row inline).\n this._openToDetail(questionId);\n }\n }\n\n this._unsubCta = this.runtime.events.subscribe(\n { patterns: ['^action\\\\.tooltip_cta_clicked$', '^action\\\\.modal_cta_clicked$'] },\n (event) => {\n // Same staleness guard as the history scan above: some EventBus\n // implementations replay recent history into new subscribers. Without\n // this guard the history scan AND this live handler both fire for the\n // same old event \u2014 a spurious second `canvas.requestOpen`. Skip any\n // event older than ~10s (a fresh click always has ts\u2248now).\n const ts = (event as { ts?: number }).ts;\n if (typeof ts === 'number' && Date.now() - ts >= 10000) return;\n const actionId = event.props?.actionId;\n if (typeof actionId !== 'string' || !actionId.startsWith('faq:open:')) return;\n const questionId = actionId.replace('faq:open:', '');\n // Takeover: flip to the question's detail (back) face instead of inline expand.\n this._openToDetail(questionId);\n this.runtime?.events.publish('canvas.requestOpen');\n }\n );\n }\n\n // notification.deep_link events\n if (this.runtime.events.subscribe) {\n const handleDeepLink = (event: { name: string; props?: Record<string, unknown> }) => {\n const tileId = event.props?.tileId as string | undefined;\n const itemId = event.props?.itemId as string | undefined;\n if (tileId !== this.instanceId) return;\n if (!itemId) return;\n\n // Takeover: flip to the question's detail (back) face (previously inline expand).\n this._openToDetail(itemId);\n this._highlightId = itemId;\n\n if (this._highlightTimer !== null) clearTimeout(this._highlightTimer);\n this._highlightTimer = setTimeout(() => {\n this._highlightId = null;\n this._highlightTimer = null;\n }, 1500);\n };\n\n // Check recent events (may have fired before widget mounted)\n if (this.runtime.events.getRecent) {\n const recent = this.runtime.events.getRecent({ names: ['notification.deep_link'] }, 5);\n const pending = recent\n .filter((e) => (e.props?.tileId as string) === this.instanceId && e.props?.itemId)\n .pop();\n if (pending && Date.now() - pending.ts < 10000) {\n handleDeepLink(pending);\n }\n }\n\n this._unsubDeepLink = this.runtime.events.subscribe(\n { names: ['notification.deep_link'] },\n (event) => {\n // Mirror the history-scan staleness guard: skip replayed events older\n // than ~10s so a bus that replays history into new subscribers doesn't\n // double-handle the same deep link (history scan + live handler).\n const ts = (event as { ts?: number }).ts;\n if (typeof ts === 'number' && Date.now() - ts >= 10000) return;\n handleDeepLink(event);\n }\n );\n }\n\n // Compositional children: faq:question items mounted into this\n // accordion's tile. The ItemHandler publishes one event per kind-\n // agnostic item mount; we filter to our tile and route into\n // `faqConfig.actions`. Mirrors AdaptiveChipsStrip's CompositionalContainer.\n this._subscribeCompositional();\n }\n\n /**\n * Subscribe to `element.compositional_*` events targeting this accordion's\n * tile, and ask the element store to replay any items it already holds for\n * us (covers the case where the accordion mounts AFTER the item did \u2014 the\n * inline-slot hydration race that otherwise silently drops the question).\n */\n private _subscribeCompositional() {\n const bus = this.runtime?.events;\n if (!bus?.subscribe) return;\n this._tileId = this._resolveTileId();\n if (!this._tileId) return;\n const tileId = this._tileId;\n this._unsubCompositional = bus.subscribe((event) => {\n const props = event.props ?? {};\n if (props.tile_id !== tileId) return;\n const instanceId = String(props.instance_id ?? '');\n if (event.name === 'element.compositional_append') {\n this._insertItem(\n instanceId,\n props.item as FAQQuestionAction,\n (props.position as 'append' | 'prepend') ?? 'append'\n );\n } else if (event.name === 'element.compositional_patch') {\n this._patchItem(instanceId, props.item as FAQQuestionAction);\n } else if (event.name === 'element.compositional_remove') {\n this._removeItem(instanceId);\n }\n });\n bus.publish('element.compositional_replay_request', { tile_id: tileId });\n }\n\n /** Tile id for compositional filtering: the enclosing tile card's\n * `data-tile-id` (set by SyntroTileCard), falling back to `instanceId`\n * when the accordion is mounted outside a tile card. */\n private _resolveTileId(): string | null {\n const anchor = this.closest<HTMLElement>('[data-tile-id]');\n const fromDom = anchor?.getAttribute('data-tile-id');\n if (fromDom) return fromDom;\n return this.instanceId && this.instanceId !== DEFAULT_INSTANCE_ID ? this.instanceId : null;\n }\n\n /** Append (or prepend) a faq:question the LLM mounted. The wire shape is a\n * fully-formed `{kind, config}` envelope, so we store it verbatim. */\n private _insertItem(\n instanceId: string,\n item: FAQQuestionAction,\n position: 'append' | 'prepend'\n ): void {\n if (!instanceId || !item?.config?.id) return;\n if (this._llmAppendedIds.has(instanceId)) return; // dedup replay / re-subscribe\n this._llmAppendedIds.add(instanceId);\n const actions = this.faqConfig.actions ?? [];\n const next = position === 'prepend' ? [item, ...actions] : [...actions, item];\n // Reassign so Lit sees the change (faqConfig is attribute:false).\n this.faqConfig = { ...this.faqConfig, actions: next };\n }\n\n /** Replace an existing question's content (full replacement). */\n private _patchItem(instanceId: string, item: FAQQuestionAction): void {\n if (!instanceId || !item?.config) return;\n const actions = this.faqConfig.actions ?? [];\n const idx = actions.findIndex((a) => a.config.id === instanceId);\n if (idx < 0) return;\n const next = [...actions];\n next[idx] = item;\n // If the patch changes the open question's id, our `_openId` would point at\n // a gone id and the detail view would fall back to the list. Re-point it at\n // the replacement's new id so the open question stays open through a patch.\n if (this._openId === instanceId && item.config.id !== instanceId) {\n this._openId = item.config.id;\n }\n this.faqConfig = { ...this.faqConfig, actions: next };\n // A patch can also gate the open question OUT (e.g. a new `triggerWhen` that\n // now evaluates false, or content that drops it from the ordered list). If\n // the open id no longer resolves, reset BOTH `_openId` and `_activeFace` to\n // the front. Without resetting `_activeFace`, render() would locally force\n // the front face while leaving state on 'back' \u2014 so a later re-appearance of\n // the question would snap straight to the back face with no flip animation.\n this._resetFaceIfOpenStale();\n }\n\n /** Remove a question by instance id. */\n private _removeItem(instanceId: string): void {\n if (!instanceId) return;\n const actions = this.faqConfig.actions ?? [];\n const next = actions.filter((a) => a.config.id !== instanceId);\n if (next.length === actions.length) return;\n this._llmAppendedIds.delete(instanceId);\n // Removing the currently-open question would leave `_openId` pointing at a\n // gone id. Flip back to the list view instead of resolving to an empty\n // detail. Clear synchronously (the source question is gone \u2014 there's nothing\n // to keep mounted through a flip-out) and cancel any pending back-clear.\n if (this._openId === instanceId) {\n this._clearFlipSettleTimer();\n this._flipAnimating = false;\n this._openId = null;\n this._activeFace = 'front';\n // The open detail set _pendingFocus/_focusReturnId (a11y focus return to\n // the originating row). That row is now gone \u2014 clear the dead focus intent\n // so the next render's _applyPendingFocus doesn't chase a missing element.\n this._pendingFocus = null;\n this._focusReturnId = null;\n }\n this.faqConfig = { ...this.faqConfig, actions: next };\n }\n\n /**\n * Re-evaluate whether the open question is still visible, then re-render.\n * Called from the subscription-driven re-render paths (context / accumulator /\n * sessionMetric changes). Those subscriptions can flip an open question's\n * `triggerWhen` to false WITHOUT a compositional patch/remove \u2014 so without\n * this hook the stale-open reset (`_resetFaceIfOpenStale`, otherwise only\n * invoked on patch/remove) would never fire on the triggerWhen path, and an\n * open question gated out this way would snap straight to the back face on\n * re-appearance. `_resetFaceIfOpenStale` mutates reactive state (which itself\n * schedules an update), and we still `requestUpdate()` for the no-op case.\n */\n private _reevaluateOpenAndUpdate(): void {\n this._resetFaceIfOpenStale();\n this.requestUpdate();\n }\n\n /**\n * If `_openId` no longer resolves against the visible/ordered list (gated out\n * by triggerWhen, removed, or patched away), reset BOTH `_openId` and\n * `_activeFace` to the front (and clear the pending a11y focus intent).\n * Resolves against the SAME `ordered` list render() uses (visible + ordered,\n * NOT search-filtered). Also cancels any pending back-clear timer. Invoked on\n * compositional patch/remove AND on the subscription-driven re-render paths\n * (via {@link _reevaluateOpenAndUpdate}) so the triggerWhen-gated-out case is\n * genuinely covered. Keeping `_activeFace` on 'back' after the open id went\n * stale is the bug this guards: render() would force the front face locally\n * but leave state on 'back', so a later re-appearance snaps to the back face\n * with no animation.\n */\n private _resetFaceIfOpenStale(): void {\n if (this._openId === null) return;\n const ordered = this._orderedQuestions(this._visibleQuestions());\n const stillPresent = ordered.some((q) => q.config.id === this._openId);\n if (stillPresent) return;\n this._clearFlipSettleTimer();\n this._flipAnimating = false;\n this._openId = null;\n this._activeFace = 'front';\n // The open detail set _pendingFocus/_focusReturnId; the originating row is\n // now gated out, so clear the dead focus intent (see _removeItem).\n this._pendingFocus = null;\n this._focusReturnId = null;\n }\n\n private _unsubscribeAll() {\n this._unsubContext?.();\n this._unsubAccumulator?.();\n this._unsubSessionMetrics?.();\n this._unsubCta?.();\n this._unsubDeepLink?.();\n this._unsubCompositional?.();\n this._unsubContext = null;\n this._unsubAccumulator = null;\n this._unsubSessionMetrics = null;\n this._unsubCta = null;\n this._unsubDeepLink = null;\n this._unsubCompositional = null;\n }\n\n // -----------------------------------------------------------------------\n // Handlers\n // -----------------------------------------------------------------------\n\n /**\n * Master\u2192detail takeover: tapping a list row opens its single-Q&A detail\n * view (the whole widget swaps), so a long answer fills \u2014 and never\n * overflows \u2014 the bounded velvet tile. There is no simultaneous multi-expand;\n * one question owns the tile at a time.\n */\n private _handleOpen(id: string) {\n this._openToDetail(id);\n\n this.runtime?.events.publish('faq:toggled', {\n instanceId: this.instanceId,\n questionId: id,\n expanded: true,\n timestamp: Date.now(),\n });\n }\n\n /**\n * Put the widget into the DETAIL (back) face for `id` and flip to it. Shared\n * by the tap handler and the event-driven entry points (faq:open CTA,\n * notification.deep_link) so every \"open this question\" path flips the same\n * way. Cancels any pending back-clear timer (a re-open before the flip-out\n * finishes must keep the detail mounted). Remembers the originating question\n * id so focus can return to that exact row on flip-to-front (a11y), and moves\n * focus to the \u2039 Back button once the back face renders.\n */\n private _openToDetail(id: string) {\n // A re-open before a previous flip settled must clear the pending settle and\n // restart cleanly (interrupted flip).\n this._clearFlipSettleTimer();\n this._openId = id;\n this._activeFace = 'back';\n this._pendingFocus = 'back';\n this._focusReturnId = id;\n if (prefersReducedMotion()) {\n // No animation runs (CSS disables it) \u2192 no animationend to await. Settle\n // straight to the flat back face.\n this._flipAnimating = false;\n return;\n }\n // Turn the 3D flip machinery on for the duration of the rotation only.\n this._flipDir = 'in';\n this._flipAnimating = true;\n // Belt-and-suspenders: if animationend is dropped, force the settle so the\n // resting back face never stays on a composited perspective/rotateY layer.\n this._flipSettleTimer = setTimeout(this._settleFlip, this._flipDurationMs());\n }\n\n /** Back affordance: FLIP from the detail (back) face to the list (front) face.\n * We flip immediately but keep `_openId` (so the back face stays populated\n * through the rotation, never blank mid-flip); the id clears once the\n * transition completes. With reduced motion there's no transition, so it\n * clears synchronously.\n *\n * Timer/token coupling: the tear-down delay is {@link _flipDurationMs} \u2014 the\n * computed `--vc-transition-duration` token (clamped to be >= our fallback),\n * NOT a hardcoded constant. A host that sets the token slower than the\n * fallback would otherwise clear `_openId` (blanking the back face) before\n * the rotation finished. Focus returns to the originating question row. */\n private _handleBack() {\n const id = this._openId;\n this._activeFace = 'front';\n // Return focus to the row the visitor opened from (a11y).\n this._pendingFocus = 'front';\n\n // Interrupted flip: clear any pending settle and restart in the new\n // direction.\n this._clearFlipSettleTimer();\n if (prefersReducedMotion()) {\n // No rotation to wait for \u2014 settle synchronously and clear `_openId`.\n this._flipAnimating = false;\n this._openId = null;\n } else {\n // Flip-out: keep `_openId` (so the back face stays populated through the\n // rotation, never blank mid-flip); the settle (animationend or belt timer)\n // clears it once the flip visibly finishes.\n this._flipDir = 'out';\n this._flipAnimating = true;\n this._flipSettleTimer = setTimeout(this._settleFlip, this._flipDurationMs());\n }\n\n if (id !== null) {\n this.runtime?.events.publish('faq:toggled', {\n instanceId: this.instanceId,\n questionId: id,\n expanded: false,\n timestamp: Date.now(),\n });\n }\n }\n\n /** Tear down the flip's 3D machinery so the shown face renders FLAT. Fired by\n * the container's `animationend`, the belt timer, or synchronously under\n * reduced motion. A flip-OUT (back\u2192front) also clears `_openId` here \u2014 the\n * moment it's finally safe to empty the back face without blanking it\n * mid-rotation. */\n private _settleFlip = (): void => {\n this._clearFlipSettleTimer();\n this._flipAnimating = false;\n if (this._activeFace === 'front') {\n this._openId = null;\n }\n };\n\n /** animationend on the flip container \u2192 the rotation is done, render flat. */\n private _onFlipAnimationEnd = (e: AnimationEvent): void => {\n if (e.animationName === 'sc-faq-flip-in' || e.animationName === 'sc-faq-flip-out') {\n this._settleFlip();\n }\n };\n\n private _clearFlipSettleTimer(): void {\n if (this._flipSettleTimer !== null) {\n clearTimeout(this._flipSettleTimer);\n this._flipSettleTimer = null;\n }\n }\n\n private _handleFeedback(itemId: string, question: string, value: FeedbackValue) {\n const next = new Map(this._feedbackState);\n next.set(itemId, value);\n this._feedbackState = next;\n\n this.runtime?.events.publish('faq:feedback', { itemId, question, value });\n }\n\n // -----------------------------------------------------------------------\n // Computed helpers\n // -----------------------------------------------------------------------\n\n /**\n * Unified render list. Merges compositionally-appended rows\n * (`faqConfig.actions`, the container-then-stream path) with atomically\n * authored rows (`faqConfig.questions`, the struct_list path) normalized\n * into the same `FAQQuestionAction` shape. The atomic path is how the LLM\n * mounts a complete FAQ in one call \u2014 so it renders whole, never empty.\n */\n private _allQuestions(): FAQQuestionAction[] {\n const compositional = this.faqConfig.actions ?? [];\n // Content-derived id (not positional). Expand/feedback state is keyed by id;\n // if the LLM re-mounts the FAQ with a reordered or shortened list, a\n // positional `atomic-${i}` would re-point that state at a different\n // question. Hashing the question text keeps the id stable per question\n // across re-mounts. A `-N` suffix disambiguates same-text rows (including\n // empty strings, which all hash to the same value) so they never share\n // expand/feedback state \u2014 stable as long as their relative order is.\n const seen = new Map<string, number>();\n const atomic = (this.faqConfig.questions ?? []).map((q): FAQQuestionAction => {\n let id = `atomic-${hashFaqId(q.question)}`;\n const dup = seen.get(id) ?? 0;\n seen.set(id, dup + 1);\n if (dup > 0) id = `${id}-${dup}`;\n return {\n kind: 'faq:question',\n config: {\n id,\n question: q.question,\n answer: q.answer,\n category: q.category,\n },\n };\n });\n return [...atomic, ...compositional];\n }\n\n private _visibleQuestions(): FAQQuestionAction[] {\n return this._allQuestions().filter((q) => {\n if (!q.triggerWhen) return true;\n if (!this.runtime) return true;\n const result = this.runtime.evaluateSync<boolean>(q.triggerWhen);\n return result.value;\n });\n }\n\n private _orderedQuestions(visible: FAQQuestionAction[]): FAQQuestionAction[] {\n if (this.faqConfig.ordering === 'priority') {\n return [...visible].sort((a, b) => (b.config.priority ?? 0) - (a.config.priority ?? 0));\n }\n return visible;\n }\n\n private _filteredQuestions(ordered: FAQQuestionAction[]): FAQQuestionAction[] {\n const q = this._searchQuery.trim().toLowerCase();\n if (!this.faqConfig.searchable || !q) return ordered;\n return ordered.filter(\n (item) =>\n item.config.question.toLowerCase().includes(q) ||\n getAnswerText(item.config.answer).toLowerCase().includes(q) ||\n item.config.category?.toLowerCase().includes(q)\n );\n }\n\n private _categoryGroups(\n filtered: FAQQuestionAction[]\n ): Map<string | undefined, FAQQuestionAction[]> {\n const groups = new Map<string | undefined, FAQQuestionAction[]>();\n for (const item of filtered) {\n const cat = item.config.category;\n if (!groups.has(cat)) groups.set(cat, []);\n groups.get(cat)!.push(item);\n }\n return groups;\n }\n\n // -----------------------------------------------------------------------\n // Render helpers\n // -----------------------------------------------------------------------\n\n /**\n * The `data-adaptive-id` scope for this element's injected <style>. When\n * `instanceId` is left at the shared default (`'faq-widget'`), two tiles would\n * both scope to `[data-adaptive-id=\"faq-widget\"]` and cross-style each other.\n * In that case we fall back to a per-element generated uid so each default tile\n * gets its own scope. A caller-provided instanceId is used verbatim.\n */\n private get _scopeId(): string {\n if (this.instanceId && this.instanceId !== DEFAULT_INSTANCE_ID) return this.instanceId;\n if (this._generatedUid === null) {\n _faqUidCounter += 1;\n this._generatedUid = `faq-${Date.now().toString(36)}-${_faqUidCounter}`;\n }\n return this._generatedUid;\n }\n\n /**\n * The flip transition duration in ms, coupled to the CSS. Reads the computed\n * `--vc-transition-duration` token off this element at runtime so the JS timer\n * that tears down the back face (see {@link _handleBack}) is ALWAYS >= the CSS\n * transition \u2014 a host that sets the token slower than our hardcoded default no\n * longer blanks the back face before the flip visually finishes. Falls back to\n * {@link FLIP_TRANSITION_MS} when the token is absent/unreadable (no live\n * layout, jsdom). We add a small buffer and take the max of the token and the\n * fallback so the timer never undershoots either.\n */\n private _flipDurationMs(): number {\n let tokenMs = 0;\n if (typeof window !== 'undefined' && typeof window.getComputedStyle === 'function') {\n try {\n const raw = window\n .getComputedStyle(this)\n .getPropertyValue('--vc-transition-duration')\n .trim();\n if (raw.endsWith('ms')) tokenMs = Number.parseFloat(raw);\n else if (raw.endsWith('s')) tokenMs = Number.parseFloat(raw) * 1000;\n } catch {\n // getComputedStyle can throw on a detached element \u2014 fall through to the\n // hardcoded default below.\n }\n }\n // A malformed token (e.g. \"abcms\") makes Number.parseFloat return NaN. Left\n // unguarded, Math.max(NaN, fallback) is NaN \u2192 setTimeout(\u2026, NaN) \u2248 0, which\n // clears _openId instantly and blanks the back face mid-flip \u2014 the exact bug\n // this method exists to prevent. Treat any non-finite token as absent so we\n // fall back to FLIP_TRANSITION_MS.\n if (!Number.isFinite(tokenMs)) tokenMs = 0;\n // Guarantee the timer >= CSS transition (whichever is larger) plus a buffer,\n // so the back face is never torn down before the flip settles.\n return Math.max(tokenMs, FLIP_TRANSITION_MS) + (tokenMs > 0 ? FLIP_CLEAR_BUFFER_MS : 0);\n }\n\n /**\n * Self-injected flip CSS. The FAQ tile is a light-DOM widget mounted across\n * several canvases (velvet, default, onflow) and standalone \u2014 velvet ships a\n * GENERIC, unscoped `[data-active-face]` flip rule (for the product card), but\n * the other hosts don't. So the FAQ carries its own copy of the SAME 3D-flip\n * technique, scoped to THIS instance (`[data-adaptive-id=\"\u2026\"]`) so it can't\n * leak onto a sibling widget.\n *\n * Two deliberate departures from a naive copy of velvet's rule:\n *\n * 1. PERSPECTIVE goes on the PARENT viewport wrapper\n * (`[data-faq-flip-viewport]`), NOT on the rotating `[data-faq-flip]`\n * element. CSS `perspective` only gives depth to a DESCENDANT's transform \u2014\n * putting it on the element that itself rotates does nothing (a flat\n * squash). We ALSO fold `perspective(...)` into the\n * rotate transform itself as a belt-and-braces measure, so the flip has\n * real 3D depth even inside velvet's bounded `overflow:hidden` deck card\n * (which can otherwise flatten a parent `perspective`).\n * 2. The active-face attribute is NAMESPACED (`data-faq-active-face`), so\n * velvet's generic `[data-active-face]` rotate rule can't ALSO reach this\n * container and double-apply its (perspective-less) transform.\n *\n * It mirrors the product card / velvet house style: rotateY, preserve-3d,\n * backface-visibility, and the `--vc-transition-duration` / `--vc-transition-\n * easing` tokens (with a ~240ms fallback) so the two tiles feel consistent.\n * `prefers-reduced-motion` disables the rotation \u2014 the JS (see\n * {@link _handleBack}) clears state synchronously in that case.\n */\n private _renderFlipStyles() {\n const scope = `[data-adaptive-id=\"${this._scopeId}\"]`;\n const css = `\n ${scope} [data-faq-flip] {\n position: relative;\n }\n /* --- 3D machinery: ONLY while the flip is animating --- */\n /* Perspective (the composited GPU context) lives on the parent viewport,\n gated to the animating window so the RESTING open face never sits on a\n perspective layer (GPU/compositing hazard: rotateY+perspective at rest\n blanks text on some Android GPUs, rasterizes soft everywhere). */\n ${scope} [data-faq-flip-viewport][data-faq-flip-animating] {\n perspective: 1000px;\n }\n ${scope} [data-faq-flip][data-faq-flip-animating] {\n transform-style: preserve-3d;\n }\n ${scope} [data-faq-flip][data-faq-flip-animating] > [data-face] {\n backface-visibility: hidden;\n -webkit-backface-visibility: hidden;\n }\n ${scope} [data-faq-flip][data-faq-flip-animating] > [data-face=\"front\"] {\n position: relative;\n }\n ${scope} [data-faq-flip][data-faq-flip-animating] > [data-face=\"back\"] {\n position: absolute;\n inset: 0;\n transform: rotateY(180deg);\n }\n ${scope} [data-faq-flip][data-faq-flip-animating][data-faq-flip-dir=\"in\"] {\n animation: sc-faq-flip-in var(--vc-transition-duration, 240ms)\n var(--vc-transition-easing, ease-out) forwards;\n }\n ${scope} [data-faq-flip][data-faq-flip-animating][data-faq-flip-dir=\"out\"] {\n animation: sc-faq-flip-out var(--vc-transition-duration, 240ms)\n var(--vc-transition-easing, ease-out) forwards;\n }\n @keyframes sc-faq-flip-in {\n from { transform: perspective(1000px) rotateY(0deg); }\n to { transform: perspective(1000px) rotateY(180deg); }\n }\n @keyframes sc-faq-flip-out {\n from { transform: perspective(1000px) rotateY(180deg); }\n to { transform: perspective(1000px) rotateY(0deg); }\n }\n /* --- RESTING state: the active face is FLAT, the inactive face is gone --- */\n ${scope} [data-faq-flip]:not([data-faq-flip-animating]) {\n transform: none;\n }\n ${scope} [data-faq-flip]:not([data-faq-flip-animating]) > [data-face] {\n position: relative;\n transform: none;\n }\n ${scope} [data-faq-flip]:not([data-faq-flip-animating])[data-faq-active-face=\"front\"] > [data-face=\"back\"] {\n display: none;\n }\n ${scope} [data-faq-flip]:not([data-faq-flip-animating])[data-faq-active-face=\"back\"] > [data-face=\"front\"] {\n display: none;\n }\n @media (prefers-reduced-motion: reduce) {\n ${scope} [data-faq-flip] { animation: none; }\n }\n `;\n return html`<style>${css}</style>`;\n }\n\n private _renderAnswer(answer: FAQAnswer) {\n const html_str = renderAnswerHtml(answer);\n // Item 5: cap the answer render to a few readable lines (a declared\n // line-clamp ellipsis box). A longer answer isn't scrolled in the cramped\n // takeover \u2014 \"Dive deeper\" carries the visitor to the full answer in chat.\n return html`<div style=${styleMap(sm(baseStyles.detailAnswerClamp))} data-faq-markdown=\"\">${unsafeHTML(html_str)}</div>`;\n }\n\n private _renderFeedback(\n item: FAQQuestionAction,\n feedbackConfig: FeedbackConfig,\n feedbackValue: FeedbackValue | undefined,\n theme: 'light' | 'dark'\n ) {\n const colors = themeStyles[theme];\n const feedbackStyle = { ...baseStyles.feedback, ...colors.feedbackPrompt };\n\n return html`\n <div style=${styleMap(sm(feedbackStyle))}>\n <span>${getFeedbackPrompt(feedbackConfig)}</span>\n <button\n type=\"button\"\n style=${styleMap(\n sm({\n ...baseStyles.feedbackButton,\n ...(feedbackValue === 'up' ? baseStyles.feedbackButtonSelected : {}),\n })\n )}\n aria-label=\"Thumbs up\"\n @click=${() => this._handleFeedback(item.config.id, item.config.question, 'up')}\n >\\uD83D\\uDC4D</button>\n <button\n type=\"button\"\n style=${styleMap(\n sm({\n ...baseStyles.feedbackButton,\n ...(feedbackValue === 'down' ? baseStyles.feedbackButtonSelected : {}),\n })\n )}\n aria-label=\"Thumbs down\"\n @click=${() => this._handleFeedback(item.config.id, item.config.question, 'down')}\n >\\uD83D\\uDC4E</button>\n </div>\n `;\n }\n\n /**\n * LIST-view row. A tappable question with its chevron \\u2014 but NO inline answer\n * body. Tapping it takes over the tile with the question's detail view (see\n * {@link _renderDetail}). Keeps the per-row look/markers/highlight from the\n * old accordion minus the inline-expanded answer that overflowed the tile.\n */\n private _renderItem(item: FAQQuestionAction, isLast: boolean, theme: 'light' | 'dark') {\n const colors = themeStyles[theme];\n const isHighlighted = this._highlightId === item.config.id;\n const isHovered = this._hoveredId === item.config.id;\n\n const itemStyle = {\n ...baseStyles.item,\n ...colors.item,\n ...(isHighlighted\n ? {\n boxShadow: `0 0 0 2px ${purple[4]}, 0 0 12px rgba(106, 89, 206, 0.4)`,\n transition: 'box-shadow 0.3s ease',\n }\n : {}),\n ...(!isLast ? { borderBottom: 'var(--sc-content-item-divider, none)' } : {}),\n };\n\n const questionStyle = {\n ...baseStyles.question,\n ...colors.question,\n ...(isHovered ? colors.questionHover : {}),\n };\n\n const chevronStyle = {\n ...baseStyles.chevron,\n transform: 'rotate(0deg)',\n };\n\n return html`\n <div\n style=${styleMap(sm(itemStyle))}\n data-faq-item-id=${item.config.id}\n >\n <button\n type=\"button\"\n style=${styleMap(sm(questionStyle))}\n aria-expanded=${false}\n @click=${() => this._handleOpen(item.config.id)}\n @mouseenter=${() => {\n this._hoveredId = item.config.id;\n }}\n @mouseleave=${() => {\n this._hoveredId = null;\n }}\n >\n <span>${item.config.question}</span>\n <span style=${styleMap(sm(chevronStyle))}>\\u203A</span>\n </button>\n </div>\n `;\n }\n\n private _renderItems(items: FAQQuestionAction[], theme: 'light' | 'dark') {\n return items.map((item, index) => this._renderItem(item, index === items.length - 1, theme));\n }\n\n /**\n * DETAIL view \\u2014 the master\\u2192detail takeover. Renders ONLY the open question:\n * a back affordance, the question prominently, and its full answer via the\n * SAME {@link _renderAnswer}/`renderAnswerHtml` path (all answer formats still\n * work). The answer area scrolls if extremely long, so a single answer fills\n * the bounded velvet tile and never pushes content past the `overflow:hidden`\n * clip.\n */\n private _renderDetail(\n item: FAQQuestionAction,\n theme: 'light' | 'dark',\n feedbackConfig: FeedbackConfig | null\n ) {\n const colors = themeStyles[theme];\n\n const backStyle = {\n ...baseStyles.detailBack,\n ...colors.question,\n };\n const detailQuestionStyle = {\n ...baseStyles.detailQuestion,\n ...colors.question,\n };\n const detailAnswerStyle = {\n ...baseStyles.detailAnswer,\n ...colors.answer,\n };\n\n return html`\n <div style=${styleMap(sm(baseStyles.detail))} data-faq-detail=${item.config.id}>\n <button\n type=\"button\"\n style=${styleMap(sm(backStyle))}\n data-faq-back=\"\"\n aria-label=\"Back to questions\"\n @click=${() => this._handleBack()}\n >\n \\u2039 Back to questions\n </button>\n\n <div style=${styleMap(sm(detailQuestionStyle))} data-faq-detail-question>${item.config.question}</div>\n\n <div style=${styleMap(sm(detailAnswerStyle))} aria-hidden=${false}>\n ${this._renderAnswer(item.config.answer)}\n <div style=${styleMap(sm(baseStyles.detailFooter))}>\n <button\n type=\"button\"\n style=${styleMap(sm(baseStyles.detailDiveDeeper))}\n data-faq-dive-deeper\n @click=${() => this._handleDiveDeeper(item)}\n >Dive deeper \u203A</button>\n </div>\n ${\n feedbackConfig\n ? this._renderFeedback(\n item,\n feedbackConfig,\n this._feedbackState.get(item.config.id),\n theme\n )\n : nothing\n }\n </div>\n </div>\n `;\n }\n\n /**\n * \"Dive deeper\" (items 4/5) \u2014 hand the open question's context back to the\n * CHAT as a deep-dive turn, so a clamped answer never leaves the visitor\n * stuck. Canvas-agnostic: dispatches the neutral DIVE_DEEPER_EVENT; the\n * chat-bar mountable turns it into a user-visible turn. The answer text rides\n * along as (trimmed) context so the agent grounds its fuller reply.\n */\n private _handleDiveDeeper(item: FAQQuestionAction) {\n const question = item.config.question;\n const answerText = getAnswerText(item.config.answer);\n dispatchDiveDeeper({\n prompt: `Tell me more about: ${question}`,\n title: question,\n context: answerText ? answerText.slice(0, 500) : undefined,\n });\n }\n\n // -----------------------------------------------------------------------\n // Render\n // -----------------------------------------------------------------------\n\n override render() {\n const theme = resolveTheme(this.faqConfig.theme);\n const colors = themeStyles[theme];\n const feedbackConfig = resolveFeedbackConfig(this.faqConfig.feedback);\n\n const visible = this._visibleQuestions();\n const ordered = this._orderedQuestions(visible);\n const filtered = this._filteredQuestions(ordered);\n const hasCategories = filtered.some((q) => q.config.category);\n const groups = hasCategories ? this._categoryGroups(filtered) : null;\n\n const containerStyle = {\n ...baseStyles.container,\n ...colors.container,\n };\n\n const emptyStateStyle = {\n ...baseStyles.emptyState,\n ...colors.emptyState,\n };\n\n const categoryHeaderStyle = {\n ...baseStyles.categoryHeader,\n ...colors.categoryHeader,\n };\n\n const searchInputStyle = {\n ...baseStyles.searchInput,\n ...colors.searchInput,\n };\n\n // Empty state \u2014 no visible questions at all.\n //\n // A runtime-backed FAQ is a compositional container: its `faq:question`\n // items stream in as `element.compositional_append` events after mount\n // (and a replay request). Rendering the \"all set\" reassurance box while\n // we're still empty flashes an empty shell on every mount \u2014 and leaves\n // an orphaned empty box if the agent mounted a container it never filled\n // (the recurring \"empty FAQ showed up then disappeared\" bug). Render\n // nothing until the first question lands. The reassurance copy is only\n // for a standalone (runtime-less) FAQ configured directly with no items.\n if (visible.length === 0) {\n if (this.runtime) return nothing;\n return html`\n <div\n style=${styleMap(sm(containerStyle))}\n data-adaptive-id=${this._scopeId}\n data-adaptive-type=\"adaptive-faq\"\n >\n <div style=${styleMap(sm(emptyStateStyle))}>\n You're all set for now! We'll surface answers here when they're relevant to what\n you're doing.\n </div>\n </div>\n `;\n }\n\n // Two-face FLIP takeover. BOTH faces are always in the DOM (a flip animates\n // between two live faces): front = the question LIST, back = the open\n // question's DETAIL. `_activeFace` drives the CSS rotateY via\n // `data-active-face` \u2014 the SAME mechanism the product card uses.\n //\n // We resolve `_openId` against the *visible/ordered* list (`ordered`), NOT\n // the search-filtered list (`filtered`) \u2014 search is a list-view concern\n // only, so a `faq:open` for a question that doesn't match an active\n // `_searchQuery` must still open its detail. A genuinely stale id (gated out\n // via triggerWhen, or removed) is absent from `ordered`, so the back face\n // renders empty and the container stays on the front face.\n const openItem =\n this._openId !== null ? ordered.find((q) => q.config.id === this._openId) : undefined;\n // If the open id resolved to nothing (stale), fall back to the front face so\n // we never show a rotated-but-empty back face.\n const activeFace = openItem ? this._activeFace : 'front';\n const backActive = activeFace === 'back';\n const flipAnimating = this._flipAnimating;\n\n return html`\n <div\n style=${styleMap(sm(containerStyle))}\n data-adaptive-id=${this._scopeId}\n data-adaptive-type=\"adaptive-faq\"\n >\n ${this._renderFlipStyles()}\n <div data-faq-flip-viewport ?data-faq-flip-animating=${flipAnimating}>\n <div\n data-faq-flip\n data-faq-active-face=${activeFace}\n ?data-faq-flip-animating=${flipAnimating}\n data-faq-flip-dir=${flipAnimating ? this._flipDir : nothing}\n @animationend=${this._onFlipAnimationEnd}\n >\n <div data-face=\"front\" ?inert=${backActive} aria-hidden=${backActive}>\n ${this._renderListFace(\n groups,\n filtered,\n theme,\n searchInputStyle,\n categoryHeaderStyle,\n {\n ...baseStyles.noResults,\n ...colors.emptyState,\n }\n )}\n </div>\n <div data-face=\"back\" ?inert=${!backActive} aria-hidden=${!backActive}>\n ${openItem ? this._renderDetail(openItem, theme, feedbackConfig) : nothing}\n </div>\n </div>\n </div>\n </div>\n `;\n }\n\n /** The FRONT face \u2014 search box, the question list (optionally grouped by\n * category), and the no-results message. Extracted so both faces can live in\n * the DOM at once for the flip. */\n private _renderListFace(\n groups: Map<string | undefined, FAQQuestionAction[]> | null,\n filtered: FAQQuestionAction[],\n theme: 'light' | 'dark',\n searchInputStyle: Record<string, unknown>,\n categoryHeaderStyle: Record<string, unknown>,\n noResultsStyle: Record<string, unknown>\n ) {\n return html`\n ${\n this.faqConfig.searchable\n ? html`\n <div style=${styleMap(sm(baseStyles.searchWrapper))}>\n <style>\n [data-adaptive-id=\"${this._scopeId}\"] input::placeholder {\n color: var(--sc-content-search-color, inherit);\n opacity: 0.7;\n }\n </style>\n <input\n type=\"text\"\n placeholder=\"Search questions...\"\n .value=${this._searchQuery}\n style=${styleMap(sm(searchInputStyle))}\n @input=${(e: InputEvent) => {\n this._searchQuery = (e.target as HTMLInputElement).value;\n }}\n />\n </div>\n `\n : nothing\n }\n\n <div style=${styleMap(sm(baseStyles.accordion))}>\n ${\n groups\n ? Array.from(groups.entries()).map(\n ([category, items]) => html`\n ${\n category\n ? html`\n <div\n style=${styleMap(sm(categoryHeaderStyle))}\n data-category-header=${category}\n >\n ${category}\n </div>\n `\n : nothing\n }\n ${this._renderItems(items, theme)}\n `\n )\n : this._renderItems(filtered, theme)\n }\n </div>\n\n ${\n this.faqConfig.searchable && filtered.length === 0 && this._searchQuery\n ? html`\n <div style=${styleMap(sm(noResultsStyle))}>\n No questions found matching "${this._searchQuery}"\n </div>\n `\n : nothing\n }\n `;\n }\n}\n\n// ============================================================================\n// Custom element registration\n// ============================================================================\n\nif (!customElements.get('syntro-faq-accordion')) {\n customElements.define('syntro-faq-accordion', FAQAccordionElement);\n}\n\nexport default FAQAccordionElement;\n", "/**\n * Adaptive FAQ - Styles\n *\n * BaseStyles object (font, layout, colors, animations) and theme integration\n * for the FAQWidget component.\n */\n\nimport { purple, slateGrey } from '@syntro/design-system/tokens';\n\n// ============================================================================\n// Base Styles\n// ============================================================================\n\nexport const baseStyles = {\n container: {\n fontFamily: 'var(--sc-font-family, system-ui, -apple-system, sans-serif)',\n maxWidth: '800px',\n margin: '0 auto',\n },\n searchWrapper: {\n marginBottom: '8px',\n },\n searchInput: {\n width: '100%',\n padding: '12px 16px',\n borderRadius: '8px',\n fontSize: '14px',\n outline: 'none',\n transition: 'border-color 0.15s ease',\n backgroundColor: 'var(--sc-content-search-background)',\n color: 'var(--sc-content-search-color)',\n },\n accordion: {\n display: 'flex',\n flexDirection: 'column' as const,\n gap: 'var(--sc-content-item-gap, 6px)',\n },\n item: {\n borderRadius: 'var(--sc-content-item-border-radius, 10px)',\n border: 'var(--sc-content-item-border, 1px solid rgba(0, 0, 0, 0.08))',\n background: 'var(--sc-content-item-background, rgba(255, 255, 255, 0.5))',\n overflow: 'hidden',\n transition: 'box-shadow 0.15s ease',\n },\n question: {\n width: '100%',\n padding: 'var(--sc-content-item-padding, 12px 16px)',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n border: 'none',\n cursor: 'pointer',\n fontSize: 'var(--sc-content-item-font-size, 15px)',\n fontWeight: 500,\n textAlign: 'left' as const,\n transition: 'background-color 0.15s ease',\n },\n chevron: {\n fontSize: '20px',\n transition: 'transform 0.2s ease',\n color: 'var(--sc-content-chevron-color, currentColor)',\n },\n answer: {\n padding: 'var(--sc-content-body-padding, 0 16px 12px 16px)',\n fontSize: 'var(--sc-content-body-font-size, 14px)',\n lineHeight: 1.6,\n overflow: 'hidden',\n transition: 'max-height 0.2s ease, padding 0.2s ease',\n },\n category: {\n display: 'inline-block',\n fontSize: '11px',\n fontWeight: 600,\n textTransform: 'uppercase' as const,\n letterSpacing: '0.05em',\n padding: '4px 8px',\n borderRadius: '4px',\n marginBottom: '8px',\n },\n categoryHeader: {\n fontSize: 'var(--sc-content-category-font-size, 12px)',\n fontWeight: 700,\n textTransform: 'uppercase' as const,\n letterSpacing: '0.05em',\n padding: 'var(--sc-content-category-padding, 8px 4px 4px 4px)',\n marginTop: 'var(--sc-content-category-gap, 4px)',\n },\n feedback: {\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n marginTop: '12px',\n paddingTop: '10px',\n borderTop: '1px solid rgba(0, 0, 0, 0.08)',\n fontSize: '13px',\n },\n feedbackButton: {\n background: 'none',\n border: '1px solid transparent',\n cursor: 'pointer',\n fontSize: '16px',\n padding: '4px 8px',\n borderRadius: '4px',\n transition: 'background-color 0.15s ease, border-color 0.15s ease',\n },\n feedbackButtonSelected: {\n borderColor: 'rgba(0, 0, 0, 0.2)',\n backgroundColor: 'rgba(0, 0, 0, 0.04)',\n },\n // \u2500\u2500 Master\u2192detail takeover \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Item 5: the detail view is a tight stack \u2014 back, then the QUESTION with the\n // ANSWER hugging right below it (the old 12px flex gap between Q and A wasted\n // vertical space and pushed the answer down). Row spacing is now set by\n // explicit per-element margins, so gap:0 here.\n detail: {\n display: 'flex',\n flexDirection: 'column' as const,\n gap: '0',\n minHeight: '0',\n // Inset the master\u2192detail takeover to match the list questions' padding so\n // a single Q&A taking over the tile doesn't render flush to the edges.\n // Vertical inset is tightened (12px\u219210px default) because the takeover lives\n // in the fixed-height velvet deck band, where every px of chrome pushes the\n // \"Dive deeper\" footer toward the card's overflow:hidden clip edge\n // (BUG-1783496844). Horizontal stays 16px to keep alignment with the list.\n padding: 'var(--sc-content-item-padding, 10px 16px)',\n },\n detailBack: {\n display: 'inline-flex',\n alignItems: 'center',\n gap: '4px',\n alignSelf: 'flex-start',\n background: 'none',\n border: 'none',\n padding: '4px 0',\n // Clear space below the back control (was the container gap). Tightened\n // 10px\u21926px to reclaim vertical room in the bounded deck band so the\n // \"Dive deeper\" footer stays clear of the card clip edge (BUG-1783496844).\n marginBottom: '6px',\n cursor: 'pointer',\n fontSize: '13px',\n fontWeight: 600,\n opacity: 0.85,\n },\n detailQuestion: {\n fontSize: 'var(--sc-content-item-font-size, 16px)',\n fontWeight: 600,\n lineHeight: 1.4,\n // Tight to the answer \u2014 kill the wasted Q\u2194A gap.\n marginBottom: '4px',\n },\n detailAnswer: {\n fontSize: 'var(--sc-content-body-font-size, 14px)',\n lineHeight: 1.6,\n // No longer a long scroll region \u2014 the answer render is line-clamped\n // (detailAnswerClamp) and \"Dive deeper\" carries the user to the full answer\n // in chat. flex:1 keeps the footer (dive-deeper + feedback) pinned below.\n display: 'flex',\n flexDirection: 'column' as const,\n flex: '1 1 auto',\n minHeight: '0',\n },\n // The answer TEXT render, capped to a few readable lines with a visible\n // ellipsis cap (a declared -webkit-line-clamp box \u2014 the clip battery treats\n // this as an intentional ellipsis, not a hidden-content bug). The cap is\n // token-tunable so a roomy surface can raise it.\n detailAnswerClamp: {\n margin: '0',\n display: '-webkit-box',\n // Default cap lowered 4\u21923 lines: the takeover renders inside the\n // fixed-height velvet deck card (overflow:hidden), so a 4-line preview\n // plus chrome pushed the \"Dive deeper\" footer past the clip edge on\n // smaller decks / two-line questions (BUG-1783496844). 3 lines is a\n // compact preview; \"Dive deeper\" carries the full answer to chat. Roomy\n // surfaces can raise it via the token.\n webkitLineClamp: 'var(--sc-faq-answer-clamp, 3)',\n webkitBoxOrient: 'vertical' as const,\n overflow: 'hidden',\n // Shrink-ready: as a flex child of detailAnswer, yield space first so that\n // WHEN a host bounds the tile height (definite deck-card height) the answer\n // gives up lines before the pinned footer is ever clipped.\n flex: '1 1 auto',\n minHeight: '0',\n },\n // Footer row under the answer: the \"Dive deeper\" chip sits bottom-right.\n detailFooter: {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'flex-end',\n gap: '8px',\n // Tightened 8px\u21926px to reclaim deck-band vertical room (BUG-1783496844).\n marginTop: '6px',\n // Never shrink or clip the footer \u2014 the answer above yields space first.\n flexShrink: 0,\n },\n detailDiveDeeper: {\n flex: '0 0 auto',\n padding: '4px 10px',\n borderRadius: '9999px',\n border: '1px solid var(--sc-color-border, rgba(0,0,0,0.14))',\n background: 'transparent',\n cursor: 'pointer',\n font: '600 12px/1.2 var(--sc-font-family, inherit)',\n color: 'var(--sc-color-primary, #3d8a5e)',\n whiteSpace: 'nowrap' as const,\n },\n emptyState: {\n textAlign: 'center' as const,\n padding: '48px 24px',\n fontSize: '14px',\n },\n noResults: {\n textAlign: 'center' as const,\n padding: '32px 16px',\n fontSize: '14px',\n },\n} as const;\n\n// ============================================================================\n// Theme Styles\n// ============================================================================\n\nexport const themeStyles = {\n light: {\n container: {\n backgroundColor: 'transparent',\n color: 'inherit',\n },\n searchInput: {\n border: `1px solid ${slateGrey[11]}`,\n },\n item: {\n backgroundColor: 'var(--sc-content-background)',\n borderTop: 'var(--sc-content-border)',\n borderRight: 'var(--sc-content-border)',\n borderBottom: 'var(--sc-content-border)',\n borderLeft: 'var(--sc-content-border)',\n },\n itemExpanded: {\n boxShadow: '0 4px 12px rgba(0, 0, 0, 0.08)',\n },\n question: {\n backgroundColor: 'transparent',\n color: 'var(--sc-content-text-color)',\n },\n questionHover: {\n backgroundColor: 'var(--sc-content-background-hover)',\n },\n answer: {\n color: 'var(--sc-content-text-secondary-color)',\n },\n category: {\n backgroundColor: purple[8],\n color: purple[2],\n },\n categoryHeader: {\n color: slateGrey[7],\n },\n emptyState: {\n color: slateGrey[8],\n },\n feedbackPrompt: {\n color: slateGrey[7],\n },\n },\n dark: {\n container: {\n backgroundColor: 'transparent',\n color: 'inherit',\n },\n searchInput: {\n border: `1px solid ${slateGrey[5]}`,\n },\n item: {\n backgroundColor: 'var(--sc-content-background)',\n borderTop: 'var(--sc-content-border)',\n borderRight: 'var(--sc-content-border)',\n borderBottom: 'var(--sc-content-border)',\n borderLeft: 'var(--sc-content-border)',\n },\n itemExpanded: {\n boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)',\n },\n question: {\n backgroundColor: 'transparent',\n color: 'var(--sc-content-text-color)',\n },\n questionHover: {\n backgroundColor: 'var(--sc-content-background-hover)',\n },\n answer: {\n color: 'var(--sc-content-text-secondary-color)',\n },\n category: {\n backgroundColor: purple[0],\n color: purple[6],\n },\n categoryHeader: {\n color: slateGrey[8],\n },\n emptyState: {\n color: slateGrey[7],\n },\n feedbackPrompt: {\n color: slateGrey[8],\n },\n },\n};\n", "/**\n * renderHealth \u2014 runtime self-detection of blank render and undeclared clipping\n * for the FAQ accordion.\n *\n * A tile can look correct in tests yet paint blank on a real host (a device\n * compositing quirk, a font that measures wider, a broken image CDN). After the\n * accordion paints, this module re-measures it and emits a PostHog signal via\n * the ONE consent-gated door (`window.SynOS.runtime.events.emit`) when it finds a\n * blank tile or an undeclared clip. Field-RUM for \"the FAQ broke on a customer's\n * page\".\n *\n * This is a lean, package-LOCAL copy of the pattern adaptive-product owns:\n * `@syntrologie/adapt-faq` does not (and should not) depend on\n * `@syntrologie/adapt-product`, and the two share no runtime-util package. The\n * blank check is identical; the clip check is the generic text-overflow subset\n * (the FAQ has no `[data-critical-text]` identity nodes, so the product card's\n * Tier-1 identity rule does not apply here).\n * Follow-up: extract a shared runtime-util package so both adaptives import one\n * copy.\n *\n * COE-051 lesson: `publish()` only notifies in-process canvas subscribers \u2014 it\n * NEVER reaches PostHog. `emit()` is the sanctioned capture path (the EventBus\n * forwards emit()'d events to its consent-gated posthogCapture sink). The event\n * name MUST satisfy validateEventName (`app:{category}:{action}`, colon segments)\n * or the bus silently drops it.\n *\n * NON-PII discipline: only element tag/class PATHS and pixel numbers ever leave\n * the widget \u2014 never text content, never customer copy.\n */\nimport type { ReactiveController, ReactiveControllerHost } from 'lit';\n\nexport interface ClipFinding {\n /** Structural path to the offending node (NON-PII), e.g. `div.faq>button`. */\n path: string;\n /** Horizontal overflow in px, rounded. */\n over_px: number;\n}\n\nexport interface RenderHealth {\n /** Undeclared clips found inside the tile (empty = clean). */\n clips: ClipFinding[];\n /** True when the tile occupies real area but painted no text and no complete\n * image (a blank tile). */\n blank: boolean;\n}\n\n/** Minimum tile area (px\u00B2) below which \"blank\" is not meaningful. */\nconst BLANK_MIN_AREA = 60 * 60;\n\n/** Short NON-PII path: up to 3 ancestors of `tag.class` joined by `>`. */\nfunction elementPath(el: Element, stopAt: Element): string {\n const seg = (e: Element): string => {\n const tag = e.tagName.toLowerCase();\n const cls =\n typeof e.className === 'string' && e.className.trim()\n ? `.${e.className.trim().split(/\\s+/)[0]}`\n : '';\n return `${tag}${cls}`;\n };\n const parts: string[] = [seg(el)];\n let cur: Element | null = el.parentElement;\n let hops = 0;\n while (cur && cur !== stopAt && hops < 3) {\n parts.unshift(seg(cur));\n cur = cur.parentElement;\n hops++;\n }\n return parts.join('>');\n}\n\n/** True if `el` (or an ancestor up to `root`) declares the clip is intentional. */\nfunction hasClipOkAncestor(el: Element, root: Element): boolean {\n let cur: Element | null = el;\n while (cur && cur !== root.parentElement) {\n if (cur.hasAttribute?.('data-clip-ok')) return true;\n cur = cur.parentElement;\n }\n return false;\n}\n\n/**\n * Measure the render health of a tile root. Pure DOM read \u2014 no side effects, no\n * telemetry. Safe to call any time after paint.\n * - text-clip: an element with its own text whose scrollWidth exceeds\n * clientWidth by >1px, with no textOverflow:ellipsis, not under [data-clip-ok].\n * - blank: the tile has real area but no painted text and no complete image.\n */\nexport function measureRenderHealth(root: Element): RenderHealth {\n const clips: ClipFinding[] = [];\n const seen = new Set<string>();\n const push = (f: ClipFinding): void => {\n const k = `${f.path}|${f.over_px}`;\n if (seen.has(k)) return;\n seen.add(k);\n clips.push(f);\n };\n\n let hasText = false;\n let hasCompleteImage = false;\n\n for (const el of root.querySelectorAll('*')) {\n const cs = getComputedStyle(el);\n if (cs.display === 'none' || cs.visibility === 'hidden') continue;\n const r = el.getBoundingClientRect();\n if (r.width === 0 || r.height === 0) continue;\n\n const ownText = Array.from(el.childNodes).some(\n (n) => n.nodeType === 3 && (n.textContent ?? '').trim().length > 0\n );\n if (ownText) hasText = true;\n if (el.tagName === 'IMG') {\n const img = el as HTMLImageElement;\n if (img.complete && img.naturalWidth > 0 && img.dataset.imageError !== 'true') {\n hasCompleteImage = true;\n }\n }\n\n if (\n ownText &&\n el.scrollWidth > el.clientWidth + 1 &&\n cs.textOverflow !== 'ellipsis' &&\n !hasClipOkAncestor(el, root)\n ) {\n push({ path: elementPath(el, root), over_px: el.scrollWidth - el.clientWidth });\n }\n }\n\n const rect = root.getBoundingClientRect();\n const area = rect.width * rect.height;\n const blank = area >= BLANK_MIN_AREA && !hasText && !hasCompleteImage;\n\n return { clips, blank };\n}\n\ninterface SynOSEventsWindow {\n SynOS?: {\n runtime?: {\n events?: {\n // The ONLY transport that reaches PostHog (COE-051).\n emit?: (name: string, props: Record<string, unknown>) => unknown;\n };\n };\n };\n}\n\n/** Emit a telemetry event on the PostHog-reaching path. Never throws. */\nfunction synEmit(name: string, props: Record<string, unknown>): void {\n if (typeof window === 'undefined') return;\n try {\n const emit = (window as unknown as SynOSEventsWindow).SynOS?.runtime?.events?.emit;\n if (typeof emit === 'function') emit(name, props);\n } catch {\n // Telemetry must never crash the widget.\n }\n}\n\nexport interface RenderHealthReportOptions {\n /** Event category: emits `app:<category>:blank` / `:clipped`. */\n category: string;\n /** `widget_kind` tag included in every payload. */\n widgetKind: string;\n /** Extra NON-PII context merged into every payload. */\n context?: Record<string, unknown>;\n}\n\nconst CLEAN: RenderHealth = { clips: [], blank: false };\n\n/** Measure `root` and, if it clips or paints blank, emit the self-detection\n * event via the consent-gated emit() path. Returns the measured health. Never\n * throws. */\nexport function reportRenderHealth(root: Element, opts: RenderHealthReportOptions): RenderHealth {\n try {\n const health = measureRenderHealth(root);\n if (health.clips.length === 0 && !health.blank) return health;\n const base: Record<string, unknown> = { ...(opts.context ?? {}), widget_kind: opts.widgetKind };\n if (health.blank) {\n synEmit(`app:${opts.category}:blank`, base);\n return health;\n }\n const paths = health.clips.map((c) => c.path).sort();\n const over_px = health.clips.reduce((m, c) => Math.max(m, c.over_px), 0);\n synEmit(`app:${opts.category}:clipped`, {\n paths: paths.join(','),\n over_px,\n clip_count: health.clips.length,\n ...base,\n });\n return health;\n } catch {\n return CLEAN;\n }\n}\n\n/** What the widget's probe returns for one measurement pass. */\nexport interface RenderHealthProbe extends RenderHealthReportOptions {\n /** The element to measure, or `null` to SKIP this pass (unsettled state). */\n root: Element | null;\n /** Stable key for the current visual state; a state reports AT MOST ONCE. */\n stateKey: string;\n}\n\n/**\n * Lit ReactiveController that runs a widget's render-health probe after each\n * paint settles (rAF then a 0ms timeout), at most once per visual state.\n */\nexport class RenderHealthController implements ReactiveController {\n #probe: () => RenderHealthProbe | null;\n #reported = new Set<string>();\n #raf: number | undefined;\n\n constructor(host: ReactiveControllerHost, probe: () => RenderHealthProbe | null) {\n this.#probe = probe;\n host.addController(this);\n }\n\n hostUpdated(): void {\n this.#schedule();\n }\n\n hostDisconnected(): void {\n this.#cancel();\n }\n\n #schedule(): void {\n if (typeof window === 'undefined') return;\n if (this.#raf !== undefined) return;\n const raf =\n typeof window.requestAnimationFrame === 'function'\n ? window.requestAnimationFrame.bind(window)\n : (cb: FrameRequestCallback) => window.setTimeout(() => cb(0), 0);\n this.#raf = raf(() => {\n window.setTimeout(() => {\n this.#raf = undefined;\n this.#run();\n }, 0);\n }) as unknown as number;\n }\n\n #run(): void {\n try {\n const probe = this.#probe();\n if (!probe || !probe.root) return;\n if (this.#reported.has(probe.stateKey)) return;\n this.#reported.add(probe.stateKey);\n reportRenderHealth(probe.root, probe);\n } catch {\n // Self-detection must never crash the widget.\n }\n }\n\n #cancel(): void {\n if (\n this.#raf !== undefined &&\n typeof window !== 'undefined' &&\n typeof window.cancelAnimationFrame === 'function'\n ) {\n window.cancelAnimationFrame(this.#raf);\n }\n this.#raf = undefined;\n }\n}\n", "/**\n * Adaptive FAQ - Runtime Module\n *\n * Runtime manifest for the FAQ accordion adaptive.\n * Mounts the Lit web component <syntro-faq-accordion>.\n * Provides action executors and widget registration.\n */\n\nimport { type MountPlumbing, stripMountPlumbing } from '@syntrologie/sdk-contracts';\nimport { executorDefinitions } from './executors';\nimport './FAQWidgetLit'; // registers <syntro-faq-accordion> custom element\nimport type { FAQWidgetRuntime } from './faq-types';\nimport type { FAQConfig, FAQQuestionAction } from './types';\n\n// ============================================================================\n// Lit Mountable Widget\n// ============================================================================\n\nconst DEFAULT_FAQ_CONFIG: FAQConfig = {\n expandBehavior: 'single',\n searchable: false,\n theme: 'auto',\n actions: [],\n};\n\n/**\n * Mountable widget interface for <syntro-faq-accordion> (Lit web component).\n */\nexport const FAQWidgetLitMountable = {\n mount(\n container: HTMLElement,\n config?: (FAQConfig & MountPlumbing & { runtime?: FAQWidgetRuntime }) | null\n ) {\n const incoming = config ?? null;\n const stripped = stripMountPlumbing<FAQConfig & { runtime?: FAQWidgetRuntime }>(incoming);\n // stripMountPlumbing does NOT strip the non-canonical `runtime` consumer key\n // on this widget because `runtime` is a canonical plumbing key. Pull the\n // explicit references back out for the element wiring.\n const runtime = incoming?.runtime as FAQWidgetRuntime | undefined;\n const instanceId = incoming?.instanceId ?? 'faq-widget';\n const faqConfig: FAQConfig = incoming ? (stripped as FAQConfig) : { ...DEFAULT_FAQ_CONFIG };\n\n const el = document.createElement('syntro-faq-accordion') as HTMLElement & {\n faqConfig: FAQConfig;\n runtime: FAQWidgetRuntime | null;\n instanceId: string;\n };\n\n Object.assign(el, {\n faqConfig,\n runtime: runtime ?? null,\n instanceId,\n });\n\n container.appendChild(el);\n return () => el.remove();\n },\n};\n\n// ============================================================================\n// App Runtime Manifest\n// ============================================================================\n\n/**\n * Runtime manifest for adaptive-faq.\n *\n * Provides:\n * - FAQ action executors (scroll_to, toggle_item, update)\n * - Widget-based accordion using the Lit web component\n */\nexport const runtime = {\n id: 'adaptive-faq',\n version: '2.0.0',\n name: 'FAQ Accordion',\n description:\n 'Collapsible Q&A accordion with actions, rich content, feedback, and personalization',\n\n /**\n * Action executors for programmatic FAQ interaction.\n */\n executors: executorDefinitions,\n\n /**\n * Widget definitions for the runtime's WidgetRegistry.\n */\n widgets: [\n {\n id: 'adaptive-faq:accordion',\n component: FAQWidgetLitMountable,\n metadata: {\n name: 'FAQ Accordion',\n description: 'Collapsible Q&A accordion with search, categories, and feedback',\n icon: '\u2753',\n subtitle: 'Curated just for you.',\n },\n },\n ],\n\n /**\n * Extract notify watcher entries from tile config props.\n * The runtime evaluates these continuously (even with drawer closed)\n * and publishes faq:question_revealed when triggerWhen transitions false \u2192 true.\n */\n notifyWatchers(props: Record<string, unknown>) {\n const actions = (props.actions ?? []) as FAQQuestionAction[];\n return actions\n .filter((a) => a.notify && a.triggerWhen)\n .map((a) => ({\n id: `faq:${a.config.id}`,\n strategy: a.triggerWhen!,\n eventName: 'faq:question_revealed',\n eventProps: {\n questionId: a.config.id,\n question: a.config.question,\n title: a.notify!.title,\n body: a.notify!.body,\n icon: a.notify!.icon,\n },\n }));\n },\n};\n\nexport default runtime;\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AA2BA,SAAS,YAAY,OAAiB,QAAiB,cAA0C;AAC/F,MAAI,QAAQ;AACV,UAAM,QAAQ,MAAM,SAAS,EAAE,MAAM,KAAK,CAAC,MAAM,EAAE,OAAO,OAAO,MAAM;AACvE,QAAI,MAAO,QAAO;AAAA,EACpB;AAEA,MAAI,cAAc;AAChB,UAAM,QAAQ,MAAM,eAAe,YAAY;AAC/C,QAAI,MAAO,QAAO;AAAA,EACpB;AAEA,QAAM,IAAI,MAAM,oBAAoB;AACtC;AAaA,eAAsB,mBACpB,QACA,SACA,OACyB;AACzB,QAAM,OAAO,YAAY,OAAO,OAAO,QAAQ,OAAO,YAAY;AAClE,QAAM,EAAE,GAAG,IAAI,KAAK;AAGpB,MAAI,OAAO,WAAW,OAAO;AAC3B,UAAM,OAAO,EAAE;AAAA,EACjB;AAGA,QAAM,KAAK,SAAS,cAAc,sBAAsB,EAAE,IAAI;AAC9D,MAAI,IAAI;AACN,OAAG,eAAe;AAAA,MAChB,UAAU,OAAO,YAAY;AAAA,IAC/B,CAAC;AAAA,EACH;AAGA,UAAQ,aAAa,iBAAiB,EAAE,QAAQ,GAAG,CAAC;AAEpD,SAAO;AAAA,IACL,SAAS,MAAM;AAAA,IAEf;AAAA,EACF;AACF;AASA,eAAsB,qBACpB,QACA,SACA,OACyB;AACzB,QAAM,OAAO,YAAY,OAAO,OAAO,QAAQ,OAAO,YAAY;AAClE,QAAM,EAAE,GAAG,IAAI,KAAK;AACpB,QAAM,eAAe,OAAO,SAAS;AAErC,MAAI;AAEJ,UAAQ,cAAc;AAAA,IACpB,KAAK;AACH,YAAM,OAAO,EAAE;AACf,iBAAW;AACX;AAAA,IACF,KAAK;AACH,YAAM,SAAS,EAAE;AACjB,iBAAW;AACX;AAAA,IACF,SAAS;AACP,YAAM,cAAc,MAAM,SAAS,EAAE,cAAc,IAAI,EAAE;AACzD,YAAM,OAAO,EAAE;AACf,iBAAW,cAAc,WAAW;AACpC;AAAA,IACF;AAAA,EACF;AAEA,UAAQ,aAAa,cAAc,EAAE,QAAQ,IAAI,SAAS,CAAC;AAE3D,SAAO;AAAA,IACL,SAAS,MAAM;AAAA,IAEf;AAAA,EACF;AACF;AASA,eAAsB,iBACpB,QACA,SACA,OACyB;AACzB,UAAQ,OAAO,WAAW;AAAA,IACxB,KAAK,OAAO;AACV,YAAM,QAAQ,OAAO,SAAS,CAAC;AAC/B,YAAM,WAAW,OAAO,aAAa,YAAY,YAAY;AAC7D,YAAM,SAAS,OAAO,QAAQ;AAC9B;AAAA,IACF;AAAA,IAEA,KAAK,UAAU;AACb,UAAI,CAAC,OAAO,QAAQ;AAClB,cAAM,IAAI,MAAM,oBAAoB;AAAA,MACtC;AAEA,YAAM,SAAS,MAAM,SAAS,EAAE,MAAM,KAAK,CAAC,MAAM,EAAE,OAAO,OAAO,OAAO,MAAM;AAC/E,UAAI,CAAC,QAAQ;AACX,cAAM,IAAI,MAAM,oBAAoB;AAAA,MACtC;AACA,YAAM,WAAW,OAAO,MAAM;AAC9B;AAAA,IACF;AAAA,IAEA,KAAK,WAAW;AACd,YAAM,QAAQ,OAAO,SAAS,CAAC;AAC/B,YAAM,aAAa,KAAK;AACxB;AAAA,IACF;AAAA,IAEA,KAAK,WAAW;AACd,YAAM,QAAQ,OAAO,SAAS,CAAC;AAC/B,YAAM,aAAa,KAAK;AACxB;AAAA,IACF;AAAA,EACF;AAEA,UAAQ,aAAa,cAAc,EAAE,WAAW,OAAO,UAAU,CAAC;AAElE,SAAO;AAAA,IACL,SAAS,MAAM;AAAA,IAEf;AAAA,EACF;AACF;AAUO,IAAM,sBAAsB;AAAA,EACjC,EAAE,MAAM,iBAAiB,UAAU,mBAAmB;AAAA,EACtD,EAAE,MAAM,mBAAmB,UAAU,qBAAqB;AAAA,EAC1D,EAAE,MAAM,cAAc,UAAU,iBAAiB;AACnD;;;AClLA,SAAS,MAAM,YAAY,eAAe;AAC1C,SAAS,gBAAgB;AACzB,SAAS,kBAAkB;;;ACLpB,IAAM,aAAa;AAAA,EACxB,WAAW;AAAA,IACT,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,QAAQ;AAAA,EACV;AAAA,EACA,eAAe;AAAA,IACb,cAAc;AAAA,EAChB;AAAA,EACA,aAAa;AAAA,IACX,OAAO;AAAA,IACP,SAAS;AAAA,IACT,cAAc;AAAA,IACd,UAAU;AAAA,IACV,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,iBAAiB;AAAA,IACjB,OAAO;AAAA,EACT;AAAA,EACA,WAAW;AAAA,IACT,SAAS;AAAA,IACT,eAAe;AAAA,IACf,KAAK;AAAA,EACP;AAAA,EACA,MAAM;AAAA,IACJ,cAAc;AAAA,IACd,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,YAAY;AAAA,EACd;AAAA,EACA,UAAU;AAAA,IACR,OAAO;AAAA,IACP,SAAS;AAAA,IACT,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,gBAAgB;AAAA,IAChB,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,WAAW;AAAA,IACX,YAAY;AAAA,EACd;AAAA,EACA,SAAS;AAAA,IACP,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,OAAO;AAAA,EACT;AAAA,EACA,QAAQ;AAAA,IACN,SAAS;AAAA,IACT,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,YAAY;AAAA,EACd;AAAA,EACA,UAAU;AAAA,IACR,SAAS;AAAA,IACT,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,eAAe;AAAA,IACf,eAAe;AAAA,IACf,SAAS;AAAA,IACT,cAAc;AAAA,IACd,cAAc;AAAA,EAChB;AAAA,EACA,gBAAgB;AAAA,IACd,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,eAAe;AAAA,IACf,eAAe;AAAA,IACf,SAAS;AAAA,IACT,WAAW;AAAA,EACb;AAAA,EACA,UAAU;AAAA,IACR,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,KAAK;AAAA,IACL,WAAW;AAAA,IACX,YAAY;AAAA,IACZ,WAAW;AAAA,IACX,UAAU;AAAA,EACZ;AAAA,EACA,gBAAgB;AAAA,IACd,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,SAAS;AAAA,IACT,cAAc;AAAA,IACd,YAAY;AAAA,EACd;AAAA,EACA,wBAAwB;AAAA,IACtB,aAAa;AAAA,IACb,iBAAiB;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,QAAQ;AAAA,IACN,SAAS;AAAA,IACT,eAAe;AAAA,IACf,KAAK;AAAA,IACL,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOX,SAAS;AAAA,EACX;AAAA,EACA,YAAY;AAAA,IACV,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,KAAK;AAAA,IACL,WAAW;AAAA,IACX,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,SAAS;AAAA;AAAA;AAAA;AAAA,IAIT,cAAc;AAAA,IACd,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,SAAS;AAAA,EACX;AAAA,EACA,gBAAgB;AAAA,IACd,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,YAAY;AAAA;AAAA,IAEZ,cAAc;AAAA,EAChB;AAAA,EACA,cAAc;AAAA,IACZ,UAAU;AAAA,IACV,YAAY;AAAA;AAAA;AAAA;AAAA,IAIZ,SAAS;AAAA,IACT,eAAe;AAAA,IACf,MAAM;AAAA,IACN,WAAW;AAAA,EACb;AAAA;AAAA;AAAA;AAAA;AAAA,EAKA,mBAAmB;AAAA,IACjB,QAAQ;AAAA,IACR,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOT,iBAAiB;AAAA,IACjB,iBAAiB;AAAA,IACjB,UAAU;AAAA;AAAA;AAAA;AAAA,IAIV,MAAM;AAAA,IACN,WAAW;AAAA,EACb;AAAA;AAAA,EAEA,cAAc;AAAA,IACZ,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,gBAAgB;AAAA,IAChB,KAAK;AAAA;AAAA,IAEL,WAAW;AAAA;AAAA,IAEX,YAAY;AAAA,EACd;AAAA,EACA,kBAAkB;AAAA,IAChB,MAAM;AAAA,IACN,SAAS;AAAA,IACT,cAAc;AAAA,IACd,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,OAAO;AAAA,IACP,YAAY;AAAA,EACd;AAAA,EACA,YAAY;AAAA,IACV,WAAW;AAAA,IACX,SAAS;AAAA,IACT,UAAU;AAAA,EACZ;AAAA,EACA,WAAW;AAAA,IACT,WAAW;AAAA,IACX,SAAS;AAAA,IACT,UAAU;AAAA,EACZ;AACF;AAMO,IAAM,cAAc;AAAA,EACzB,OAAO;AAAA,IACL,WAAW;AAAA,MACT,iBAAiB;AAAA,MACjB,OAAO;AAAA,IACT;AAAA,IACA,aAAa;AAAA,MACX,QAAQ,aAAa,UAAU,EAAE,CAAC;AAAA,IACpC;AAAA,IACA,MAAM;AAAA,MACJ,iBAAiB;AAAA,MACjB,WAAW;AAAA,MACX,aAAa;AAAA,MACb,cAAc;AAAA,MACd,YAAY;AAAA,IACd;AAAA,IACA,cAAc;AAAA,MACZ,WAAW;AAAA,IACb;AAAA,IACA,UAAU;AAAA,MACR,iBAAiB;AAAA,MACjB,OAAO;AAAA,IACT;AAAA,IACA,eAAe;AAAA,MACb,iBAAiB;AAAA,IACnB;AAAA,IACA,QAAQ;AAAA,MACN,OAAO;AAAA,IACT;AAAA,IACA,UAAU;AAAA,MACR,iBAAiB,OAAO,CAAC;AAAA,MACzB,OAAO,OAAO,CAAC;AAAA,IACjB;AAAA,IACA,gBAAgB;AAAA,MACd,OAAO,UAAU,CAAC;AAAA,IACpB;AAAA,IACA,YAAY;AAAA,MACV,OAAO,UAAU,CAAC;AAAA,IACpB;AAAA,IACA,gBAAgB;AAAA,MACd,OAAO,UAAU,CAAC;AAAA,IACpB;AAAA,EACF;AAAA,EACA,MAAM;AAAA,IACJ,WAAW;AAAA,MACT,iBAAiB;AAAA,MACjB,OAAO;AAAA,IACT;AAAA,IACA,aAAa;AAAA,MACX,QAAQ,aAAa,UAAU,CAAC,CAAC;AAAA,IACnC;AAAA,IACA,MAAM;AAAA,MACJ,iBAAiB;AAAA,MACjB,WAAW;AAAA,MACX,aAAa;AAAA,MACb,cAAc;AAAA,MACd,YAAY;AAAA,IACd;AAAA,IACA,cAAc;AAAA,MACZ,WAAW;AAAA,IACb;AAAA,IACA,UAAU;AAAA,MACR,iBAAiB;AAAA,MACjB,OAAO;AAAA,IACT;AAAA,IACA,eAAe;AAAA,MACb,iBAAiB;AAAA,IACnB;AAAA,IACA,QAAQ;AAAA,MACN,OAAO;AAAA,IACT;AAAA,IACA,UAAU;AAAA,MACR,iBAAiB,OAAO,CAAC;AAAA,MACzB,OAAO,OAAO,CAAC;AAAA,IACjB;AAAA,IACA,gBAAgB;AAAA,MACd,OAAO,UAAU,CAAC;AAAA,IACpB;AAAA,IACA,YAAY;AAAA,MACV,OAAO,UAAU,CAAC;AAAA,IACpB;AAAA,IACA,gBAAgB;AAAA,MACd,OAAO,UAAU,CAAC;AAAA,IACpB;AAAA,EACF;AACF;;;ACpQA,IAAM,iBAAiB,KAAK;AAG5B,SAAS,YAAY,IAAa,QAAyB;AACzD,QAAM,MAAM,CAAC,MAAuB;AAClC,UAAM,MAAM,EAAE,QAAQ,YAAY;AAClC,UAAM,MACJ,OAAO,EAAE,cAAc,YAAY,EAAE,UAAU,KAAK,IAChD,IAAI,EAAE,UAAU,KAAK,EAAE,MAAM,KAAK,EAAE,CAAC,CAAC,KACtC;AACN,WAAO,GAAG,GAAG,GAAG,GAAG;AAAA,EACrB;AACA,QAAM,QAAkB,CAAC,IAAI,EAAE,CAAC;AAChC,MAAI,MAAsB,GAAG;AAC7B,MAAI,OAAO;AACX,SAAO,OAAO,QAAQ,UAAU,OAAO,GAAG;AACxC,UAAM,QAAQ,IAAI,GAAG,CAAC;AACtB,UAAM,IAAI;AACV;AAAA,EACF;AACA,SAAO,MAAM,KAAK,GAAG;AACvB;AAGA,SAAS,kBAAkB,IAAa,MAAwB;AAC9D,MAAI,MAAsB;AAC1B,SAAO,OAAO,QAAQ,KAAK,eAAe;AACxC,QAAI,IAAI,eAAe,cAAc,EAAG,QAAO;AAC/C,UAAM,IAAI;AAAA,EACZ;AACA,SAAO;AACT;AASO,SAAS,oBAAoB,MAA6B;AAC/D,QAAM,QAAuB,CAAC;AAC9B,QAAM,OAAO,oBAAI,IAAY;AAC7B,QAAM,OAAO,CAAC,MAAyB;AACrC,UAAM,IAAI,GAAG,EAAE,IAAI,IAAI,EAAE,OAAO;AAChC,QAAI,KAAK,IAAI,CAAC,EAAG;AACjB,SAAK,IAAI,CAAC;AACV,UAAM,KAAK,CAAC;AAAA,EACd;AAEA,MAAI,UAAU;AACd,MAAI,mBAAmB;AAEvB,aAAW,MAAM,KAAK,iBAAiB,GAAG,GAAG;AAC3C,UAAM,KAAK,iBAAiB,EAAE;AAC9B,QAAI,GAAG,YAAY,UAAU,GAAG,eAAe,SAAU;AACzD,UAAM,IAAI,GAAG,sBAAsB;AACnC,QAAI,EAAE,UAAU,KAAK,EAAE,WAAW,EAAG;AAErC,UAAM,UAAU,MAAM,KAAK,GAAG,UAAU,EAAE;AAAA,MACxC,CAAC,MAAM,EAAE,aAAa,MAAM,EAAE,eAAe,IAAI,KAAK,EAAE,SAAS;AAAA,IACnE;AACA,QAAI,QAAS,WAAU;AACvB,QAAI,GAAG,YAAY,OAAO;AACxB,YAAM,MAAM;AACZ,UAAI,IAAI,YAAY,IAAI,eAAe,KAAK,IAAI,QAAQ,eAAe,QAAQ;AAC7E,2BAAmB;AAAA,MACrB;AAAA,IACF;AAEA,QACE,WACA,GAAG,cAAc,GAAG,cAAc,KAClC,GAAG,iBAAiB,cACpB,CAAC,kBAAkB,IAAI,IAAI,GAC3B;AACA,WAAK,EAAE,MAAM,YAAY,IAAI,IAAI,GAAG,SAAS,GAAG,cAAc,GAAG,YAAY,CAAC;AAAA,IAChF;AAAA,EACF;AAEA,QAAM,OAAO,KAAK,sBAAsB;AACxC,QAAM,OAAO,KAAK,QAAQ,KAAK;AAC/B,QAAM,QAAQ,QAAQ,kBAAkB,CAAC,WAAW,CAAC;AAErD,SAAO,EAAE,OAAO,MAAM;AACxB;AAcA,SAAS,QAAQ,MAAc,OAAsC;AACnE,MAAI,OAAO,WAAW,YAAa;AACnC,MAAI;AACF,UAAM,OAAQ,OAAwC,OAAO,SAAS,QAAQ;AAC9E,QAAI,OAAO,SAAS,WAAY,MAAK,MAAM,KAAK;AAAA,EAClD,QAAQ;AAAA,EAER;AACF;AAWA,IAAM,QAAsB,EAAE,OAAO,CAAC,GAAG,OAAO,MAAM;AAK/C,SAAS,mBAAmB,MAAe,MAA+C;AAC/F,MAAI;AACF,UAAM,SAAS,oBAAoB,IAAI;AACvC,QAAI,OAAO,MAAM,WAAW,KAAK,CAAC,OAAO,MAAO,QAAO;AACvD,UAAM,OAAgC,EAAE,GAAI,KAAK,WAAW,CAAC,GAAI,aAAa,KAAK,WAAW;AAC9F,QAAI,OAAO,OAAO;AAChB,cAAQ,OAAO,KAAK,QAAQ,UAAU,IAAI;AAC1C,aAAO;AAAA,IACT;AACA,UAAM,QAAQ,OAAO,MAAM,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK;AACnD,UAAM,UAAU,OAAO,MAAM,OAAO,CAAC,GAAG,MAAM,KAAK,IAAI,GAAG,EAAE,OAAO,GAAG,CAAC;AACvE,YAAQ,OAAO,KAAK,QAAQ,YAAY;AAAA,MACtC,OAAO,MAAM,KAAK,GAAG;AAAA,MACrB;AAAA,MACA,YAAY,OAAO,MAAM;AAAA,MACzB,GAAG;AAAA,IACL,CAAC;AACD,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AA/LA;AA6MO,IAAM,yBAAN,MAA2D;AAAA,EAKhE,YAAY,MAA8B,OAAuC;AAL5E;AACL;AACA,kCAAY,oBAAI,IAAY;AAC5B;AAGE,uBAAK,QAAS;AACd,SAAK,cAAc,IAAI;AAAA,EACzB;AAAA,EAEA,cAAoB;AAClB,0BAAK,gDAAL;AAAA,EACF;AAAA,EAEA,mBAAyB;AACvB,0BAAK,8CAAL;AAAA,EACF;AAuCF;AAtDE;AACA;AACA;AAHK;AAkBL,cAAS,WAAS;AAChB,MAAI,OAAO,WAAW,YAAa;AACnC,MAAI,mBAAK,UAAS,OAAW;AAC7B,QAAM,MACJ,OAAO,OAAO,0BAA0B,aACpC,OAAO,sBAAsB,KAAK,MAAM,IACxC,CAAC,OAA6B,OAAO,WAAW,MAAM,GAAG,CAAC,GAAG,CAAC;AACpE,qBAAK,MAAO,IAAI,MAAM;AACpB,WAAO,WAAW,MAAM;AACtB,yBAAK,MAAO;AACZ,4BAAK,2CAAL;AAAA,IACF,GAAG,CAAC;AAAA,EACN,CAAC;AACH;AAEA,SAAI,WAAS;AACX,MAAI;AACF,UAAM,QAAQ,mBAAK,QAAL;AACd,QAAI,CAAC,SAAS,CAAC,MAAM,KAAM;AAC3B,QAAI,mBAAK,WAAU,IAAI,MAAM,QAAQ,EAAG;AACxC,uBAAK,WAAU,IAAI,MAAM,QAAQ;AACjC,uBAAmB,MAAM,MAAM,KAAK;AAAA,EACtC,QAAQ;AAAA,EAER;AACF;AAEA,YAAO,WAAS;AACd,MACE,mBAAK,UAAS,UACd,OAAO,WAAW,eAClB,OAAO,OAAO,yBAAyB,YACvC;AACA,WAAO,qBAAqB,mBAAK,KAAI;AAAA,EACvC;AACA,qBAAK,MAAO;AACd;;;AF7NF,SAAS,GAAG,QAAyD;AACnE,SAAO;AACT;AAEA,SAAS,sBACP,UACuB;AACvB,MAAI,CAAC,SAAU,QAAO;AACtB,MAAI,aAAa,KAAM,QAAO,EAAE,OAAO,SAAS;AAChD,SAAO;AACT;AAEA,SAAS,kBAAkB,gBAAwC;AACjE,SAAO,eAAe,UAAU;AAClC;AAOA,SAAS,UAAU,MAAsB;AACvC,MAAI,IAAI;AACR,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,SAAK,KAAK,KAAK,GAAG,EAAE,IAAI,KAAK,WAAW,CAAC,OAAO;AAAA,EAClD;AACA,SAAO,EAAE,SAAS,EAAE;AACtB;AAUA,SAAS,cAAc,OAAuB;AAC5C,SAAO,MAAM,QAAQ,UAAU,MAAM;AACvC;AAEA,SAAS,aAAa,OAAyD;AAC7E,MAAI,SAAS,UAAU,OAAQ,QAAO;AACtC,MAAI,OAAO,WAAW,aAAa;AACjC,WAAO,OAAO,aAAa,8BAA8B,EAAE,UAAU,SAAS;AAAA,EAChF;AACA,SAAO;AACT;AAIA,SAAS,uBAAgC;AACvC,MAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,SAAO,OAAO,aAAa,kCAAkC,EAAE,WAAW;AAC5E;AAUA,IAAM,qBAAqB;AAI3B,IAAM,uBAAuB;AAK7B,IAAM,sBAAsB;AAG5B,IAAI,iBAAiB;AAlHrB;AAgIO,IAAM,sBAAN,cAAkC,WAAW;AAAA,EA4GlD,cAAc;AACZ,UAAM;AA7GH;AAsCL;AAAA;AAAA;AAAA,qBAAuB;AAAA,MACrB,gBAAgB;AAAA,MAChB,YAAY;AAAA,MACZ,OAAO;AAAA,MACP,SAAS,CAAC;AAAA,IACZ;AAEA,mBAAmC;AAEnC,sBAAqB;AAMrB;AAAA;AAAA;AAAA;AAAA,SAAQ,gBAA+B;AAMvC;AAAA;AAAA;AAAA;AAAA,mBAAyB;AAIzB;AAAA;AAAA;AAAA,uBAAgC;AAEhC;AAAA,0BAAiB;AAGjB;AAAA;AAAA,oBAAyB;AACzB,wBAA8B;AAC9B,wBAAuB;AACvB,0BAA6C,oBAAI,IAAI;AACrD,sBAA4B;AAQ5B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,SAAQ,mBAAyD;AAOjE;AAAA;AAAA;AAAA;AAAA;AAAA,SAAQ,gBAAyC;AACjD,SAAQ,iBAAgC;AAGxC;AAAA,SAAQ,gBAAqC;AAC7C,SAAQ,oBAAyC;AACjD,SAAQ,YAAiC;AACzC,SAAQ,iBAAsC;AAC9C,SAAQ,uBAA4C;AACpD,SAAQ,kBAAwD;AAMhE;AAAA;AAAA;AAAA;AAAA,SAAQ,sBAA2C;AACnD,SAAQ,UAAyB;AAGjC;AAAA;AAAA,SAAQ,kBAA+B,oBAAI,IAAI;AAsd/C;AAAA;AAAA;AAAA;AAAA;AAAA,SAAQ,cAAc,MAAY;AAChC,WAAK,sBAAsB;AAC3B,WAAK,iBAAiB;AACtB,UAAI,KAAK,gBAAgB,SAAS;AAChC,aAAK,UAAU;AAAA,MACjB;AAAA,IACF;AAGA;AAAA,SAAQ,sBAAsB,CAAC,MAA4B;AACzD,UAAI,EAAE,kBAAkB,oBAAoB,EAAE,kBAAkB,mBAAmB;AACjF,aAAK,YAAY;AAAA,MACnB;AAAA,IACF;AA5dE,QAAI,uBAAuB,MAAM,MAAM,sBAAK,gDAAL,UAAmB;AAAA,EAC5D;AAAA;AAAA;AAAA;AAAA,EAsBS,mBAAmB;AAC1B,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAMS,oBAAoB;AAC3B,UAAM,kBAAkB;AACxB,SAAK,cAAc;AAAA,EACrB;AAAA,EAES,uBAAuB;AAC9B,UAAM,qBAAqB;AAC3B,SAAK,gBAAgB;AACrB,QAAI,KAAK,oBAAoB,MAAM;AACjC,mBAAa,KAAK,eAAe;AACjC,WAAK,kBAAkB;AAAA,IACzB;AACA,SAAK,sBAAsB;AAAA,EAC7B;AAAA;AAAA,EAGS,QAAQ,cAAoC;AACnD,QAAI,aAAa,IAAI,SAAS,GAAG;AAC/B,WAAK,gBAAgB;AACrB,WAAK,cAAc;AAAA,IACrB;AACA,SAAK,mBAAmB;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,qBAAqB;AAC3B,QAAI,KAAK,kBAAkB,KAAM;AACjC,UAAM,SAAS,KAAK;AACpB,SAAK,gBAAgB;AACrB,QAAI,WAAW,QAAQ;AACrB,YAAM,OAAO,KAAK,cAA2B,iBAAiB;AAC9D,YAAM,MAAM;AACZ;AAAA,IACF;AAEA,UAAM,KAAK,KAAK;AAChB,SAAK,iBAAiB;AACtB,QAAI,CAAC,GAAI;AACT,UAAM,MAAM,KAAK,cAA2B,sBAAsB,cAAc,EAAE,CAAC,WAAW;AAC9F,SAAK,MAAM;AAAA,EACb;AAAA;AAAA;AAAA;AAAA,EAMQ,gBAAgB;AACtB,QAAI,CAAC,KAAK,QAAS;AAGnB,SAAK,gBAAgB,KAAK,QAAQ,QAAQ,UAAU,MAAM;AACxD,WAAK,yBAAyB;AAAA,IAChC,CAAC;AAGD,QAAI,KAAK,QAAQ,aAAa,WAAW;AACvC,WAAK,oBAAoB,KAAK,QAAQ,YAAY,UAAU,MAAM;AAChE,aAAK,yBAAyB;AAAA,MAChC,CAAC;AAAA,IACH;AAGA,QAAI,KAAK,QAAQ,gBAAgB,WAAW;AAC1C,WAAK,uBAAuB,KAAK,QAAQ,eAAe,UAAU,MAAM;AACtE,aAAK,yBAAyB;AAAA,MAChC,CAAC;AAAA,IACH;AAGA,QAAI,KAAK,QAAQ,OAAO,WAAW;AAEjC,UAAI,KAAK,QAAQ,OAAO,WAAW;AACjC,cAAM,eAAe,KAAK,QAAQ,OAAO;AAAA,UACvC,EAAE,UAAU,CAAC,kCAAkC,8BAA8B,EAAE;AAAA,UAC/E;AAAA,QACF;AACA,cAAM,eAAe,aAClB,OAAO,CAAC,MAAM;AACb,gBAAM,WAAW,EAAE,OAAO;AAC1B,iBAAO,OAAO,aAAa,YAAY,SAAS,WAAW,WAAW;AAAA,QACxE,CAAC,EACA,IAAI;AAEP,YAAI,gBAAgB,KAAK,IAAI,IAAI,aAAa,KAAK,KAAO;AACxD,gBAAM,aAAc,aAAa,MAAO,SAAoB,QAAQ,aAAa,EAAE;AAGnF,eAAK,cAAc,UAAU;AAAA,QAC/B;AAAA,MACF;AAEA,WAAK,YAAY,KAAK,QAAQ,OAAO;AAAA,QACnC,EAAE,UAAU,CAAC,kCAAkC,8BAA8B,EAAE;AAAA,QAC/E,CAAC,UAAU;AAMT,gBAAM,KAAM,MAA0B;AACtC,cAAI,OAAO,OAAO,YAAY,KAAK,IAAI,IAAI,MAAM,IAAO;AACxD,gBAAM,WAAW,MAAM,OAAO;AAC9B,cAAI,OAAO,aAAa,YAAY,CAAC,SAAS,WAAW,WAAW,EAAG;AACvE,gBAAM,aAAa,SAAS,QAAQ,aAAa,EAAE;AAEnD,eAAK,cAAc,UAAU;AAC7B,eAAK,SAAS,OAAO,QAAQ,oBAAoB;AAAA,QACnD;AAAA,MACF;AAAA,IACF;AAGA,QAAI,KAAK,QAAQ,OAAO,WAAW;AACjC,YAAM,iBAAiB,CAAC,UAA6D;AACnF,cAAM,SAAS,MAAM,OAAO;AAC5B,cAAM,SAAS,MAAM,OAAO;AAC5B,YAAI,WAAW,KAAK,WAAY;AAChC,YAAI,CAAC,OAAQ;AAGb,aAAK,cAAc,MAAM;AACzB,aAAK,eAAe;AAEpB,YAAI,KAAK,oBAAoB,KAAM,cAAa,KAAK,eAAe;AACpE,aAAK,kBAAkB,WAAW,MAAM;AACtC,eAAK,eAAe;AACpB,eAAK,kBAAkB;AAAA,QACzB,GAAG,IAAI;AAAA,MACT;AAGA,UAAI,KAAK,QAAQ,OAAO,WAAW;AACjC,cAAM,SAAS,KAAK,QAAQ,OAAO,UAAU,EAAE,OAAO,CAAC,wBAAwB,EAAE,GAAG,CAAC;AACrF,cAAM,UAAU,OACb,OAAO,CAAC,MAAO,EAAE,OAAO,WAAsB,KAAK,cAAc,EAAE,OAAO,MAAM,EAChF,IAAI;AACP,YAAI,WAAW,KAAK,IAAI,IAAI,QAAQ,KAAK,KAAO;AAC9C,yBAAe,OAAO;AAAA,QACxB;AAAA,MACF;AAEA,WAAK,iBAAiB,KAAK,QAAQ,OAAO;AAAA,QACxC,EAAE,OAAO,CAAC,wBAAwB,EAAE;AAAA,QACpC,CAAC,UAAU;AAIT,gBAAM,KAAM,MAA0B;AACtC,cAAI,OAAO,OAAO,YAAY,KAAK,IAAI,IAAI,MAAM,IAAO;AACxD,yBAAe,KAAK;AAAA,QACtB;AAAA,MACF;AAAA,IACF;AAMA,SAAK,wBAAwB;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,0BAA0B;AAChC,UAAM,MAAM,KAAK,SAAS;AAC1B,QAAI,CAAC,KAAK,UAAW;AACrB,SAAK,UAAU,KAAK,eAAe;AACnC,QAAI,CAAC,KAAK,QAAS;AACnB,UAAM,SAAS,KAAK;AACpB,SAAK,sBAAsB,IAAI,UAAU,CAAC,UAAU;AAClD,YAAM,QAAQ,MAAM,SAAS,CAAC;AAC9B,UAAI,MAAM,YAAY,OAAQ;AAC9B,YAAM,aAAa,OAAO,MAAM,eAAe,EAAE;AACjD,UAAI,MAAM,SAAS,gCAAgC;AACjD,aAAK;AAAA,UACH;AAAA,UACA,MAAM;AAAA,UACL,MAAM,YAAqC;AAAA,QAC9C;AAAA,MACF,WAAW,MAAM,SAAS,+BAA+B;AACvD,aAAK,WAAW,YAAY,MAAM,IAAyB;AAAA,MAC7D,WAAW,MAAM,SAAS,gCAAgC;AACxD,aAAK,YAAY,UAAU;AAAA,MAC7B;AAAA,IACF,CAAC;AACD,QAAI,QAAQ,wCAAwC,EAAE,SAAS,OAAO,CAAC;AAAA,EACzE;AAAA;AAAA;AAAA;AAAA,EAKQ,iBAAgC;AACtC,UAAM,SAAS,KAAK,QAAqB,gBAAgB;AACzD,UAAM,UAAU,QAAQ,aAAa,cAAc;AACnD,QAAI,QAAS,QAAO;AACpB,WAAO,KAAK,cAAc,KAAK,eAAe,sBAAsB,KAAK,aAAa;AAAA,EACxF;AAAA;AAAA;AAAA,EAIQ,YACN,YACA,MACA,UACM;AACN,QAAI,CAAC,cAAc,CAAC,MAAM,QAAQ,GAAI;AACtC,QAAI,KAAK,gBAAgB,IAAI,UAAU,EAAG;AAC1C,SAAK,gBAAgB,IAAI,UAAU;AACnC,UAAM,UAAU,KAAK,UAAU,WAAW,CAAC;AAC3C,UAAM,OAAO,aAAa,YAAY,CAAC,MAAM,GAAG,OAAO,IAAI,CAAC,GAAG,SAAS,IAAI;AAE5E,SAAK,YAAY,EAAE,GAAG,KAAK,WAAW,SAAS,KAAK;AAAA,EACtD;AAAA;AAAA,EAGQ,WAAW,YAAoB,MAA+B;AACpE,QAAI,CAAC,cAAc,CAAC,MAAM,OAAQ;AAClC,UAAM,UAAU,KAAK,UAAU,WAAW,CAAC;AAC3C,UAAM,MAAM,QAAQ,UAAU,CAAC,MAAM,EAAE,OAAO,OAAO,UAAU;AAC/D,QAAI,MAAM,EAAG;AACb,UAAM,OAAO,CAAC,GAAG,OAAO;AACxB,SAAK,GAAG,IAAI;AAIZ,QAAI,KAAK,YAAY,cAAc,KAAK,OAAO,OAAO,YAAY;AAChE,WAAK,UAAU,KAAK,OAAO;AAAA,IAC7B;AACA,SAAK,YAAY,EAAE,GAAG,KAAK,WAAW,SAAS,KAAK;AAOpD,SAAK,sBAAsB;AAAA,EAC7B;AAAA;AAAA,EAGQ,YAAY,YAA0B;AAC5C,QAAI,CAAC,WAAY;AACjB,UAAM,UAAU,KAAK,UAAU,WAAW,CAAC;AAC3C,UAAM,OAAO,QAAQ,OAAO,CAAC,MAAM,EAAE,OAAO,OAAO,UAAU;AAC7D,QAAI,KAAK,WAAW,QAAQ,OAAQ;AACpC,SAAK,gBAAgB,OAAO,UAAU;AAKtC,QAAI,KAAK,YAAY,YAAY;AAC/B,WAAK,sBAAsB;AAC3B,WAAK,iBAAiB;AACtB,WAAK,UAAU;AACf,WAAK,cAAc;AAInB,WAAK,gBAAgB;AACrB,WAAK,iBAAiB;AAAA,IACxB;AACA,SAAK,YAAY,EAAE,GAAG,KAAK,WAAW,SAAS,KAAK;AAAA,EACtD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaQ,2BAAiC;AACvC,SAAK,sBAAsB;AAC3B,SAAK,cAAc;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeQ,wBAA8B;AACpC,QAAI,KAAK,YAAY,KAAM;AAC3B,UAAM,UAAU,KAAK,kBAAkB,KAAK,kBAAkB,CAAC;AAC/D,UAAM,eAAe,QAAQ,KAAK,CAAC,MAAM,EAAE,OAAO,OAAO,KAAK,OAAO;AACrE,QAAI,aAAc;AAClB,SAAK,sBAAsB;AAC3B,SAAK,iBAAiB;AACtB,SAAK,UAAU;AACf,SAAK,cAAc;AAGnB,SAAK,gBAAgB;AACrB,SAAK,iBAAiB;AAAA,EACxB;AAAA,EAEQ,kBAAkB;AACxB,SAAK,gBAAgB;AACrB,SAAK,oBAAoB;AACzB,SAAK,uBAAuB;AAC5B,SAAK,YAAY;AACjB,SAAK,iBAAiB;AACtB,SAAK,sBAAsB;AAC3B,SAAK,gBAAgB;AACrB,SAAK,oBAAoB;AACzB,SAAK,uBAAuB;AAC5B,SAAK,YAAY;AACjB,SAAK,iBAAiB;AACtB,SAAK,sBAAsB;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYQ,YAAY,IAAY;AAC9B,SAAK,cAAc,EAAE;AAErB,SAAK,SAAS,OAAO,QAAQ,eAAe;AAAA,MAC1C,YAAY,KAAK;AAAA,MACjB,YAAY;AAAA,MACZ,UAAU;AAAA,MACV,WAAW,KAAK,IAAI;AAAA,IACtB,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWQ,cAAc,IAAY;AAGhC,SAAK,sBAAsB;AAC3B,SAAK,UAAU;AACf,SAAK,cAAc;AACnB,SAAK,gBAAgB;AACrB,SAAK,iBAAiB;AACtB,QAAI,qBAAqB,GAAG;AAG1B,WAAK,iBAAiB;AACtB;AAAA,IACF;AAEA,SAAK,WAAW;AAChB,SAAK,iBAAiB;AAGtB,SAAK,mBAAmB,WAAW,KAAK,aAAa,KAAK,gBAAgB,CAAC;AAAA,EAC7E;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaQ,cAAc;AACpB,UAAM,KAAK,KAAK;AAChB,SAAK,cAAc;AAEnB,SAAK,gBAAgB;AAIrB,SAAK,sBAAsB;AAC3B,QAAI,qBAAqB,GAAG;AAE1B,WAAK,iBAAiB;AACtB,WAAK,UAAU;AAAA,IACjB,OAAO;AAIL,WAAK,WAAW;AAChB,WAAK,iBAAiB;AACtB,WAAK,mBAAmB,WAAW,KAAK,aAAa,KAAK,gBAAgB,CAAC;AAAA,IAC7E;AAEA,QAAI,OAAO,MAAM;AACf,WAAK,SAAS,OAAO,QAAQ,eAAe;AAAA,QAC1C,YAAY,KAAK;AAAA,QACjB,YAAY;AAAA,QACZ,UAAU;AAAA,QACV,WAAW,KAAK,IAAI;AAAA,MACtB,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAsBQ,wBAA8B;AACpC,QAAI,KAAK,qBAAqB,MAAM;AAClC,mBAAa,KAAK,gBAAgB;AAClC,WAAK,mBAAmB;AAAA,IAC1B;AAAA,EACF;AAAA,EAEQ,gBAAgB,QAAgB,UAAkB,OAAsB;AAC9E,UAAM,OAAO,IAAI,IAAI,KAAK,cAAc;AACxC,SAAK,IAAI,QAAQ,KAAK;AACtB,SAAK,iBAAiB;AAEtB,SAAK,SAAS,OAAO,QAAQ,gBAAgB,EAAE,QAAQ,UAAU,MAAM,CAAC;AAAA,EAC1E;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaQ,gBAAqC;AAC3C,UAAM,gBAAgB,KAAK,UAAU,WAAW,CAAC;AAQjD,UAAM,OAAO,oBAAI,IAAoB;AACrC,UAAM,UAAU,KAAK,UAAU,aAAa,CAAC,GAAG,IAAI,CAAC,MAAyB;AAC5E,UAAI,KAAK,UAAU,UAAU,EAAE,QAAQ,CAAC;AACxC,YAAM,MAAM,KAAK,IAAI,EAAE,KAAK;AAC5B,WAAK,IAAI,IAAI,MAAM,CAAC;AACpB,UAAI,MAAM,EAAG,MAAK,GAAG,EAAE,IAAI,GAAG;AAC9B,aAAO;AAAA,QACL,MAAM;AAAA,QACN,QAAQ;AAAA,UACN;AAAA,UACA,UAAU,EAAE;AAAA,UACZ,QAAQ,EAAE;AAAA,UACV,UAAU,EAAE;AAAA,QACd;AAAA,MACF;AAAA,IACF,CAAC;AACD,WAAO,CAAC,GAAG,QAAQ,GAAG,aAAa;AAAA,EACrC;AAAA,EAEQ,oBAAyC;AAC/C,WAAO,KAAK,cAAc,EAAE,OAAO,CAAC,MAAM;AACxC,UAAI,CAAC,EAAE,YAAa,QAAO;AAC3B,UAAI,CAAC,KAAK,QAAS,QAAO;AAC1B,YAAM,SAAS,KAAK,QAAQ,aAAsB,EAAE,WAAW;AAC/D,aAAO,OAAO;AAAA,IAChB,CAAC;AAAA,EACH;AAAA,EAEQ,kBAAkB,SAAmD;AAC3E,QAAI,KAAK,UAAU,aAAa,YAAY;AAC1C,aAAO,CAAC,GAAG,OAAO,EAAE,KAAK,CAAC,GAAG,OAAO,EAAE,OAAO,YAAY,MAAM,EAAE,OAAO,YAAY,EAAE;AAAA,IACxF;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,mBAAmB,SAAmD;AAC5E,UAAM,IAAI,KAAK,aAAa,KAAK,EAAE,YAAY;AAC/C,QAAI,CAAC,KAAK,UAAU,cAAc,CAAC,EAAG,QAAO;AAC7C,WAAO,QAAQ;AAAA,MACb,CAAC,SACC,KAAK,OAAO,SAAS,YAAY,EAAE,SAAS,CAAC,KAC7C,cAAc,KAAK,OAAO,MAAM,EAAE,YAAY,EAAE,SAAS,CAAC,KAC1D,KAAK,OAAO,UAAU,YAAY,EAAE,SAAS,CAAC;AAAA,IAClD;AAAA,EACF;AAAA,EAEQ,gBACN,UAC8C;AAC9C,UAAM,SAAS,oBAAI,IAA6C;AAChE,eAAW,QAAQ,UAAU;AAC3B,YAAM,MAAM,KAAK,OAAO;AACxB,UAAI,CAAC,OAAO,IAAI,GAAG,EAAG,QAAO,IAAI,KAAK,CAAC,CAAC;AACxC,aAAO,IAAI,GAAG,EAAG,KAAK,IAAI;AAAA,IAC5B;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,IAAY,WAAmB;AAC7B,QAAI,KAAK,cAAc,KAAK,eAAe,oBAAqB,QAAO,KAAK;AAC5E,QAAI,KAAK,kBAAkB,MAAM;AAC/B,wBAAkB;AAClB,WAAK,gBAAgB,OAAO,KAAK,IAAI,EAAE,SAAS,EAAE,CAAC,IAAI,cAAc;AAAA,IACvE;AACA,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYQ,kBAA0B;AAChC,QAAI,UAAU;AACd,QAAI,OAAO,WAAW,eAAe,OAAO,OAAO,qBAAqB,YAAY;AAClF,UAAI;AACF,cAAM,MAAM,OACT,iBAAiB,IAAI,EACrB,iBAAiB,0BAA0B,EAC3C,KAAK;AACR,YAAI,IAAI,SAAS,IAAI,EAAG,WAAU,OAAO,WAAW,GAAG;AAAA,iBAC9C,IAAI,SAAS,GAAG,EAAG,WAAU,OAAO,WAAW,GAAG,IAAI;AAAA,MACjE,QAAQ;AAAA,MAGR;AAAA,IACF;AAMA,QAAI,CAAC,OAAO,SAAS,OAAO,EAAG,WAAU;AAGzC,WAAO,KAAK,IAAI,SAAS,kBAAkB,KAAK,UAAU,IAAI,uBAAuB;AAAA,EACvF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA8BQ,oBAAoB;AAC1B,UAAM,QAAQ,sBAAsB,KAAK,QAAQ;AACjD,UAAM,MAAM;AAAA,QACR,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAQL,KAAK;AAAA;AAAA;AAAA,QAGL,KAAK;AAAA;AAAA;AAAA,QAGL,KAAK;AAAA;AAAA;AAAA;AAAA,QAIL,KAAK;AAAA;AAAA;AAAA,QAGL,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA,QAKL,KAAK;AAAA;AAAA;AAAA;AAAA,QAIL,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAaL,KAAK;AAAA;AAAA;AAAA,QAGL,KAAK;AAAA;AAAA;AAAA;AAAA,QAIL,KAAK;AAAA;AAAA;AAAA,QAGL,KAAK;AAAA;AAAA;AAAA;AAAA,UAIH,KAAK;AAAA;AAAA;AAGX,WAAO,cAAc,GAAG;AAAA,EAC1B;AAAA,EAEQ,cAAc,QAAmB;AACvC,UAAM,WAAW,iBAAiB,MAAM;AAIxC,WAAO,kBAAkB,SAAS,GAAG,WAAW,iBAAiB,CAAC,CAAC,yBAAyB,WAAW,QAAQ,CAAC;AAAA,EAClH;AAAA,EAEQ,gBACN,MACA,gBACA,eACA,OACA;AACA,UAAM,SAAS,YAAY,KAAK;AAChC,UAAM,gBAAgB,EAAE,GAAG,WAAW,UAAU,GAAG,OAAO,eAAe;AAEzE,WAAO;AAAA,mBACQ,SAAS,GAAG,aAAa,CAAC,CAAC;AAAA,gBAC9B,kBAAkB,cAAc,CAAC;AAAA;AAAA;AAAA,kBAG/B;AAAA,MACN,GAAG;AAAA,QACD,GAAG,WAAW;AAAA,QACd,GAAI,kBAAkB,OAAO,WAAW,yBAAyB,CAAC;AAAA,MACpE,CAAC;AAAA,IACH,CAAC;AAAA;AAAA,mBAEQ,MAAM,KAAK,gBAAgB,KAAK,OAAO,IAAI,KAAK,OAAO,UAAU,IAAI,CAAC;AAAA;AAAA;AAAA;AAAA,kBAIvE;AAAA,MACN,GAAG;AAAA,QACD,GAAG,WAAW;AAAA,QACd,GAAI,kBAAkB,SAAS,WAAW,yBAAyB,CAAC;AAAA,MACtE,CAAC;AAAA,IACH,CAAC;AAAA;AAAA,mBAEQ,MAAM,KAAK,gBAAgB,KAAK,OAAO,IAAI,KAAK,OAAO,UAAU,MAAM,CAAC;AAAA;AAAA;AAAA;AAAA,EAIzF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,YAAY,MAAyB,QAAiB,OAAyB;AACrF,UAAM,SAAS,YAAY,KAAK;AAChC,UAAM,gBAAgB,KAAK,iBAAiB,KAAK,OAAO;AACxD,UAAM,YAAY,KAAK,eAAe,KAAK,OAAO;AAElD,UAAM,YAAY;AAAA,MAChB,GAAG,WAAW;AAAA,MACd,GAAG,OAAO;AAAA,MACV,GAAI,gBACA;AAAA,QACE,WAAW,aAAa,OAAO,CAAC,CAAC;AAAA,QACjC,YAAY;AAAA,MACd,IACA,CAAC;AAAA,MACL,GAAI,CAAC,SAAS,EAAE,cAAc,uCAAuC,IAAI,CAAC;AAAA,IAC5E;AAEA,UAAM,gBAAgB;AAAA,MACpB,GAAG,WAAW;AAAA,MACd,GAAG,OAAO;AAAA,MACV,GAAI,YAAY,OAAO,gBAAgB,CAAC;AAAA,IAC1C;AAEA,UAAM,eAAe;AAAA,MACnB,GAAG,WAAW;AAAA,MACd,WAAW;AAAA,IACb;AAEA,WAAO;AAAA;AAAA,gBAEK,SAAS,GAAG,SAAS,CAAC,CAAC;AAAA,2BACZ,KAAK,OAAO,EAAE;AAAA;AAAA;AAAA;AAAA,kBAIvB,SAAS,GAAG,aAAa,CAAC,CAAC;AAAA,0BACnB,KAAK;AAAA,mBACZ,MAAM,KAAK,YAAY,KAAK,OAAO,EAAE,CAAC;AAAA,wBACjC,MAAM;AAClB,WAAK,aAAa,KAAK,OAAO;AAAA,IAChC,CAAC;AAAA,wBACa,MAAM;AAClB,WAAK,aAAa;AAAA,IACpB,CAAC;AAAA;AAAA,kBAEO,KAAK,OAAO,QAAQ;AAAA,wBACd,SAAS,GAAG,YAAY,CAAC,CAAC;AAAA;AAAA;AAAA;AAAA,EAIhD;AAAA,EAEQ,aAAa,OAA4B,OAAyB;AACxE,WAAO,MAAM,IAAI,CAAC,MAAM,UAAU,KAAK,YAAY,MAAM,UAAU,MAAM,SAAS,GAAG,KAAK,CAAC;AAAA,EAC7F;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUQ,cACN,MACA,OACA,gBACA;AACA,UAAM,SAAS,YAAY,KAAK;AAEhC,UAAM,YAAY;AAAA,MAChB,GAAG,WAAW;AAAA,MACd,GAAG,OAAO;AAAA,IACZ;AACA,UAAM,sBAAsB;AAAA,MAC1B,GAAG,WAAW;AAAA,MACd,GAAG,OAAO;AAAA,IACZ;AACA,UAAM,oBAAoB;AAAA,MACxB,GAAG,WAAW;AAAA,MACd,GAAG,OAAO;AAAA,IACZ;AAEA,WAAO;AAAA,mBACQ,SAAS,GAAG,WAAW,MAAM,CAAC,CAAC,oBAAoB,KAAK,OAAO,EAAE;AAAA;AAAA;AAAA,kBAGlE,SAAS,GAAG,SAAS,CAAC,CAAC;AAAA;AAAA;AAAA,mBAGtB,MAAM,KAAK,YAAY,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,qBAKtB,SAAS,GAAG,mBAAmB,CAAC,CAAC,6BAA6B,KAAK,OAAO,QAAQ;AAAA;AAAA,qBAElF,SAAS,GAAG,iBAAiB,CAAC,CAAC,gBAAgB,KAAK;AAAA,YAC7D,KAAK,cAAc,KAAK,OAAO,MAAM,CAAC;AAAA,uBAC3B,SAAS,GAAG,WAAW,YAAY,CAAC,CAAC;AAAA;AAAA;AAAA,sBAGtC,SAAS,GAAG,WAAW,gBAAgB,CAAC,CAAC;AAAA;AAAA,uBAExC,MAAM,KAAK,kBAAkB,IAAI,CAAC;AAAA;AAAA;AAAA,YAI7C,iBACI,KAAK;AAAA,MACH;AAAA,MACA;AAAA,MACA,KAAK,eAAe,IAAI,KAAK,OAAO,EAAE;AAAA,MACtC;AAAA,IACF,IACA,OACN;AAAA;AAAA;AAAA;AAAA,EAIR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,kBAAkB,MAAyB;AACjD,UAAM,WAAW,KAAK,OAAO;AAC7B,UAAM,aAAa,cAAc,KAAK,OAAO,MAAM;AACnD,uBAAmB;AAAA,MACjB,QAAQ,uBAAuB,QAAQ;AAAA,MACvC,OAAO;AAAA,MACP,SAAS,aAAa,WAAW,MAAM,GAAG,GAAG,IAAI;AAAA,IACnD,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAMS,SAAS;AAChB,UAAM,QAAQ,aAAa,KAAK,UAAU,KAAK;AAC/C,UAAM,SAAS,YAAY,KAAK;AAChC,UAAM,iBAAiB,sBAAsB,KAAK,UAAU,QAAQ;AAEpE,UAAM,UAAU,KAAK,kBAAkB;AACvC,UAAM,UAAU,KAAK,kBAAkB,OAAO;AAC9C,UAAM,WAAW,KAAK,mBAAmB,OAAO;AAChD,UAAM,gBAAgB,SAAS,KAAK,CAAC,MAAM,EAAE,OAAO,QAAQ;AAC5D,UAAM,SAAS,gBAAgB,KAAK,gBAAgB,QAAQ,IAAI;AAEhE,UAAM,iBAAiB;AAAA,MACrB,GAAG,WAAW;AAAA,MACd,GAAG,OAAO;AAAA,IACZ;AAEA,UAAM,kBAAkB;AAAA,MACtB,GAAG,WAAW;AAAA,MACd,GAAG,OAAO;AAAA,IACZ;AAEA,UAAM,sBAAsB;AAAA,MAC1B,GAAG,WAAW;AAAA,MACd,GAAG,OAAO;AAAA,IACZ;AAEA,UAAM,mBAAmB;AAAA,MACvB,GAAG,WAAW;AAAA,MACd,GAAG,OAAO;AAAA,IACZ;AAYA,QAAI,QAAQ,WAAW,GAAG;AACxB,UAAI,KAAK,QAAS,QAAO;AACzB,aAAO;AAAA;AAAA,kBAEK,SAAS,GAAG,cAAc,CAAC,CAAC;AAAA,6BACjB,KAAK,QAAQ;AAAA;AAAA;AAAA,uBAGnB,SAAS,GAAG,eAAe,CAAC,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAMhD;AAaA,UAAM,WACJ,KAAK,YAAY,OAAO,QAAQ,KAAK,CAAC,MAAM,EAAE,OAAO,OAAO,KAAK,OAAO,IAAI;AAG9E,UAAM,aAAa,WAAW,KAAK,cAAc;AACjD,UAAM,aAAa,eAAe;AAClC,UAAM,gBAAgB,KAAK;AAE3B,WAAO;AAAA;AAAA,gBAEK,SAAS,GAAG,cAAc,CAAC,CAAC;AAAA,2BACjB,KAAK,QAAQ;AAAA;AAAA;AAAA,UAG9B,KAAK,kBAAkB,CAAC;AAAA,+DAC6B,aAAa;AAAA;AAAA;AAAA,mCAGzC,UAAU;AAAA,uCACN,aAAa;AAAA,gCACpB,gBAAgB,KAAK,WAAW,OAAO;AAAA,4BAC3C,KAAK,mBAAmB;AAAA;AAAA,4CAER,UAAU,gBAAgB,UAAU;AAAA,gBAChE,KAAK;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,QACE,GAAG,WAAW;AAAA,QACd,GAAG,OAAO;AAAA,MACZ;AAAA,IACF,CAAC;AAAA;AAAA,2CAE4B,CAAC,UAAU,gBAAgB,CAAC,UAAU;AAAA,gBACjE,WAAW,KAAK,cAAc,UAAU,OAAO,cAAc,IAAI,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMtF;AAAA;AAAA;AAAA;AAAA,EAKQ,gBACN,QACA,UACA,OACA,kBACA,qBACA,gBACA;AACA,WAAO;AAAA,QAEH,KAAK,UAAU,aACX;AAAA,yBACa,SAAS,GAAG,WAAW,aAAa,CAAC,CAAC;AAAA;AAAA,qCAE1B,KAAK,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,yBAQzB,KAAK,YAAY;AAAA,wBAClB,SAAS,GAAG,gBAAgB,CAAC,CAAC;AAAA,yBAC7B,CAAC,MAAkB;AAC1B,WAAK,eAAgB,EAAE,OAA4B;AAAA,IACrD,CAAC;AAAA;AAAA;AAAA,cAIL,OACN;AAAA;AAAA,mBAEa,SAAS,GAAG,WAAW,SAAS,CAAC,CAAC;AAAA,UAE3C,SACI,MAAM,KAAK,OAAO,QAAQ,CAAC,EAAE;AAAA,MAC3B,CAAC,CAAC,UAAU,KAAK,MAAM;AAAA,kBAErB,WACI;AAAA;AAAA,gCAEU,SAAS,GAAG,mBAAmB,CAAC,CAAC;AAAA,+CAClB,QAAQ;AAAA;AAAA,0BAE7B,QAAQ;AAAA;AAAA,wBAGZ,OACN;AAAA,kBACE,KAAK,aAAa,OAAO,KAAK,CAAC;AAAA;AAAA,IAEnC,IACA,KAAK,aAAa,UAAU,KAAK,CACvC;AAAA;AAAA;AAAA,QAIA,KAAK,UAAU,cAAc,SAAS,WAAW,KAAK,KAAK,eACvD;AAAA,yBACa,SAAS,GAAG,cAAc,CAAC,CAAC;AAAA,kDACH,KAAK,YAAY;AAAA;AAAA,cAGvD,OACN;AAAA;AAAA,EAEJ;AACF;AA9rCO;AAAA;AAAA;AAAA;AAuHL,iBAAY,WAA6B;AACvC,QAAM,UAAU,KAAK,kBAAkB;AACvC,MAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,QAAM,OAAO,KAAK,cAAc,qCAAqC;AACrE,MAAI,CAAC,KAAM,QAAO;AAClB,SAAO;AAAA,IACL;AAAA,IACA,UAAU,OAAO,QAAQ,MAAM,IAAI,KAAK,WAAW;AAAA,IACnD,UAAU;AAAA,IACV,YAAY;AAAA,EACd;AACF;AAAA;AAAA;AAAA;AAlIW,oBAKK,aAAa;AAAA;AAAA,EAE3B,WAAW,EAAE,WAAW,MAAM;AAAA,EAC9B,SAAS,EAAE,WAAW,MAAM;AAAA,EAC5B,YAAY,EAAE,MAAM,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAS3B,SAAS,EAAE,OAAO,KAAK;AAAA,EACvB,aAAa,EAAE,OAAO,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAO3B,gBAAgB,EAAE,OAAO,KAAK;AAAA,EAC9B,UAAU,EAAE,OAAO,KAAK;AAAA,EACxB,cAAc,EAAE,OAAO,KAAK;AAAA,EAC5B,cAAc,EAAE,OAAO,KAAK;AAAA,EAC5B,gBAAgB,EAAE,OAAO,KAAK;AAAA,EAC9B,YAAY,EAAE,OAAO,KAAK;AAC5B;AAoqCF,IAAI,CAAC,eAAe,IAAI,sBAAsB,GAAG;AAC/C,iBAAe,OAAO,wBAAwB,mBAAmB;AACnE;;;AGpzCA,IAAM,qBAAgC;AAAA,EACpC,gBAAgB;AAAA,EAChB,YAAY;AAAA,EACZ,OAAO;AAAA,EACP,SAAS,CAAC;AACZ;AAKO,IAAM,wBAAwB;AAAA,EACnC,MACE,WACA,QACA;AACA,UAAM,WAAW,UAAU;AAC3B,UAAM,WAAW,mBAA+D,QAAQ;AAIxF,UAAMA,WAAU,UAAU;AAC1B,UAAM,aAAa,UAAU,cAAc;AAC3C,UAAM,YAAuB,WAAY,WAAyB,EAAE,GAAG,mBAAmB;AAE1F,UAAM,KAAK,SAAS,cAAc,sBAAsB;AAMxD,WAAO,OAAO,IAAI;AAAA,MAChB;AAAA,MACA,SAASA,YAAW;AAAA,MACpB;AAAA,IACF,CAAC;AAED,cAAU,YAAY,EAAE;AACxB,WAAO,MAAM,GAAG,OAAO;AAAA,EACzB;AACF;AAaO,IAAM,UAAU;AAAA,EACrB,IAAI;AAAA,EACJ,SAAS;AAAA,EACT,MAAM;AAAA,EACN,aACE;AAAA;AAAA;AAAA;AAAA,EAKF,WAAW;AAAA;AAAA;AAAA;AAAA,EAKX,SAAS;AAAA,IACP;AAAA,MACE,IAAI;AAAA,MACJ,WAAW;AAAA,MACX,UAAU;AAAA,QACR,MAAM;AAAA,QACN,aAAa;AAAA,QACb,MAAM;AAAA,QACN,UAAU;AAAA,MACZ;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,eAAe,OAAgC;AAC7C,UAAM,UAAW,MAAM,WAAW,CAAC;AACnC,WAAO,QACJ,OAAO,CAAC,MAAM,EAAE,UAAU,EAAE,WAAW,EACvC,IAAI,CAAC,OAAO;AAAA,MACX,IAAI,OAAO,EAAE,OAAO,EAAE;AAAA,MACtB,UAAU,EAAE;AAAA,MACZ,WAAW;AAAA,MACX,YAAY;AAAA,QACV,YAAY,EAAE,OAAO;AAAA,QACrB,UAAU,EAAE,OAAO;AAAA,QACnB,OAAO,EAAE,OAAQ;AAAA,QACjB,MAAM,EAAE,OAAQ;AAAA,QAChB,MAAM,EAAE,OAAQ;AAAA,MAClB;AAAA,IACF,EAAE;AAAA,EACN;AACF;AAEA,IAAO,kBAAQ;",
|
|
6
6
|
"names": ["runtime"]
|
|
7
7
|
}
|
package/package.json
CHANGED