@syntrologie/adapt-guide 0.0.0-stage → 2.8.0-canary.621

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.
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../node_modules/@lit/context/src/lib/create-context.ts", "../../../sdk-contracts/dist/canvas-context.js", "../../../sdk-contracts/dist/telemetry-events.js", "../../../sdk-contracts/dist/card-lifecycle.js", "../../../sdk-contracts/dist/detector-events.js", "../../../sdk-contracts/dist/image-standin.js", "../../../sdk-contracts/dist/mount-plumbing.js", "../../../sdk-contracts/dist/routes.js", "../../../sdk-contracts/dist/schemas.js", "../../../sdk-contracts/dist/viewport.js", "../../../sdk-contracts/dist/live-price.js", "../../../canvases/postcard-guide/src/cart.ts", "../../../canvases/postcard-guide/src/host.ts", "../../../canvases/postcard-guide/src/page.ts", "../../../canvases/postcard-guide/src/copy.ts", "../../../canvases/postcard-guide/src/templates/compare.ts", "../../../canvases/postcard-guide/src/templates/base.ts", "../../../canvases/postcard-guide/src/templates/fit.ts", "../../../canvases/postcard-guide/src/templates/format.ts", "../../../canvases/postcard-guide/src/templates/parts.ts", "../../../canvases/postcard-guide/src/templates/styles.ts", "../../../canvases/postcard-guide/src/templates/hero-pick.ts", "../../../canvases/postcard-guide/src/templates/product-styles.ts", "../../../canvases/postcard-guide/src/templates/pick-from-pictures.ts", "../../../canvases/postcard-guide/src/templates/product-detail.ts", "../../../canvases/postcard-guide/src/templates/what-you-need.ts", "../../../canvases/postcard-guide/src/templates/index.ts", "../../../canvases/postcard-guide/src/guide-client.ts", "../../../canvases/postcard-guide/src/product-history.ts", "../../../canvases/postcard-guide/src/seen.ts", "../../../canvases/postcard-guide/src/styles.ts", "../../../canvases/postcard-guide/src/words-stream.ts", "../../../canvases/postcard-guide/src/words.ts", "../src/guide-widget.ts", "../src/runtime.ts"],
4
+ "sourcesContent": ["/**\n * @license\n * Copyright 2021 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n/**\n * The Context type defines a type brand to associate a key value with the context value type\n */\nexport type Context<KeyType, ValueType> = KeyType & {__context__: ValueType};\n\n/**\n * @deprecated use Context instead\n */\nexport type ContextKey<KeyType, ValueType> = Context<KeyType, ValueType>;\n\n/**\n * A helper type which can extract a Context value type from a Context type\n */\nexport type ContextType<Key extends Context<unknown, unknown>> =\n Key extends Context<unknown, infer ValueType> ? ValueType : never;\n\n/**\n * Creates a typed Context.\n *\n * Contexts are compared with strict equality.\n *\n * If you want two separate `createContext()` calls to referer to the same\n * context, then use a key that will by equal under strict equality like a\n * string for `Symbol.for()`:\n *\n * ```ts\n * // true\n * createContext('my-context') === createContext('my-context')\n * // true\n * createContext(Symbol.for('my-context')) === createContext(Symbol.for('my-context'))\n * ```\n *\n * If you want a context to be unique so that it's guaranteed to not collide\n * with other contexts, use a key that's unique under strict equality, like\n * a `Symbol()` or object.:\n *\n * ```\n * // false\n * createContext({}) === createContext({})\n * // false\n * createContext(Symbol('my-context')) === createContext(Symbol('my-context'))\n * ```\n *\n * @param key a context key value\n * @template ValueType the type of value that can be provided by this context.\n * @returns the context key value cast to `Context<K, ValueType>`\n */\nexport function createContext<ValueType, K = unknown>(key: K) {\n return key as Context<K, ValueType>;\n}\n", "/**\n * Canvas runtime context \u2014 the shared @lit/context symbol both\n * runtime-sdk (the provider) and canvas-sdk / canvas authors (the\n * consumers) use to thread a narrow runtime handle through the canvas\n * element tree.\n *\n * Living here keeps the symbol identity stable across both packages.\n * If canvas-sdk created its own symbol with `createContext(...)`, it\n * would never match the one runtime-sdk publishes, and `<sc-mount>`\n * would silently see `undefined` instead of the widget registry.\n *\n * The shape declared here is a NARROW VIEW of `SmartCanvasRuntime`.\n * Canvas-side code reads only this subset. The runtime-sdk's\n * `SmartCanvasRuntime` type is a structural superset.\n */\nimport { createContext } from '@lit/context';\n/**\n * The @lit/context symbol. Both runtime-sdk's ContextProvider and\n * canvas-sdk's ContextConsumer must import THIS exact symbol \u2014 not a\n * symbol with the same string name \u2014 for context propagation to work.\n */\nexport const canvasRuntimeContext = createContext('syntrologie:canvas-runtime');\n", "/**\n * Telemetry catalog \u2014 the single source of truth for every `syntro_*` event\n * the runtime SDK emits and the backend queries (FEAT-1789150184).\n *\n * Names are `syntro_<subject>_<verb>`. Verbs are CLOSED: a new surface adds\n * subjects, never verbs. `scope` says which context must be active when the\n * event fires: `plan` (a config has been served) or `open` (the concierge is\n * open, so `open_id` and `surface` exist). `props` are the event's own\n * required properties; the envelope (variant_id, experiment_key, and while\n * open surface + open_id) is stamped by the core and not listed. The core\n * whitelists props to this list \u2014 anything else is dropped.\n *\n * `telemetry-events.json` is emitted from this file at build time and turned\n * into a Python enum for the backend (`make generate-types`).\n */\nexport const TELEMETRY_VERBS = [\n 'opened',\n 'closed',\n 'seen',\n 'triggered',\n 'interacted',\n 'paged',\n 'tapped',\n 'dismissed',\n 'sent',\n 'received',\n 'interrupted',\n 'toggled',\n 'served',\n];\n/**\n * What opened the concierge. `auto` = scroll handoff or a persisted-open\n * restore; `deep_link` = programmatic `open()`; `host` = the surface itself\n * gave the canvas the screen (an agent surface switching its card to\n * `fullscreen`). `host` is deliberately its own value: the SDK observes the\n * display-mode change, never the gesture behind it, so folding it into `fab`\n * or `auto` would assert intent nobody measured.\n */\nexport const CONCIERGE_OPEN_TRIGGERS = ['fab', 'auto', 'alert_chip', 'deep_link', 'host'];\n/**\n * What closed it. `fab` covers every pressed close affordance and programmatic\n * `close()`.\n *\n * `navigation` is NOT only \"the visitor clicked a link\". It is the catch-all\n * for every close that is not a gesture on the concierge itself, and today it\n * covers four distinct situations:\n * 1. a velvet nav-chip click / alert activate that navigates the host page;\n * 2. the empty-tileset auto-close (nothing left to show \u2014 not a gesture);\n * 3. the host removing the canvas element from the DOM while it was open;\n * 4. page unload (tab close / navigating away), emitted on `pagehide`.\n * Do not read `reason = 'navigation'` as intent. Splitting it is a catalog\n * change (the enum is spec-binding), so until that happens any \"why did they\n * leave?\" analysis must treat this value as \"ended without a gesture\".\n *\n * `host` is the agent-surface counterpart of the `host` open trigger: the\n * surface took the screen back and put the card inline again. It stays out of\n * `navigation` precisely so it does not inherit that value's ambiguity.\n */\nexport const CONCIERGE_CLOSE_REASONS = ['fab', 'escape', 'navigation', 'host'];\n/**\n * The in-app render container an open happened in, stamped on every\n * open-scope event. `overlay` is the bundled surface, `velvet` a custom\n * canvas mounted on the merchant's page, `inline` the canvas painted inside a\n * CHAT APP's card (surface type `mcp-app`).\n *\n * Closed, and pinned by the contract test, because every open-scope row the\n * backend has ever stored carries one of these: adding, removing or reusing a\n * value rewrites what the existing rows mean. Telegram is not a value here.\n * It renders through the same inline shell but reports as `overlay` or\n * `velvet`, the way it always has.\n */\nexport const TELEMETRY_SURFACES = ['velvet', 'overlay', 'inline'];\nexport const DECK_PAGE_METHODS = ['swipe', 'tap', 'auto'];\n/**\n * What the shopper left the canvas FOR. `page` is an alert chip or any other\n * editorial link; `product` is an anchor inside a tile (a product page, a\n * comparison row, a recommendation).\n *\n * Closed, like every other vocabulary here. It deliberately does NOT carry\n * the label or the destination: both are model- or merchant-authored text,\n * and this catalog carries none.\n */\nexport const LINK_OPEN_KINDS = ['page', 'product'];\n/**\n * How a product page came to open inside a chat-app card: a product link on a\n * tile, the rail of the conversation's picks inside the view, the view\n * reopening after the host remounted the card, or a related product the page\n * itself offered (a cross-sell tile's View).\n */\nexport const PRODUCT_VIEW_OPEN_SOURCES = ['tile', 'rail', 'restore', 'related'];\nexport const CHAT_SEND_VIAS = ['typed', 'intro_suggestion', 'pill', 'dive_deeper'];\n/**\n * Whether a search query looked like a single-term lookup or something the\n * takeover surface should reach for retrieval/ranking to answer. Stamped by\n * the interceptor's own classifier, never inferred downstream from the (never\n * emitted) query text.\n */\nexport const SEARCH_CLASSIFICATIONS = ['simple', 'complex'];\n/**\n * The layouts the search takeover surface can render results in. Closed to\n * what the surface actually ships, so a variant added here without a\n * renderer is a lie the catalog would otherwise let through.\n */\nexport const SEARCH_VARIANTS = ['grid', 'stacked', 'rail', 'hero', 'compare'];\n/**\n * How the visitor's search reached the interceptor. `enter` is a keydown on\n * the host's own search input; `submit` is the host's search form\n * submitting. These are the only two entry points the interceptor installs\n * on \u2014 no value here should exist that the SDK cannot itself produce.\n */\nexport const SEARCH_ENTRY_POINTS = ['enter', 'submit'];\n/**\n * Why the takeover fell back to the store's own native search instead of\n * rendering. `not_ready` = the adaptive or the platform adapter had not landed\n * yet; `error` = a request or a render threw, or a surface painted nothing;\n * `budget` = a latency budget was exceeded. Distinct from\n * `SEARCH_DISMISS_REASONS`: this enum is about the surface never taking over,\n * that one is about a takeover the visitor left.\n *\n * There is deliberately no `disabled`. A workspace the takeover is switched\n * off for never claims the shopper's Enter at all, so there is nothing to\n * report a fallback FROM \u2014 the store's search simply runs, as it does on\n * every storefront that has never heard of us. A value nothing can emit is a\n * value someone eventually reads as a real zero.\n */\nexport const SEARCH_FALLBACK_REASONS = ['not_ready', 'error', 'budget'];\n/**\n * Why an already-rendered search takeover closed. `escape` = Escape key or\n * an equivalent close gesture on our own panel; `navigation` = the visitor\n * followed a result or otherwise left the page; `native_link` = the visitor\n * used the surface's own \"see all results\" / native-search fallback link.\n * Revision 1 of this catalog reused `SEARCH_FALLBACK_REASONS` here, which is\n * wrong: a dismissal is a takeover that DID render, never a fallback.\n */\nexport const SEARCH_DISMISS_REASONS = ['escape', 'navigation', 'native_link'];\n/**\n * What kind of change a search follow-up represents relative to the search\n * already on screen. `refine` = the visitor typed a new/adjusted query;\n * `chip_removed` = a filter chip was cleared; `item_dismissed` = a single\n * result was dismissed from the surface without leaving it.\n */\nexport const SEARCH_DELTA_KINDS = ['refine', 'chip_removed', 'item_dismissed'];\nconst entry = (e) => e;\nexport const TELEMETRY_EVENTS = {\n // --- intervention metrics (April 2026) \u2014 wire names unchanged, all plan-scope\n syntro_config_served: entry({\n subject: 'config',\n verb: 'served',\n scope: 'plan',\n props: ['tiles', 'actions'],\n }),\n syntro_intervention_seen: entry({\n subject: 'intervention',\n verb: 'seen',\n scope: 'plan',\n props: ['intervention_id', 'intervention_kind'],\n }),\n syntro_intervention_triggered: entry({\n subject: 'intervention',\n verb: 'triggered',\n scope: 'plan',\n props: ['intervention_id', 'intervention_kind'],\n }),\n syntro_intervention_interacted: entry({\n subject: 'intervention',\n verb: 'interacted',\n scope: 'plan',\n props: ['intervention_id', 'intervention_kind', 'interaction_type'],\n }),\n // --- concierge (FEAT-1789150184) ---------------------------------------\n syntro_concierge_opened: entry({\n subject: 'concierge',\n verb: 'opened',\n scope: 'open',\n props: ['trigger', 'page_path'], // trigger: ConciergeOpenTrigger\n }),\n syntro_concierge_closed: entry({\n subject: 'concierge',\n verb: 'closed',\n scope: 'open',\n props: ['duration_ms', 'reason', 'messages_sent', 'tiles_dismissed', 'pages_viewed'], // reason: ConciergeCloseReason\n }),\n /**\n * The shopper asked to leave the canvas by a link. Emitted on intent, before\n * the host answers, so refused or denied opens are counted too (parity with\n * syntro_concierge_closed reason navigation).\n *\n * On a merchant's own page a link tap closes the canvas, and\n * `syntro_concierge_closed { reason: 'navigation' }` is the row that\n * records it. Inside a chat app's card the frame is sandboxed: the canvas\n * cannot navigate, does not close, and asks the host to open the link\n * instead, so that close row never fires. This is its counterpart, and it\n * carries the same `reason` so one query covers both surfaces.\n */\n syntro_link_opened: entry({\n subject: 'link',\n verb: 'opened',\n scope: 'open',\n props: ['kind', 'reason'], // kind: LinkOpenKind, reason: ConciergeCloseReason\n }),\n /**\n * A store's product page opened inside a chat-app card instead of leaving\n * through the host (the card's product view). `product` is the store's\n * handle, never the display name; a rail switch is a new open. Its\n * counterpart when the shopper goes back to the conversation's picks is\n * syntro_product_view_closed.\n *\n * Plan scope, like the card's own syntro_card_lifecycle_seen: an inline\n * card holds an open scope only while the host has it fullscreen, and a\n * view opened from a tile fires before the host answers that request (or\n * after it refused), so an open-scope row here would be dropped. The core\n * still stamps surface and open_id whenever an open is live.\n */\n syntro_product_view_opened: entry({\n subject: 'product_view',\n verb: 'opened',\n scope: 'plan',\n props: ['product', 'from'], // from: ProductViewOpenSource\n }),\n syntro_product_view_closed: entry({\n subject: 'product_view',\n verb: 'closed',\n scope: 'plan',\n props: ['product'],\n }),\n syntro_deck_paged: entry({\n subject: 'deck',\n verb: 'paged',\n scope: 'open',\n props: ['from_index', 'to_index', 'method'], // method: DeckPageMethod\n }),\n syntro_tile_dismissed: entry({\n subject: 'tile',\n verb: 'dismissed',\n scope: 'open',\n props: ['tile_id', 'tile_kind', 'visible_ms'],\n }),\n // A tile's own data load finished: the network wait between the concierge\n // mounting a tile and the shopper seeing its content (BUG-1784767817 was a\n // 30 s reviews hang here). `outcome` is 'ok' | 'error'; `requested` is how\n // many items the tile asked for, `received` how many came back.\n syntro_tile_data_received: entry({\n subject: 'tile_data',\n verb: 'received',\n scope: 'open',\n props: ['tile_id', 'tile_kind', 'total_ms', 'outcome', 'requested', 'received'],\n }),\n // The three chat atoms are OPEN-scope: they carry `surface` / `open_id` and\n // only make sense inside a concierge open. An INLINE chat bar (no concierge)\n // still dispatches them on every turn, and the core drops each one and\n // increments the `telemetry_dropped` health counter. That is EXPECTED on an\n // inline-chat host page, not a bug \u2014 `telemetry_dropped` there mixes \"real\n // defect\" with \"inline chat has no open scope\", so do not alert on it alone.\n syntro_chat_message_sent: entry({\n subject: 'chat_message',\n verb: 'sent',\n scope: 'open',\n props: ['turn', 'chars', 'via'], // via: ChatSendVia\n }),\n syntro_chat_reply_received: entry({\n subject: 'chat_reply',\n verb: 'received',\n scope: 'open',\n // mount_tools: the mount_* tool name of every mount this reply fired, in order (two product cards appear twice).\n props: ['turn', 'ttft_ms', 'total_ms', 'mounts', 'cards', 'mount_tools'],\n }),\n syntro_chat_interrupted: entry({\n subject: 'chat',\n verb: 'interrupted',\n scope: 'open',\n props: ['turn', 'after_ms'],\n }),\n syntro_chat_maximize_toggled: entry({\n subject: 'chat_maximize',\n verb: 'toggled',\n scope: 'open',\n props: ['maximized'],\n }),\n syntro_chat_takeover_triggered: entry({\n subject: 'chat_takeover',\n verb: 'triggered',\n scope: 'open',\n props: ['turn'],\n }),\n // --- search takeover (2026-09-17) ---------------------------------------\n // All plan-scope: the search interceptor fires before any concierge open\n // exists. No prop here ever carries query text (SEE the contract test's\n // no-text-prop rule) \u2014 classification/signal_hits are the interceptor's\n // own derived signals, never the string itself.\n syntro_search_sent: entry({\n subject: 'search',\n verb: 'sent',\n scope: 'plan',\n props: ['classification', 'word_count', 'signal_hits', 'entry_point', 'platform'], // classification: SearchClassification, entry_point: SearchEntryPoint\n }),\n // `ms_to_skeleton` is intentionally the only timing prop: it is always\n // knowable the instant the surface mounts. `ms_to_first_content` is NOT\n // declared here (revision 1 defect) because a surface that never painted\n // content still fires this event, and a required prop it cannot supply\n // would make TelemetryCore.emit drop exactly the row worth measuring.\n syntro_search_surface_seen: entry({\n subject: 'search_surface',\n verb: 'seen',\n scope: 'plan',\n props: ['variant', 'classification', 'result_count', 'ms_to_skeleton'], // variant: SearchVariant, classification: SearchClassification\n }),\n syntro_search_result_tapped: entry({\n subject: 'search_result',\n verb: 'tapped',\n scope: 'plan',\n props: ['variant', 'region_id', 'module', 'position'], // variant: SearchVariant\n }),\n syntro_search_followup_sent: entry({\n subject: 'search_followup',\n verb: 'sent',\n scope: 'plan',\n props: ['delta_kind', 'variant'], // delta_kind: SearchDeltaKind, variant: SearchVariant\n }),\n // Declares `ms_since_boot` (not a \"since surface mounted\" timing prop) so a\n // not-ready fallback the takeover never rendered is still measurable \u2014\n // revision 1 emitted nothing on this path.\n syntro_search_native_served: entry({\n subject: 'search_native',\n verb: 'served',\n scope: 'plan',\n props: ['reason', 'platform', 'ms_since_boot'], // reason: SearchFallbackReason\n }),\n syntro_search_takeover_dismissed: entry({\n subject: 'search_takeover',\n verb: 'dismissed',\n scope: 'plan',\n props: ['variant', 'reason'], // variant: SearchVariant, reason: SearchDismissReason\n }),\n // --- chat-app card lifecycle (2026-09-24) --------------------------------\n // One row per stage a card reached, from `reportCardStage` in\n // card-lifecycle.ts. Plan scope: most stages happen before a config is\n // served and none happen inside a concierge open. `stage` is a\n // CardLifecycleStage; `since_shell_ms` is the card shell's own clock, so a\n // card's rows order themselves from the first byte of the page. The same\n // stages reach the connector through the shell's beacon, which is the only\n // record of a card whose SDK never booted.\n syntro_card_lifecycle_seen: entry({\n subject: 'card_lifecycle',\n verb: 'seen',\n scope: 'plan',\n props: ['stage', 'since_shell_ms'], // stage: CardLifecycleStage\n }),\n};\nexport const TELEMETRY_EVENT_NAMES = Object.keys(TELEMETRY_EVENTS);\n/** DOM bridge: components dispatch this (bubbles + composed) and the core listens at document. */\nexport const TELEMETRY_DOM_EVENT = 'syntro:telemetry';\n/**\n * Build the exact object written to `src/telemetry-events.json` at build time\n * (`scripts/emit-telemetry-catalog.mjs`), which `make app-generate-telemetry-events`\n * turns into the Python enum. Lives here \u2014 not in the script \u2014 so the shape is\n * declared once and `telemetry-events.contract.test.ts` can prove the committed\n * JSON has not drifted from this file.\n */\nexport function buildTelemetryCatalogJson() {\n return {\n verbs: [...TELEMETRY_VERBS],\n events: Object.fromEntries(Object.entries(TELEMETRY_EVENTS).map(([name, e]) => [\n name,\n {\n subject: e.subject,\n verb: e.verb,\n scope: e.scope,\n props: [...e.props],\n },\n ])),\n };\n}\nexport function isTelemetryEventName(name) {\n // ES2020 target: hasOwnProperty via the prototype, not the ES2022 static helper.\n // biome-ignore lint/suspicious/noPrototypeBuiltins: Object.hasOwn is ES2022; repo target is ES2020.\n return Object.prototype.hasOwnProperty.call(TELEMETRY_EVENTS, name);\n}\n", "/**\n * Every stage of a chat-app card's life, as one closed vocabulary.\n *\n * A card that never starts leaves nothing behind: the SDK's telemetry only\n * exists once the SDK has booted and PostHog is up, which is exactly what did\n * not happen. So the card shell (the connector's `render-shell.ts`) installs a\n * tiny beacon as the FIRST script on the page, reports its own stages through\n * it, and exposes it as `window.__syntroCardBeacon` for the SDK's seams to\n * report theirs. The same stages then go to PostHog as one catalog event once\n * the telemetry core is listening.\n *\n * Shared by the connector (which refuses any stage not listed here), the host\n * bridge, the host-agent controller and the canvas. Adding a stage is a\n * contract change on both sides of the wire.\n */\nimport { TELEMETRY_DOM_EVENT } from './telemetry-events.js';\nexport const CARD_LIFECYCLE_STAGES = [\n // The shell, before any SDK code exists.\n 'shell_parsed',\n 'sdk_script_loaded',\n 'sdk_script_failed',\n 'csp_violation',\n 'js_error',\n 'visibility',\n 'pagehide',\n // The SDK's own seams.\n 'sdk_init',\n 'bridge_connecting',\n 'bridge_connected',\n 'bridge_failed',\n 'tool_result_received',\n 'tool_result_rejected',\n 'tool_result_missing',\n 'session_bound',\n 'frame_applied',\n 'tiles_rendered',\n 'tiles_empty',\n 'display_mode_changed',\n 'read_brand_view_called',\n 'read_brand_view_answered',\n // The published config asked for something this card cannot do; the card\n // does the safe thing instead (e.g. links out) and says which field.\n 'config_refused',\n // The card could not fetch its live config (GrowthBook failed or timed\n // out); `reason` says which. The card goes on as a storefront would.\n 'config_failed',\n];\n/**\n * The longest turn id and session ref a card event may carry. The connector\n * validates against these, and `reportCardStage` cuts any longer id to them\n * before it leaves: the shell keeps a turn id for every later event, so one\n * the connector refused would cost the rest of that card's events.\n */\nexport const CARD_TURN_ID_MAX = 100;\nexport const CARD_SESSION_REF_MAX = 300;\n/** The window property the card shell installs; absent everywhere else. */\nexport const CARD_BEACON_GLOBAL = '__syntroCardBeacon';\n/**\n * Report one stage. A no-op, returning false, wherever no card shell\n * installed the beacon, which is every storefront. Never throws into the\n * seam that called it: a card that cannot report must still render.\n */\nexport function reportCardStage(stage, detail) {\n if (typeof window === 'undefined')\n return false;\n const beacon = window[CARD_BEACON_GLOBAL];\n if (typeof beacon !== 'function')\n return false;\n let since;\n try {\n since = beacon(stage, boundIds(detail));\n }\n catch {\n // The beacon is best effort; the catalog event below still goes.\n }\n const sinceShellMs = Math.round(typeof since === 'number' && Number.isFinite(since) ? since : performance.now());\n // A listener that throws is reported by the event loop, never rethrown here.\n document.dispatchEvent(new CustomEvent(TELEMETRY_DOM_EVENT, {\n detail: {\n name: 'syntro_card_lifecycle_seen',\n props: { stage, since_shell_ms: sinceShellMs },\n },\n }));\n return true;\n}\nfunction boundIds(detail) {\n if (!detail)\n return detail;\n const { turnId, sessionRef } = detail;\n const longTurn = typeof turnId === 'string' && turnId.length > CARD_TURN_ID_MAX;\n const longRef = typeof sessionRef === 'string' && sessionRef.length > CARD_SESSION_REF_MAX;\n if (!longTurn && !longRef)\n return detail;\n return {\n ...detail,\n ...(longTurn ? { turnId: turnId.slice(0, CARD_TURN_ID_MAX) } : {}),\n ...(longRef ? { sessionRef: sessionRef.slice(0, CARD_SESSION_REF_MAX) } : {}),\n };\n}\n/**\n * Where a connector names the visitor a card is drawn for, on every tool\n * result it returns: `_meta['syntro/visitor'] = { id }`.\n *\n * The id rides the result and never the card's page, because a chat app keeps\n * one copy of that page per connector and serves it to every conversation. The\n * id is the one the connector resolved the agent's config with, so the card\n * that adopts it buckets into the same variation as the turn it renders.\n */\nexport const CARD_VISITOR_META_KEY = 'syntro/visitor';\n/**\n * The GrowthBook attributes that say \"a chat-app card\", as both evaluators of\n * a card's config set them: the runtime SDK in the card, once the host\n * handshake answers, and the connector, resolving the agent's turn. One\n * source, so the two cannot drift into different variations for one visitor.\n */\nexport const MCP_APP_SURFACE_ATTRIBUTES = Object.freeze({\n surface_type: 'mcp-app',\n surface: 'personal_agent',\n});\n/** The runtime SDK's own identity bounds (`IDENTITY_MIN`/`IDENTITY_MAX` in bootstrap-init.ts). */\nconst VISITOR_ID_MIN = 16;\nconst VISITOR_ID_MAX = 200;\n/**\n * The visitor one tool result names: its id, `absent` when the result names\n * nobody (another tool, or an older connector), or the reason it was refused.\n */\nexport function cardVisitorIdFrom(result) {\n const meta = result?._meta;\n if (!meta || typeof meta !== 'object')\n return { absent: true };\n const visitor = meta[CARD_VISITOR_META_KEY];\n if (visitor === undefined)\n return { absent: true };\n if (!visitor || typeof visitor !== 'object' || Array.isArray(visitor)) {\n return { refused: 'not_an_object' };\n }\n const id = visitor.id;\n if (typeof id !== 'string')\n return { refused: 'not_a_string' };\n const trimmed = id.trim();\n if (trimmed.length < VISITOR_ID_MIN)\n return { refused: 'too_short' };\n if (trimmed.length > VISITOR_ID_MAX)\n return { refused: 'too_long' };\n if (trimmed.startsWith('$'))\n return { refused: 'sentinel' };\n return { id: trimmed };\n}\n", "/**\n * The prop shapes of every CANONICAL runtime-bus event \u2014 the producer/consumer\n * contract for the visitor-behavior signals that reach the chat agent.\n *\n * ## Why this module exists\n *\n * Two independent packages have to agree on these prop names and nothing was\n * holding them to it:\n *\n * producer `packages/event-processor/src/detectors/*` (rrweb detectors)\n * `packages/runtime-sdk/src/instrumentation/*` (bus instrumentation)\n * consumer `packages/adaptives/adaptive-chatbot/src/observer/allowlist.ts`\n *\n * Both sides passed `Record<string, unknown>` around, so a disagreement was not\n * a build error \u2014 it was an observation rendered into the model's prompt with\n * its content missing. `hovered on ''`. `idle`. Shipped for months\n * (BUG-1786923485), and once before that as a name mismatch (BUG-1784146789).\n *\n * ## The rule this module enforces\n *\n * `CanonicalBusEventProps` is the ONE declaration. Producers emit through\n * `detectorEvent()` (event-processor), which types `props` as\n * `CanonicalBusEventProps[N]`; consumers render through an exhaustive\n * `Record<CanonicalBusEventName, \u2026>` whose functions receive\n * `Partial<CanonicalBusEventProps[N]>`. Therefore:\n *\n * - renaming a key in a detector \u2192 producer fails `tsc`\n * - renaming a key in the summarizer \u2192 consumer fails `tsc`\n * - adding an event with no summarizer case \u2192 consumer fails `tsc` (missing\n * key in the exhaustive record)\n * - renaming a key HERE \u2192 both sides fail `tsc`\n *\n * None of those can degrade to an empty string at runtime.\n *\n * ## Deliberately dependency-free\n *\n * No zod, no `@lit/context`, no DOM types \u2014 `@syntrologie/event-processor` has\n * zero runtime dependencies and imports this via the `./detector-events`\n * subpath so it stays that way.\n *\n * ## Deliberately WITHOUT string index signatures\n *\n * An index signature on a props type makes `props.anyTypoAtAll` legal and\n * silently reintroduces the exact bug this module exists to prevent. The one\n * index signature below is a template-literal pattern (`attr__${string}`), so\n * arbitrary DOM attributes are still expressible while `text` / `tag_name`\n * typos remain errors.\n */\nexport const DETECTOR_EVENT_NAMES = [\n 'ui.hover',\n 'ui.idle',\n 'ui.hesitate',\n 'ui.rage_click',\n 'ui.scroll_thrash',\n 'ui.focus_bounce',\n];\nexport const CANONICAL_BUS_EVENT_NAMES = [\n ...DETECTOR_EVENT_NAMES,\n 'nav.section_viewed',\n 'nav.scroll_depth',\n];\nconst _canonicalExhaustive = true;\nconst _detectorExhaustive = true;\nvoid _canonicalExhaustive;\nvoid _detectorExhaustive;\n/** Runtime membership test \u2014 narrows an arbitrary event name to the contract. */\nexport function isCanonicalBusEvent(name) {\n return CANONICAL_BUS_EVENT_NAMES.includes(name);\n}\n", "/**\n * The branded stand-in for an item with no photo: one look on every surface\n * that shows one (product card, comparison, reco carousel, chat-trail rows).\n *\n * Alpha sheen over the single brand primary token, so it reads on any brand\n * colour (black included); the initial sits in the launcher's paired\n * foreground token (`--sc-launcher-color`). Decorative: every consumer shows\n * the item's name as text next to it. History: adaptive-product\n * imageStandin.ts (FEAT-1787964101).\n */\nexport const IMAGE_STANDIN_BACKGROUND = 'linear-gradient(135deg, rgba(255, 255, 255, 0.16), rgba(255, 255, 255, 0.02) 45%, rgba(0, 0, 0, 0.22)), var(--sc-color-primary, #3a3a3a)';\n/** The name's first character, upper-cased; null when the name has none. */\nexport function standinInitial(name) {\n // Spread iterates CODE POINTS, not UTF-16 units: an emoji-led name keeps\n // its whole first character instead of an unpaired surrogate.\n const first = [...name.trim()][0];\n return first ? first.toUpperCase() : null;\n}\n", "/**\n * Mount contract types and helper for adaptive widget mountables.\n *\n * The `WidgetRegistry` in `@syntrologie/runtime-sdk` delivers props to each\n * mountable as `{ ...tile.props, instanceId, runtime, tileId? }` spread flat\n * (see `MountableContract.test.ts` in runtime-sdk for the end-to-end lockdown).\n *\n * Adaptives that strip plumbing manually maintain private blacklists that\n * silently drift when the contract grows (PR #2234 and #2238 documented this).\n * `stripMountPlumbing` centralizes the list so adding a new plumbing key in\n * the future is a one-line change here that every adaptive picks up automatically.\n *\n * Adaptives whose widget schemas use Zod `.strict()` MUST call this before\n * validating, or strict-mode will reject the runtime-injected keys and the\n * widget will silently render its empty/error state.\n */\nexport const MOUNT_PLUMBING_KEYS = ['instanceId', 'runtime', 'tileId'];\nexport function stripMountPlumbing(config) {\n if (!config || typeof config !== 'object') {\n return {};\n }\n const out = { ...config };\n for (const key of MOUNT_PLUMBING_KEYS) {\n delete out[key];\n }\n return out;\n}\n", "/**\n * Canonical route normalization. See `routes.md` for rules and\n * `normalize-route.cases.json` for the parity corpus shared with the\n * Python implementation in syntrologie_common/sdk/routing.py.\n *\n * Two exports \u2014 `normalizeRoute` for literal paths, `normalizeRoutePattern`\n * for activation patterns containing `*`, `**`, `:param`. Today they share\n * an implementation because the rules happen to be wildcard-safe (no\n * lowercase, unreserved-only decode, slash collapse preserves `**`).\n * The seam is preserved as separate exports so the API can diverge\n * without consumer churn if rules change.\n */\n// RFC 3986 reserved characters (gen-delims + sub-delims). When a `%XX`\n// sequence decodes to one of these bytes, we keep the percent-encoded\n// form \u2014 decoding would re-segment the path or change its meaning.\nconst RESERVED_BYTES = new Set([\n 0x21, // !\n 0x23, // #\n 0x24, // $\n 0x26, // &\n 0x27, // '\n 0x28, // (\n 0x29, // )\n 0x2a, // *\n 0x2b, // +\n 0x2c, // ,\n 0x2f, // /\n 0x3a, // :\n 0x3b, // ;\n 0x3d, // =\n 0x3f, // ?\n 0x40, // @\n 0x5b, // [\n 0x5d, // ]\n]);\nconst utf8Decoder = new TextDecoder('utf-8', { fatal: false });\n/** Decode `%XX` sequences for unreserved bytes only. Collapses\n * adjacent `%XX` runs into a UTF-8 decode so `%C3%A9` \u2192 `\u00E9`. */\nfunction decodeUnreservedOnly(input) {\n let out = '';\n let pending = [];\n const flushPending = () => {\n if (pending.length === 0)\n return;\n const bytes = new Uint8Array(pending);\n out += utf8Decoder.decode(bytes);\n pending = [];\n };\n let i = 0;\n while (i < input.length) {\n const ch = input[i];\n if (ch === '%' && i + 2 < input.length && isHex(input[i + 1]) && isHex(input[i + 2])) {\n const byte = parseInt(input.slice(i + 1, i + 3), 16);\n if (RESERVED_BYTES.has(byte)) {\n flushPending();\n // Keep raw, but normalize hex case to uppercase so the\n // canonical form is stable across input casing.\n out += `%${input.slice(i + 1, i + 3).toUpperCase()}`;\n i += 3;\n }\n else {\n pending.push(byte);\n i += 3;\n }\n }\n else {\n flushPending();\n out += ch;\n i += 1;\n }\n }\n flushPending();\n return out;\n}\nfunction isHex(c) {\n return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');\n}\n/** Strip query string and hash fragment. */\nfunction stripQueryAndHash(s) {\n const q = s.indexOf('?');\n if (q !== -1)\n s = s.slice(0, q);\n const h = s.indexOf('#');\n if (h !== -1)\n s = s.slice(0, h);\n return s;\n}\n/**\n * Normalize a literal path (e.g. `window.location.pathname`, an\n * action's `route` field, a wiki route key).\n *\n * Throws `TypeError` if the input is not an absolute path. Callers\n * that want a soft API should use {@link normalizeRouteWithChange}.\n */\nexport function normalizeRoute(path) {\n if (typeof path !== 'string' || path.length === 0) {\n throw new TypeError('normalizeRoute: input must be a non-empty string');\n }\n if (!path.startsWith('/')) {\n throw new TypeError(`normalizeRoute: input must be absolute (start with '/'); got ${JSON.stringify(path)}`);\n }\n let s = stripQueryAndHash(path);\n s = decodeUnreservedOnly(s);\n s = s.replace(/\\/+/g, '/');\n if (s.length > 1 && s.endsWith('/'))\n s = s.slice(0, -1);\n return s;\n}\n/**\n * Normalize an activation route pattern. Preserves `*`, `**`,\n * `:param` exactly. Today equivalent to {@link normalizeRoute} \u2014 kept\n * as a separate export so rules can diverge later without API churn.\n */\nexport function normalizeRoutePattern(pattern) {\n return normalizeRoute(pattern);\n}\n/**\n * Normalize a route and report whether the input was already\n * canonical. Used by authoring tools to decide whether to emit a\n * warning to the LLM.\n */\nexport function normalizeRouteWithChange(path) {\n const canonical = normalizeRoute(path);\n return { canonical, changed: canonical !== path };\n}\n/** Pattern-side counterpart of {@link normalizeRouteWithChange}. */\nexport function normalizeRoutePatternWithChange(pattern) {\n const canonical = normalizeRoutePattern(pattern);\n return { canonical, changed: canonical !== pattern };\n}\n/**\n * Case-insensitive comparison of two already-canonical paths. Use\n * this anywhere two routes are compared for equality (wiki lookups,\n * non-pattern action route gates) \u2014 preserves casing in the inputs\n * while honoring case-insensitive routing on the customer's site.\n */\nexport function routesMatch(a, b) {\n return a.toLowerCase() === b.toLowerCase();\n}\n", "/**\n * Shared Zod schemas for decision strategies, conditions, and event scoping.\n *\n * These are the canonical definitions \u2014 runtime-sdk and all adaptive packages\n * should import from here instead of duplicating.\n */\nimport { z } from 'zod';\nimport { normalizeRoute } from './routes.js';\n// =============================================================================\n// ANCHOR ID SCHEMA\n// =============================================================================\n// A selector containing \"{\" or \"}\" can never match a real DOM element via\n// querySelectorAll \u2014 those characters are exclusively CSS rule delimiters,\n// not valid selector syntax. They ARE, however, exactly what\n// content:hideBySelector's executor needs to escape the single CSS rule it\n// injects as raw text into a live <style> element\n// (`${selector} { ${prop}: ${value} !important; }` \u2014 see\n// executeHideBySelector in adaptive-content/src/runtime.ts): a selector\n// containing `}` closes that rule early and lets the rest of the string\n// splice in arbitrary attacker-controlled CSS anywhere on the host page\n// (defacement, CSS-based data exfiltration via attribute selectors,\n// clickjacking overlays). This constraint lives on THIS canonical AnchorIdZ\n// (not a hideBySelector-only variant, and not a second copy in a\n// downstream package) so that every action kind in every package \u2014 core\n// and adaptive \u2014 inherits it automatically: there is exactly one\n// `AnchorIdZ`, and every consumer imports it from here (SEC-067,\n// BUG-1786764688). No legitimate selector for any action kind needs a\n// literal brace.\nexport const NO_CSS_BREAKOUT_PATTERN = /^[^{}]*$/;\nexport const AnchorIdZ = z\n .object({\n selector: z\n .string()\n .regex(NO_CSS_BREAKOUT_PATTERN, {\n message: 'selector must not contain \"{\" or \"}\" \u2014 not valid CSS selector syntax, and content:hideBySelector injects this value directly into a <style> element where these characters would break out of the generated rule.',\n })\n .describe('CSS selector for the target element'),\n route: z\n .union([z.string(), z.array(z.string())])\n .superRefine((value, ctx) => {\n // Backend parity check (Finding 1, .superpowers/sdd/\n // duplicated-definitions-audit.md): the backend's\n // RouteCanonicalityCheck (platform/backend/app/services/\n // sdk_config_checks.py) rejects any actions[].anchorId.route that\n // isn't already in `normalize_route`'s canonical form (absolute,\n // no trailing slash, no doubled slashes, no query/hash, unreserved\n // percent-decoding only). Before this check, authoring accepted\n // configs the backend would 422 on (e.g. a trailing slash, or a\n // bare \"**\" missing the leading \"/\"). Reuses `normalizeRoute` from\n // ./routes.ts \u2014 the SAME function already parity-tested against\n // the Python implementation via normalize-route.cases.json \u2014 so\n // this is single-sourced, not a third reimplementation of the\n // canonicalization rule.\n for (const route of Array.isArray(value) ? value : [value]) {\n let canonical;\n try {\n canonical = normalizeRoute(route);\n }\n catch (err) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `route must be an absolute path starting with \"/\" (got ${JSON.stringify(route)}): ${err instanceof Error ? err.message : String(err)}`,\n });\n continue;\n }\n if (canonical !== route) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `route ${JSON.stringify(route)} is not canonical \u2014 use ${JSON.stringify(canonical)} (this must match what the backend's RouteCanonicalityCheck accepts)`,\n });\n }\n }\n })\n .describe('URL path(s) where this element exists'),\n})\n .strict()\n .describe('DOM element target. selector = CSS selector, route = URL path(s) where the element exists.');\n// =============================================================================\n// AUTHORING FIELDS \u2014 id / title / description / validation\n//\n// Shared fields every action carries. `id` is the action identifier the\n// runtime uses to dispatch, dedupe, and drop/replace actions \u2014 it is NOT\n// stripped before serving. `title` / `description` / `validation` are\n// authoring-only metadata stripped server-side in `to_runtime_config`\n// (platform/backend/app/domains/experiments/helpers.py).\n//\n// They all appear in the JSON Schema (and therefore in the tactician's\n// prompt) because the LLM needs to know they are valid action properties \u2014\n// otherwise schema validation would reject what the prompt commands.\n//\n// Each action variant should `.extend(AuthoringFieldsZ)` alongside any\n// triggerWhen/condition extensions.\n// =============================================================================\nexport const AuthoringFieldsZ = {\n id: z.string().optional().describe('Stable action identifier (e.g. \"act_3db6a14d2ab0\").'),\n title: z\n .string()\n .max(200)\n .optional()\n .describe('Authoring-only: short label shown on the action plan dashboard. Stripped before serving to the runtime SDK.'),\n description: z\n .string()\n .max(1000)\n .optional()\n .describe('Authoring-only: one-sentence explanation of what this action does and why. Stripped before serving to the runtime SDK.'),\n validation: z\n .array(z.string().max(500))\n .max(10)\n .optional()\n .describe('Authoring-only: ordered steps a reviewer can follow to trigger this action and visually confirm it works. Each entry is one step. Stripped before serving to the runtime SDK.'),\n};\n// =============================================================================\n// TRIGGER VOCABULARY \u2014 canonical lists of valid event names, metric keys, etc.\n// These flow through to the JSON schema as enums and are used by the LLM prompt.\n// =============================================================================\n/** Events that can be counted in event_count conditions.\n *\n * Every value here must be an event the runtime actually emits \u2014 either a\n * PostHog-autocapture normalization (ui.click/scroll/input/change/submit) or\n * an event-processor detector (ui.hover/idle/scroll_thrash/focus_bounce/\n * hesitate/rage_click). Do not add aspirational names; a trigger counting an\n * event nothing emits never fires.\n */\nexport const COUNTABLE_EVENTS = [\n // User interactions (from PostHog autocapture normalization)\n 'ui.click',\n 'ui.scroll',\n 'ui.input',\n 'ui.change',\n 'ui.submit',\n // Behavioral detectors (from event-processor)\n 'ui.hover',\n 'ui.idle',\n 'ui.scroll_thrash',\n 'ui.focus_bounce',\n 'ui.hesitate',\n 'ui.rage_click',\n // Navigation\n 'nav.page_view',\n 'nav.page_leave',\n];\nexport const CountableEventZ = z\n .enum(COUNTABLE_EVENTS)\n .describe('Event name to count. ui.* = user interactions and behavioral detectors (hesitate, rage_click, scroll_thrash, focus_bounce, idle, hover); nav.* = page navigation.');\n/** Valid session metric keys. */\nexport const SESSION_METRIC_KEYS = ['time_on_page', 'page_views', 'scroll_depth'];\nexport const SessionMetricKeyZ = z\n .enum(SESSION_METRIC_KEYS)\n .describe('Session metric key. time_on_page = seconds on current page, page_views = pages visited this session, scroll_depth = 0-100 percentage.');\n/** Element chain match field prefixes for counter filters. */\nexport const ELEMENT_MATCH_FIELDS = ['tag_name', '$el_text'];\n// Note: attr__* is a dynamic prefix (attr__data-id, attr__class, attr__href, etc.)\n// and cannot be enumerated. The match key is either one of ELEMENT_MATCH_FIELDS\n// or starts with \"attr__\".\n// =============================================================================\n// CONDITION SCHEMAS\n// =============================================================================\nexport const PageUrlConditionZ = z\n .object({\n type: z.literal('page_url'),\n url: z.string().describe('URL path to match (e.g. \"/pricing\", \"/dashboard\")'),\n})\n .describe('Fires when the current page URL matches. Use for page-specific actions. ' +\n 'Example: {\"type\": \"page_url\", \"url\": \"/pricing\"}');\nexport const RouteConditionZ = z\n .object({\n type: z.literal('route'),\n routeId: z.string().describe('Named route ID from the route filter'),\n})\n .describe('Fires when the current route matches a named route ID.');\nexport const AnchorVisibleConditionZ = z\n .object({\n type: z.literal('anchor_visible'),\n anchorId: z.string().describe('CSS selector of the anchor element'),\n state: z\n .enum(['visible', 'present', 'absent'])\n .describe('\"visible\" = in viewport, \"present\" = in DOM, \"absent\" = not in DOM'),\n})\n .describe(\"Fires based on a DOM element's visibility state. \" +\n 'Example: {\"type\": \"anchor_visible\", \"anchorId\": \"#cta-button\", \"state\": \"visible\"}');\nexport const EventOccurredConditionZ = z\n .object({\n type: z.literal('event_occurred'),\n eventName: z.string().describe('Event name (e.g. \"ui.click\", \"$pageview\")'),\n withinMs: z.number().optional().describe('Time window in ms. Omit = any time this session.'),\n})\n .describe('Fires when a specific event has occurred during this session. ' +\n 'Example: {\"type\": \"event_occurred\", \"eventName\": \"ui.click\", \"withinMs\": 5000}');\nexport const StateEqualsConditionZ = z\n .object({\n type: z.literal('state_equals'),\n key: z\n .string()\n .describe('Key in the SDK persistent state store (localStorage). Only valid for keys the host app explicitly sets via syntro.state.set().'),\n value: z.unknown().describe('Expected value to match against'),\n})\n .describe('Checks the SDK persistent state store (localStorage). ONLY for host-app state set via syntro.state.set() \u2014 ' +\n 'NOT for user attributes like region, device, or UTM params (those are handled by segment targeting). ' +\n 'Do NOT use this for targeting. If you do not know the valid state keys, do not use this condition type.');\nexport const ViewportConditionZ = z\n .object({\n type: z.literal('viewport'),\n minWidth: z.number().optional().describe('Minimum viewport width in pixels'),\n maxWidth: z.number().optional().describe('Maximum viewport width in pixels'),\n minHeight: z.number().optional().describe('Minimum viewport height in pixels'),\n maxHeight: z.number().optional().describe('Maximum viewport height in pixels'),\n})\n .describe('Fires based on viewport (screen) size. Use for responsive behavior. ' +\n 'Example: {\"type\": \"viewport\", \"minWidth\": 768} \u2014 fires on tablet and larger.');\nexport const SessionMetricConditionZ = z\n .object({\n type: z.literal('session_metric'),\n key: SessionMetricKeyZ,\n operator: z.enum(['gte', 'lte', 'eq', 'gt', 'lt']),\n threshold: z.number().describe('Numeric threshold to compare against'),\n})\n .describe('Fires when a session metric crosses a threshold. Valid keys: \"time_on_page\" (seconds), ' +\n '\"page_views\" (count), \"scroll_depth\" (0-100). ' +\n 'Example: {\"type\": \"session_metric\", \"key\": \"time_on_page\", \"operator\": \"gte\", \"threshold\": 30}');\nexport const DismissedConditionZ = z\n .object({\n type: z.literal('dismissed'),\n key: z.string().describe('Dismissal key (usually a tile or action ID)'),\n inverted: z\n .boolean()\n .optional()\n .describe('When true, fires if NOT dismissed (default behavior)'),\n})\n .describe('Checks if an item has been dismissed by the user. Use with inverted: true to show only if not dismissed.');\nexport const CooldownActiveConditionZ = z\n .object({\n type: z.literal('cooldown_active'),\n key: z.string().describe('Cooldown key'),\n inverted: z.boolean().optional().describe('When true, fires if cooldown is NOT active'),\n})\n .describe('Checks if a cooldown timer is currently active. Use to prevent showing the same intervention too frequently.');\nexport const FrequencyLimitConditionZ = z\n .object({\n type: z.literal('frequency_limit'),\n key: z.string().describe('Frequency counter key'),\n limit: z.number().describe('Maximum allowed count'),\n inverted: z.boolean().optional().describe('When true, fires if limit NOT reached'),\n})\n .describe('Checks if a frequency limit has been reached. Use to cap how many times an action fires per session.');\nexport const MatchOpZ = z\n .object({\n equals: z.union([z.string(), z.number(), z.boolean()]).optional(),\n contains: z.string().optional(),\n})\n .refine((operator) => Number(operator.equals !== undefined) + Number(operator.contains !== undefined) === 1, {\n message: 'Exactly one of equals or contains must be specified.',\n})\n .describe('Match operator for counter filters. Exactly one of equals or contains must be specified.');\nexport const CounterDefZ = z\n .object({\n events: z\n .array(CountableEventZ)\n .min(1)\n .describe('Event names to count. Use values from the countable events enum.'),\n match: z\n .record(z.string(), MatchOpZ)\n .optional()\n .describe('Property filters. Keys are event prop names or element-chain fields ' +\n '(tag_name, $el_text, attr__*). All entries AND together.'),\n})\n .describe('Defines what events to count. Registered as an accumulator predicate at config-load time.');\nexport const EventCountConditionZ = z\n .object({\n type: z.literal('event_count'),\n key: z.string().describe('Unique key for this counter (used for accumulator registration)'),\n operator: z.enum(['gte', 'lte', 'eq', 'gt', 'lt']),\n count: z.number().int().min(0).describe('Target count threshold'),\n withinMs: z\n .number()\n .positive()\n .optional()\n .describe('Time window in ms. Omit = count across entire session.'),\n counter: CounterDefZ.optional().describe('Inline counter definition. Defines what events to count.'),\n})\n .describe('Fires when accumulated event count crosses a threshold. Most powerful trigger type. ' +\n 'Example: {\"type\": \"event_count\", \"key\": \"pricing-clicks\", \"operator\": \"gte\", \"count\": 3, ' +\n '\"counter\": {\"events\": [\"ui.click\"], \"match\": {\"attr__data-cta\": {\"contains\": \"pricing\"}}}}');\nexport const ConditionZ = z.discriminatedUnion('type', [\n PageUrlConditionZ,\n RouteConditionZ,\n AnchorVisibleConditionZ,\n EventOccurredConditionZ,\n StateEqualsConditionZ,\n ViewportConditionZ,\n SessionMetricConditionZ,\n DismissedConditionZ,\n CooldownActiveConditionZ,\n FrequencyLimitConditionZ,\n EventCountConditionZ,\n]);\n// =============================================================================\n// STRATEGY SCHEMAS\n// =============================================================================\nexport const RuleZ = z\n .object({\n conditions: z\n .array(ConditionZ)\n .describe('Array of conditions \u2014 ALL must match (AND logic) for this rule to fire.'),\n value: z\n .unknown()\n .describe('Value returned when all conditions match. For triggerWhen: true = fire the action.'),\n})\n .describe('A single rule. ALL conditions must match (AND logic). Rules in a strategy are evaluated ' +\n 'top-to-bottom \u2014 first rule where all conditions match wins and returns its value.');\nexport const RuleStrategyZ = z\n .object({\n type: z.literal('rules'),\n rules: z\n .array(RuleZ)\n .describe('Ordered list of rules. Evaluated top-to-bottom \u2014 first match wins.'),\n default: z\n .unknown()\n .describe('Fallback value when no rule matches. For triggerWhen: false = do not fire by default.'),\n})\n .describe('Rule-based strategy. Evaluates rules top-to-bottom. First rule where ALL conditions match ' +\n 'returns its value. If no rule matches, returns default. ' +\n 'For triggerWhen: set value=true on matching rules, default=false.');\nexport const ScoreStrategyZ = z\n .object({\n type: z.literal('score'),\n field: z.string(),\n threshold: z.number(),\n above: z.unknown(),\n below: z.unknown(),\n})\n .describe('Score-based strategy. Compares a field value against a threshold.');\nexport const ModelStrategyZ = z\n .object({\n type: z.literal('model'),\n modelId: z.string(),\n inputs: z.array(z.string()),\n outputMapping: z.record(z.string(), z.unknown()),\n default: z.unknown(),\n})\n .describe('ML model strategy. Sends inputs to a model and maps outputs.');\nexport const ExternalStrategyZ = z\n .object({\n type: z.literal('external'),\n endpoint: z.string(),\n method: z.enum(['GET', 'POST']).optional(),\n default: z.unknown(),\n timeoutMs: z.number().optional(),\n})\n .describe('External API strategy. Calls an endpoint to determine the value.');\nexport const DecisionStrategyZ = z.discriminatedUnion('type', [\n RuleStrategyZ,\n ScoreStrategyZ,\n ModelStrategyZ,\n ExternalStrategyZ,\n]);\n/** Canonical Zod schema for the optional triggerWhen field on actions and adaptive items. */\nexport const TriggerWhenZ = DecisionStrategyZ.nullable().optional();\n// =============================================================================\n// TRIGGER DOCUMENTATION \u2014 examples and match field docs\n// Exported as constants so the schema generator can inject them into the\n// JSON schema. The Python prompt builder reads them from the schema.\n// =============================================================================\n/** Complete triggerWhen examples showing the full rules wrapper structure. */\nexport const TRIGGER_EXAMPLES = [\n {\n name: 'Click count on a specific element',\n description: 'Fire when user clicks an element with data-id=\"hero-cta\" 2+ times',\n triggerWhen: {\n type: 'rules',\n rules: [\n {\n conditions: [\n {\n type: 'event_count',\n key: 'cta-clicks',\n operator: 'gte',\n count: 2,\n counter: {\n events: ['ui.click'],\n match: { 'attr__data-id': { equals: 'hero-cta' } },\n },\n },\n ],\n value: true,\n },\n ],\n default: false,\n },\n },\n {\n name: 'Time on page threshold',\n description: 'Fire after user spends 30+ seconds on the page',\n triggerWhen: {\n type: 'rules',\n rules: [\n {\n conditions: [\n {\n type: 'session_metric',\n key: 'time_on_page',\n operator: 'gte',\n threshold: 30,\n },\n ],\n value: true,\n },\n ],\n default: false,\n },\n },\n {\n name: 'Element visible in viewport',\n description: 'Fire when a DOM element becomes visible',\n triggerWhen: {\n type: 'rules',\n rules: [\n {\n conditions: [\n {\n type: 'anchor_visible',\n anchorId: '#pricing-section',\n state: 'visible',\n },\n ],\n value: true,\n },\n ],\n default: false,\n },\n },\n {\n name: 'No trigger (fire immediately)',\n description: 'Action fires as soon as the segment matches \u2014 no in-session condition needed',\n triggerWhen: null,\n },\n];\n/** Documentation for counter.match field keys. */\nexport const MATCH_FIELD_DOCS = {\n tag_name: 'HTML tag name (e.g. \"button\", \"a\", \"input\")',\n $el_text: 'Visible text content of the element',\n 'attr__*': 'HTML attribute prefixed with attr__. Example: attr__data-id matches the data-id attribute, ' +\n 'attr__class matches the class attribute, attr__href matches the href attribute.',\n};\n// =============================================================================\n// EVENT SCOPE SCHEMA\n// =============================================================================\n/** Scopes a widget to specific events/URLs. */\nexport const EventScopeZ = z.object({\n events: z.array(z.string()),\n urlContains: z.string().optional(),\n props: z.record(z.union([z.string(), z.number(), z.boolean()])).optional(),\n});\n// =============================================================================\n// NOTIFY SCHEMA\n// =============================================================================\n/** Toast notification config for triggerWhen transitions. */\nexport const NotifyZ = z\n .object({\n title: z.string().optional().describe('Notification title'),\n body: z.string().optional().describe('Notification body text'),\n icon: z.string().optional().describe('Notification icon (emoji or URL)'),\n})\n .describe('Optional toast notification shown when this action triggers.')\n .nullable()\n .optional();\n", "/**\n * THE viewport classification \u2014 decided in exactly one place.\n *\n * Parker, 2026-08-29: \"if the point is to determine mobile vs desktop..\n * they shouldn't be checking a width.. If we change how we decide mobile\n * vs desktop away from checking just width.. it fucks everything. There\n * should be a central function that determines this; the only thing\n * various modules need to know is: is this desktop, mobile, or a tablet.\"\n *\n * Consumers may ONLY ask `classifyViewport()` (or subscribe /\n * ViewportClassController). The criterion \u2014 today a width threshold \u2014 is\n * a private implementation detail of this module and is deliberately NOT\n * exported. If classification ever adds pointer type, UA hints, or a\n * tablet tier, only this file changes.\n *\n * CSS cannot call a function, so components carry a stamped\n * `data-viewport=\"mobile|tablet|desktop\"` attribute (kept current by\n * ViewportClassController) and their stylesheets key off attribute\n * selectors instead of media queries. A media query in component CSS is\n * the criterion leaking \u2014 don't add them.\n */\n/** The attribute components stamp; CSS keys off `[data-viewport=\"\u2026\"]`. */\nexport const VIEWPORT_CLASS_ATTR = 'data-viewport';\n// --- private criterion ------------------------------------------------------\n// Width threshold matching the historical 720px desktop breakpoint. A\n// landscape phone classifies as desktop \u2014 width is (currently) the axis,\n// deliberately: those layouts genuinely have side-panel room. 'tablet' is\n// reserved in the type but not yet emitted; when a layout needs it, add\n// the tier HERE and TypeScript will surface every switch that must decide.\nconst DESKTOP_MIN_WIDTH_PX = 720;\n/**\n * TRUE when the primary pointer is coarse (finger) \u2014 a real touch device\n * with a set vertical, as opposed to a desktop window someone resized\n * small. Central for the same reason as classifyViewport(): consumers ask\n * the question; the criterion (today a pointer media query) lives here.\n */\nexport function isCoarsePointer() {\n // FALLBACK IS PART OF THE CRITERION: when the question cannot be asked\n // (SSR, jsdom, pre-2013 browsers), assume COARSE. Chosen deliberately:\n // the phone exemption for tight-mode must fail SAFE on odd mobile\n // webviews (a phone misread as fine-pointer would get desktop exclusive\n // modes), while the cost of misreading a desktop as coarse is inert\n // touch handlers and an unfired hover style. NOTE this flipped\n // SyntroTileCard's historical no-matchMedia default (was not-touch) \u2014\n // accepted 2026-08-29 adversarial review, pinned in viewport.test.ts.\n if (typeof window === 'undefined' || typeof window.matchMedia !== 'function')\n return true;\n return window.matchMedia('(pointer: coarse)').matches;\n}\n/**\n * PRIMARY pointer classification \u2014 one criterion, one place. 'coarse' is a\n * finger-first device (phones, tablets); 'fine' is mouse/trackpad-first.\n * The old scatter answered this three different ways ((pointer: coarse)\n * alone, (hover: none) and (pointer: coarse), (hover: hover) and\n * (pointer: fine)) which could DISAGREE on hybrid hardware. Canonical\n * criterion: the primary pointer's coarseness; hover is deliberately not\n * consulted (a fine-pointer-no-hover device counts as 'fine' \u2014 its rare\n * un-triggered :hover styles are harmless).\n */\nexport function classifyPointer() {\n return isCoarsePointer() ? 'coarse' : 'fine';\n}\n/**\n * TRUE when ANY fine pointer is available (mouse attached to a tablet,\n * trackpad on a touch laptop) \u2014 a CAPABILITY question, distinct from the\n * primary-pointer class. Gates affordances that need precise targets\n * (e.g. a small dismiss \u2715) even on touch-first devices.\n */\nexport function hasAnyFinePointer() {\n if (typeof window === 'undefined' || typeof window.matchMedia !== 'function')\n return true;\n return window.matchMedia('(any-pointer: fine)').matches;\n}\n/**\n * Subscribe to pointer-signal CHANGES (primary class and any-fine\n * capability \u2014 either can flip when hardware attaches/detaches). Fires\n * immediately, then on change. Returns the unsubscribe function.\n */\nexport function observePointer(cb) {\n const read = () => ({ primary: classifyPointer(), anyFine: hasAnyFinePointer() });\n let current = read();\n cb(current);\n if (typeof window === 'undefined' || typeof window.matchMedia !== 'function')\n return () => { };\n const coarse = window.matchMedia('(pointer: coarse)');\n const anyFine = window.matchMedia('(any-pointer: fine)');\n const onChange = () => {\n const next = read();\n if (next.primary !== current.primary || next.anyFine !== current.anyFine) {\n current = next;\n cb(next);\n }\n };\n // addEventListener is the modern API; older Safari needs addListener.\n const sub = (mq) => {\n if (mq.addEventListener)\n mq.addEventListener('change', onChange);\n else\n mq.addListener?.(onChange);\n };\n const unsub = (mq) => {\n if (mq.removeEventListener)\n mq.removeEventListener('change', onChange);\n else\n mq.removeListener?.(onChange);\n };\n sub(coarse);\n sub(anyFine);\n return () => {\n unsub(coarse);\n unsub(anyFine);\n };\n}\n/** The attribute for the primary pointer class; CSS keys off\n * `[data-pointer=\"coarse\"|\"fine\"]`. Stamped by ViewportClassController. */\nexport const POINTER_CLASS_ATTR = 'data-pointer';\nexport function classifyViewport() {\n if (typeof window === 'undefined')\n return 'mobile'; // SSR: match the historical default\n return window.innerWidth >= DESKTOP_MIN_WIDTH_PX ? 'desktop' : 'mobile';\n}\n/**\n * Subscribe to classification CHANGES. Fires `cb` immediately with the\n * current class, then only when the class actually flips. Returns the\n * unsubscribe function.\n */\nexport function observeViewportClass(cb) {\n let current = classifyViewport();\n cb(current);\n if (typeof window === 'undefined')\n return () => { };\n const onResize = () => {\n const next = classifyViewport();\n if (next !== current) {\n current = next;\n cb(next);\n }\n };\n window.addEventListener('resize', onResize, { passive: true });\n return () => window.removeEventListener('resize', onResize);\n}\n/**\n * Stamps `data-viewport` on the host element and keeps it current, so the\n * component's CSS can use attribute selectors ([data-viewport=\"mobile\"] /\n * :host([data-viewport=\"mobile\"])) instead of media queries. Stamps in\n * hostConnected \u2014 BEFORE the first render, so there is no unclassified\n * first paint. Read `.value` for the JS-side class.\n */\nexport class ViewportClassController {\n constructor(host) {\n this.value = classifyViewport();\n this.pointer = classifyPointer();\n this.anyFinePointer = hasAnyFinePointer();\n this._unsubscribes = [];\n this._host = host;\n host.addController(this);\n }\n hostConnected() {\n this._unsubscribes = [\n observeViewportClass((c) => {\n this.value = c;\n this._host.setAttribute(VIEWPORT_CLASS_ATTR, c);\n this._host.requestUpdate();\n }),\n observePointer(({ primary, anyFine }) => {\n this.pointer = primary;\n this.anyFinePointer = anyFine;\n this._host.setAttribute(POINTER_CLASS_ATTR, primary);\n this._host.requestUpdate();\n }),\n ];\n }\n hostDisconnected() {\n for (const off of this._unsubscribes)\n off();\n this._unsubscribes = [];\n }\n}\n/** The attribute components stamp; CSS keys off `[data-size=\"\u2026\"]`. */\nexport const SIZE_CLASS_ATTR = 'data-size';\n// --- private criterion ------------------------------------------------------\nconst MEDIUM_MIN_WIDTH_PX = 600; // two phone columns fit side by side\nconst EXPANDED_MIN_WIDTH_PX = 1200; // room for a composed page\n/** Classify a measured content width. A box not yet laid out (0) is compact,\n * the mobile-first default every stylesheet starts from. */\nexport function classifySize(widthPx) {\n if (widthPx >= EXPANDED_MIN_WIDTH_PX)\n return 'expanded';\n if (widthPx >= MEDIUM_MIN_WIDTH_PX)\n return 'medium';\n return 'compact';\n}\n/** The attribute canvases carry; CSS keys off `[data-height=\"\u2026\"]`. */\nexport const HEIGHT_MODE_ATTR = 'data-height';\n/**\n * Height real estate, decided here alone. On the web the canvas owns its\n * frame, so its box is fixed. Embedded, the host's reported sizes decide;\n * only a host that reports none falls back to its display-mode name\n * (fullscreen is a set box; inline and picture-in-picture size to fit).\n */\nexport function classifyHeightMode(input) {\n if (!input.embedded)\n return 'fixed';\n const dims = input.dimensions;\n if (dims)\n return typeof dims.height === 'number' ? 'fixed' : 'flexible';\n // No sizes reported: only fullscreen is known to be a set box. A\n // picture-in-picture window may be sized FROM the view's reported height,\n // and filling it (100dvh) would then pin it at its first height.\n return input.displayMode === 'fullscreen' ? 'fixed' : 'flexible';\n}\n/**\n * Stamps `data-size` on the host from the host's OWN width and keeps it\n * current. Stamps in hostConnected, before the first render, from a\n * synchronous measure; a ResizeObserver then follows the box. Re-renders the\n * host only when the class flips; `.width` always holds the latest measure.\n * Without ResizeObserver (pre-2020 engines) the window is the best measure\n * available, re-read on resize.\n */\nexport class ContainerSizeController {\n constructor(host) {\n this.value = 'compact';\n this.width = 0;\n this._off = null;\n this._host = host;\n host.addController(this);\n }\n _take(width) {\n this.width = width;\n const next = classifySize(width);\n const stamped = this._host.getAttribute(SIZE_CLASS_ATTR);\n this.value = next;\n if (stamped !== next) {\n this._host.setAttribute(SIZE_CLASS_ATTR, next);\n this._host.requestUpdate();\n }\n }\n hostConnected() {\n const RO = typeof ResizeObserver === 'function' ? ResizeObserver : undefined;\n if (!RO) {\n // hostConnected only runs on an element in a live document, so a window\n // is always there to fall back to.\n const onResize = () => this._take(window.innerWidth);\n onResize();\n window.addEventListener('resize', onResize, { passive: true });\n this._off = () => window.removeEventListener('resize', onResize);\n return;\n }\n this._take(this._host.getBoundingClientRect().width);\n const ro = new RO((entries) => {\n for (const entry of entries)\n this._take(entry.contentRect.width);\n });\n ro.observe(this._host);\n this._off = () => ro.disconnect();\n }\n hostDisconnected() {\n this._off?.();\n this._off = null;\n }\n}\n", "/**\n * The store's live price, read in the browser. The one place every product\n * surface gets it from, so they share one cache and one rule.\n *\n * Every product surface first paints the catalog price the server sent (our\n * search index: a snapshot from ingest). The store is the authority, so the\n * surface then asks the commerce adapter the runtime published for this page\n * (`__SYNTRO_PRODUCT_ADAPTER__`) and paints the store's price instead. The\n * adapter knows where the store is: same-origin on a Shopify or WooCommerce\n * storefront, the configured `storefrontUrl` in a chat app's card. Nothing\n * here, and nothing on the server, needs the store's address.\n *\n * - One cache per published adapter: a product is asked for at most once a\n * minute, concurrent reads share one request, and a product the store\n * could not price is asked again after a short wait.\n * - `watchLivePrice` re-asks when the runtime publishes an adapter, so a\n * widget that painted before the adapter existed still gets the price.\n * - `formatProductPrice` follows the server's catalog formatter exactly\n * (platform/common `prices.py format_price`), so an unchanged price repaints\n * byte-identically: nothing flickers.\n *\n * Null means \"keep the catalog price\": no adapter, an adapter that cannot\n * price, a product the store did not return, or a currency the store did not\n * name. A live price is never guessed.\n */\n/** The window key the runtime publishes its product commerce adapter on. */\nexport const PRODUCT_ADAPTER_KEY = '__SYNTRO_PRODUCT_ADAPTER__';\n/** Dispatched on `window` whenever the runtime (re)publishes that adapter. */\nexport const PRODUCT_ADAPTER_EVENT = 'syntro:product-adapter';\nconst ANSWER_TTL_MS = 60000;\nconst NO_PRICE_TTL_MS = 10000;\n/** The server's symbol table (prices.py `_SYMBOLS`); keep the two in step. */\nconst SYMBOLS = {\n USD: '$',\n INR: '\u20B9',\n EUR: '\u20AC',\n GBP: '\u00A3',\n JPY: '\u00A5',\n CNY: '\u00A5',\n KRW: '\u20A9',\n ILS: '\u20AA',\n NGN: '\u20A6',\n PHP: '\u20B1',\n THB: '\u0E3F',\n VND: '\u20AB',\n UAH: '\u20B4',\n TRY: '\u20BA',\n BRL: 'R$',\n CAD: 'CA$',\n AUD: 'A$',\n NZD: 'NZ$',\n SGD: 'S$',\n HKD: 'HK$',\n MXN: 'MX$',\n};\nfunction amount(value) {\n return Number.isInteger(value) ? String(value) : value.toFixed(2);\n}\nfunction money(value, code) {\n const symbol = SYMBOLS[code];\n return symbol ? `${symbol}${amount(value)}` : `${code} ${amount(value)}`;\n}\nfunction span(min, max, code) {\n return max > min ? `${money(min, code)}\u2013${money(max, code)}` : money(min, code);\n}\nconst usable = (min, max) => Number.isFinite(min) && Number.isFinite(max) && min >= 0 && max >= min;\n/** \"$72\", \"$24.99\", \"\u20B91000\u2013\u20B91950\", \"AED 10\"; null for anything not renderable. */\nexport function formatProductPrice(price) {\n const { min, max, currency } = price;\n if (!usable(min, max) || !/^[A-Za-z]{3}$/.test(currency))\n return null;\n const code = currency.toUpperCase();\n const was = price.compareAt;\n const onSale = was && usable(was.min, was.max) && was.min > min;\n return { price: span(min, max, code), compareAt: onSale ? span(was.min, was.max, code) : null };\n}\nfunction priceSource() {\n if (typeof window === 'undefined')\n return null;\n return (window[PRODUCT_ADAPTER_KEY] ??\n null);\n}\nlet caches = new WeakMap();\n/** The product's live price, or null to keep the catalog price. Never rejects. */\nexport function fetchLivePrice(productId) {\n const source = priceSource();\n if (!source?.fetchPrice || !productId)\n return Promise.resolve(null);\n let cache = caches.get(source);\n if (!cache) {\n cache = new Map();\n caches.set(source, cache);\n }\n const now = Date.now();\n const known = cache.get(productId);\n if (known && now - known.at < known.ttl)\n return known.value;\n const entry = { at: now, ttl: ANSWER_TTL_MS, value: Promise.resolve(null) };\n entry.value = source\n .fetchPrice(productId)\n .then((price) => (price ? formatProductPrice(price) : null))\n .catch((err) => {\n console.warn('[syntro] live price unavailable for', productId, err);\n return null;\n })\n .then((display) => {\n if (!display)\n entry.ttl = NO_PRICE_TTL_MS;\n return display;\n });\n cache.set(productId, entry);\n return entry.value;\n}\n/**\n * Call `onPrice` with the product's live price now and whenever the runtime\n * publishes a commerce adapter. Returns the unsubscribe. `onPrice` is only\n * ever called with a price: no answer keeps the catalog price on screen.\n */\nexport function watchLivePrice(productId, onPrice) {\n let active = true;\n const ask = () => {\n void fetchLivePrice(productId).then((live) => {\n if (active && live)\n onPrice(live);\n });\n };\n ask();\n if (typeof window !== 'undefined')\n window.addEventListener(PRODUCT_ADAPTER_EVENT, ask);\n return () => {\n active = false;\n if (typeof window !== 'undefined')\n window.removeEventListener(PRODUCT_ADAPTER_EVENT, ask);\n };\n}\n/** Test seam: forget every cached answer. */\nexport function resetLivePriceCache() {\n caches = new WeakMap();\n}\n", "import { PRODUCT_ADAPTER_KEY } from '@syntrologie/sdk-contracts';\n\n/** The narrow slice of the platform product adapter the guide uses. */\nexport interface GuideCart {\n addToCart(input: { product_id: string; variant_id: string | null; quantity: number }): Promise<{\n ok: boolean;\n error?: { code: string; message: string };\n }>;\n openCart(): Promise<void> | void;\n /** Present on platforms with variants (Shopify: /products/<handle>.js). */\n fetchVariants?(handle: string): Promise<GuideVariants>;\n}\n\n/** The slice of the platform VariantPayload the guide reads (platform-shopify product-adapter-types.ts). */\nexport interface GuideVariants {\n attributes?: {\n key: string;\n label: string;\n kind: 'pill' | 'swatch';\n values: {\n id: string;\n label: string;\n swatch: { kind: 'color' | 'image'; value: string } | null;\n }[];\n }[];\n variants: { id?: string; selection?: Record<string, string>; in_stock: boolean }[];\n}\n\n/** The platform product adapter the runtime publishes (Shopify: /cart/add.js), or null. */\nexport function getCart(): GuideCart | null {\n const adapter = (window as unknown as Record<string, unknown>)[PRODUCT_ADAPTER_KEY] as\n | Partial<GuideCart>\n | undefined;\n return adapter &&\n typeof adapter.addToCart === 'function' &&\n typeof adapter.openCart === 'function'\n ? (adapter as GuideCart)\n : null;\n}\n", "/**\n * What the guided page reads from the store page it sits on: landing hints for\n * the first pictures, the shopper's tracking choice and the store's name. The\n * SDK tile that mounts <postcard-guide-page> hands these to it.\n */\nimport type { AuthedFetch } from './guide-client';\n\nconst HINT_MAX = 60;\nconst PART_MAX = 20;\n/** Paths that can carry a shopper's own records or tokens: never sent as a hint. */\nconst PRIVATE_PATHS = /^\\/(account|orders|checkouts?|cart)(\\/|$)/i;\n/** The SDK's own landing record (bootstrap-init.ts: initial_page_path, external referrer_hostname). */\nconst LANDING_RECORD_KEY = 'syntro_initial_attrs';\nconst NAME_MAX = 60;\n\ndeclare global {\n interface Window {\n SynOS?: {\n authedFetch?: AuthedFetch;\n /** The SDK's read-only consent view (runtime-sdk bootstrap). */\n consent?: { getStatus?: () => string };\n };\n }\n}\n\n/** Path words a hint may carry: no part with digits (ids, tokens) and no long opaque part. */\nfunction pathWords(path: string): string {\n if (PRIVATE_PATHS.test(path)) return '';\n return path\n .split('/')\n .map((segment) =>\n segment\n .split(/[-_.]+/)\n .filter((part) => /^[A-Za-z]+$/.test(part) && part.length <= PART_MAX)\n .join('-')\n )\n .filter(Boolean)\n .join(' ')\n .slice(-HINT_MAX)\n .trim();\n}\n\nfunction landingRecord(): { initial_page_path?: string; referrer_hostname?: string } {\n try {\n const raw = localStorage.getItem(LANDING_RECORD_KEY);\n const data = raw ? (JSON.parse(raw) as { data?: unknown }).data : null;\n return data && typeof data === 'object' ? (data as Record<string, string>) : {};\n } catch {\n return {};\n }\n}\n\n/**\n * Landing hints for the first pictures: the words of the path the shopper\n * landed on and the external host that sent them, from the SDK's own landing\n * record (falling back to this page). Never a query string, so never UTM\n * values; never account, order, cart or checkout paths; never a word with\n * digits or a long opaque token.\n */\nexport function landingHints(): string[] {\n const record = landingRecord();\n const hints: string[] = [];\n const words = pathWords(\n typeof record.initial_page_path === 'string' ? record.initial_page_path : location.pathname\n );\n if (words) hints.push(words);\n const host = typeof record.referrer_hostname === 'string' ? record.referrer_hostname : '';\n if (/^[A-Za-z0-9.-]{1,60}$/.test(host) && host !== location.hostname) hints.push(host);\n return hints.slice(0, 3);\n}\n\n/** Whether the shopper accepted tracking, read from the SDK's consent gate at call time. */\nexport function consentGranted(): boolean {\n try {\n return window.SynOS?.consent?.getStatus?.() === 'granted';\n } catch {\n return false;\n }\n}\n\n/** The store's name for the brand bar: the theme's og:site_name (Shopify themes print it). */\nexport function storeName(): string {\n const meta = document.querySelector<HTMLMetaElement>('meta[property=\"og:site_name\"]');\n return (meta?.content ?? '').trim().slice(0, NAME_MAX);\n}\n", "/**\n * <postcard-guide-page>: the guided page (spec 2026-10-25, \"The page\").\n *\n * The frame: a brand bar, what we know as stamps (a band below 1200 px of our own\n * width; a rail with each stamp's words at 1200 and wider), the feed, one\n * status line and the text box. Every server answer carries a turn; its shown\n * templates land as sections at the bottom of the feed on the store (scroll\n * chains on to the page at its ends), or replace the one on screen on a card\n * (a snapping feed that keeps its scroll).\n *\n * Paints only what the server answered. One tap is in flight at a time: while\n * it is, controls are aria-disabled (still focusable) and every handler\n * ignores another. Arrival never takes focus or scrolls; a shopper's tap that\n * brings a turn scrolls the feed to it and focuses its first heading.\n *\n * On the store it is a section of the page (the SDK's guided page tile mounts it\n * in an inline slot): a labelled region that never holds focus, so Tab moves on\n * to the host page as from any other section.\n *\n * Every shopper-facing word that is not the frame's plain mechanics (Cart, Send)\n * is the store's own, from the view's `copy` (copy.ts).\n */\nimport { ContainerSizeController } from '@syntrologie/sdk-contracts';\nimport { html, LitElement, nothing } from 'lit';\nimport { repeat } from 'lit/directives/repeat.js';\nimport { html as staticHtml, unsafeStatic } from 'lit/static-html.js';\nimport { type GuideCart, getCart } from './cart';\nimport { copyOf } from './copy';\nimport {\n type AuthedFetch,\n type GuideHost,\n type GuideParams,\n type GuideResult,\n type GuideTap,\n type GuideView,\n type GuideWidth,\n guideRequest,\n loadGuide,\n newPageSession,\n type ShownTemplate,\n sendMessage,\n sendSeen,\n sendTap,\n type TurnView,\n} from './guide-client';\nimport { ProductDetailHistory } from './product-history';\nimport { SeenTracker } from './seen';\nimport { pageStyles } from './styles';\nimport {\n GUIDE_TURN_EVENT,\n type GuidePickDetail,\n type GuideTemplate,\n type GuideTurnDetail,\n templateElement,\n} from './templates';\nimport { WordsController } from './words';\nimport { WORDS_PATH } from './words-stream';\n\n/** One id per page load, sent with every call (plan 1's turn record keys on it). */\nconst PAGE_SESSION = newPageSession();\n\ntype Status = 'loading' | 'ready' | 'gone';\ntype Cause = 'arrival' | 'tap' | 'say';\ninterface Origin {\n turn: string | null;\n move: string | null;\n}\ninterface Section {\n turnId: string;\n shown: ShownTemplate;\n tag: string;\n}\n\nconst COPY = {\n saveFailed: \"We couldn't save that. Try again.\",\n noCart: \"Adding to the cart isn't available here.\",\n soldOut: 'That one is sold out.',\n network: \"We couldn't reach the cart. Try again.\",\n addFailed: \"We couldn't add that to your cart.\",\n};\n\nexport class PostcardGuidePage extends LitElement {\n static override styles = pageStyles;\n static override properties = {\n authedFetch: { attribute: false },\n getCart: { attribute: false },\n hints: { attribute: false },\n consent: { attribute: false },\n guideHost: { attribute: false },\n brand: { attribute: false },\n allowedTemplates: { attribute: false },\n _view: { state: true },\n _turns: { state: true },\n _status: { state: true },\n _busy: { state: true },\n _notice: { state: true },\n _heard: { state: true },\n };\n\n declare authedFetch: AuthedFetch | null;\n declare getCart: () => GuideCart | null;\n /** Landing hints (landing path words, referrer host) for the first load. Never UTM values. */\n declare hints: string[];\n /** True only when the SDK's consent gate says the shopper accepted tracking. Read on every call. */\n declare consent: () => boolean;\n /** The store's whole page (a snap feed) or a chat-app card (one template, replaced in place). */\n declare guideHost: GuideHost;\n /** The store's name for the brand bar. */\n declare brand: string;\n /** The store's own list of templates; null means every template this page can draw. */\n declare allowedTemplates: readonly string[] | null;\n declare _view: GuideView | null;\n declare _turns: TurnView[];\n declare _status: Status;\n declare _busy: boolean;\n declare _notice: string | null;\n /** The status text is heard, not shown: a line already on screen (the words' lead line). */\n declare _heard: boolean;\n\n /** Our own width decides band or rail, and the width the server is told. */\n private readonly _size = new ContainerSizeController(this);\n private _seen: SeenTracker | null = null;\n /** Each turn's live words, streamed into its templates' slots over their fallbacks. */\n private readonly _words = new WordsController(this, {\n fetchFn: () => this.authedFetch,\n // The turn travels as the decorated `turn` param, like every guide call.\n request: (turnId) => guideRequest(WORDS_PATH, this._params(turnId)),\n width: () => this._width,\n announce: (text) => this._announce(text, true),\n });\n private _shownWidth: GuideWidth | null = null;\n /** The address bar for product detail: the store's page only, never a card's host. */\n private _history: ProductDetailHistory | null = null;\n private readonly _noElement = new Set<string>();\n\n constructor() {\n super();\n this.authedFetch = null;\n this.getCart = getCart;\n this.hints = [];\n this.consent = () => false;\n this.guideHost = 'store';\n this.brand = '';\n this.allowedTemplates = null;\n this._view = null;\n this._turns = [];\n this._status = 'loading';\n this._busy = false;\n this._notice = null;\n this._heard = false;\n }\n\n override connectedCallback(): void {\n super.connectedCallback();\n if (this.guideHost === 'store') {\n this._history = new ProductDetailHistory(this, () => this.renderRoot);\n this._history.attach();\n }\n document.addEventListener('visibilitychange', this._onVisibility);\n window.addEventListener('pagehide', this._onPageHide);\n // Moved, not new: keep the turns; never a second arrival.\n if (this._view) return;\n void this._load();\n }\n\n override disconnectedCallback(): void {\n this._history?.detach();\n this._history = null;\n document.removeEventListener('visibilitychange', this._onVisibility);\n window.removeEventListener('pagehide', this._onPageHide);\n this._seen?.flush();\n this._seen?.disconnect();\n this._seen = null;\n super.disconnectedCallback();\n }\n\n protected override willUpdate(): void {\n this.toggleAttribute('data-card', this.guideHost === 'card');\n }\n\n protected override updated(): void {\n // Band to rail (or back): show each filled slot's form for the new width, no new request.\n if (this._shownWidth !== null && this._shownWidth !== this._width) this._words.relayout();\n this._shownWidth = this._width;\n }\n\n private _onVisibility = (): void => {\n if (document.visibilityState === 'hidden') this._seen?.flush();\n };\n\n private _onPageHide = (): void => this._seen?.flush();\n\n private get _width(): GuideWidth {\n return this._size.value === 'expanded' ? 'desktop' : 'phone';\n }\n\n private _params(turn?: string | null, move?: string | null): GuideParams {\n return {\n host: this.guideHost,\n width: this._width,\n consent: this.consent(),\n turn: turn ?? null,\n move: move ?? null,\n pageSession: PAGE_SESSION,\n allowedTemplates: this.allowedTemplates,\n };\n }\n\n private _latestTurn(): string | null {\n return this._turns.at(-1)?.turn_id ?? null;\n }\n\n /**\n * The one live region (WCAG: one per surface). A notice is shown; a line that is\n * already on screen (`heard`) is only spoken, so it takes no row and the snapped\n * feed never shifts under the shopper.\n */\n private _announce(text: string, heard = false): void {\n this._notice = text;\n this._heard = heard;\n }\n\n private async _load(): Promise<void> {\n if (!this.authedFetch) {\n console.error('[postcard-guide] no authed fetch on the page; the guide cannot load');\n this._giveBack('guide-unavailable');\n return;\n }\n this._status = 'loading';\n this._apply(await loadGuide(this.authedFetch, this._params(), this.hints), 'arrival');\n }\n\n /** Render nothing and tell the host, which collapses the section so the theme's page shows. */\n private _giveBack(event: 'guide-unpublished' | 'guide-unavailable'): void {\n this._status = 'gone';\n this.dispatchEvent(new CustomEvent(event, { bubbles: true, composed: true }));\n }\n\n private _apply(result: GuideResult, cause: Cause): void {\n if (result.kind === 'view') {\n const turn = result.view.turn;\n if (!turn) {\n console.error('[postcard-guide] a guide answer came without a turn');\n if (this._view && cause !== 'arrival') this._announce(COPY.saveFailed);\n else this._giveBack('guide-unavailable');\n return;\n }\n if (this.guideHost === 'card') this._seen?.flush();\n const first = !this._view;\n this._view = result.view;\n this._turns = this.guideHost === 'card' ? [turn] : [...this._turns, turn];\n this._status = 'ready';\n if (first) {\n // The first view is painted. The guide tile holds the section at no height and\n // unseen while it loads (data-pending), and gives it its height on this event.\n this.dispatchEvent(new CustomEvent('guide-ready', { bubbles: true, composed: true }));\n }\n void this._landed(turn, cause);\n } else if (result.kind === 'unpublished') {\n this._giveBack('guide-unpublished');\n } else if (result.kind === 'unavailable') {\n if (this._view && cause !== 'arrival') this._announce(COPY.saveFailed);\n else this._giveBack('guide-unavailable');\n }\n }\n\n /** After a turn renders: watch its sections, bring it into view on a tap, tell listeners. */\n private async _landed(turn: TurnView, cause: Cause): Promise<void> {\n await this.updateComplete;\n const feed = this.renderRoot.querySelector<HTMLElement>('.feed');\n if (!feed) return;\n this._seen ??= new SeenTracker(feed, (report, final) => {\n if (this.authedFetch) void sendSeen(this.authedFetch, report, this._params(), final);\n });\n const all = [...feed.querySelectorAll<HTMLElement>('section.snap')];\n for (const section of all) this._seen.observe(section);\n const first = all.find((s) => s.dataset.turnId === turn.turn_id);\n if (first && cause !== 'arrival' && this.guideHost === 'store')\n feed.scrollTop = first.offsetTop;\n if (first && cause === 'tap') {\n await (first.firstElementChild as GuideTemplate<unknown> | null)?.focusHeading?.();\n }\n this.dispatchEvent(\n new CustomEvent<GuideTurnDetail>(GUIDE_TURN_EVENT, {\n detail: { turnId: turn.turn_id },\n bubbles: true,\n composed: true,\n })\n );\n }\n\n /** One tap. Returns whether the server answered with a view. */\n protected async _tap(tap: GuideTap, from: Origin): Promise<boolean> {\n if (this._busy || !this.authedFetch) return false;\n this._busy = true;\n this._notice = null;\n try {\n const sent = await sendTap(this.authedFetch, tap, this._params(from.turn, from.move));\n // A refused tap reloads the guide as it is, but the tap itself was not saved.\n const result =\n sent.kind === 'rejected' ? await loadGuide(this.authedFetch, this._params()) : sent;\n this._apply(result, 'tap');\n return sent.kind === 'view';\n } finally {\n this._busy = false;\n }\n }\n\n /** The section an event came from: a tap answers the turn it was shown in, and names its move. */\n private _originOf(event: Event): Origin {\n const section = (event.target as Element | null)?.closest?.(\n 'section.snap'\n ) as HTMLElement | null;\n return {\n turn: section?.dataset.turnId ?? this._latestTurn(),\n move: section?.dataset.moveId ?? null,\n };\n }\n\n /** The frame's own controls (stamps, done) answer the latest turn and come from no move. */\n private _frameOrigin(): Origin {\n return { turn: this._latestTurn(), move: null };\n }\n\n private _onTap = (event: Event): void => {\n void this._tap((event as CustomEvent<GuideTap>).detail, this._originOf(event));\n };\n\n private _onPick = (event: Event): void => {\n const { itemId, handle, variantId } = (event as CustomEvent<GuidePickDetail>).detail;\n void this._pick(itemId, handle, variantId ?? null, this._originOf(event));\n };\n\n private async _pick(\n itemId: string,\n handle: string,\n variantId: string | null,\n from: Origin\n ): Promise<void> {\n if (this._busy) return;\n const cart = this.getCart();\n if (!cart) {\n this._announce(COPY.noCart);\n return;\n }\n this._busy = true;\n this._notice = null;\n let added: Awaited<ReturnType<GuideCart['addToCart']>>;\n try {\n added = await cart.addToCart({ product_id: handle, variant_id: variantId, quantity: 1 });\n } catch {\n added = { ok: false, error: { code: 'network', message: '' } };\n } finally {\n this._busy = false;\n }\n if (!added.ok) {\n const code = added.error?.code;\n this._announce(\n code === 'oos' ? COPY.soldOut : code === 'network' ? COPY.network : COPY.addFailed\n );\n return;\n }\n const saved = await this._tap({ kind: 'pick', item_id: itemId, handle }, from);\n if (!saved) this._announce(copyOf(this._view).saved_failed);\n }\n\n private _onSubmit = (event: Event): void => {\n event.preventDefault();\n const input = this.renderRoot.querySelector<HTMLInputElement>('form.composer input');\n const text = input?.value.trim() ?? '';\n if (!input || !text || this._busy) return;\n void this._say(text, input);\n };\n\n private async _say(text: string, input: HTMLInputElement): Promise<void> {\n if (this._busy || !this.authedFetch) return;\n this._busy = true;\n this._notice = null;\n try {\n let result = await sendMessage(this.authedFetch, text, this._params(this._latestTurn()));\n if (result.kind === 'rejected') result = await loadGuide(this.authedFetch, this._params());\n if (result.kind === 'view' && (result.view as { heard?: boolean }).heard === false) {\n // Not understood (or messages are down): nothing changed on the server, so keep\n // the screen and the shopper's words, and say so where they will hear it.\n this._announce(result.view.voice ?? COPY.saveFailed);\n return;\n }\n this._apply(result, 'say');\n if (result.kind === 'view') input.value = '';\n } finally {\n this._busy = false;\n }\n }\n\n private _busyAttr(): 'true' | 'false' {\n return this._busy ? 'true' : 'false';\n }\n\n private _sections(): Section[] {\n const turns = this.guideHost === 'card' ? this._turns.slice(-1) : this._turns;\n const out: Section[] = [];\n for (const turn of turns) {\n const drawable: Section[] = [];\n for (const shown of [...turn.shown].sort((a, b) => a.position - b.position)) {\n const tag = templateElement(shown.template);\n if (tag) drawable.push({ turnId: turn.turn_id, shown, tag });\n else if (!this._noElement.has(shown.template)) {\n this._noElement.add(shown.template);\n console.warn(\n `[postcard-guide] no template element for ${shown.template}; it is not drawn`\n );\n }\n }\n out.push(...(this.guideHost === 'card' ? drawable.slice(0, 1) : drawable));\n }\n return out;\n }\n\n override render() {\n if (this._status === 'gone') return nothing;\n const view = this._view;\n if (this._status === 'loading' || !view) {\n return html`<div class=\"frame\" aria-busy=\"true\">${this._brandBar(null)}</div>`;\n }\n const copy = copyOf(view);\n return html`<div class=\"frame\" role=\"region\" aria-label=${copy.guide_name}>\n ${this._brandBar(view)} ${this._known(view)}\n <div class=\"feed\" @guide-tap=${this._onTap} @guide-pick=${this._onPick}>\n ${repeat(\n this._sections(),\n (s) => `${s.turnId}:${s.shown.move_id}`,\n (s) => this._section(s)\n )}\n </div>\n <p class=${this._heard ? 'notice heard' : 'notice'} role=\"status\">${this._notice ?? nothing}</p>\n <form class=\"composer\" @submit=${this._onSubmit}>\n <input\n type=\"text\"\n maxlength=\"300\"\n autocomplete=\"off\"\n enterkeyhint=\"send\"\n aria-label=${copy.ask_placeholder}\n placeholder=${copy.ask_placeholder}\n aria-disabled=${this._busyAttr()}\n />\n <button type=\"submit\" aria-disabled=${this._busyAttr()}>Send</button>\n </form>\n </div>`;\n }\n\n private _brandBar(view: GuideView | null) {\n const cart = this.getCart();\n const count = view?.pick_count ?? 0;\n return html`<div class=\"brand\">\n <span class=\"name\">${this.brand}</span>\n ${\n cart\n ? html`<button\n class=\"cart\"\n aria-label=${count ? `Cart, ${count} added here` : 'Cart'}\n @click=${() => void cart.openCart()}\n >\n Cart${count ? html`<span aria-hidden=\"true\"> \u00B7 ${count}</span>` : nothing}\n </button>`\n : nothing\n }\n </div>`;\n }\n\n private _known(view: GuideView) {\n const latest = this._frameOrigin();\n return html`<div class=\"known\">\n <p class=\"known-label\">${copyOf(view).known_label}</p>\n <ul class=\"stamps\">\n ${view.stamps.map(\n (s) => html`<li>\n <button\n class=\"stamp\"\n aria-disabled=${this._busyAttr()}\n aria-label=${`Change ${s.label}`}\n @click=${() => void this._tap({ kind: 'stamp', fact_id: s.fact_id }, latest)}\n >\n <img src=${s.image} alt=${s.label} />\n </button>\n <span class=\"words\" aria-hidden=\"true\">${s.label}</span>\n </li>`\n )}\n ${view.missing.map(\n (m) => html`<li>\n <span class=\"stamp empty\" role=\"img\" aria-label=${`${m.label}, not chosen yet`}></span>\n <span class=\"words unknown\" aria-hidden=\"true\">${m.label}</span>\n </li>`\n )}\n </ul>\n ${\n view.stamps.length && view.stage === 'postcard'\n ? html`<button\n class=\"done\"\n aria-disabled=${this._busyAttr()}\n @click=${() => void this._tap({ kind: 'done' }, latest)}\n >\n ${copyOf(view).done_label}\n </button>`\n : nothing\n }\n </div>`;\n }\n\n private _section(s: Section) {\n // The tag comes from our own TEMPLATE_ELEMENTS, never from the server.\n const tag = unsafeStatic(s.tag);\n return html`<section\n class=\"snap\"\n data-turn-id=${s.turnId}\n data-move-id=${s.shown.move_id}\n data-template=${s.shown.template}\n >\n ${staticHtml`<${tag}\n .view=${s.shown.view}\n .turnId=${s.turnId}\n .moveId=${s.shown.move_id}\n .cart=${this.getCart}\n .busy=${this._busy}\n .copy=${copyOf(this._view)}\n ></${tag}>`}\n </section>`;\n }\n}\n\nif (!customElements.get('postcard-guide-page')) {\n customElements.define('postcard-guide-page', PostcardGuidePage);\n}\n", "/**\n * The guide's shopper-facing words. They are the store's own (GuideContent.copy,\n * sent on every GuideView as `copy`); the canvas never writes a domain word of its\n * own. The neutral defaults below match the server's GuideCopy defaults and show\n * only when an older server (or a cached answer from before `copy`) sends none.\n */\nimport type { Schemas } from '@syntrologie/runtime-contracts';\nimport type { GuideView } from './guide-client';\n\n/** runtime-backend's GuideCopy: every field filled on every view. */\nexport type GuideCopy = Schemas['GuideCopy'];\n\nexport const DEFAULT_COPY: GuideCopy = Object.freeze({\n ask_placeholder: \"Tell us what you're looking for\",\n known_label: 'What we know',\n need_headline: 'What you need',\n best_fit: 'Best fit for you',\n because_label: \"Why it's here\",\n back_label: 'Back to your picks',\n saved_failed: \"Added to your cart. We couldn't save that just now.\",\n guide_name: 'Shopping guide',\n done_label: \"That's everything\",\n also_label: 'Also worth a look',\n});\n\nconst filled = new WeakMap<object, GuideCopy>();\n\n/** The view's words, each one the store left out (or an older server never sent) filled by the default. */\nexport function copyOf(view: GuideView | null): GuideCopy {\n const sent = (view as { copy?: Partial<GuideCopy> | null } | null)?.copy;\n if (!sent || typeof sent !== 'object') return DEFAULT_COPY;\n // The same answer gives the same object, so templates are not redrawn for nothing.\n const known = filled.get(sent);\n if (known) return known;\n const out = { ...DEFAULT_COPY };\n for (const key of Object.keys(DEFAULT_COPY) as (keyof GuideCopy)[]) {\n const word = sent[key];\n if (typeof word === 'string' && word.trim()) out[key] = word;\n }\n filled.set(sent, out);\n return out;\n}\n", "/**\n * <guide-compare>: two or three products for one gear item, with the\n * trade-offs drawn (legend icons, meters as dots) and the best fit marked.\n *\n * The variant rule (spec, \"The templates\"): add directly only when exactly one\n * variant is in stock (the platform picks a variant when none is named, and a\n * guess is a wrong size). Several in stock open product detail; none says\n * sold out; while the check is out, the button reads \"Checking stock\" and does\n * nothing. A pick goes upward as `guide-pick`; the frame adds to the cart.\n */\nimport { css, html, nothing, type PropertyDeclarations, type PropertyValues } from 'lit';\nimport type { CompareView } from '../guide-client';\nimport { GuideTemplate } from './base';\nimport { checkFit, type Fit } from './fit';\nimport { formatPrice, picture } from './format';\nimport { dots, partStyles } from './parts';\nimport { templateStyles } from './styles';\n\nexport const GUIDE_COMPARE = 'guide-compare';\n\ntype Option = CompareView['options'][number];\n\nexport class GuideCompare extends GuideTemplate<CompareView> {\n static override properties: PropertyDeclarations = { _fits: { state: true } };\n static override styles = [\n templateStyles,\n partStyles,\n css`\n .legend {\n display: flex;\n flex-wrap: wrap;\n gap: 8px 16px;\n margin: 0 0 16px;\n }\n .legend li {\n display: flex;\n align-items: center;\n gap: 8px;\n font-weight: 700;\n font-size: 0.9rem;\n }\n .legend img {\n width: 32px;\n height: 32px;\n border-radius: 8px;\n }\n .options {\n display: flex;\n gap: 14px;\n overflow-x: auto;\n scroll-snap-type: x mandatory;\n overscroll-behavior-x: contain;\n margin: 0 -16px;\n padding: 0 16px 8px;\n scroll-padding-inline: 16px;\n }\n :host([data-size='medium']) .options,\n :host([data-size='expanded']) .options {\n display: grid;\n grid-template-columns: repeat(3, minmax(0, 1fr));\n gap: 18px;\n overflow: visible;\n margin: 0;\n padding: 0;\n }\n :host([data-size='medium']) .options[data-count='2'],\n :host([data-size='expanded']) .options[data-count='2'] {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n }\n .option {\n flex: 0 0 82%;\n scroll-snap-align: start;\n display: flex;\n flex-direction: column;\n border-radius: var(--sc-border-radius, 18px);\n overflow: hidden;\n /* A shade off the page, so a card reads as a card in both schemes. */\n background: var(--sc-content-search-background, #f4f4f5);\n box-shadow: 0 8px 22px rgb(0 0 0 / 0.12);\n border: 2px solid transparent;\n }\n .option[data-best] {\n border-color: var(--sc-color-primary, #111827);\n }\n /* The headline is a sentence of voice, not a title: one size down. */\n .headline {\n font-size: 1.35rem;\n line-height: 1.2;\n }\n :host([data-size='medium']) .headline,\n :host([data-size='expanded']) .headline {\n font-size: 1.75rem;\n }\n .photo {\n position: relative;\n flex: none;\n aspect-ratio: 1 / 1;\n overflow: hidden;\n background: #ffffff;\n }\n /* Out of flow, so a tall store photo can never stretch the square. */\n .photo img {\n position: absolute;\n inset: 0;\n object-fit: contain;\n padding: 8% 8% 26%;\n }\n .photo .standin {\n position: absolute;\n inset: 0;\n }\n /* Store photos sit on white: the name sits on a solid caption plate. */\n .photo .on-photo {\n padding: 10px 12px 12px;\n background: rgb(12 20 18 / 0.86);\n }\n .best {\n position: absolute;\n top: 12px;\n left: 12px;\n padding: 4px 10px;\n border-radius: 999px;\n background: var(--sc-color-primary, #111827);\n color: var(--sc-accent-foreground, #ffffff);\n font-size: 0.72rem;\n font-weight: 800;\n letter-spacing: 0.04em;\n text-transform: uppercase;\n }\n /* A button that reads as the name on the photo: it opens product detail. */\n .on-photo .name {\n display: block;\n padding: 0;\n border: 0;\n background: none;\n color: #ffffff;\n font-size: 1.1rem;\n font-weight: 800;\n line-height: 1.15;\n text-align: left;\n }\n .on-photo .price {\n display: block;\n margin-top: 2px;\n font-weight: 700;\n }\n .body {\n display: flex;\n flex-direction: column;\n gap: 8px;\n flex: 1;\n padding: 12px 14px 14px;\n }\n .meters {\n display: flex;\n flex-direction: column;\n gap: 6px;\n }\n .meter {\n min-height: 0;\n }\n .meter img {\n width: 26px;\n height: 26px;\n border-radius: 6px;\n }\n .reason {\n margin: 0;\n font-weight: 600;\n }\n .add,\n .choose,\n .sold-out {\n display: block;\n width: 100%;\n min-height: 44px;\n margin-top: auto;\n padding: 12px;\n border-radius: 999px;\n text-align: center;\n font-weight: 800;\n }\n .add {\n border: 0;\n background: var(--sc-color-primary, #111827);\n color: var(--sc-accent-foreground, #ffffff);\n }\n .choose {\n border: 2px solid var(--sc-color-primary, #111827);\n background: transparent;\n color: var(--sc-tile-title-color, #111827);\n }\n .sold-out {\n border: 1px dashed var(--sc-tile-text-color, #4b5563);\n color: var(--sc-tile-text-color, #4b5563);\n }\n `,\n ];\n\n declare _fits: Record<string, Fit>;\n private readonly _asked = new Set<string>();\n\n constructor() {\n super();\n this._fits = {};\n }\n\n protected override updated(changed: PropertyValues): void {\n if (changed.has('view') || changed.has('cart')) void this._checkVariants();\n }\n\n /** Ask the platform how many variants each product has in stock, once per product. */\n private async _checkVariants(): Promise<void> {\n const cart = this.cart();\n const view = this.view;\n if (!view || !cart?.fetchVariants) return;\n const pending = view.options.map((o) => o.product.handle).filter((h) => !this._asked.has(h));\n if (!pending.length) return;\n for (const handle of pending) this._asked.add(handle);\n const fits: Record<string, Fit> = {};\n await Promise.all(\n pending.map(async (handle) => {\n fits[handle] = await checkFit(cart, handle);\n })\n );\n this._fits = { ...this._fits, ...fits };\n }\n\n override render() {\n const view = this.view;\n if (!view) return nothing;\n return html`<p class=\"eyebrow\">${view.label}</p>\n <h2 class=\"headline\" tabindex=\"-1\" data-heading data-slot=\"headline\">${this.slotText('headline', view.headline)}</h2>\n <ul class=\"legend\">\n ${view.tradeoffs.map((t) => html`<li><img src=${t.image} alt=\"\" /><span>${t.label}</span></li>`)}\n </ul>\n <ul class=\"options\" data-count=${view.options.length}>\n ${view.options.map((o) => this._option(view, o))}\n </ul>`;\n }\n\n private _option(view: CompareView, o: Option) {\n const handle = o.product.handle;\n const fit: Fit = this._fits[handle] ?? (this.cart()?.fetchVariants ? 'checking' : 'choose');\n const path = `options.${handle}.reason`;\n const price = formatPrice(o.product.price, o.product.currency);\n return html`<li class=\"option\" ?data-best=${o.best}>\n <div class=\"photo\">\n ${picture(o.product.image_url, o.product.name)}\n ${o.best ? html`<span class=\"best\">${this.copy.best_fit}</span>` : nothing}\n <div class=\"on-photo\">\n <button\n class=\"name\"\n aria-disabled=${this.busyAttr()}\n @click=${() => this.emitTap({ kind: 'product', handle: o.product.handle })}\n >\n ${o.product.name}\n </button>\n ${price ? html`<span class=\"price\">${price}</span>` : nothing}\n </div>\n </div>\n <div class=\"body\">\n <div class=\"meters\">\n ${view.tradeoffs.map((t) => {\n const value = o.values[t.id] ?? 0;\n return html`<div class=\"meter\">\n <img src=${t.image} alt=${t.label} />\n ${dots(value, `${value} of 3`)}\n </div>`;\n })}\n </div>\n <p class=\"reason\" data-slot=${path}>${this.slotText(path, o.reason)}</p>\n ${this._action(fit, view.item_id, handle, o.product.name)}\n </div>\n </li>`;\n }\n\n /** Visible words first (WCAG 2.5.3), then the product: three of these sit side by side. */\n private _action(fit: Fit, itemId: string, handle: string, name: string) {\n if (fit === 'soldout') return html`<span class=\"sold-out\">Sold out</span>`;\n if (fit === 'choose')\n return html`<button\n class=\"choose\"\n aria-label=${`Choose options: ${name}`}\n aria-disabled=${this.busyAttr()}\n @click=${() => this.emitTap({ kind: 'product', handle })}\n >\n Choose options\n </button>`;\n if (fit === 'checking')\n return html`<button class=\"add\" aria-disabled=\"true\">Checking stock</button>`;\n return html`<button\n class=\"add\"\n aria-label=${`Add to cart: ${name}`}\n aria-disabled=${this.busyAttr()}\n @click=${() => this.emitPick(itemId, handle)}\n >\n Add to cart\n </button>`;\n }\n}\n\nif (!customElements.get(GUIDE_COMPARE)) {\n customElements.define(GUIDE_COMPARE, GuideCompare);\n}\n", "/**\n * The shared shape of every guided page template (plan 2; plans 3 and 4 build on it).\n *\n * A template is a Lit element in its own open shadow root. The frame sets:\n * .view the template's payload (one ShownTemplate's view)\n * .turnId the turn it belongs to\n * .moveId the move it shows\n * .cart the cart adapter getter (returns null on a platform without one)\n * .busy true while a tap is in flight: controls go aria-disabled, never disabled\n * .copy the store's words (GuideView.copy, defaults filled); a template writes no domain word\n * and listens for `guide-tap` (detail: the tap body) and `guide-pick`\n * (detail: { itemId, handle, variantId }), both bubbling and composed.\n * variantId is null unless the shopper chose one (product detail, plan 3).\n *\n * Text the words stream can rewrite sits in an element with\n * `data-slot=\"<path>\"` whose only content is that one text binding, so it can\n * be filled in place without disturbing Lit (plan 4's fillSlot), or replaced\n * with setSlot(). Until then the payload's fallback shows.\n *\n * The heading carries `data-heading` and tabindex=\"-1\": the frame focuses it\n * when the shopper's own tap brings the template in, never on arrival.\n */\nimport { ContainerSizeController } from '@syntrologie/sdk-contracts';\nimport { LitElement, type PropertyDeclarations } from 'lit';\nimport type { GuideCart } from '../cart';\nimport { DEFAULT_COPY, type GuideCopy } from '../copy';\nimport type { GuideTap } from '../guide-client';\n\nexport const GUIDE_TAP_EVENT = 'guide-tap';\nexport const GUIDE_PICK_EVENT = 'guide-pick';\nexport const GUIDE_TURN_EVENT = 'guide-turn';\n\nexport interface GuidePickDetail {\n itemId: string;\n handle: string;\n /** The variant the shopper chose; null lets the platform pick (only ever with one in stock). */\n variantId: string | null;\n}\n\nexport interface GuideTurnDetail {\n turnId: string;\n}\n\nexport class GuideTemplate<V> extends LitElement {\n static override properties: PropertyDeclarations = {\n view: { attribute: false },\n turnId: { attribute: false },\n moveId: { attribute: false },\n cart: { attribute: false },\n busy: { type: Boolean },\n copy: { attribute: false },\n _slots: { state: true },\n };\n\n declare view: V | null;\n declare turnId: string;\n declare moveId: string;\n declare cart: () => GuideCart | null;\n declare busy: boolean;\n declare copy: GuideCopy;\n declare _slots: Record<string, string>;\n\n /** Each template lays itself out from its own width (data-size), never the window's. */\n protected readonly size = new ContainerSizeController(this);\n\n constructor() {\n super();\n this.view = null;\n this.turnId = '';\n this.moveId = '';\n this.cart = () => null;\n this.busy = false;\n this.copy = DEFAULT_COPY;\n this._slots = {};\n }\n\n /** Replace one slot's text (the words stream). A path this template does not draw is kept and never shown. */\n setSlot(path: string, text: string): void {\n this._slots = { ...this._slots, [path]: text };\n }\n\n /** Focus this template's heading. The frame calls it only after a shopper's own tap. */\n async focusHeading(): Promise<void> {\n await this.updateComplete;\n this.renderRoot.querySelector<HTMLElement>('[data-heading]')?.focus({ preventScroll: true });\n }\n\n protected slotText(path: string, fallback: string | null | undefined): string {\n return this._slots[path] ?? fallback ?? '';\n }\n\n protected busyAttr(): 'true' | 'false' {\n return this.busy ? 'true' : 'false';\n }\n\n protected emitTap(tap: GuideTap): void {\n if (this.busy) return;\n this.dispatchEvent(\n new CustomEvent<GuideTap>(GUIDE_TAP_EVENT, { detail: tap, bubbles: true, composed: true })\n );\n }\n\n protected emitPick(itemId: string, handle: string, variantId: string | null = null): void {\n if (this.busy) return;\n this.dispatchEvent(\n new CustomEvent<GuidePickDetail>(GUIDE_PICK_EVENT, {\n detail: { itemId, handle, variantId },\n bubbles: true,\n composed: true,\n })\n );\n }\n}\n", "/**\n * The guide's variant rule (spec, \"The templates\"), shared by every template\n * with a cart action: a template adds directly only when exactly one variant\n * is in stock. The platform picks a variant when none is named, and a guess is\n * a wrong size, so several in stock (or an answer we cannot read) sends the\n * shopper to choose; none in stock says sold out.\n */\nimport type { GuideCart, GuideVariants } from '../cart';\n\nexport type Fit = 'add' | 'choose' | 'soldout' | 'checking';\n\n/** What one variants answer allows. */\nexport function fitOf(payload: GuideVariants): Exclude<Fit, 'checking'> {\n const inStock = payload.variants.filter((v) => v.in_stock).length;\n return inStock === 0 ? 'soldout' : inStock === 1 ? 'add' : 'choose';\n}\n\n/** Ask the platform, never throwing: anything we cannot read is 'choose'. */\nexport async function checkFit(cart: GuideCart | null, handle: string): Promise<Fit> {\n if (!cart?.fetchVariants) return 'choose';\n try {\n return fitOf(await cart.fetchVariants(handle));\n } catch {\n return 'choose';\n }\n}\n", "import { standinInitial } from '@syntrologie/sdk-contracts';\nimport { html } from 'lit';\n\n/** A product or gear photo, or the brand stand-in with its initial when there is none. Decorative: the name is text nearby. */\nexport function picture(src: string | null | undefined, name: string) {\n return src\n ? html`<img src=${src} alt=\"\" loading=\"lazy\" />`\n : html`<span class=\"standin\" aria-hidden=\"true\">${standinInitial(name) ?? ''}</span>`;\n}\n\n/**\n * A price only with its currency; never a bare number a shopper could misread.\n * Zero is \"the catalog has no price\", never a free product, so it shows nothing.\n */\nexport function formatPrice(\n price: number | null | undefined,\n currency: string | null | undefined\n): string | null {\n if (price == null || !(price > 0) || !currency) return null;\n try {\n return new Intl.NumberFormat(undefined, { style: 'currency', currency }).format(price);\n } catch {\n return null;\n }\n}\n", "/**\n * Drawn parts shared by the templates that show products (compare, hero pick,\n * product detail): trade-off meters and the because-stamps. Pictures and\n * dots, never sentences (the visual bar).\n */\nimport { css, html, nothing } from 'lit';\nimport type { StampView, TradeoffView } from '../guide-client';\n\n/** Three dots, `value` of them filled; one image to a screen reader, named by `label`. */\nexport function dots(value: number, label: string) {\n return html`<span class=\"dots\" role=\"img\" aria-label=${label}\n >${[1, 2, 3].map((n) => html`<span class=${n <= value ? 'dot on' : 'dot'}></span>`)}</span\n >`;\n}\n\n/** A trade-off drawn: its icon, its short label, and the dots (\"Grip on rock: 2 of 3\"). */\nexport function meter(t: TradeoffView, value: number) {\n return html`<div class=\"meter\">\n <img src=${t.image} alt=\"\" />\n <span class=\"meter-label\" aria-hidden=\"true\">${t.label}</span>\n ${dots(value, `${t.label}: ${value} of 3`)}\n </div>`;\n}\n\n/** The stamps that put this product here, as the frame's stamps: pictures on a white mount. Named in the store's words. */\nexport function stampRow(stamps: StampView[], label: string) {\n if (!stamps.length) return nothing;\n return html`<ul class=\"because\" aria-label=${label}>\n ${stamps.map((s) => html`<li><img class=\"stamp\" src=${s.image} alt=${s.label} /></li>`)}\n </ul>`;\n}\n\n/** Styles for the parts above. Tokens only; literals are fallbacks. */\nexport const partStyles = css`\n /* Read by a screen reader, not drawn. */\n .sr {\n position: absolute;\n width: 1px;\n height: 1px;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n }\n .dots {\n display: inline-flex;\n gap: 4px;\n flex: none;\n }\n .dot {\n width: 10px;\n height: 10px;\n border-radius: 50%;\n /* An empty dot is a ring in the text color, so it stays visible (WCAG 1.4.11). */\n box-shadow: inset 0 0 0 1.5px var(--sc-tile-text-color, #4b5563);\n }\n .dot.on {\n background: var(--sc-color-primary, #111827);\n box-shadow: none;\n }\n .meter {\n display: flex;\n align-items: center;\n gap: 10px;\n min-height: 36px;\n /* The dots stay near their label on a wide body. */\n max-width: 420px;\n }\n .meter img {\n flex: none;\n width: 36px;\n height: 36px;\n border-radius: 8px;\n }\n .meter-label {\n flex: 1;\n min-width: 0;\n font-weight: 700;\n font-size: 0.95rem;\n line-height: 1.2;\n }\n .because {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n }\n /* Forced colors (Windows High Contrast) drops backgrounds and box-shadows, which\n erased every dot: a filled dot is CanvasText, an empty one a CanvasText ring.\n Filled buttons lose their fill too, so they get a border (:host raises them over\n each template's own border: 0). */\n @media (forced-colors: active) {\n .dot {\n border: 1.5px solid CanvasText;\n box-shadow: none;\n }\n .dot.on {\n background: CanvasText;\n forced-color-adjust: none;\n }\n :host .add,\n :host .choose,\n :host .ghost {\n border: 2px solid ButtonText;\n }\n }\n .because .stamp {\n width: 40px;\n height: 48px;\n padding: 2px;\n border: 2px solid var(--sc-color-primary, #111827);\n border-radius: 5px;\n /* A stamp's own white mount, in both schemes (the frame's stamps). */\n background: #ffffff;\n box-shadow:\n inset 0 0 0 2px #ffffff,\n 0 2px 6px rgb(0 0 0 / 0.18);\n object-fit: cover;\n }\n`;\n", "import { IMAGE_STANDIN_BACKGROUND } from '@syntrologie/sdk-contracts';\nimport { css, unsafeCSS } from 'lit';\n\n/**\n * Styles every template shares. Templates are full-screen compositions (the\n * visual bar): pictures carry the meaning, one bold line of voice, no rows.\n * Every value reads a --sc-* token first; literals are fallbacks only.\n */\nexport const templateStyles = css`\n :host {\n display: block;\n box-sizing: border-box;\n min-height: 100%;\n padding: 20px 16px 28px;\n color: var(--sc-tile-title-color, #111827);\n font-family: var(--sc-font-family, system-ui, sans-serif);\n }\n :host([data-size='medium']),\n :host([data-size='expanded']) {\n padding: 28px 32px 36px;\n }\n * {\n box-sizing: border-box;\n }\n ul {\n list-style: none;\n margin: 0;\n padding: 0;\n }\n button {\n font: inherit;\n color: inherit;\n cursor: pointer;\n }\n button[aria-disabled='true'] {\n cursor: progress;\n }\n [data-heading] {\n outline: none;\n }\n button:focus-visible,\n a:focus-visible,\n [data-heading]:focus-visible {\n outline: 3px solid var(--sc-color-primary, #111827);\n outline-offset: 3px;\n }\n img {\n display: block;\n width: 100%;\n height: 100%;\n object-fit: cover;\n }\n .eyebrow {\n margin: 0 0 6px;\n font-size: 0.8rem;\n font-weight: 700;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n color: var(--sc-tile-text-color, #4b5563);\n }\n .headline {\n margin: 0 0 16px;\n font-size: 1.75rem;\n line-height: 1.1;\n font-weight: 800;\n letter-spacing: -0.01em;\n }\n :host([data-size='medium']) .headline,\n :host([data-size='expanded']) .headline {\n font-size: 2.25rem;\n }\n .voice {\n margin: -6px 0 18px;\n font-size: 1.05rem;\n font-weight: 700;\n line-height: 1.3;\n color: var(--sc-color-primary, #111827);\n }\n .standin {\n display: grid;\n place-items: center;\n width: 100%;\n height: 100%;\n background: ${unsafeCSS(IMAGE_STANDIN_BACKGROUND)};\n color: var(--sc-launcher-color, #ffffff);\n font-size: 2.5rem;\n font-weight: 800;\n }\n .on-photo {\n position: absolute;\n left: 0;\n right: 0;\n bottom: 0;\n padding: 36px 14px 12px;\n color: #ffffff;\n font-weight: 800;\n line-height: 1.15;\n text-align: left;\n background: linear-gradient(transparent, rgb(0 0 0 / 0.68));\n }\n @media (forced-colors: active) {\n .on-photo {\n background: Canvas;\n color: CanvasText;\n }\n button {\n border: 1px solid ButtonText;\n }\n }\n`;\n", "/**\n * <guide-hero-pick>: one product, big and confident, when one clearly fits.\n *\n * The photo takes the template, with the name and price on it and the stamps\n * that put it there; one bold line says why it fits; its strengths are drawn\n * as meters. The cart action follows the guide's variant rule (./fit): add\n * directly only with exactly one variant in stock, otherwise open product\n * detail. Actions leave as guide-pick / guide-tap for the frame to send.\n */\nimport { css, html, nothing, type PropertyDeclarations, type PropertyValues } from 'lit';\nimport type { HeroPickView } from '../guide-client';\nimport { GuideTemplate } from './base';\nimport { checkFit, type Fit } from './fit';\nimport { formatPrice, picture } from './format';\nimport { meter, partStyles, stampRow } from './parts';\nimport { productStyles } from './product-styles';\nimport { templateStyles } from './styles';\n\nexport const GUIDE_HERO_PICK = 'guide-hero-pick';\n\nexport class GuideHeroPick extends GuideTemplate<HeroPickView> {\n static override properties: PropertyDeclarations = { _fit: { state: true } };\n static override styles = [\n templateStyles,\n partStyles,\n productStyles,\n css`\n :host {\n display: flex;\n flex-direction: column;\n }\n .hero {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 16px;\n }\n .photo {\n flex: 1 1 auto;\n min-height: 260px;\n }\n .body {\n display: flex;\n flex-direction: column;\n gap: 12px;\n }\n .strengths {\n display: grid;\n gap: 8px 20px;\n }\n :host([data-size='medium']) .hero,\n :host([data-size='expanded']) .hero {\n display: grid;\n grid-template-columns: minmax(0, 1.2fr) minmax(300px, 1fr);\n align-items: center;\n gap: 44px;\n }\n :host([data-size='medium']) .photo,\n :host([data-size='expanded']) .photo {\n align-self: stretch;\n min-height: 520px;\n }\n :host([data-size='medium']) .body,\n :host([data-size='expanded']) .body {\n gap: 20px;\n }\n `,\n ];\n\n declare _fit: Fit;\n private _fitSeq = 0;\n\n constructor() {\n super();\n this._fit = 'checking';\n }\n\n protected override willUpdate(changed: PropertyValues): void {\n if (changed.has('view') || changed.has('cart')) void this._checkFit();\n }\n\n private async _checkFit(): Promise<void> {\n const seq = ++this._fitSeq;\n const handle = this.view?.product.handle;\n if (!handle) return;\n this._fit = 'checking';\n const fit = await checkFit(this.cart(), handle);\n if (seq === this._fitSeq) this._fit = fit;\n }\n\n private _openDetail = (): void => {\n if (this.view) this.emitTap({ kind: 'product', handle: this.view.product.handle });\n };\n\n private _add = (): void => {\n if (this.view && this._fit === 'add') {\n this.emitPick(this.view.item_id, this.view.product.handle);\n }\n };\n\n override render() {\n const v = this.view;\n if (!v) return nothing;\n const p = v.product;\n const price = formatPrice(p.price, p.currency);\n return html`<article class=\"hero\">\n <div class=\"photo\">\n ${picture(p.image_url, p.name)}\n <span class=\"best\">${this.copy.best_fit}</span>\n ${stampRow(v.because, this.copy.because_label)}\n <div class=\"plate\">\n <h2 tabindex=\"-1\" data-heading>${p.name}</h2>\n ${price ? html`<span class=\"price\">${price}</span>` : nothing}\n </div>\n </div>\n <div class=\"body\">\n <p class=\"eyebrow\">${v.label}</p>\n <p class=\"reason\" data-slot=\"reason\">${this.slotText('reason', v.reason)}</p>\n <ul class=\"strengths\" aria-label=\"How it does\">\n ${v.tradeoffs.map((t) => html`<li>${meter(t, v.values[t.id] ?? 0)}</li>`)}\n </ul>\n <div class=\"actions\">\n ${this._action()}\n ${\n this._fit === 'choose'\n ? nothing\n : html`<button\n class=\"ghost\"\n aria-label=${this._named('See details')}\n aria-disabled=${this.busyAttr()}\n @click=${this._openDetail}\n >\n See details\n </button>`\n }\n </div>\n </div>\n </article>`;\n }\n\n /** The visible words first (WCAG 2.5.3), then which product: several heroes can share a feed. */\n private _named(action: string): string {\n return `${action}: ${this.view?.product.name ?? ''}`;\n }\n\n private _action() {\n switch (this._fit) {\n case 'checking':\n return html`<button class=\"add\" aria-disabled=\"true\">Checking stock</button>`;\n case 'soldout':\n return html`<span class=\"sold-out\">Sold out</span>`;\n case 'add':\n return html`<button\n class=\"add\"\n aria-label=${this._named('Add to cart')}\n aria-disabled=${this.busyAttr()}\n @click=${this._add}\n >\n Add to cart\n </button>`;\n default:\n return html`<button\n class=\"add\"\n aria-label=${this._named('Choose options')}\n aria-disabled=${this.busyAttr()}\n @click=${this._openDetail}\n >\n Choose options\n </button>`;\n }\n }\n}\n\nif (!customElements.get(GUIDE_HERO_PICK)) {\n customElements.define(GUIDE_HERO_PICK, GuideHeroPick);\n}\n", "import { css } from 'lit';\n\n/**\n * The one-product look shared by hero pick and product detail: the packshot on\n * its own white paper filling the photo, the name on a solid caption plate\n * (a fade over white store photos reads as a grey smudge), the best tag, the\n * stamps pinned to the photo, the bold reason line and the cart actions.\n */\nexport const productStyles = css`\n .photo {\n position: relative;\n overflow: hidden;\n border-radius: var(--sc-border-radius, 18px);\n /* Store photos are shot on white: their own paper, in both schemes. */\n background: #ffffff;\n box-shadow: 0 12px 30px rgb(0 0 0 / 0.16);\n }\n /* Out of flow, so a tall packshot never sizes the photo; contained, never cropped. */\n .photo img,\n .photo .standin {\n position: absolute;\n inset: 0;\n }\n /* The packshot clears the tag and stops at the plate. */\n .photo > img {\n object-fit: contain;\n padding: 44px 16px 80px;\n }\n .photo .because {\n position: absolute;\n top: 14px;\n right: 14px;\n justify-content: flex-end;\n max-width: 60%;\n }\n .photo .because img {\n position: static;\n }\n .best {\n position: absolute;\n top: 16px;\n left: 16px;\n padding: 5px 12px;\n border-radius: 999px;\n background: var(--sc-color-primary, #111827);\n color: var(--sc-accent-foreground, #ffffff);\n font-size: 0.75rem;\n font-weight: 800;\n letter-spacing: 0.05em;\n text-transform: uppercase;\n }\n .plate {\n position: absolute;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n align-items: flex-end;\n justify-content: space-between;\n gap: 12px;\n padding: 12px 16px 14px;\n background: rgb(12 20 18 / 0.88);\n color: #ffffff;\n }\n .plate h2 {\n margin: 0;\n font-size: 1.15rem;\n line-height: 1.15;\n font-weight: 800;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n }\n .plate .price {\n flex: none;\n font-size: 1.15rem;\n font-weight: 800;\n }\n :host([data-size='medium']) .plate,\n :host([data-size='expanded']) .plate {\n padding: 18px 24px 20px;\n }\n :host([data-size='medium']) .plate h2,\n :host([data-size='expanded']) .plate h2 {\n font-size: 1.65rem;\n -webkit-line-clamp: 3;\n }\n :host([data-size='medium']) .photo > img,\n :host([data-size='expanded']) .photo > img {\n padding: 64px 40px 128px;\n }\n .eyebrow {\n margin: 0;\n }\n .reason {\n margin: 0;\n font-size: 1.2rem;\n line-height: 1.25;\n font-weight: 800;\n letter-spacing: -0.005em;\n color: var(--sc-color-primary, #111827);\n }\n :host([data-size='medium']) .reason,\n :host([data-size='expanded']) .reason {\n font-size: 1.85rem;\n line-height: 1.15;\n }\n .actions {\n display: flex;\n gap: 10px;\n }\n .add,\n .ghost,\n .sold-out {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-height: 52px;\n padding: 0 22px;\n border-radius: 999px;\n font-weight: 800;\n font-size: 1rem;\n text-decoration: none;\n }\n .add {\n flex: 1.4;\n border: 0;\n background: var(--sc-color-primary, #111827);\n color: var(--sc-accent-foreground, #ffffff);\n }\n .add[aria-disabled='true'] {\n opacity: 0.55;\n }\n .ghost {\n flex: 1;\n border: 2px solid var(--sc-color-primary, #111827);\n background: transparent;\n color: var(--sc-tile-title-color, #111827);\n }\n .sold-out {\n flex: 1.4;\n border: 1px dashed var(--sc-tile-text-color, #4b5563);\n color: var(--sc-tile-text-color, #4b5563);\n }\n :host([data-size='medium']) .actions .add,\n :host([data-size='expanded']) .actions .add,\n :host([data-size='medium']) .actions .sold-out,\n :host([data-size='expanded']) .actions .sold-out {\n flex: none;\n min-width: 200px;\n }\n :host([data-size='medium']) .actions .ghost,\n :host([data-size='expanded']) .actions .ghost {\n flex: none;\n }\n @media (forced-colors: active) {\n .plate {\n background: Canvas;\n color: CanvasText;\n }\n }\n`;\n", "/**\n * <guide-pick-from-pictures>: one question, answered by tapping a picture\n * (the postcard feed). Full-bleed photos with a short title on each; the first\n * is the large one of the composition. A tap sends the postcard; the frame\n * does the rest.\n */\nimport type { SizeClass } from '@syntrologie/sdk-contracts';\nimport { css, html, nothing } from 'lit';\nimport type { PickFromPicturesView } from '../guide-client';\nimport { GuideTemplate } from './base';\nimport { templateStyles } from './styles';\n\nexport const GUIDE_PICK_FROM_PICTURES = 'guide-pick-from-pictures';\n\n/**\n * How many columns the last picture spans so the grid's last row is full.\n * The first picture takes a whole row on a phone (2 columns) and a 2x2 block\n * wider (3 or 4 columns); every other picture takes one cell.\n */\nexport function lastSpan(count: number, size: SizeClass): number {\n if (count <= 1) return 1;\n const columns = size === 'expanded' ? 4 : size === 'medium' ? 3 : 2;\n const cells = (size === 'compact' ? 2 : 4) + count - 1;\n const rest = cells % columns;\n return rest === 0 ? 1 : columns - rest + 1;\n}\n\nexport class GuidePickFromPictures extends GuideTemplate<PickFromPicturesView> {\n static override styles = [\n templateStyles,\n css`\n .choices {\n display: grid;\n gap: 12px;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n grid-auto-rows: 11rem;\n }\n .choices li:first-child {\n grid-column: span 2;\n }\n :host([data-size='medium']) .choices {\n grid-template-columns: repeat(3, minmax(0, 1fr));\n grid-auto-rows: 12rem;\n gap: 16px;\n }\n :host([data-size='expanded']) .choices {\n grid-template-columns: repeat(4, minmax(0, 1fr));\n grid-auto-rows: 13rem;\n gap: 18px;\n }\n :host([data-size='medium']) .choices li:first-child,\n :host([data-size='expanded']) .choices li:first-child {\n grid-column: span 2;\n grid-row: span 2;\n }\n /* The last picture closes the last row (lastSpan); a class, not a\n style attribute, so a strict host CSP cannot drop it. */\n .choices li.span-2 {\n grid-column: span 2;\n }\n .choices li.span-3 {\n grid-column: span 3;\n }\n .choices li.span-4 {\n grid-column: span 4;\n }\n .choice {\n position: relative;\n display: block;\n width: 100%;\n height: 100%;\n padding: 0;\n border: 0;\n border-radius: var(--sc-border-radius, 18px);\n overflow: hidden;\n background: var(--sc-content-search-background, #e7e2d6);\n box-shadow: 0 8px 20px rgb(0 0 0 / 0.12);\n }\n .choice .on-photo {\n font-size: 1.1rem;\n }\n li:first-child .choice .on-photo {\n font-size: 1.5rem;\n }\n @media (prefers-reduced-motion: no-preference) {\n .choice img {\n transition: transform 300ms ease;\n }\n .choice:hover img {\n transform: scale(1.04);\n }\n }\n `,\n ];\n\n override render() {\n const view = this.view;\n if (!view) return nothing;\n const span = lastSpan(view.choices.length, this.size.value);\n const last = view.choices.length - 1;\n return html`<h2 class=\"headline\" tabindex=\"-1\" data-heading data-slot=\"question\">${this.slotText('question', view.question)}</h2>\n <ul class=\"choices\">\n ${view.choices.map(\n (c, i) => html`<li class=${i === last && span > 1 ? `span-${span}` : nothing}>\n <button\n class=\"choice\"\n aria-label=${c.title}\n aria-disabled=${this.busyAttr()}\n @click=${() => this.emitTap({ kind: 'postcard', postcard_id: c.id })}\n >\n <img src=${c.image} alt=\"\" loading=\"lazy\" /><span class=\"on-photo\" aria-hidden=\"true\">${c.title}</span>\n </button>\n </li>`\n )}\n </ul>`;\n }\n}\n\nif (!customElements.get(GUIDE_PICK_FROM_PICTURES)) {\n customElements.define(GUIDE_PICK_FROM_PICTURES, GuidePickFromPictures);\n}\n", "/**\n * <guide-product-detail>: the personalized product page as a feed template.\n *\n * The product big with why it fits, the options to choose (native radios in a\n * fieldset per option), every trade-off drawn, and the product page's\n * other-products module as picture tiles that open their own detail. It never\n * guesses a variant: Add to cart needs a fully chosen, in-stock variant, or a\n * product with exactly one in stock. When the platform cannot read variants it\n * links to the store's own product page. The back button asks the frame to\n * go back (ProductDetailHistory turns it into history.back()).\n */\nimport { css, html, nothing, type PropertyDeclarations, type PropertyValues } from 'lit';\nimport { styleMap } from 'lit/directives/style-map.js';\nimport type { GuideVariants } from '../cart';\nimport type { OtherProductTile, ProductDetailView } from '../guide-client';\nimport { GuideTemplate } from './base';\nimport { formatPrice, picture } from './format';\nimport { meter, partStyles, stampRow } from './parts';\nimport { productStyles } from './product-styles';\nimport { templateStyles } from './styles';\n\nexport const GUIDE_PRODUCT_DETAIL = 'guide-product-detail';\nexport const GUIDE_DETAIL_BACK_EVENT = 'guide-detail-back';\n\ntype Variants =\n | { state: 'checking' }\n | { state: 'unreadable' }\n | { state: 'ready'; data: GuideVariants };\ntype Module = ProductDetailView['modules'][number];\n\n/** A plain CSS color (hex, rgb()/hsl(), or a name): the only swatch value that reaches a style. */\nconst COLOR = /^(#[0-9a-f]{3,8}|(rgb|rgba|hsl|hsla)\\([0-9.,%\\s/]+\\)|[a-z]{3,20})$/i;\n\n/** A tile's price, unless it is zero: the catalog without a price, never a free product. */\nfunction tilePrice(t: OtherProductTile): string | null {\n if (!t.price || t.price_cents === 0) return null;\n return /[1-9]/.test(t.price) ? t.price : null;\n}\n\nexport class GuideProductDetail extends GuideTemplate<ProductDetailView> {\n static override properties: PropertyDeclarations = {\n _variants: { state: true },\n _choice: { state: true },\n };\n static override styles = [\n templateStyles,\n partStyles,\n productStyles,\n css`\n .detail {\n display: grid;\n gap: 20px;\n }\n /* The first screen on a phone holds the photo, the reason and the buy row. */\n .photo {\n height: 340px;\n }\n .body {\n display: flex;\n flex-direction: column;\n gap: 22px;\n min-width: 0;\n }\n h3 {\n margin: 0 0 10px;\n font-size: 0.8rem;\n font-weight: 800;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n color: var(--sc-tile-text-color, #4b5563);\n }\n .buy {\n display: flex;\n flex-direction: column;\n gap: 14px;\n }\n .buy .add,\n .buy .sold-out {\n flex: none;\n }\n .options {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n margin: 0;\n padding: 0;\n border: 0;\n min-width: 0;\n }\n .options legend {\n margin-bottom: 10px;\n padding: 0;\n font-weight: 800;\n }\n .pill {\n position: relative;\n display: inline-flex;\n }\n .pill input {\n position: absolute;\n inset: 0;\n margin: 0;\n opacity: 0;\n cursor: pointer;\n }\n .pill-face {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n min-width: 48px;\n min-height: 44px;\n padding: 0 16px;\n border-radius: 999px;\n border: 2px solid var(--sc-content-divider-color, rgb(0 0 0 / 0.2));\n background: var(--sc-content-search-background, transparent);\n font-weight: 700;\n }\n .pill input:checked + .pill-face {\n border-color: var(--sc-color-primary, #111827);\n background: var(--sc-color-primary, #111827);\n color: var(--sc-accent-foreground, #ffffff);\n }\n .pill input:focus-visible + .pill-face {\n outline: 3px solid var(--sc-color-primary, #111827);\n outline-offset: 3px;\n }\n .pill.out .pill-face {\n opacity: 0.5;\n text-decoration: line-through;\n }\n .pill.out input {\n cursor: not-allowed;\n }\n .swatch {\n width: 20px;\n height: 20px;\n border-radius: 50%;\n box-shadow:\n inset 0 0 0 1px rgb(0 0 0 / 0.25),\n 0 0 0 2px #ffffff;\n }\n .hint {\n margin: -4px 0 0;\n font-size: 0.9rem;\n font-weight: 600;\n color: var(--sc-tile-text-color, #4b5563);\n }\n .meters {\n display: grid;\n gap: 8px 24px;\n }\n .tiles {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 12px;\n }\n .tile {\n position: relative;\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n padding: 0;\n border: 0;\n border-radius: var(--sc-border-radius, 16px);\n overflow: hidden;\n text-align: left;\n background: var(--sc-content-search-background, #f4f4f5);\n box-shadow: 0 6px 16px rgb(0 0 0 / 0.1);\n }\n .tile .pic {\n position: relative;\n display: block;\n aspect-ratio: 1 / 1;\n background: #ffffff;\n }\n .tile .pic img {\n position: absolute;\n inset: 0;\n object-fit: contain;\n padding: 10%;\n }\n .tile .pic .standin {\n position: absolute;\n inset: 0;\n }\n .tile .tile-price {\n position: absolute;\n top: 8px;\n right: 8px;\n padding: 3px 9px;\n border-radius: 999px;\n background: rgb(12 20 18 / 0.88);\n color: #ffffff;\n font-size: 0.8rem;\n font-weight: 800;\n }\n .tile .name {\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n /* Margin, not padding: a clamped line would show through padding. */\n margin: 10px 12px 4px;\n font-weight: 800;\n font-size: 0.95rem;\n line-height: 1.2;\n color: var(--sc-tile-title-color, #111827);\n }\n .tile .why {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-top: auto;\n padding: 4px 12px 12px;\n font-size: 0.85rem;\n font-weight: 700;\n color: var(--sc-color-primary, #111827);\n }\n .tile .why img {\n flex: none;\n width: 24px;\n height: 24px;\n border-radius: 6px;\n }\n .back {\n align-self: flex-start;\n flex: none;\n }\n :host([data-size='medium']) .detail,\n :host([data-size='expanded']) .detail {\n grid-template-columns: minmax(0, 1.1fr) minmax(320px, 1fr);\n align-items: start;\n gap: 44px;\n }\n :host([data-size='medium']) .photo,\n :host([data-size='expanded']) .photo {\n position: sticky;\n top: 0;\n height: 600px;\n }\n :host([data-size='medium']) .tiles,\n :host([data-size='expanded']) .tiles {\n gap: 16px;\n }\n `,\n ];\n\n declare _variants: Variants;\n declare _choice: Record<string, string>;\n private _seq = 0;\n private readonly _warned = new Set<string>();\n\n constructor() {\n super();\n this._variants = { state: 'checking' };\n this._choice = {};\n }\n\n protected override willUpdate(changed: PropertyValues): void {\n if (changed.has('view')) {\n this._choice = {};\n void this._loadVariants();\n }\n }\n\n private async _loadVariants(): Promise<void> {\n const seq = ++this._seq;\n const handle = this.view?.product.handle;\n const cart = this.cart();\n if (!handle || !cart?.fetchVariants) {\n this._variants = { state: 'unreadable' };\n return;\n }\n this._variants = { state: 'checking' };\n try {\n const data = await cart.fetchVariants(handle);\n if (seq === this._seq) this._variants = { state: 'ready', data };\n } catch {\n if (seq === this._seq) this._variants = { state: 'unreadable' };\n }\n }\n\n private _add(variantId: string | null): void {\n const v = this.view;\n if (v) this.emitPick(v.item_id, v.product.handle, variantId);\n }\n\n private _back = (): void => {\n this.dispatchEvent(\n new CustomEvent(GUIDE_DETAIL_BACK_EVENT, { detail: {}, bubbles: true, composed: true })\n );\n };\n\n override render() {\n const v = this.view;\n if (!v) return nothing;\n const p = v.product;\n const price = formatPrice(p.price, p.currency);\n return html`<article class=\"detail\">\n <div class=\"photo\">\n ${picture(p.image_url, p.name)}\n ${v.best ? html`<span class=\"best\">${this.copy.best_fit}</span>` : nothing}\n ${stampRow(v.because, this.copy.because_label)}\n <div class=\"plate\">\n <h2 tabindex=\"-1\" data-heading>${p.name}</h2>\n ${price ? html`<span class=\"price\">${price}</span>` : nothing}\n </div>\n </div>\n <div class=\"body\">\n <p class=\"reason\" data-slot=\"reason\">${this.slotText('reason', v.reason)}</p>\n ${this._buy(v)}\n <section aria-labelledby=\"how\">\n <h3 id=\"how\">How it does</h3>\n <ul class=\"meters\">\n ${v.tradeoffs.map((t) => html`<li>${meter(t, v.values[t.id] ?? 0)}</li>`)}\n </ul>\n </section>\n ${v.modules.map((m) => this._module(m))}\n <button class=\"ghost back\" @click=${this._back}>${this.copy.back_label}</button>\n </div>\n </article>`;\n }\n\n private _buy(v: ProductDetailView) {\n const s = this._variants;\n if (s.state === 'checking') {\n return html`<div class=\"buy\">\n <button class=\"add\" aria-disabled=\"true\">Checking stock</button>\n </div>`;\n }\n const choose = html`<div class=\"buy\">\n <a class=\"add\" href=${v.product.href}>Choose options</a>\n </div>`;\n if (s.state === 'unreadable') return choose;\n const inStock = s.data.variants.filter((x) => x.in_stock);\n if (inStock.length === 0) {\n return html`<div class=\"buy\"><span class=\"sold-out\">Sold out</span></div>`;\n }\n if (inStock.length === 1) {\n return html`<div class=\"buy\">\n <button\n class=\"add\"\n aria-label=${`Add to cart: ${v.product.name}`}\n aria-disabled=${this.busyAttr()}\n @click=${() => this._add(inStock[0].id ?? null)}\n >\n Add to cart\n </button>\n </div>`;\n }\n const attrs = s.data.attributes ?? [];\n if (!attrs.length) return choose;\n const chosen = inStock.find((x) =>\n attrs.every((a) => x.selection?.[a.key] === this._choice[a.key])\n );\n const ready = !!chosen && !this.busy;\n return html`<div class=\"buy\">\n ${attrs.map(\n (a) => html`<fieldset class=\"options\">\n <legend>${a.label}</legend>\n ${a.values.map((val) => {\n const available = inStock.some((x) => x.selection?.[a.key] === val.id);\n const color =\n val.swatch?.kind === 'color' && COLOR.test(val.swatch.value)\n ? val.swatch.value\n : null;\n return html`<label class=${available ? 'pill' : 'pill out'}>\n <input\n type=\"radio\"\n name=${a.key}\n .value=${val.id}\n .checked=${this._choice[a.key] === val.id}\n ?disabled=${!available}\n @change=${() => {\n this._choice = { ...this._choice, [a.key]: val.id };\n }}\n />\n <span class=\"pill-face\">\n ${\n color\n ? html`<span\n class=\"swatch\"\n style=${styleMap({ backgroundColor: color })}\n ></span>`\n : nothing\n }\n <span>${val.label}</span>${available ? nothing : html`<span class=\"sr\">, sold out</span>`}\n </span>\n </label>`;\n })}\n </fieldset>`\n )}\n <button\n class=\"add\"\n aria-label=${`Add to cart: ${v.product.name}`}\n aria-disabled=${ready ? 'false' : 'true'}\n aria-describedby=${chosen ? nothing : 'need-choice'}\n @click=${() => {\n if (chosen) this._add(chosen.id ?? null);\n }}\n >\n Add to cart\n </button>\n ${chosen ? nothing : html`<p id=\"need-choice\" class=\"hint\">Choose your options first.</p>`}\n </div>`;\n }\n\n private _module(m: Module) {\n if (m.kind !== 'other-products') {\n if (!this._warned.has(m.kind)) {\n this._warned.add(m.kind);\n console.warn(`[postcard-guide] product detail cannot draw a ${m.kind} module; left out`);\n }\n return nothing;\n }\n const icons = new Map((this.view?.tradeoffs ?? []).map((t) => [t.id, t]));\n return html`<section class=\"others\" aria-labelledby=\"others\">\n <h3 id=\"others\">${this.copy.also_label}</h3>\n <ul class=\"tiles\">\n ${m.payload.tiles.map((t) => {\n const icon = icons.get(t.badges?.[0] ?? '');\n const path = `modules.${m.kind}.tiles.${t.recommended_product_id}.why_blurb`;\n const price = tilePrice(t);\n return html`<li>\n <button\n class=\"tile\"\n aria-disabled=${this.busyAttr()}\n @click=${() => this.emitTap({ kind: 'product', handle: t.recommended_product_id })}\n >\n <span class=\"pic\">\n ${picture(t.image_url, t.name)}\n ${price ? html`<span class=\"tile-price\">${price}</span>` : nothing}\n </span>\n <span class=\"name\">${t.name}</span>\n <span class=\"why\">\n ${icon ? html`<img src=${icon.image} alt=\"\" />` : nothing}\n <span data-slot=${path}>${this.slotText(path, t.why_blurb)}</span>\n </span>\n </button>\n </li>`;\n })}\n </ul>\n </section>`;\n }\n}\n\nif (!customElements.get(GUIDE_PRODUCT_DETAIL)) {\n customElements.define(GUIDE_PRODUCT_DETAIL, GuideProductDetail);\n}\n", "/**\n * <guide-what-you-need>: what the shopper needs (the spread). Each item\n * is its picture with its name on it and the stamps that put it there; the\n * voice line says what matters most. A tap opens the item (the server answers\n * with a compare or a hero pick).\n */\nimport { css, html, nothing } from 'lit';\nimport type { WhatYouNeedView } from '../guide-client';\nimport { GuideTemplate } from './base';\nimport { picture } from './format';\nimport { templateStyles } from './styles';\n\nexport const GUIDE_WHAT_YOU_NEED = 'guide-what-you-need';\n\nexport class GuideWhatYouNeed extends GuideTemplate<WhatYouNeedView> {\n static override styles = [\n templateStyles,\n css`\n .items {\n display: grid;\n gap: 12px;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n }\n :host([data-size='medium']) .items {\n grid-template-columns: repeat(3, minmax(0, 1fr));\n gap: 16px;\n }\n :host([data-size='expanded']) .items {\n grid-template-columns: repeat(4, minmax(0, 1fr));\n gap: 18px;\n }\n .item {\n display: block;\n width: 100%;\n padding: 0;\n border: 0;\n border-radius: var(--sc-border-radius, 16px);\n overflow: hidden;\n background: var(--sc-tile-background, #ffffff);\n box-shadow: 0 6px 16px rgb(0 0 0 / 0.1);\n text-align: left;\n }\n .pic {\n position: relative;\n display: block;\n aspect-ratio: 4 / 5;\n /* Store photos are shot on white: their own paper, in both schemes. */\n background: #ffffff;\n }\n .pic img {\n object-fit: contain;\n padding: 8% 8% 30%;\n }\n /* Store photos sit on white: a fade to black over white reads as a grey\n smudge, so the name sits on a solid caption plate instead. */\n .item .on-photo {\n padding: 10px 12px 12px;\n background: rgb(12 20 18 / 0.86);\n }\n .on-photo .name {\n display: block;\n font-size: 1.05rem;\n }\n .on-photo .reason {\n display: block;\n margin-top: 4px;\n font-size: 0.85rem;\n font-weight: 600;\n opacity: 0.92;\n }\n .why {\n position: absolute;\n top: 8px;\n right: 8px;\n display: flex;\n gap: 4px;\n }\n .why img {\n width: 30px;\n height: 36px;\n padding: 2px;\n border: 2px solid var(--sc-color-primary, #111827);\n border-radius: 4px;\n background: #ffffff;\n }\n .in-cart {\n position: absolute;\n top: 8px;\n left: 8px;\n max-width: calc(100% - 60px);\n padding: 4px 8px;\n border-radius: 999px;\n background: var(--sc-color-primary, #111827);\n color: var(--sc-accent-foreground, #ffffff);\n font-size: 0.72rem;\n font-weight: 800;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n `,\n ];\n\n override render() {\n const view = this.view;\n if (!view) return nothing;\n return html`<h2 class=\"headline\" tabindex=\"-1\" data-heading>${this.copy.need_headline}</h2>\n <p class=\"voice\" data-slot=\"voice\">${this.slotText('voice', view.voice)}</p>\n <ul class=\"items\">\n ${view.items.map((item) => {\n const path = `items.${item.item_id}.reason`;\n return html`<li>\n <button\n class=\"item\"\n aria-disabled=${this.busyAttr()}\n @click=${() => this.emitTap({ kind: 'open', item_id: item.item_id })}\n >\n <span class=\"pic\">\n ${picture(item.picked?.image_url ?? item.image, item.label)}\n <span class=\"on-photo\"\n ><span class=\"name\">${item.label}</span\n ><span class=\"reason\" data-slot=${path}>${this.slotText(path, item.reason)}</span></span\n >\n <span class=\"why\">${item.because.map((s) => html`<img src=${s.image} alt=${s.label} />`)}</span>\n ${item.picked ? html`<span class=\"in-cart\">In your cart: ${item.picked.name}</span>` : nothing}\n </span>\n </button>\n </li>`;\n })}\n </ul>`;\n }\n}\n\nif (!customElements.get(GUIDE_WHAT_YOU_NEED)) {\n customElements.define(GUIDE_WHAT_YOU_NEED, GuideWhatYouNeed);\n}\n", "/**\n * The guided page's templates: one Lit element per template name (spec, \"The\n * templates\"). The frame draws a shown template by looking up its element\n * here; a name with no element yet is skipped and logged by the frame.\n */\nimport { GUIDE_COMPARE } from './compare';\nimport { GUIDE_HERO_PICK } from './hero-pick';\nimport { GUIDE_PICK_FROM_PICTURES } from './pick-from-pictures';\nimport { GUIDE_PRODUCT_DETAIL } from './product-detail';\nimport { GUIDE_WHAT_YOU_NEED } from './what-you-need';\n\nexport type TemplateName =\n | 'pick_from_pictures'\n | 'what_you_need'\n | 'compare'\n | 'hero_pick'\n | 'product_detail';\n\nexport const TEMPLATE_ELEMENTS: Partial<Record<TemplateName, string>> = {\n pick_from_pictures: GUIDE_PICK_FROM_PICTURES,\n what_you_need: GUIDE_WHAT_YOU_NEED,\n compare: GUIDE_COMPARE,\n hero_pick: GUIDE_HERO_PICK,\n product_detail: GUIDE_PRODUCT_DETAIL,\n};\n\nexport function templateElement(name: string): string | null {\n // Own keys only: 'constructor' or 'toString' from a server must never name an element.\n return Object.hasOwn(TEMPLATE_ELEMENTS, name)\n ? ((TEMPLATE_ELEMENTS as Record<string, string | undefined>)[name] ?? null)\n : null;\n}\n\nexport {\n GUIDE_PICK_EVENT,\n GUIDE_TAP_EVENT,\n GUIDE_TURN_EVENT,\n type GuidePickDetail,\n GuideTemplate,\n type GuideTurnDetail,\n} from './base';\n", "/**\n * The guide's only transport: runtime-backend's guide routes, through the\n * runtime's authed fetch (token, distinct id and session headers; same-origin\n * to the backend only). Types are generated from runtime-backend's OpenAPI, so\n * a server change that breaks this canvas fails typecheck, not a shopper.\n *\n * Every call says where the guide is drawn (host, width), whether the shopper\n * accepted tracking (consent, sent only when granted), which turn a tap or\n * message answers, and this page load's id (a header). Plan 1's turn record\n * keys on them.\n */\nimport type { Schemas } from '@syntrologie/runtime-contracts';\nimport type { SeenReport } from './seen';\nimport { TEMPLATE_ELEMENTS } from './templates/index';\n\nexport type { SeenReport } from './seen';\nexport type GuideView = Schemas['GuideView'];\nexport type TurnView = Schemas['TurnView'];\nexport type ShownTemplate = Schemas['ShownTemplate'];\nexport type TemplateView = ShownTemplate['view'];\nexport type PickFromPicturesView = Schemas['PickFromPicturesView'];\nexport type WhatYouNeedView = Schemas['WhatYouNeedView'];\nexport type CompareView = Schemas['CompareView'];\nexport type HeroPickView = Schemas['HeroPickView'];\nexport type ProductDetailView = Schemas['ProductDetailView'];\nexport type StampView = Schemas['StampView'];\nexport type TradeoffView = Schemas['TradeoffView'];\nexport type OtherProductTile = Schemas['OtherProductTile'];\nexport type GuideTap =\n | Schemas['PostcardTap']\n | Schemas['StampTap']\n | Schemas['DoneTap']\n | Schemas['OpenTap']\n | Schemas['BackTap']\n | Schemas['PickTap']\n | Schemas['NewTripTap']\n | Schemas['ProductTap'];\nexport type AuthedFetch = (path: string, init?: RequestInit) => Promise<Response>;\nexport type GuideResult =\n | { kind: 'view'; view: GuideView }\n | { kind: 'unpublished' }\n | { kind: 'unavailable' }\n | { kind: 'rejected' };\nexport type GuideHost = 'store' | 'card';\nexport type GuideWidth = 'phone' | 'desktop';\n\nexport interface GuideParams {\n host: GuideHost;\n width: GuideWidth;\n /** True only when the SDK's consent gate says the shopper accepted tracking. */\n consent: boolean;\n /** The turn a tap or message answers; none on the first load. */\n turn?: string | null;\n /** The move a tap came from (the template's section), for the rollup; none for the frame's own controls. */\n move?: string | null;\n pageSession: string;\n /** The store's own list of templates (the tile's `templates` prop); absent means every drawable one. */\n allowedTemplates?: readonly string[] | null;\n}\n\nexport const GUIDE_PATH = '/api/adaptive/guide';\nexport const TAP_PATH = '/api/adaptive/guide/tap';\nexport const SAY_PATH = '/api/adaptive/guide/say';\nexport const SEEN_PATH = '/api/adaptive/guide/seen';\nexport const PAGE_SESSION_HEADER = 'X-Syntro-Page-Session';\nconst TURN_ID = /^[0-9a-f]{32}$/;\n/** runtime-backend's move id (SeenRequest.move_id): a template name, then an optional target. */\nconst MOVE_ID = /^[a-z_]+(:[a-z0-9][a-z0-9_-]*)?$/;\nconst MOVE_ID_MAX = 300;\n\n/** A random id for this page load: 32 hex (the server takes 8 to 64 of [A-Za-z0-9_-]). */\nexport function newPageSession(): string {\n const bytes = new Uint8Array(16);\n crypto.getRandomValues(bytes);\n return Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');\n}\n\nfunction turnQuery(params: GuideParams): string[] {\n const parts = [`host=${params.host}`, `width=${params.width}`];\n if (params.consent) parts.push('consent=granted');\n if (params.turn && TURN_ID.test(params.turn)) parts.push(`turn=${params.turn}`);\n if (params.move && params.move.length <= MOVE_ID_MAX && MOVE_ID.test(params.move)) {\n parts.push(`move=${encodeURIComponent(params.move)}`);\n }\n return parts;\n}\n\nexport const TEMPLATES_HEADER = 'X-Syntro-Guide-Templates';\n\n/**\n * The templates this page can draw, narrowed to the store's own list when it has one. The server\n * offers only those moves (plan 1, Task 7). A name this page cannot draw is dropped, never sent.\n */\nexport function drawableTemplates(allowed?: readonly string[] | null): string {\n const drawable = Object.keys(TEMPLATE_ELEMENTS);\n const names = allowed ? drawable.filter((name) => allowed.includes(name)) : drawable;\n return names.sort().join(',');\n}\n\nfunction headers(params: GuideParams): Record<string, string> {\n return {\n [PAGE_SESSION_HEADER]: params.pageSession,\n [TEMPLATES_HEADER]: drawableTemplates(params.allowedTemplates),\n };\n}\n\n/** Any guide path, decorated like every guide call: the turn params appended, the page session header. */\nexport function guideRequest(\n path: string,\n params: GuideParams\n): { path: string; headers: Record<string, string> } {\n const sep = path.includes('?') ? '&' : '?';\n return { path: `${path}${sep}${turnQuery(params).join('&')}`, headers: headers(params) };\n}\n\nfunction isView(body: unknown): body is GuideView {\n const v = body as Partial<GuideView> | null;\n return (\n !!v &&\n (v.stage === 'postcard' || v.stage === 'spread') &&\n Array.isArray(v.stamps) &&\n Array.isArray(v.feed)\n );\n}\n\nasync function call(\n fetchFn: AuthedFetch,\n path: string,\n init: RequestInit,\n on404: GuideResult\n): Promise<GuideResult> {\n let res: Response;\n try {\n res = await fetchFn(path, init);\n } catch {\n return { kind: 'unavailable' };\n }\n if (res.status === 404) return on404;\n if (!res.ok) return { kind: 'unavailable' };\n const body: unknown = await res.json().catch(() => null);\n return isView(body) ? { kind: 'view', view: body } : { kind: 'unavailable' };\n}\n\n/** The current view. `hints` (landing path words, referrer host) lean the first pictures. */\nexport function loadGuide(\n fetchFn: AuthedFetch,\n params: GuideParams,\n hints: string[] = []\n): Promise<GuideResult> {\n const query = [...turnQuery(params), ...hints.map((h) => `hint=${encodeURIComponent(h)}`)];\n return call(\n fetchFn,\n `${GUIDE_PATH}?${query.join('&')}`,\n { method: 'GET', headers: headers(params) },\n { kind: 'unpublished' }\n );\n}\n\nexport function sendTap(\n fetchFn: AuthedFetch,\n tap: GuideTap,\n params: GuideParams\n): Promise<GuideResult> {\n return call(\n fetchFn,\n `${TAP_PATH}?${turnQuery(params).join('&')}`,\n { method: 'POST', body: JSON.stringify(tap), headers: headers(params) },\n { kind: 'rejected' }\n );\n}\n\n/** A typed sentence. The answer is a view like a tap's (a 404 means the guide went away). */\nexport function sendMessage(\n fetchFn: AuthedFetch,\n text: string,\n params: GuideParams\n): Promise<GuideResult> {\n return call(\n fetchFn,\n `${SAY_PATH}?${turnQuery(params).join('&')}`,\n { method: 'POST', body: JSON.stringify({ text }), headers: headers(params) },\n { kind: 'rejected' }\n );\n}\n\n/** How long a template was on screen. `keepalive` when the page is going away. Never throws. */\nexport async function sendSeen(\n fetchFn: AuthedFetch,\n report: SeenReport,\n params: GuideParams,\n keepalive = false\n): Promise<void> {\n try {\n await fetchFn(`${SEEN_PATH}${params.consent ? '?consent=granted' : ''}`, {\n method: 'POST',\n body: JSON.stringify(report),\n headers: headers(params),\n keepalive,\n });\n } catch {\n // A lost report costs one record, never the shopper's screen.\n console.warn('[postcard-guide] a seen report did not reach the server');\n }\n}\n", "/**\n * The address bar for product detail in the feed (spec, \"The page\").\n *\n * When a product's detail lands in the feed after the shopper opened it, its\n * real URL goes in the address bar with history.pushState and no page load, so\n * share and refresh reach the store's own product page. Back pops that entry:\n * the feed returns to the template that opened the product and focus goes back\n * to the control the shopper used. Only explicit opens and back or forward move\n * the address bar; scrolling does not. Entries we push carry only our own key.\n * The runtime's NavigationMonitor sees the push like any SPA navigation (route\n * actions re-run, page_views counts it); plan 3, Notes for the controller, item 9.\n */\n\nimport { GUIDE_TAP_EVENT, GUIDE_TURN_EVENT } from './templates/index';\nimport { GUIDE_DETAIL_BACK_EVENT } from './templates/product-detail';\n\nconst STATE_KEY = 'syntroGuideDetail';\n\ninterface DetailEntry {\n turnId: string;\n moveId: string;\n originTurnId: string | null;\n originMoveId: string | null;\n}\n\ninterface Origin {\n turnId: string | null;\n moveId: string | null;\n opener: HTMLElement | null;\n}\n\ntype DetailElement = HTMLElement & {\n view?: { product?: { href?: string } } | null;\n focusHeading?: () => Promise<void>;\n};\n\n/** A same-site path: one leading slash, no scheme or host, no backslash, no whitespace or control characters. */\nexport function isSitePath(href: string): boolean {\n if (!/^\\/(?![/\\\\])[^\\s\\\\]*$/.test(href)) return false;\n return ![...href].some((c) => c.charCodeAt(0) < 0x20 || c.charCodeAt(0) === 0x7f);\n}\n\nfunction ours(state: unknown): DetailEntry | null {\n const entry = (state as Record<string, unknown> | null)?.[STATE_KEY];\n return entry && typeof entry === 'object' ? (entry as DetailEntry) : null;\n}\n\nfunction sectionFor(\n root: ParentNode,\n turnId: string | null,\n moveId: string | null\n): HTMLElement | null {\n if (!turnId) return null;\n return (\n [...root.querySelectorAll<HTMLElement>('section.snap')].find(\n (s) => s.dataset.turnId === turnId && (moveId === null || s.dataset.moveId === moveId)\n ) ?? null\n );\n}\n\n/** Whether `node` sits inside `ancestor`, across shadow roots. */\nfunction within(ancestor: Node, node: Node | null): boolean {\n let n: Node | null = node;\n while (n) {\n if (n === ancestor) return true;\n n = n.parentNode ?? (n instanceof ShadowRoot ? n.host : null);\n }\n return false;\n}\n\nfunction show(section: HTMLElement | null): void {\n const reduced =\n typeof matchMedia === 'function' && matchMedia('(prefers-reduced-motion: reduce)').matches;\n section?.scrollIntoView({ block: 'start', behavior: reduced ? 'auto' : 'smooth' });\n}\n\nexport class ProductDetailHistory {\n private _pending: Origin | null = null;\n private _current: DetailEntry | null = null;\n /** The control the shopper last clicked (or pressed Enter or Space on): a template's\n * tap is dispatched from the template itself, so this is how back finds the opener. */\n private _lastControl: HTMLElement | null = null;\n private readonly _openers = new Map<string, HTMLElement | null>();\n\n constructor(\n private readonly host: HTMLElement,\n private readonly root: () => ParentNode | null\n ) {}\n\n attach(): void {\n this.host.addEventListener('click', this._onClick, true);\n this.host.addEventListener(GUIDE_TAP_EVENT, this._onTap);\n this.host.addEventListener(GUIDE_TURN_EVENT, this._onTurn);\n this.host.addEventListener(GUIDE_DETAIL_BACK_EVENT, this._onBack);\n window.addEventListener('popstate', this._onPop);\n }\n\n detach(): void {\n this.host.removeEventListener('click', this._onClick, true);\n this.host.removeEventListener(GUIDE_TAP_EVENT, this._onTap);\n this.host.removeEventListener(GUIDE_TURN_EVENT, this._onTurn);\n this.host.removeEventListener(GUIDE_DETAIL_BACK_EVENT, this._onBack);\n window.removeEventListener('popstate', this._onPop);\n this._pending = null;\n this._lastControl = null;\n }\n\n private _onClick = (event: Event): void => {\n this._lastControl =\n event\n .composedPath()\n .find(\n (n): n is HTMLElement => n instanceof HTMLElement && n.matches('button, a[href], input')\n ) ?? null;\n };\n\n private _onTap = (event: Event): void => {\n const tap = (event as CustomEvent<{ kind?: string }>).detail;\n if (tap?.kind !== 'product') return;\n const path = event.composedPath();\n const section = path.find(\n (n): n is HTMLElement => n instanceof HTMLElement && n.matches('section.snap')\n );\n const control = this._lastControl;\n const source = path[0] instanceof HTMLElement ? path[0] : null;\n this._pending = {\n turnId: section?.dataset.turnId ?? null,\n moveId: section?.dataset.moveId ?? null,\n opener: control && section && within(section, control) ? control : source,\n };\n };\n\n private _onTurn = (event: Event): void => {\n const origin = this._pending;\n this._pending = null;\n const turnId = (event as CustomEvent<{ turnId?: string }>).detail?.turnId;\n const root = this.root();\n if (!origin || !turnId || !root) return;\n const section = [...root.querySelectorAll<HTMLElement>('section.snap')].find(\n (s) => s.dataset.turnId === turnId && s.dataset.template === 'product_detail'\n );\n const detail = section?.firstElementChild as DetailElement | null | undefined;\n const href = detail?.view?.product?.href;\n if (!section || !detail || !href) return;\n if (!isSitePath(href)) {\n console.error('[postcard-guide] refusing to put a non-site path in the address bar:', href);\n return;\n }\n const entry: DetailEntry = {\n turnId,\n moveId: section.dataset.moveId ?? '',\n originTurnId: origin.turnId,\n originMoveId: origin.moveId,\n };\n history.pushState({ [STATE_KEY]: entry }, '', href);\n this._openers.set(`${entry.turnId}|${entry.moveId}`, origin.opener);\n this._current = entry;\n void detail.focusHeading?.();\n };\n\n private _onPop = (event: PopStateEvent): void => {\n const root = this.root();\n if (!root) return;\n const entry = ours(event.state);\n if (entry) {\n this._current = entry;\n show(sectionFor(root, entry.turnId, entry.moveId));\n return;\n }\n const left = this._current;\n this._current = null;\n if (!left) return;\n show(sectionFor(root, left.originTurnId, left.originMoveId));\n const opener = this._openers.get(`${left.turnId}|${left.moveId}`) as DetailElement | null;\n if (!opener?.isConnected) return;\n // A template that sent the tap itself (no control seen) takes focus on its heading.\n if (opener.focusHeading) void opener.focusHeading();\n else opener.focus({ preventScroll: true });\n };\n\n private _onBack = (): void => {\n if (ours(history.state)) history.back();\n };\n}\n", "/**\n * How long each shown template was on screen (spec, \"The record\": time on each\n * template before scrolling past, reported by the canvas).\n *\n * Once per section: the time it filled at least 60% of the screen, reported\n * when the shopper scrolls it away or the page is hidden. \"Filled\" is the share\n * of the section on screen or the share of the feed it covers, whichever is\n * larger, so a template taller than the feed still counts when it fills it.\n *\n * The guide is a section of the store page, so filling the feed is not enough:\n * the section must also be on the shopper's screen. Two observers watch each\n * section, one against the feed and one against the viewport (an element root\n * ignores whether the root itself is on screen), and time runs only while both\n * say it fills.\n */\n\nexport interface SeenReport {\n turn_id: string;\n move_id: string;\n visible_ms: number;\n}\n\nexport const SEEN_SHARE = 0.6;\nexport const MAX_VISIBLE_MS = 3_600_000;\nconst THRESHOLDS = Array.from({ length: 21 }, (_, i) => i / 20);\n\ntype Observer = Pick<IntersectionObserver, 'observe' | 'unobserve' | 'disconnect'>;\nexport type ObserverFactory = (\n cb: IntersectionObserverCallback,\n init: IntersectionObserverInit\n) => Observer;\n/** `final` is true when the page is going away (send with keepalive). */\nexport type SeenSink = (report: SeenReport, final: boolean) => void;\n\ninterface Watch {\n turnId: string;\n moveId: string;\n since: number | null;\n done: boolean;\n /** Fills the feed (feed observer). */\n inFeed: boolean;\n /** Fills the screen (viewport observer). */\n onScreen: boolean;\n}\n\nconst browserObserver: ObserverFactory = (cb, init) => new IntersectionObserver(cb, init);\n\nexport function visibleShare(\n entry: Pick<IntersectionObserverEntry, 'intersectionRect' | 'boundingClientRect' | 'rootBounds'>\n): number {\n const shown = entry.intersectionRect.height;\n const own = entry.boundingClientRect.height;\n const base = Math.min(own, entry.rootBounds?.height ?? own);\n return base > 0 ? shown / base : 0;\n}\n\nexport class SeenTracker {\n private readonly _watch = new Map<Element, Watch>();\n private readonly _keys = new Set<string>();\n private readonly _sink: SeenSink;\n private readonly _now: () => number;\n private readonly _observer: Observer | null;\n private readonly _screen: Observer | null;\n\n constructor(\n root: Element,\n sink: SeenSink,\n now: () => number = () => performance.now(),\n factory: ObserverFactory | null = typeof IntersectionObserver === 'function'\n ? browserObserver\n : null\n ) {\n this._sink = sink;\n this._now = now;\n this._observer = factory\n ? factory(this._onEntries('inFeed'), { root, threshold: THRESHOLDS })\n : null;\n this._screen = factory\n ? factory(this._onEntries('onScreen'), { root: null, threshold: THRESHOLDS })\n : null;\n if (!this._observer) {\n console.warn(\n '[postcard-guide] no IntersectionObserver here; time on each template is not reported'\n );\n }\n }\n\n observe(section: HTMLElement): void {\n const turnId = section.dataset.turnId ?? '';\n const moveId = section.dataset.moveId ?? '';\n const key = `${turnId}:${moveId}`;\n if (!turnId || !moveId || this._keys.has(key)) return;\n this._keys.add(key);\n this._watch.set(section, {\n turnId,\n moveId,\n since: null,\n done: false,\n inFeed: false,\n onScreen: false,\n });\n this._observer?.observe(section);\n this._screen?.observe(section);\n }\n\n /** The page is going away: report every section on screen now. */\n flush(): void {\n for (const [el, watch] of this._watch) {\n if (watch.since !== null && !watch.done) this._report(el, watch, true);\n }\n }\n\n disconnect(): void {\n this._observer?.disconnect();\n this._screen?.disconnect();\n this._watch.clear();\n }\n\n private _onEntries =\n (flag: 'inFeed' | 'onScreen') =>\n (entries: IntersectionObserverEntry[]): void => {\n for (const entry of entries) {\n const watch = this._watch.get(entry.target);\n if (!watch || watch.done) continue;\n watch[flag] = entry.isIntersecting && visibleShare(entry) >= SEEN_SHARE;\n const seen = watch.inFeed && watch.onScreen;\n if (seen && watch.since === null) watch.since = this._now();\n else if (!seen && watch.since !== null) this._report(entry.target, watch, false);\n }\n };\n\n private _report(el: Element, watch: Watch, final: boolean): void {\n watch.done = true;\n this._observer?.unobserve(el);\n this._screen?.unobserve(el);\n const ms = Math.round(this._now() - (watch.since ?? this._now()));\n this._sink(\n {\n turn_id: watch.turnId,\n move_id: watch.moveId,\n visible_ms: Math.min(Math.max(ms, 0), MAX_VISIBLE_MS),\n },\n final\n );\n }\n}\n", "import { css } from 'lit';\n\n/**\n * The guided page's frame. Every value reads a --sc-* token first; literals\n * are fallbacks only. Layout keys off our own width (data-size): a stamps band\n * below 1200 px, a rail at 1200 and wider. No width media queries.\n */\nexport const pageStyles = css`\n :host {\n display: block;\n height: 100%;\n color: var(--sc-content-text-color, #1f2937);\n font-family: var(--sc-font-family, system-ui, sans-serif);\n }\n * {\n box-sizing: border-box;\n }\n button {\n font: inherit;\n color: inherit;\n cursor: pointer;\n }\n button[aria-disabled='true'] {\n cursor: progress;\n opacity: 0.7;\n }\n button:focus-visible,\n input:focus-visible {\n outline: 3px solid var(--sc-color-primary, #111827);\n outline-offset: 2px;\n }\n ul {\n list-style: none;\n margin: 0;\n padding: 0;\n }\n img {\n display: block;\n width: 100%;\n height: 100%;\n object-fit: cover;\n }\n\n .frame {\n height: 100%;\n display: grid;\n grid-template-columns: minmax(0, 1fr);\n grid-template-rows: auto auto minmax(0, 1fr) auto auto;\n grid-template-areas: 'brand' 'known' 'feed' 'notice' 'composer';\n background: var(--sc-tile-background, #ffffff);\n color: var(--sc-tile-title-color, #111827);\n }\n :host([data-size='expanded']) .frame {\n grid-template-columns: 17rem minmax(0, 1fr);\n grid-template-rows: auto minmax(0, 1fr) auto auto;\n grid-template-areas: 'brand brand' 'known feed' 'known notice' 'known composer';\n }\n\n .brand {\n grid-area: brand;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n padding: 14px 18px 8px;\n }\n .brand .name {\n font-weight: 800;\n font-size: 0.95rem;\n letter-spacing: 0.04em;\n text-transform: uppercase;\n }\n .brand .cart {\n min-height: 32px;\n padding: 6px 12px;\n border: 0;\n border-radius: 999px;\n background: var(--sc-tile-title-color, #111827);\n color: var(--sc-tile-background, #ffffff);\n font-size: 0.8rem;\n font-weight: 700;\n }\n\n .known {\n grid-area: known;\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 4px 14px 10px;\n border-bottom: 1px solid var(--sc-content-divider-color, rgb(0 0 0 / 0.08));\n overflow-x: auto;\n }\n .known-label,\n .words {\n display: none;\n }\n .stamps {\n display: flex;\n gap: 8px;\n }\n .stamps li {\n display: flex;\n align-items: center;\n gap: 10px;\n }\n .stamp {\n display: block;\n flex: none;\n width: 46px;\n height: 54px;\n padding: 3px;\n border: 2px solid var(--sc-color-primary, #111827);\n border-radius: 6px;\n /* A stamp's own white mount, in both schemes. */\n background: #ffffff;\n box-shadow:\n inset 0 0 0 3px #ffffff,\n 0 2px 6px rgb(0 0 0 / 0.1);\n }\n .stamp.empty {\n border: 2px dashed var(--sc-content-divider-color, #d6d3cc);\n background: transparent;\n box-shadow: none;\n }\n .done {\n flex: none;\n min-height: 36px;\n margin-left: auto;\n padding: 8px 14px;\n border: 0;\n border-radius: 999px;\n background: var(--sc-color-primary, #111827);\n color: var(--sc-accent-foreground, #ffffff);\n font-weight: 700;\n white-space: nowrap;\n }\n :host([data-size='expanded']) .known {\n flex-direction: column;\n align-items: stretch;\n gap: 14px;\n padding: 18px;\n border-bottom: 0;\n border-right: 1px solid var(--sc-content-divider-color, rgb(0 0 0 / 0.08));\n overflow: hidden auto;\n }\n :host([data-size='expanded']) .known-label {\n display: block;\n margin: 0;\n font-size: 0.75rem;\n font-weight: 700;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n color: var(--sc-tile-text-color, #4b5563);\n }\n :host([data-size='expanded']) .stamps {\n flex-direction: column;\n gap: 12px;\n }\n :host([data-size='expanded']) .words {\n display: block;\n font-size: 0.95rem;\n font-weight: 700;\n }\n :host([data-size='expanded']) .words.unknown {\n font-weight: 600;\n color: var(--sc-tile-text-color, #4b5563);\n }\n :host([data-size='expanded']) .done {\n align-self: flex-start;\n margin: 4px 0 0;\n }\n\n .feed {\n grid-area: feed;\n position: relative;\n overflow-y: auto;\n scroll-behavior: smooth;\n }\n /* On the store the guide is a section of the page: at the feed's ends, scroll chains on\n to the page. No snapping there: Chrome does not chain past the end of a snapping feed\n whose last template is taller than it. A chat-app card is the whole surface, so it\n keeps the scroll to itself and snaps. A tap still lands its turn at the top (page.ts). */\n :host([data-card]) .feed {\n overscroll-behavior: contain;\n scroll-snap-type: y mandatory;\n }\n .snap {\n display: flex;\n flex-direction: column;\n min-height: 100%;\n scroll-snap-align: start;\n }\n .snap > * {\n flex: 1 0 auto;\n }\n\n .notice {\n grid-area: notice;\n margin: 0;\n padding: 0 16px;\n font-weight: 700;\n }\n /* Always mounted, so a screen reader announces the message when it arrives. */\n .notice:not(:empty) {\n padding: 8px 16px 0;\n }\n /* Heard, not shown: the line is already on screen in its template. */\n .notice.heard {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n }\n\n .composer {\n grid-area: composer;\n display: flex;\n gap: 8px;\n margin: 8px 12px max(12px, env(safe-area-inset-bottom));\n padding: 6px;\n border-radius: 999px;\n background: var(--sc-content-search-background, #f3f4f6);\n box-shadow: 0 6px 20px rgb(0 0 0 / 0.12);\n }\n .composer input {\n flex: 1;\n min-width: 0;\n padding: 10px 14px;\n border: 0;\n /* 16 px or more, or iOS zooms the page on focus. */\n font: inherit;\n font-size: 1rem;\n color: var(--sc-tile-title-color, #111827);\n background: transparent;\n }\n .composer input::placeholder {\n color: var(--sc-tile-text-color, #4b5563);\n }\n .composer input:focus-visible {\n border-radius: 999px;\n }\n .composer button {\n min-height: 40px;\n padding: 8px 18px;\n border: 0;\n border-radius: 999px;\n background: var(--sc-color-primary, #111827);\n color: var(--sc-accent-foreground, #ffffff);\n font-weight: 700;\n }\n\n @media (prefers-reduced-motion: reduce) {\n .feed {\n scroll-behavior: auto;\n }\n }\n @media (forced-colors: active) {\n .stamp,\n .composer,\n .brand .cart,\n .done,\n .composer button {\n border: 1px solid CanvasText;\n }\n }\n`;\n", "/**\n * The words stream for one turn: `GET /api/adaptive/guide/words?turn=<id>`,\n * read as server-sent events from a fetch body through the runtime's authed\n * fetch. EventSource cannot send the Authorization header, and a token in the\n * URL would reach proxy logs. Transport and parser only: no DOM.\n *\n * Like runtime-sdk's search surfaceStream, a watchdog and the caller's abort\n * are raced against every read, and every way it can end is an outcome, never\n * a throw: the caller's only job on failure is to keep the fallback text.\n */\nimport type { AuthedFetch } from './guide-client';\n\nexport const WORDS_PATH = '/api/adaptive/guide/words';\n/** Past the server's own 6 s limit: only a hung connection reaches it. */\nexport const WORDS_BUDGET_MS = 10_000;\n\nexport interface SlotWords {\n move_id: string;\n slot: string;\n form: 'short' | 'long';\n text: string;\n}\n\n/**\n * done: the server finished. empty: nothing to write (204). ended: closed\n * without done. aborted: the caller stopped it. failed: anything else.\n */\nexport type WordsOutcome = 'done' | 'empty' | 'ended' | 'aborted' | 'failed';\n\nexport interface StreamWordsOptions {\n signal: AbortSignal;\n onSlot: (words: SlotWords) => void;\n headers?: Record<string, string>;\n budgetMs?: number;\n}\n\nconst STOPPED = Symbol('words-stopped');\n\nexport function wordsPath(turnId: string): string {\n return `${WORDS_PATH}?turn=${encodeURIComponent(turnId)}`;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction readSlot(value: unknown): SlotWords | null {\n if (!isRecord(value)) return null;\n const { move_id, slot, form, text } = value;\n if (typeof move_id !== 'string' || typeof slot !== 'string') return null;\n if (typeof text !== 'string' || !text) return null;\n if (form !== 'short' && form !== 'long') return null;\n return { move_id, slot, form, text };\n}\n\n/** The `data:` payload of one frame; comments (`: hb`) collapse to ''. */\nfunction frameData(frame: string): string {\n return frame\n .split('\\n')\n .filter((line) => line.startsWith('data:'))\n .map((line) => (line.startsWith('data: ') ? line.slice(6) : line.slice(5)))\n .join('');\n}\n\nexport async function streamWords(\n fetchFn: AuthedFetch,\n path: string,\n options: StreamWordsOptions\n): Promise<WordsOutcome> {\n const { signal, onSlot } = options;\n if (signal.aborted) return 'aborted';\n const abort = new AbortController();\n const onAbort = (): void => abort.abort();\n signal.addEventListener('abort', onAbort, { once: true });\n const watchdog = setTimeout(() => abort.abort(), options.budgetMs ?? WORDS_BUDGET_MS);\n const stopped = new Promise<never>((_resolve, reject) => {\n abort.signal.addEventListener('abort', () => reject(STOPPED), { once: true });\n });\n stopped.catch(() => {\n // Handled: the race leaves it pending on every path that finishes.\n });\n let reader: ReadableStreamDefaultReader<Uint8Array> | null = null;\n try {\n const res = await Promise.race([\n fetchFn(path, {\n method: 'GET',\n headers: { Accept: 'text/event-stream', ...options.headers },\n signal: abort.signal,\n }),\n stopped,\n ]);\n if (res.status === 204) return 'empty';\n if (!res.ok || !res.body) return 'failed';\n reader = res.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n for (;;) {\n const { done, value } = await Promise.race([reader.read(), stopped]);\n if (done) return 'ended';\n buffer += decoder.decode(value, { stream: true });\n for (let sep = buffer.indexOf('\\n\\n'); sep >= 0; sep = buffer.indexOf('\\n\\n')) {\n const data = frameData(buffer.slice(0, sep));\n buffer = buffer.slice(sep + 2);\n if (!data) continue;\n const parsed: unknown = JSON.parse(data);\n if (isRecord(parsed) && parsed.done === true) return 'done';\n const words = readSlot(parsed);\n if (words) onSlot(words);\n }\n }\n } catch {\n return signal.aborted ? 'aborted' : 'failed';\n } finally {\n clearTimeout(watchdog);\n signal.removeEventListener('abort', onAbort);\n reader?.cancel().catch(() => {\n // The stream is already gone; nothing to release.\n });\n }\n}\n", "/**\n * The words for each turn. When the frame paints a turn it fires\n * `guide-turn`; this controller streams that turn's live text into the\n * templates' `[data-slot]` nodes. Every node already shows its written\n * fallback, so a slow, failed or stale stream leaves the page as it was. One\n * stream at a time: a newer turn aborts the older, and words for any other\n * turn are dropped. The frame's one status region hears the lead line once\n * per turn, after the server says it is done; slots are never live regions.\n */\nimport type { ReactiveController, ReactiveControllerHost } from 'lit';\nimport type { AuthedFetch } from './guide-client';\nimport { type SlotWords, streamWords } from './words-stream';\n\nconst TURN_ID = /^[0-9a-f]{32}$/;\nconst SHORT = 'data-words-short';\nconst LONG = 'data-words-long';\n\nexport type Width = 'phone' | 'desktop';\n\nexport interface WordsOptions {\n fetchFn: () => AuthedFetch | null;\n /** The words call for a turn, decorated like every other guide call. */\n request: (turnId: string) => { path: string; headers: Record<string, string> };\n width: () => Width;\n /** Sets the frame's one always-mounted status region. */\n announce: (text: string) => void;\n}\n\ntype WordsHostElement = ReactiveControllerHost &\n HTMLElement & { renderRoot: HTMLElement | DocumentFragment };\n\n/** Every `[data-slot]` node under root, through the templates' shadow roots. */\nexport function* slotNodes(root: ParentNode): Generator<Element> {\n for (const el of root.querySelectorAll('*')) {\n if (el.hasAttribute('data-slot')) yield el;\n if (el.shadowRoot) yield* slotNodes(el.shadowRoot);\n }\n}\n\nfunction turnSections(root: ParentNode, turnId: string): HTMLElement[] {\n return [...root.querySelectorAll<HTMLElement>('section.snap')].filter(\n (s) => s.dataset.turnId === turnId\n );\n}\n\nexport function findSlot(\n root: ParentNode,\n turnId: string,\n moveId: string,\n slot: string\n): Element | null {\n for (const section of turnSections(root, turnId)) {\n if (section.dataset.moveId !== moveId) continue;\n for (const node of slotNodes(section)) {\n if (node.getAttribute('data-slot') === slot) return node;\n }\n }\n return null;\n}\n\n/**\n * Words into a node Lit rendered, keeping Lit's part markers: the text node\n * Lit committed is edited in place, so a re-render with the same fallback\n * leaves the live words, and a new view still replaces them.\n */\nexport function fillSlot(node: Element, text: string): void {\n const texts = [...node.childNodes].filter((c): c is Text => c.nodeType === Node.TEXT_NODE);\n const target = texts.find((t) => t.data.trim()) ?? texts[0];\n if (target) target.data = text;\n else node.append(text);\n}\n\nfunction show(node: Element, width: Width): void {\n const text = (width === 'desktop' && node.getAttribute(LONG)) || node.getAttribute(SHORT);\n if (text) fillSlot(node, text);\n}\n\nexport class WordsController implements ReactiveController {\n private readonly _host: WordsHostElement;\n private readonly _options: WordsOptions;\n private _turn: string | null = null;\n private _abort: AbortController | null = null;\n\n constructor(host: WordsHostElement, options: WordsOptions) {\n this._host = host;\n this._options = options;\n host.addController(this);\n }\n\n hostConnected(): void {\n this._host.addEventListener('guide-turn', this._onTurn);\n }\n\n hostDisconnected(): void {\n this._host.removeEventListener('guide-turn', this._onTurn);\n this._abort?.abort();\n this._abort = null;\n this._turn = null;\n }\n\n /** Show each filled slot's form for the current width. Never a new request. */\n relayout(): void {\n const width = this._options.width();\n for (const node of slotNodes(this._host.renderRoot)) {\n if (node.hasAttribute(SHORT)) show(node, width);\n }\n }\n\n private _onTurn = (event: Event): void => {\n const turnId = (event as CustomEvent<{ turnId?: unknown }>).detail?.turnId;\n if (typeof turnId !== 'string' || !TURN_ID.test(turnId) || turnId === this._turn) return;\n void this._stream(turnId);\n };\n\n private async _stream(turnId: string): Promise<void> {\n this._abort?.abort();\n const abort = new AbortController();\n this._abort = abort;\n this._turn = turnId;\n const fetchFn = this._options.fetchFn();\n if (!fetchFn) return;\n const root = this._host.renderRoot;\n const lead = turnSections(root, turnId)[0]?.dataset.moveId;\n const leadLines: string[] = [];\n const onSlot = (words: SlotWords): void => {\n if (this._turn !== turnId) return;\n const node = findSlot(root, turnId, words.move_id, words.slot);\n if (!node) return;\n node.setAttribute(words.form === 'long' ? LONG : SHORT, words.text);\n show(node, this._options.width());\n if (words.form === 'short' && words.move_id === lead) leadLines.push(words.text);\n };\n const { path, headers } = this._options.request(turnId);\n const outcome = await streamWords(fetchFn, path, { signal: abort.signal, headers, onSlot });\n if (this._abort === abort) this._abort = null;\n if (this._turn === turnId && outcome === 'done' && leadLines[0]) {\n this._options.announce(leadLines[0]);\n }\n }\n}\n", "/**\n * <syntro-guide-page>: the guided page as a tile widget.\n *\n * Mounted by the SDK in an inline slot anchored to the store's guided page\n * block, so it is a section of the store page right after that block, full\n * width. It draws <postcard-guide-page> through the SDK's own transport\n * (window.SynOS.authedFetch) and hands it what the page reads from the store:\n * landing hints, the shopper's consent, the store's name and the cart.\n *\n * It takes no room until the first view is painted (guide-ready); then the\n * section shows. It never covers the page. The section is as tall as the viewport, capped\n * under the tile card's own height limit (--sc-card-max-height), and the\n * guide's feed scrolls inside it. Unpublished, down on arrival, or nothing to load it with: it\n * draws nothing and takes no room.\n */\nimport {\n type AuthedFetch,\n consentGranted,\n getCart,\n landingHints,\n storeName,\n TEMPLATE_ELEMENTS,\n} from '@syntrologie/canvas-postcard-guide/page';\nimport { css, html, LitElement, nothing } from 'lit';\n\nexport const GUIDE_PAGE_TAG = 'syntro-guide-page';\n\nexport class GuidePageWidget extends LitElement {\n static override styles = css`\n :host {\n display: block;\n width: 100%;\n }\n :host([hidden]) {\n display: none;\n }\n /* Loading the first view: laid out, so the page measures its width, but no height and\n unseen, so the store page does not move until there is something to show. */\n :host([data-pending]) postcard-guide-page {\n height: 0;\n overflow: hidden;\n visibility: hidden;\n }\n postcard-guide-page {\n display: block;\n /* At most a viewport tall, and under the tile card's cap (less the card's top\n gap) so the card body never scrolls: the guide's own feed does. */\n height: min(\n 100svh,\n calc(var(--sc-card-max-height, 100svh) - var(--sc-tile-gap, 0.25rem))\n );\n }\n `;\n\n static override properties = {\n templates: { attribute: false },\n };\n\n /** The store's own list of templates (the tile's props); null means every drawable one. */\n declare templates: readonly string[] | null;\n\n private _transport: AuthedFetch | null = null;\n private _hints: string[] = [];\n private _brand = '';\n /** Nothing to draw for the life of this tile: unpublished, down, or no way to load it. */\n private _gone = false;\n\n constructor() {\n super();\n this.templates = null;\n }\n\n override connectedCallback(): void {\n super.connectedCallback();\n if (this._transport || this._gone) return;\n const transport = window.SynOS?.authedFetch ?? null;\n if (!transport) {\n console.error('[adaptive-guide] no authed fetch on the page; the guide cannot load');\n this._collapse();\n return;\n }\n if (\n this.templates &&\n !this.templates.some((name) => Object.keys(TEMPLATE_ELEMENTS).includes(name))\n ) {\n console.error(\n `[adaptive-guide] none of the templates ${JSON.stringify(this.templates)} can be drawn; the guide is not shown`\n );\n this._collapse();\n return;\n }\n this._transport = transport;\n this.setAttribute('data-pending', '');\n this._hints = landingHints();\n this._brand = storeName();\n if (!this._brand) {\n console.warn(\n '[adaptive-guide] the store page has no og:site_name; the brand bar shows no name'\n );\n }\n this.requestUpdate();\n }\n\n override render() {\n if (this._gone || !this._transport) return nothing;\n return html`<postcard-guide-page\n .authedFetch=${this._transport}\n .getCart=${getCart}\n .hints=${this._hints}\n .consent=${consentGranted}\n .brand=${this._brand}\n .guideHost=${'store'}\n .allowedTemplates=${this.templates}\n @guide-ready=${this._show}\n @guide-unpublished=${this._collapse}\n @guide-unavailable=${this._collapse}\n ></postcard-guide-page>`;\n }\n\n /** The first view is painted: the section takes its height now, never before. */\n private _show = (): void => {\n this.removeAttribute('data-pending');\n };\n\n /** Draw nothing and take no room, so the store's page reads as if the tile were not there. */\n private _collapse = (): void => {\n this._gone = true;\n this.hidden = true;\n this.removeAttribute('data-pending');\n this.requestUpdate();\n };\n}\n\nexport function registerGuidePageElement(): void {\n if (typeof customElements !== 'undefined' && !customElements.get(GUIDE_PAGE_TAG)) {\n customElements.define(GUIDE_PAGE_TAG, GuidePageWidget);\n }\n}\n\nregisterGuidePageElement();\n", "/**\n * adaptive-guide \u2014 Runtime manifest\n *\n * Registers <syntro-guide-page> as a side effect of importing this module and\n * exposes the mountable the runtime's WidgetRegistry uses for\n * `adaptive-guide:page`.\n */\n\nimport { type MountPlumbing, stripMountPlumbing } from '@syntrologie/sdk-contracts';\n\nimport { GUIDE_PAGE_TAG, type GuidePageWidget } from './guide-widget';\nimport type { GuidePageProps } from './schema';\n\n/** The tile card adds `chromeless` to the props it passes down; the page has no use for it. */\ntype MountedProps = GuidePageProps & MountPlumbing & { chromeless?: boolean };\n\nexport const GuidePageMountable = {\n mount(container: HTMLElement, config?: MountedProps | null) {\n const props = stripMountPlumbing<MountedProps>(config ?? null);\n const element = document.createElement(GUIDE_PAGE_TAG) as GuidePageWidget;\n element.templates = props.templates ?? null;\n // The tile card gives every widget a loading footprint; the guide sizes itself, and a\n // guide that draws nothing (unpublished, down) must take no room on the store page.\n container.style.minHeight = '0px';\n container.appendChild(element);\n return () => element.remove();\n },\n\n /** A refetch or republish keeps the same page, so the shopper keeps their place in it. */\n update(container: HTMLElement, config?: MountedProps | null) {\n const element = container.querySelector(GUIDE_PAGE_TAG) as GuidePageWidget | null;\n if (!element) return;\n const templates = stripMountPlumbing<MountedProps>(config ?? null).templates ?? null;\n if (JSON.stringify(templates) !== JSON.stringify(element.templates))\n element.templates = templates;\n },\n};\n\nexport const runtime = {\n id: 'adaptive-guide',\n version: '1.0.0',\n name: 'Guided page',\n description: \"The store's guided page, drawn as a tile in an inline slot.\",\n\n /** No DOM-mutation executors: this tile renders only. */\n executors: [],\n\n widgets: [\n {\n id: 'adaptive-guide:page',\n component: GuidePageMountable,\n metadata: {\n name: 'Guided page',\n description: \"The store's published guide, a section of the page after its anchor\",\n icon: '\uD83E\uDDED',\n },\n },\n ],\n};\n\nexport default runtime;\n"],
5
+ "mappings": ";;;;;AAqDM,SAAUA,EAAsCC,IAAAA;AACpD,SAAOA;AACT;;;AClCO,IAAM,uBAAuB,EAAc,4BAA4B;;;ACyH9E,IAAM,QAAQ,CAACC,OAAMA;AACd,IAAM,mBAAmB;AAAA;AAAA,EAE5B,sBAAsB,MAAM;AAAA,IACxB,SAAS;AAAA,IACT,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO,CAAC,SAAS,SAAS;AAAA,EAC9B,CAAC;AAAA,EACD,0BAA0B,MAAM;AAAA,IAC5B,SAAS;AAAA,IACT,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO,CAAC,mBAAmB,mBAAmB;AAAA,EAClD,CAAC;AAAA,EACD,+BAA+B,MAAM;AAAA,IACjC,SAAS;AAAA,IACT,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO,CAAC,mBAAmB,mBAAmB;AAAA,EAClD,CAAC;AAAA,EACD,gCAAgC,MAAM;AAAA,IAClC,SAAS;AAAA,IACT,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO,CAAC,mBAAmB,qBAAqB,kBAAkB;AAAA,EACtE,CAAC;AAAA;AAAA,EAED,yBAAyB,MAAM;AAAA,IAC3B,SAAS;AAAA,IACT,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO,CAAC,WAAW,WAAW;AAAA;AAAA,EAClC,CAAC;AAAA,EACD,yBAAyB,MAAM;AAAA,IAC3B,SAAS;AAAA,IACT,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO,CAAC,eAAe,UAAU,iBAAiB,mBAAmB,cAAc;AAAA;AAAA,EACvF,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaD,oBAAoB,MAAM;AAAA,IACtB,SAAS;AAAA,IACT,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO,CAAC,QAAQ,QAAQ;AAAA;AAAA,EAC5B,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcD,4BAA4B,MAAM;AAAA,IAC9B,SAAS;AAAA,IACT,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO,CAAC,WAAW,MAAM;AAAA;AAAA,EAC7B,CAAC;AAAA,EACD,4BAA4B,MAAM;AAAA,IAC9B,SAAS;AAAA,IACT,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO,CAAC,SAAS;AAAA,EACrB,CAAC;AAAA,EACD,mBAAmB,MAAM;AAAA,IACrB,SAAS;AAAA,IACT,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO,CAAC,cAAc,YAAY,QAAQ;AAAA;AAAA,EAC9C,CAAC;AAAA,EACD,uBAAuB,MAAM;AAAA,IACzB,SAAS;AAAA,IACT,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO,CAAC,WAAW,aAAa,YAAY;AAAA,EAChD,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,EAKD,2BAA2B,MAAM;AAAA,IAC7B,SAAS;AAAA,IACT,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO,CAAC,WAAW,aAAa,YAAY,WAAW,aAAa,UAAU;AAAA,EAClF,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOD,0BAA0B,MAAM;AAAA,IAC5B,SAAS;AAAA,IACT,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO,CAAC,QAAQ,SAAS,KAAK;AAAA;AAAA,EAClC,CAAC;AAAA,EACD,4BAA4B,MAAM;AAAA,IAC9B,SAAS;AAAA,IACT,MAAM;AAAA,IACN,OAAO;AAAA;AAAA,IAEP,OAAO,CAAC,QAAQ,WAAW,YAAY,UAAU,SAAS,aAAa;AAAA,EAC3E,CAAC;AAAA,EACD,yBAAyB,MAAM;AAAA,IAC3B,SAAS;AAAA,IACT,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO,CAAC,QAAQ,UAAU;AAAA,EAC9B,CAAC;AAAA,EACD,8BAA8B,MAAM;AAAA,IAChC,SAAS;AAAA,IACT,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO,CAAC,WAAW;AAAA,EACvB,CAAC;AAAA,EACD,gCAAgC,MAAM;AAAA,IAClC,SAAS;AAAA,IACT,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO,CAAC,MAAM;AAAA,EAClB,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMD,oBAAoB,MAAM;AAAA,IACtB,SAAS;AAAA,IACT,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO,CAAC,kBAAkB,cAAc,eAAe,eAAe,UAAU;AAAA;AAAA,EACpF,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMD,4BAA4B,MAAM;AAAA,IAC9B,SAAS;AAAA,IACT,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO,CAAC,WAAW,kBAAkB,gBAAgB,gBAAgB;AAAA;AAAA,EACzE,CAAC;AAAA,EACD,6BAA6B,MAAM;AAAA,IAC/B,SAAS;AAAA,IACT,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO,CAAC,WAAW,aAAa,UAAU,UAAU;AAAA;AAAA,EACxD,CAAC;AAAA,EACD,6BAA6B,MAAM;AAAA,IAC/B,SAAS;AAAA,IACT,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO,CAAC,cAAc,SAAS;AAAA;AAAA,EACnC,CAAC;AAAA;AAAA;AAAA;AAAA,EAID,6BAA6B,MAAM;AAAA,IAC/B,SAAS;AAAA,IACT,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO,CAAC,UAAU,YAAY,eAAe;AAAA;AAAA,EACjD,CAAC;AAAA,EACD,kCAAkC,MAAM;AAAA,IACpC,SAAS;AAAA,IACT,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO,CAAC,WAAW,QAAQ;AAAA;AAAA,EAC/B,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASD,4BAA4B,MAAM;AAAA,IAC9B,SAAS;AAAA,IACT,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO,CAAC,SAAS,gBAAgB;AAAA;AAAA,EACrC,CAAC;AACL;AACO,IAAM,wBAAwB,OAAO,KAAK,gBAAgB;;;ACzO1D,IAAM,6BAA6B,OAAO,OAAO;AAAA,EACpD,cAAc;AAAA,EACd,SAAS;AACb,CAAC;;;ACtEM,IAAM,uBAAuB;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACJ;AACO,IAAM,4BAA4B;AAAA,EACrC,GAAG;AAAA,EACH;AAAA,EACA;AACJ;;;AClDO,IAAM,2BAA2B;AAEjC,SAAS,eAAe,MAAM;AAGjC,QAAM,QAAQ,CAAC,GAAG,KAAK,KAAK,CAAC,EAAE,CAAC;AAChC,SAAO,QAAQ,MAAM,YAAY,IAAI;AACzC;;;ACDO,IAAM,sBAAsB,CAAC,cAAc,WAAW,QAAQ;AAC9D,SAAS,mBAAmB,QAAQ;AACvC,MAAI,CAAC,UAAU,OAAO,WAAW,UAAU;AACvC,WAAO,CAAC;AAAA,EACZ;AACA,QAAM,MAAM,EAAE,GAAG,OAAO;AACxB,aAAW,OAAO,qBAAqB;AACnC,WAAO,IAAI,GAAG;AAAA,EAClB;AACA,SAAO;AACX;;;ACXA,IAAM,iBAAiB,oBAAI,IAAI;AAAA,EAC3B;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AACJ,CAAC;AACD,IAAM,cAAc,IAAI,YAAY,SAAS,EAAE,OAAO,MAAM,CAAC;AAG7D,SAAS,qBAAqB,OAAO;AACjC,MAAI,MAAM;AACV,MAAI,UAAU,CAAC;AACf,QAAM,eAAe,MAAM;AACvB,QAAI,QAAQ,WAAW;AACnB;AACJ,UAAM,QAAQ,IAAI,WAAW,OAAO;AACpC,WAAO,YAAY,OAAO,KAAK;AAC/B,cAAU,CAAC;AAAA,EACf;AACA,MAAIC,KAAI;AACR,SAAOA,KAAI,MAAM,QAAQ;AACrB,UAAM,KAAK,MAAMA,EAAC;AAClB,QAAI,OAAO,OAAOA,KAAI,IAAI,MAAM,UAAU,MAAM,MAAMA,KAAI,CAAC,CAAC,KAAK,MAAM,MAAMA,KAAI,CAAC,CAAC,GAAG;AAClF,YAAM,OAAO,SAAS,MAAM,MAAMA,KAAI,GAAGA,KAAI,CAAC,GAAG,EAAE;AACnD,UAAI,eAAe,IAAI,IAAI,GAAG;AAC1B,qBAAa;AAGb,eAAO,IAAI,MAAM,MAAMA,KAAI,GAAGA,KAAI,CAAC,EAAE,YAAY,CAAC;AAClD,QAAAA,MAAK;AAAA,MACT,OACK;AACD,gBAAQ,KAAK,IAAI;AACjB,QAAAA,MAAK;AAAA,MACT;AAAA,IACJ,OACK;AACD,mBAAa;AACb,aAAO;AACP,MAAAA,MAAK;AAAA,IACT;AAAA,EACJ;AACA,eAAa;AACb,SAAO;AACX;AACA,SAAS,MAAMC,IAAG;AACd,SAAQA,MAAK,OAAOA,MAAK,OAASA,MAAK,OAAOA,MAAK,OAASA,MAAK,OAAOA,MAAK;AACjF;AAEA,SAAS,kBAAkBC,IAAG;AAC1B,QAAM,IAAIA,GAAE,QAAQ,GAAG;AACvB,MAAI,MAAM;AACN,IAAAA,KAAIA,GAAE,MAAM,GAAG,CAAC;AACpB,QAAM,IAAIA,GAAE,QAAQ,GAAG;AACvB,MAAI,MAAM;AACN,IAAAA,KAAIA,GAAE,MAAM,GAAG,CAAC;AACpB,SAAOA;AACX;AAQO,SAAS,eAAe,MAAM;AACjC,MAAI,OAAO,SAAS,YAAY,KAAK,WAAW,GAAG;AAC/C,UAAM,IAAI,UAAU,kDAAkD;AAAA,EAC1E;AACA,MAAI,CAAC,KAAK,WAAW,GAAG,GAAG;AACvB,UAAM,IAAI,UAAU,gEAAgE,KAAK,UAAU,IAAI,CAAC,EAAE;AAAA,EAC9G;AACA,MAAIA,KAAI,kBAAkB,IAAI;AAC9B,EAAAA,KAAI,qBAAqBA,EAAC;AAC1B,EAAAA,KAAIA,GAAE,QAAQ,QAAQ,GAAG;AACzB,MAAIA,GAAE,SAAS,KAAKA,GAAE,SAAS,GAAG;AAC9B,IAAAA,KAAIA,GAAE,MAAM,GAAG,EAAE;AACrB,SAAOA;AACX;;;AC/EO,IAAM,0BAA0B;AAChC,IAAM,YAAY,iBACpB,OAAO;AAAA,EACR,UAAU,iBACL,OAAO,EACP,MAAM,yBAAyB;AAAA,IAChC,SAAS;AAAA,EACb,CAAC,EACI,SAAS,qCAAqC;AAAA,EACnD,OAAO,iBACF,MAAM,CAAC,iBAAE,OAAO,GAAG,iBAAE,MAAM,iBAAE,OAAO,CAAC,CAAC,CAAC,EACvC,YAAY,CAAC,OAAO,QAAQ;AAc7B,eAAW,SAAS,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK,GAAG;AACxD,UAAI;AACJ,UAAI;AACA,oBAAY,eAAe,KAAK;AAAA,MACpC,SACO,KAAK;AACR,YAAI,SAAS;AAAA,UACT,MAAM,iBAAE,aAAa;AAAA,UACrB,SAAS,yDAAyD,KAAK,UAAU,KAAK,CAAC,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,QACjJ,CAAC;AACD;AAAA,MACJ;AACA,UAAI,cAAc,OAAO;AACrB,YAAI,SAAS;AAAA,UACT,MAAM,iBAAE,aAAa;AAAA,UACrB,SAAS,SAAS,KAAK,UAAU,KAAK,CAAC,gCAA2B,KAAK,UAAU,SAAS,CAAC;AAAA,QAC/F,CAAC;AAAA,MACL;AAAA,IACJ;AAAA,EACJ,CAAC,EACI,SAAS,uCAAuC;AACzD,CAAC,EACI,OAAO,EACP,SAAS,4FAA4F;AAiBnG,IAAM,mBAAmB;AAAA,EAC5B,IAAI,iBAAE,OAAO,EAAE,SAAS,EAAE,SAAS,qDAAqD;AAAA,EACxF,OAAO,iBACF,OAAO,EACP,IAAI,GAAG,EACP,SAAS,EACT,SAAS,6GAA6G;AAAA,EAC3H,aAAa,iBACR,OAAO,EACP,IAAI,GAAI,EACR,SAAS,EACT,SAAS,wHAAwH;AAAA,EACtI,YAAY,iBACP,MAAM,iBAAE,OAAO,EAAE,IAAI,GAAG,CAAC,EACzB,IAAI,EAAE,EACN,SAAS,EACT,SAAS,+KAA+K;AACjM;AAaO,IAAM,mBAAmB;AAAA;AAAA,EAE5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AACJ;AACO,IAAM,kBAAkB,iBAC1B,KAAK,gBAAgB,EACrB,SAAS,mKAAmK;AAE1K,IAAM,sBAAsB,CAAC,gBAAgB,cAAc,cAAc;AACzE,IAAM,oBAAoB,iBAC5B,KAAK,mBAAmB,EACxB,SAAS,uIAAuI;AAS9I,IAAM,oBAAoB,iBAC5B,OAAO;AAAA,EACR,MAAM,iBAAE,QAAQ,UAAU;AAAA,EAC1B,KAAK,iBAAE,OAAO,EAAE,SAAS,mDAAmD;AAChF,CAAC,EACI,SAAS,0HACwC;AAC/C,IAAM,kBAAkB,iBAC1B,OAAO;AAAA,EACR,MAAM,iBAAE,QAAQ,OAAO;AAAA,EACvB,SAAS,iBAAE,OAAO,EAAE,SAAS,sCAAsC;AACvE,CAAC,EACI,SAAS,wDAAwD;AAC/D,IAAM,0BAA0B,iBAClC,OAAO;AAAA,EACR,MAAM,iBAAE,QAAQ,gBAAgB;AAAA,EAChC,UAAU,iBAAE,OAAO,EAAE,SAAS,oCAAoC;AAAA,EAClE,OAAO,iBACF,KAAK,CAAC,WAAW,WAAW,QAAQ,CAAC,EACrC,SAAS,oEAAoE;AACtF,CAAC,EACI,SAAS,qIAC0E;AACjF,IAAM,0BAA0B,iBAClC,OAAO;AAAA,EACR,MAAM,iBAAE,QAAQ,gBAAgB;AAAA,EAChC,WAAW,iBAAE,OAAO,EAAE,SAAS,2CAA2C;AAAA,EAC1E,UAAU,iBAAE,OAAO,EAAE,SAAS,EAAE,SAAS,kDAAkD;AAC/F,CAAC,EACI,SAAS,8IACsE;AAC7E,IAAM,wBAAwB,iBAChC,OAAO;AAAA,EACR,MAAM,iBAAE,QAAQ,cAAc;AAAA,EAC9B,KAAK,iBACA,OAAO,EACP,SAAS,gIAAgI;AAAA,EAC9I,OAAO,iBAAE,QAAQ,EAAE,SAAS,iCAAiC;AACjE,CAAC,EACI,SAAS,8TAE+F;AACtG,IAAM,qBAAqB,iBAC7B,OAAO;AAAA,EACR,MAAM,iBAAE,QAAQ,UAAU;AAAA,EAC1B,UAAU,iBAAE,OAAO,EAAE,SAAS,EAAE,SAAS,kCAAkC;AAAA,EAC3E,UAAU,iBAAE,OAAO,EAAE,SAAS,EAAE,SAAS,kCAAkC;AAAA,EAC3E,WAAW,iBAAE,OAAO,EAAE,SAAS,EAAE,SAAS,mCAAmC;AAAA,EAC7E,WAAW,iBAAE,OAAO,EAAE,SAAS,EAAE,SAAS,mCAAmC;AACjF,CAAC,EACI,SAAS,uJACoE;AAC3E,IAAM,0BAA0B,iBAClC,OAAO;AAAA,EACR,MAAM,iBAAE,QAAQ,gBAAgB;AAAA,EAChC,KAAK;AAAA,EACL,UAAU,iBAAE,KAAK,CAAC,OAAO,OAAO,MAAM,MAAM,IAAI,CAAC;AAAA,EACjD,WAAW,iBAAE,OAAO,EAAE,SAAS,sCAAsC;AACzE,CAAC,EACI,SAAS,qOAEsF;AAC7F,IAAM,sBAAsB,iBAC9B,OAAO;AAAA,EACR,MAAM,iBAAE,QAAQ,WAAW;AAAA,EAC3B,KAAK,iBAAE,OAAO,EAAE,SAAS,6CAA6C;AAAA,EACtE,UAAU,iBACL,QAAQ,EACR,SAAS,EACT,SAAS,sDAAsD;AACxE,CAAC,EACI,SAAS,0GAA0G;AACjH,IAAM,2BAA2B,iBACnC,OAAO;AAAA,EACR,MAAM,iBAAE,QAAQ,iBAAiB;AAAA,EACjC,KAAK,iBAAE,OAAO,EAAE,SAAS,cAAc;AAAA,EACvC,UAAU,iBAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,4CAA4C;AAC1F,CAAC,EACI,SAAS,8GAA8G;AACrH,IAAM,2BAA2B,iBACnC,OAAO;AAAA,EACR,MAAM,iBAAE,QAAQ,iBAAiB;AAAA,EACjC,KAAK,iBAAE,OAAO,EAAE,SAAS,uBAAuB;AAAA,EAChD,OAAO,iBAAE,OAAO,EAAE,SAAS,uBAAuB;AAAA,EAClD,UAAU,iBAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,uCAAuC;AACrF,CAAC,EACI,SAAS,sGAAsG;AAC7G,IAAM,WAAW,iBACnB,OAAO;AAAA,EACR,QAAQ,iBAAE,MAAM,CAAC,iBAAE,OAAO,GAAG,iBAAE,OAAO,GAAG,iBAAE,QAAQ,CAAC,CAAC,EAAE,SAAS;AAAA,EAChE,UAAU,iBAAE,OAAO,EAAE,SAAS;AAClC,CAAC,EACI,OAAO,CAAC,aAAa,OAAO,SAAS,WAAW,MAAS,IAAI,OAAO,SAAS,aAAa,MAAS,MAAM,GAAG;AAAA,EAC7G,SAAS;AACb,CAAC,EACI,SAAS,0FAA0F;AACjG,IAAM,cAAc,iBACtB,OAAO;AAAA,EACR,QAAQ,iBACH,MAAM,eAAe,EACrB,IAAI,CAAC,EACL,SAAS,kEAAkE;AAAA,EAChF,OAAO,iBACF,OAAO,iBAAE,OAAO,GAAG,QAAQ,EAC3B,SAAS,EACT,SAAS,8HACgD;AAClE,CAAC,EACI,SAAS,2FAA2F;AAClG,IAAM,uBAAuB,iBAC/B,OAAO;AAAA,EACR,MAAM,iBAAE,QAAQ,aAAa;AAAA,EAC7B,KAAK,iBAAE,OAAO,EAAE,SAAS,iEAAiE;AAAA,EAC1F,UAAU,iBAAE,KAAK,CAAC,OAAO,OAAO,MAAM,MAAM,IAAI,CAAC;AAAA,EACjD,OAAO,iBAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,SAAS,wBAAwB;AAAA,EAChE,UAAU,iBACL,OAAO,EACP,SAAS,EACT,SAAS,EACT,SAAS,wDAAwD;AAAA,EACtE,SAAS,YAAY,SAAS,EAAE,SAAS,0DAA0D;AACvG,CAAC,EACI,SAAS,yQAEkF;AACzF,IAAM,aAAa,iBAAE,mBAAmB,QAAQ;AAAA,EACnD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACJ,CAAC;AAIM,IAAM,QAAQ,iBAChB,OAAO;AAAA,EACR,YAAY,iBACP,MAAM,UAAU,EAChB,SAAS,8EAAyE;AAAA,EACvF,OAAO,iBACF,QAAQ,EACR,SAAS,oFAAoF;AACtG,CAAC,EACI,SAAS,gLACyE;AAChF,IAAM,gBAAgB,iBACxB,OAAO;AAAA,EACR,MAAM,iBAAE,QAAQ,OAAO;AAAA,EACvB,OAAO,iBACF,MAAM,KAAK,EACX,SAAS,yEAAoE;AAAA,EAClF,SAAS,iBACJ,QAAQ,EACR,SAAS,uFAAuF;AACzG,CAAC,EACI,SAAS,qNAEyD;AAChE,IAAM,iBAAiB,iBACzB,OAAO;AAAA,EACR,MAAM,iBAAE,QAAQ,OAAO;AAAA,EACvB,OAAO,iBAAE,OAAO;AAAA,EAChB,WAAW,iBAAE,OAAO;AAAA,EACpB,OAAO,iBAAE,QAAQ;AAAA,EACjB,OAAO,iBAAE,QAAQ;AACrB,CAAC,EACI,SAAS,mEAAmE;AAC1E,IAAM,iBAAiB,iBACzB,OAAO;AAAA,EACR,MAAM,iBAAE,QAAQ,OAAO;AAAA,EACvB,SAAS,iBAAE,OAAO;AAAA,EAClB,QAAQ,iBAAE,MAAM,iBAAE,OAAO,CAAC;AAAA,EAC1B,eAAe,iBAAE,OAAO,iBAAE,OAAO,GAAG,iBAAE,QAAQ,CAAC;AAAA,EAC/C,SAAS,iBAAE,QAAQ;AACvB,CAAC,EACI,SAAS,8DAA8D;AACrE,IAAM,oBAAoB,iBAC5B,OAAO;AAAA,EACR,MAAM,iBAAE,QAAQ,UAAU;AAAA,EAC1B,UAAU,iBAAE,OAAO;AAAA,EACnB,QAAQ,iBAAE,KAAK,CAAC,OAAO,MAAM,CAAC,EAAE,SAAS;AAAA,EACzC,SAAS,iBAAE,QAAQ;AAAA,EACnB,WAAW,iBAAE,OAAO,EAAE,SAAS;AACnC,CAAC,EACI,SAAS,kEAAkE;AACzE,IAAM,oBAAoB,iBAAE,mBAAmB,QAAQ;AAAA,EAC1D;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACJ,CAAC;AAEM,IAAM,eAAe,kBAAkB,SAAS,EAAE,SAAS;AA2F3D,IAAM,cAAc,iBAAE,OAAO;AAAA,EAChC,QAAQ,iBAAE,MAAM,iBAAE,OAAO,CAAC;AAAA,EAC1B,aAAa,iBAAE,OAAO,EAAE,SAAS;AAAA,EACjC,OAAO,iBAAE,OAAO,iBAAE,MAAM,CAAC,iBAAE,OAAO,GAAG,iBAAE,OAAO,GAAG,iBAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,SAAS;AAC7E,CAAC;AAKM,IAAM,UAAU,iBAClB,OAAO;AAAA,EACR,OAAO,iBAAE,OAAO,EAAE,SAAS,EAAE,SAAS,oBAAoB;AAAA,EAC1D,MAAM,iBAAE,OAAO,EAAE,SAAS,EAAE,SAAS,wBAAwB;AAAA,EAC7D,MAAM,iBAAE,OAAO,EAAE,SAAS,EAAE,SAAS,kCAAkC;AAC3E,CAAC,EACI,SAAS,8DAA8D,EACvE,SAAS,EACT,SAAS;;;AC7RP,IAAM,kBAAkB;AAE/B,IAAM,sBAAsB;AAC5B,IAAM,wBAAwB;AAGvB,SAAS,aAAa,SAAS;AAClC,MAAI,WAAW;AACX,WAAO;AACX,MAAI,WAAW;AACX,WAAO;AACX,SAAO;AACX;AA4BO,IAAM,0BAAN,MAA8B;AAAA,EACjC,YAAY,MAAM;AACd,SAAK,QAAQ;AACb,SAAK,QAAQ;AACb,SAAK,OAAO;AACZ,SAAK,QAAQ;AACb,SAAK,cAAc,IAAI;AAAA,EAC3B;AAAA,EACA,MAAM,OAAO;AACT,SAAK,QAAQ;AACb,UAAM,OAAO,aAAa,KAAK;AAC/B,UAAM,UAAU,KAAK,MAAM,aAAa,eAAe;AACvD,SAAK,QAAQ;AACb,QAAI,YAAY,MAAM;AAClB,WAAK,MAAM,aAAa,iBAAiB,IAAI;AAC7C,WAAK,MAAM,cAAc;AAAA,IAC7B;AAAA,EACJ;AAAA,EACA,gBAAgB;AACZ,UAAM,KAAK,OAAO,mBAAmB,aAAa,iBAAiB;AACnE,QAAI,CAAC,IAAI;AAGL,YAAM,WAAW,MAAM,KAAK,MAAM,OAAO,UAAU;AACnD,eAAS;AACT,aAAO,iBAAiB,UAAU,UAAU,EAAE,SAAS,KAAK,CAAC;AAC7D,WAAK,OAAO,MAAM,OAAO,oBAAoB,UAAU,QAAQ;AAC/D;AAAA,IACJ;AACA,SAAK,MAAM,KAAK,MAAM,sBAAsB,EAAE,KAAK;AACnD,UAAM,KAAK,IAAI,GAAG,CAAC,YAAY;AAC3B,iBAAWC,UAAS;AAChB,aAAK,MAAMA,OAAM,YAAY,KAAK;AAAA,IAC1C,CAAC;AACD,OAAG,QAAQ,KAAK,KAAK;AACrB,SAAK,OAAO,MAAM,GAAG,WAAW;AAAA,EACpC;AAAA,EACA,mBAAmB;AACf,SAAK,OAAO;AACZ,SAAK,OAAO;AAAA,EAChB;AACJ;;;AC1OO,IAAM,sBAAsB;;;ACG7B,SAAU,UAAO;AACrB,QAAM,UAAW,OAA8C,mBAAmB;AAGlF,SAAO,WACL,OAAO,QAAQ,cAAc,cAC7B,OAAO,QAAQ,aAAa,aACzB,UACD;AACN;;;AC/BA,IAAM,WAAW;AACjB,IAAM,WAAW;AAEjB,IAAM,gBAAgB;AAEtB,IAAM,qBAAqB;AAC3B,IAAM,WAAW;AAajB,SAAS,UAAU,MAAY;AAC7B,MAAI,cAAc,KAAK,IAAI;AAAG,WAAO;AACrC,SAAO,KACJ,MAAM,GAAG,EACT,IAAI,CAAC,YACJ,QACG,MAAM,QAAQ,EACd,OAAO,CAAC,SAAS,cAAc,KAAK,IAAI,KAAK,KAAK,UAAU,QAAQ,EACpE,KAAK,GAAG,CAAC,EAEb,OAAO,OAAO,EACd,KAAK,GAAG,EACR,MAAM,CAAC,QAAQ,EACf,KAAI;AACT;AAEA,SAAS,gBAAa;AACpB,MAAI;AACF,UAAM,MAAM,aAAa,QAAQ,kBAAkB;AACnD,UAAM,OAAO,MAAO,KAAK,MAAM,GAAG,EAAyB,OAAO;AAClE,WAAO,QAAQ,OAAO,SAAS,WAAY,OAAkC,CAAA;EAC/E,QAAQ;AACN,WAAO,CAAA;EACT;AACF;AASM,SAAU,eAAY;AAC1B,QAAM,SAAS,cAAa;AAC5B,QAAM,QAAkB,CAAA;AACxB,QAAM,QAAQ,UACZ,OAAO,OAAO,sBAAsB,WAAW,OAAO,oBAAoB,SAAS,QAAQ;AAE7F,MAAI;AAAO,UAAM,KAAK,KAAK;AAC3B,QAAM,OAAO,OAAO,OAAO,sBAAsB,WAAW,OAAO,oBAAoB;AACvF,MAAI,wBAAwB,KAAK,IAAI,KAAK,SAAS,SAAS;AAAU,UAAM,KAAK,IAAI;AACrF,SAAO,MAAM,MAAM,GAAG,CAAC;AACzB;AAGM,SAAU,iBAAc;AAC5B,MAAI;AACF,WAAO,OAAO,OAAO,SAAS,YAAW,MAAO;EAClD,QAAQ;AACN,WAAO;EACT;AACF;AAGM,SAAU,YAAS;AACvB,QAAM,OAAO,SAAS,cAA+B,+BAA+B;AACpF,UAAQ,MAAM,WAAW,IAAI,KAAI,EAAG,MAAM,GAAG,QAAQ;AACvD;;;AC7DA,SAAS,QAAAC,OAAM,cAAAC,aAAY,WAAAC,gBAAe;AAC1C,SAAS,cAAc;AACvB,SAAS,QAAQ,YAAY,oBAAoB;;;ACb1C,IAAM,eAA0B,OAAO,OAAO;EACnD,iBAAiB;EACjB,aAAa;EACb,eAAe;EACf,UAAU;EACV,eAAe;EACf,YAAY;EACZ,cAAc;EACd,YAAY;EACZ,YAAY;EACZ,YAAY;CACb;AAED,IAAM,SAAS,oBAAI,QAAO;AAGpB,SAAU,OAAO,MAAsB;AAC3C,QAAM,OAAQ,MAAsD;AACpE,MAAI,CAAC,QAAQ,OAAO,SAAS;AAAU,WAAO;AAE9C,QAAM,QAAQ,OAAO,IAAI,IAAI;AAC7B,MAAI;AAAO,WAAO;AAClB,QAAM,MAAM,EAAE,GAAG,aAAY;AAC7B,aAAW,OAAO,OAAO,KAAK,YAAY,GAA0B;AAClE,UAAM,OAAO,KAAK,GAAG;AACrB,QAAI,OAAO,SAAS,YAAY,KAAK,KAAI;AAAI,UAAI,GAAG,IAAI;EAC1D;AACA,SAAO,IAAI,MAAM,GAAG;AACpB,SAAO;AACT;;;AC/BA,SAAS,OAAAC,MAAK,QAAAC,OAAM,WAAAC,gBAA+D;;;ACanF,SAAS,kBAA6C;AAK/C,IAAM,kBAAkB;AACxB,IAAM,mBAAmB;AACzB,IAAM,mBAAmB;AAa1B,IAAO,iBAAP,MAAO,uBAAyB,WAAU;EAsB9C,cAAA;AACE,UAAK;AAHY,SAAA,OAAO,IAAI,wBAAwB,IAAI;AAIxD,SAAK,OAAO;AACZ,SAAK,SAAS;AACd,SAAK,SAAS;AACd,SAAK,OAAO,MAAM;AAClB,SAAK,OAAO;AACZ,SAAK,OAAO;AACZ,SAAK,SAAS,CAAA;EAChB;;EAGA,QAAQ,MAAc,MAAY;AAChC,SAAK,SAAS,EAAE,GAAG,KAAK,QAAQ,CAAC,IAAI,GAAG,KAAI;EAC9C;;EAGA,MAAM,eAAY;AAChB,UAAM,KAAK;AACX,SAAK,WAAW,cAA2B,gBAAgB,GAAG,MAAM,EAAE,eAAe,KAAI,CAAE;EAC7F;EAEU,SAAS,MAAc,UAAmC;AAClE,WAAO,KAAK,OAAO,IAAI,KAAK,YAAY;EAC1C;EAEU,WAAQ;AAChB,WAAO,KAAK,OAAO,SAAS;EAC9B;EAEU,QAAQ,KAAa;AAC7B,QAAI,KAAK;AAAM;AACf,SAAK,cACH,IAAI,YAAsB,iBAAiB,EAAE,QAAQ,KAAK,SAAS,MAAM,UAAU,KAAI,CAAE,CAAC;EAE9F;EAEU,SAAS,QAAgB,QAAgB,YAA2B,MAAI;AAChF,QAAI,KAAK;AAAM;AACf,SAAK,cACH,IAAI,YAA6B,kBAAkB;MACjD,QAAQ,EAAE,QAAQ,QAAQ,UAAS;MACnC,SAAS;MACT,UAAU;KACX,CAAC;EAEN;;AAnEgB,eAAA,aAAmC;EACjD,MAAM,EAAE,WAAW,MAAK;EACxB,QAAQ,EAAE,WAAW,MAAK;EAC1B,QAAQ,EAAE,WAAW,MAAK;EAC1B,MAAM,EAAE,WAAW,MAAK;EACxB,MAAM,EAAE,MAAM,QAAO;EACrB,MAAM,EAAE,WAAW,MAAK;EACxB,QAAQ,EAAE,OAAO,KAAI;;AARnB,IAAO,gBAAP;;;AC/BA,SAAU,MAAM,SAAsB;AAC1C,QAAM,UAAU,QAAQ,SAAS,OAAO,CAAC,MAAM,EAAE,QAAQ,EAAE;AAC3D,SAAO,YAAY,IAAI,YAAY,YAAY,IAAI,QAAQ;AAC7D;AAGA,eAAsB,SAAS,MAAwB,QAAc;AACnE,MAAI,CAAC,MAAM;AAAe,WAAO;AACjC,MAAI;AACF,WAAO,MAAM,MAAM,KAAK,cAAc,MAAM,CAAC;EAC/C,QAAQ;AACN,WAAO;EACT;AACF;;;ACxBA,SAAS,YAAY;AAGf,SAAU,QAAQ,KAAgC,MAAY;AAClE,SAAO,MACH,gBAAgB,GAAG,8BACnB,gDAAgD,eAAe,IAAI,KAAK,EAAE;AAChF;AAMM,SAAU,YACd,OACA,UAAmC;AAEnC,MAAI,SAAS,QAAQ,EAAE,QAAQ,MAAM,CAAC;AAAU,WAAO;AACvD,MAAI;AACF,WAAO,IAAI,KAAK,aAAa,QAAW,EAAE,OAAO,YAAY,SAAQ,CAAE,EAAE,OAAO,KAAK;EACvF,QAAQ;AACN,WAAO;EACT;AACF;;;ACnBA,SAAS,KAAK,QAAAC,OAAM,eAAe;AAI7B,SAAU,KAAK,OAAe,OAAa;AAC/C,SAAOA,iDAAgD,KAAK;OACvD,CAAC,GAAG,GAAG,CAAC,EAAE,IAAI,CAACC,OAAMD,oBAAmBC,MAAK,QAAQ,WAAW,KAAK,UAAU,CAAC;;AAEvF;AAGM,SAAU,MAAMC,IAAiB,OAAa;AAClD,SAAOF;eACME,GAAE,KAAK;mDAC6BA,GAAE,KAAK;MACpD,KAAK,OAAO,GAAGA,GAAE,KAAK,KAAK,KAAK,OAAO,CAAC;;AAE9C;AAGM,SAAU,SAAS,QAAqB,OAAa;AACzD,MAAI,CAAC,OAAO;AAAQ,WAAO;AAC3B,SAAOF,uCAAsC,KAAK;MAC9C,OAAO,IAAI,CAACG,OAAMH,mCAAkCG,GAAE,KAAK,QAAQA,GAAE,KAAK,UAAU,CAAC;;AAE3F;AAGO,IAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AChC1B,SAAS,OAAAC,MAAK,iBAAiB;AAOxB,IAAM,iBAAiBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBA2EZ,UAAU,wBAAwB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ALjE9C,IAAM,gBAAgB;AAIvB,IAAO,gBAAP,MAAO,sBAAqB,cAA0B;EAoL1D,cAAA;AACE,UAAK;AAHU,SAAA,SAAS,oBAAI,IAAG;AAI/B,SAAK,QAAQ,CAAA;EACf;EAEmB,QAAQ,SAAuB;AAChD,QAAI,QAAQ,IAAI,MAAM,KAAK,QAAQ,IAAI,MAAM;AAAG,WAAK,KAAK,eAAc;EAC1E;;EAGQ,MAAM,iBAAc;AAC1B,UAAM,OAAO,KAAK,KAAI;AACtB,UAAM,OAAO,KAAK;AAClB,QAAI,CAAC,QAAQ,CAAC,MAAM;AAAe;AACnC,UAAM,UAAU,KAAK,QAAQ,IAAI,CAAC,MAAM,EAAE,QAAQ,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,KAAK,OAAO,IAAI,CAAC,CAAC;AAC3F,QAAI,CAAC,QAAQ;AAAQ;AACrB,eAAW,UAAU;AAAS,WAAK,OAAO,IAAI,MAAM;AACpD,UAAM,OAA4B,CAAA;AAClC,UAAM,QAAQ,IACZ,QAAQ,IAAI,OAAO,WAAU;AAC3B,WAAK,MAAM,IAAI,MAAM,SAAS,MAAM,MAAM;IAC5C,CAAC,CAAC;AAEJ,SAAK,QAAQ,EAAE,GAAG,KAAK,OAAO,GAAG,KAAI;EACvC;EAES,SAAM;AACb,UAAM,OAAO,KAAK;AAClB,QAAI,CAAC;AAAM,aAAOC;AAClB,WAAOC,2BAA0B,KAAK,KAAK;6EAC8B,KAAK,SAAS,YAAY,KAAK,QAAQ,CAAC;;UAE3G,KAAK,UAAU,IAAI,CAACC,OAAMD,qBAAoBC,GAAE,KAAK,mBAAmBA,GAAE,KAAK,cAAc,CAAC;;uCAEjE,KAAK,QAAQ,MAAM;UAChD,KAAK,QAAQ,IAAI,CAAC,MAAM,KAAK,QAAQ,MAAM,CAAC,CAAC,CAAC;;EAEtD;EAEQ,QAAQ,MAAmB,GAAS;AAC1C,UAAM,SAAS,EAAE,QAAQ;AACzB,UAAM,MAAW,KAAK,MAAM,MAAM,MAAM,KAAK,KAAI,GAAI,gBAAgB,aAAa;AAClF,UAAM,OAAO,WAAW,MAAM;AAC9B,UAAM,QAAQ,YAAY,EAAE,QAAQ,OAAO,EAAE,QAAQ,QAAQ;AAC7D,WAAOD,sCAAqC,EAAE,IAAI;;UAE5C,QAAQ,EAAE,QAAQ,WAAW,EAAE,QAAQ,IAAI,CAAC;UAC5C,EAAE,OAAOA,2BAA0B,KAAK,KAAK,QAAQ,YAAYD,QAAO;;;;4BAItD,KAAK,SAAQ,CAAE;qBACtB,MAAM,KAAK,QAAQ,EAAE,MAAM,WAAW,QAAQ,EAAE,QAAQ,OAAM,CAAE,CAAC;;cAExE,EAAE,QAAQ,IAAI;;YAEhB,QAAQC,4BAA2B,KAAK,YAAYD,QAAO;;;;;YAK3D,KAAK,UAAU,IAAI,CAACE,OAAK;AACzB,YAAM,QAAQ,EAAE,OAAOA,GAAE,EAAE,KAAK;AAChC,aAAOD;yBACMC,GAAE,KAAK,QAAQA,GAAE,KAAK;gBAC/B,KAAK,OAAO,GAAG,KAAK,OAAO,CAAC;;IAElC,CAAC,CAAC;;sCAE0B,IAAI,IAAI,KAAK,SAAS,MAAM,EAAE,MAAM,CAAC;UACjE,KAAK,QAAQ,KAAK,KAAK,SAAS,QAAQ,EAAE,QAAQ,IAAI,CAAC;;;EAG/D;;EAGQ,QAAQ,KAAU,QAAgB,QAAgB,MAAY;AACpE,QAAI,QAAQ;AAAW,aAAOD;AAC9B,QAAI,QAAQ;AACV,aAAOA;;qBAEQ,mBAAmB,IAAI,EAAE;wBACtB,KAAK,SAAQ,CAAE;iBACtB,MAAM,KAAK,QAAQ,EAAE,MAAM,WAAW,OAAM,CAAE,CAAC;;;;AAI5D,QAAI,QAAQ;AACV,aAAOA;AACT,WAAOA;;mBAEQ,gBAAgB,IAAI,EAAE;sBACnB,KAAK,SAAQ,CAAE;eACtB,MAAM,KAAK,SAAS,QAAQ,MAAM,CAAC;;;;EAIhD;;AApRgB,cAAA,aAAmC,EAAE,OAAO,EAAE,OAAO,KAAI,EAAE;AAC3D,cAAA,SAAS;EACvB;EACA;EACAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AALE,IAAO,eAAP;AAwRN,IAAI,CAAC,eAAe,IAAI,aAAa,GAAG;AACtC,iBAAe,OAAO,eAAe,YAAY;AACnD;;;AMvSA,SAAS,OAAAC,MAAK,QAAAC,OAAM,WAAAC,gBAA+D;;;ACTnF,SAAS,OAAAC,YAAW;AAQb,IAAM,gBAAgBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ADUtB,IAAM,kBAAkB;AAEzB,IAAO,iBAAP,MAAO,uBAAsB,cAA2B;EAoD5D,cAAA;AACE,UAAK;AAHC,SAAA,UAAU;AAoBV,SAAA,cAAc,MAAW;AAC/B,UAAI,KAAK;AAAM,aAAK,QAAQ,EAAE,MAAM,WAAW,QAAQ,KAAK,KAAK,QAAQ,OAAM,CAAE;IACnF;AAEQ,SAAA,OAAO,MAAW;AACxB,UAAI,KAAK,QAAQ,KAAK,SAAS,OAAO;AACpC,aAAK,SAAS,KAAK,KAAK,SAAS,KAAK,KAAK,QAAQ,MAAM;MAC3D;IACF;AAxBE,SAAK,OAAO;EACd;EAEmB,WAAW,SAAuB;AACnD,QAAI,QAAQ,IAAI,MAAM,KAAK,QAAQ,IAAI,MAAM;AAAG,WAAK,KAAK,UAAS;EACrE;EAEQ,MAAM,YAAS;AACrB,UAAM,MAAM,EAAE,KAAK;AACnB,UAAM,SAAS,KAAK,MAAM,QAAQ;AAClC,QAAI,CAAC;AAAQ;AACb,SAAK,OAAO;AACZ,UAAM,MAAM,MAAM,SAAS,KAAK,KAAI,GAAI,MAAM;AAC9C,QAAI,QAAQ,KAAK;AAAS,WAAK,OAAO;EACxC;EAYS,SAAM;AACb,UAAM,IAAI,KAAK;AACf,QAAI,CAAC;AAAG,aAAOC;AACf,UAAM,IAAI,EAAE;AACZ,UAAM,QAAQ,YAAY,EAAE,OAAO,EAAE,QAAQ;AAC7C,WAAOC;;UAED,QAAQ,EAAE,WAAW,EAAE,IAAI,CAAC;6BACT,KAAK,KAAK,QAAQ;UACrC,SAAS,EAAE,SAAS,KAAK,KAAK,aAAa,CAAC;;2CAEX,EAAE,IAAI;YACrC,QAAQA,4BAA2B,KAAK,YAAYD,QAAO;;;;6BAI1C,EAAE,KAAK;+CACW,KAAK,SAAS,UAAU,EAAE,MAAM,CAAC;;YAEpE,EAAE,UAAU,IAAI,CAACE,OAAMD,YAAW,MAAMC,IAAG,EAAE,OAAOA,GAAE,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC;;;YAGvE,KAAK,QAAO,CAAE;YAEd,KAAK,SAAS,WACVF,WACAC;;+BAEe,KAAK,OAAO,aAAa,CAAC;kCACvB,KAAK,SAAQ,CAAE;2BACtB,KAAK,WAAW;;;0BAIjC;;;;EAIR;;EAGQ,OAAO,QAAc;AAC3B,WAAO,GAAG,MAAM,KAAK,KAAK,MAAM,QAAQ,QAAQ,EAAE;EACpD;EAEQ,UAAO;AACb,YAAQ,KAAK,MAAM;MACjB,KAAK;AACH,eAAOA;MACT,KAAK;AACH,eAAOA;MACT,KAAK;AACH,eAAOA;;uBAEQ,KAAK,OAAO,aAAa,CAAC;0BACvB,KAAK,SAAQ,CAAE;mBACtB,KAAK,IAAI;;;;MAItB;AACE,eAAOA;;uBAEQ,KAAK,OAAO,gBAAgB,CAAC;0BAC1B,KAAK,SAAQ,CAAE;mBACtB,KAAK,WAAW;;;;IAI/B;EACF;;AArJgB,eAAA,aAAmC,EAAE,MAAM,EAAE,OAAO,KAAI,EAAE;AAC1D,eAAA,SAAS;EACvB;EACA;EACA;EACAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AANE,IAAO,gBAAP;AAyJN,IAAI,CAAC,eAAe,IAAI,eAAe,GAAG;AACxC,iBAAe,OAAO,iBAAiB,aAAa;AACtD;;;AExKA,SAAS,OAAAC,MAAK,QAAAC,OAAM,WAAAC,gBAAe;AAK5B,IAAM,2BAA2B;AAOlC,SAAU,SAAS,OAAe,MAAe;AACrD,MAAI,SAAS;AAAG,WAAO;AACvB,QAAM,UAAU,SAAS,aAAa,IAAI,SAAS,WAAW,IAAI;AAClE,QAAM,SAAS,SAAS,YAAY,IAAI,KAAK,QAAQ;AACrD,QAAM,OAAO,QAAQ;AACrB,SAAO,SAAS,IAAI,IAAI,UAAU,OAAO;AAC3C;AAEM,IAAO,yBAAP,MAAO,+BAA8B,cAAmC;EAoEnE,SAAM;AACb,UAAM,OAAO,KAAK;AAClB,QAAI,CAAC;AAAM,aAAOC;AAClB,UAAM,OAAO,SAAS,KAAK,QAAQ,QAAQ,KAAK,KAAK,KAAK;AAC1D,UAAM,OAAO,KAAK,QAAQ,SAAS;AACnC,WAAOC,6EAA4E,KAAK,SAAS,YAAY,KAAK,QAAQ,CAAC;;UAErH,KAAK,QAAQ,IACb,CAACC,IAAGC,OAAMF,kBAAiBE,OAAM,QAAQ,OAAO,IAAI,QAAQ,IAAI,KAAKH,QAAO;;;2BAG3DE,GAAE,KAAK;8BACJ,KAAK,SAAQ,CAAE;uBACtB,MAAM,KAAK,QAAQ,EAAE,MAAM,YAAY,aAAaA,GAAE,GAAE,CAAE,CAAC;;yBAEzDA,GAAE,KAAK,sEAAsEA,GAAE,KAAK;;gBAE7F,CACP;;EAEP;;AAvFgB,uBAAA,SAAS;EACvB;EACAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAHE,IAAO,wBAAP;AA2FN,IAAI,CAAC,eAAe,IAAI,wBAAwB,GAAG;AACjD,iBAAe,OAAO,0BAA0B,qBAAqB;AACvE;;;AC7GA,SAAS,OAAAC,MAAK,QAAAC,OAAM,WAAAC,gBAA+D;AACnF,SAAS,gBAAgB;AASlB,IAAM,uBAAuB;AAC7B,IAAM,0BAA0B;AASvC,IAAM,QAAQ;AAGd,SAAS,UAAUC,IAAmB;AACpC,MAAI,CAACA,GAAE,SAASA,GAAE,gBAAgB;AAAG,WAAO;AAC5C,SAAO,QAAQ,KAAKA,GAAE,KAAK,IAAIA,GAAE,QAAQ;AAC3C;AAEM,IAAO,sBAAP,MAAO,4BAA2B,cAAgC;EAuNtE,cAAA;AACE,UAAK;AAJC,SAAA,OAAO;AACE,SAAA,UAAU,oBAAI,IAAG;AAqC1B,SAAA,QAAQ,MAAW;AACzB,WAAK,cACH,IAAI,YAAY,yBAAyB,EAAE,QAAQ,CAAA,GAAI,SAAS,MAAM,UAAU,KAAI,CAAE,CAAC;IAE3F;AArCE,SAAK,YAAY,EAAE,OAAO,WAAU;AACpC,SAAK,UAAU,CAAA;EACjB;EAEmB,WAAW,SAAuB;AACnD,QAAI,QAAQ,IAAI,MAAM,GAAG;AACvB,WAAK,UAAU,CAAA;AACf,WAAK,KAAK,cAAa;IACzB;EACF;EAEQ,MAAM,gBAAa;AACzB,UAAM,MAAM,EAAE,KAAK;AACnB,UAAM,SAAS,KAAK,MAAM,QAAQ;AAClC,UAAM,OAAO,KAAK,KAAI;AACtB,QAAI,CAAC,UAAU,CAAC,MAAM,eAAe;AACnC,WAAK,YAAY,EAAE,OAAO,aAAY;AACtC;IACF;AACA,SAAK,YAAY,EAAE,OAAO,WAAU;AACpC,QAAI;AACF,YAAM,OAAO,MAAM,KAAK,cAAc,MAAM;AAC5C,UAAI,QAAQ,KAAK;AAAM,aAAK,YAAY,EAAE,OAAO,SAAS,KAAI;IAChE,QAAQ;AACN,UAAI,QAAQ,KAAK;AAAM,aAAK,YAAY,EAAE,OAAO,aAAY;IAC/D;EACF;EAEQ,KAAK,WAAwB;AACnC,UAAM,IAAI,KAAK;AACf,QAAI;AAAG,WAAK,SAAS,EAAE,SAAS,EAAE,QAAQ,QAAQ,SAAS;EAC7D;EAQS,SAAM;AACb,UAAM,IAAI,KAAK;AACf,QAAI,CAAC;AAAG,aAAOC;AACf,UAAM,IAAI,EAAE;AACZ,UAAM,QAAQ,YAAY,EAAE,OAAO,EAAE,QAAQ;AAC7C,WAAOC;;UAED,QAAQ,EAAE,WAAW,EAAE,IAAI,CAAC;UAC5B,EAAE,OAAOA,2BAA0B,KAAK,KAAK,QAAQ,YAAYD,QAAO;UACxE,SAAS,EAAE,SAAS,KAAK,KAAK,aAAa,CAAC;;2CAEX,EAAE,IAAI;YACrC,QAAQC,4BAA2B,KAAK,YAAYD,QAAO;;;;+CAIxB,KAAK,SAAS,UAAU,EAAE,MAAM,CAAC;UACtE,KAAK,KAAK,CAAC,CAAC;;;;cAIR,EAAE,UAAU,IAAI,CAACD,OAAME,YAAW,MAAMF,IAAG,EAAE,OAAOA,GAAE,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC;;;UAG3E,EAAE,QAAQ,IAAI,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC;4CACH,KAAK,KAAK,IAAI,KAAK,KAAK,UAAU;;;EAG5E;EAEQ,KAAK,GAAoB;AAC/B,UAAMG,KAAI,KAAK;AACf,QAAIA,GAAE,UAAU,YAAY;AAC1B,aAAOD;;;IAGT;AACA,UAAM,SAASA;4BACS,EAAE,QAAQ,IAAI;;AAEtC,QAAIC,GAAE,UAAU;AAAc,aAAO;AACrC,UAAM,UAAUA,GAAE,KAAK,SAAS,OAAO,CAAC,MAAM,EAAE,QAAQ;AACxD,QAAI,QAAQ,WAAW,GAAG;AACxB,aAAOD;IACT;AACA,QAAI,QAAQ,WAAW,GAAG;AACxB,aAAOA;;;uBAGU,gBAAgB,EAAE,QAAQ,IAAI,EAAE;0BAC7B,KAAK,SAAQ,CAAE;mBACtB,MAAM,KAAK,KAAK,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;;;;;IAKrD;AACA,UAAM,QAAQC,GAAE,KAAK,cAAc,CAAA;AACnC,QAAI,CAAC,MAAM;AAAQ,aAAO;AAC1B,UAAM,SAAS,QAAQ,KAAK,CAAC,MAC3B,MAAM,MAAM,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,KAAK,QAAQ,EAAE,GAAG,CAAC,CAAC;AAElE,UAAM,QAAQ,CAAC,CAAC,UAAU,CAAC,KAAK;AAChC,WAAOD;QACH,MAAM,IACN,CAAC,MAAMA;oBACK,EAAE,KAAK;YACf,EAAE,OAAO,IAAI,CAAC,QAAO;AACrB,YAAM,YAAY,QAAQ,KAAK,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,IAAI,EAAE;AACrE,YAAM,QACJ,IAAI,QAAQ,SAAS,WAAW,MAAM,KAAK,IAAI,OAAO,KAAK,IACvD,IAAI,OAAO,QACX;AACN,aAAOA,qBAAoB,YAAY,SAAS,UAAU;;;uBAG/C,EAAE,GAAG;yBACH,IAAI,EAAE;2BACJ,KAAK,QAAQ,EAAE,GAAG,MAAM,IAAI,EAAE;4BAC7B,CAAC,SAAS;0BACZ,MAAK;AACb,aAAK,UAAU,EAAE,GAAG,KAAK,SAAS,CAAC,EAAE,GAAG,GAAG,IAAI,GAAE;MACnD,CAAC;;;kBAIC,QACIA;;gCAEU,SAAS,EAAE,iBAAiB,MAAK,CAAE,CAAC;kCAE9CD,QACN;wBACQ,IAAI,KAAK,UAAU,YAAYA,WAAUC,yCAAwC;;;IAG/F,CAAC,CAAC;oBACQ,CACb;;;qBAGc,gBAAgB,EAAE,QAAQ,IAAI,EAAE;wBAC7B,QAAQ,UAAU,MAAM;2BACrB,SAASD,WAAU,aAAa;iBAC1C,MAAK;AACZ,UAAI;AAAQ,aAAK,KAAK,OAAO,MAAM,IAAI;IACzC,CAAC;;;;QAID,SAASA,WAAUC,sEAAqE;;EAE9F;EAEQ,QAAQ,GAAS;AACvB,QAAI,EAAE,SAAS,kBAAkB;AAC/B,UAAI,CAAC,KAAK,QAAQ,IAAI,EAAE,IAAI,GAAG;AAC7B,aAAK,QAAQ,IAAI,EAAE,IAAI;AACvB,gBAAQ,KAAK,iDAAiD,EAAE,IAAI,mBAAmB;MACzF;AACA,aAAOD;IACT;AACA,UAAM,QAAQ,IAAI,KAAK,KAAK,MAAM,aAAa,CAAA,GAAI,IAAI,CAACD,OAAM,CAACA,GAAE,IAAIA,EAAC,CAAC,CAAC;AACxE,WAAOE;wBACa,KAAK,KAAK,UAAU;;UAElC,EAAE,QAAQ,MAAM,IAAI,CAACF,OAAK;AAC1B,YAAM,OAAO,MAAM,IAAIA,GAAE,SAAS,CAAC,KAAK,EAAE;AAC1C,YAAM,OAAO,WAAW,EAAE,IAAI,UAAUA,GAAE,sBAAsB;AAChE,YAAM,QAAQ,UAAUA,EAAC;AACzB,aAAOE;;;8BAGa,KAAK,SAAQ,CAAE;uBACtB,MAAM,KAAK,QAAQ,EAAE,MAAM,WAAW,QAAQF,GAAE,uBAAsB,CAAE,CAAC;;;kBAG9E,QAAQA,GAAE,WAAWA,GAAE,IAAI,CAAC;kBAC5B,QAAQE,iCAAgC,KAAK,YAAYD,QAAO;;mCAE/CD,GAAE,IAAI;;kBAEvB,OAAOE,iBAAgB,KAAK,KAAK,eAAeD,QAAO;kCACvC,IAAI,IAAI,KAAK,SAAS,MAAMD,GAAE,SAAS,CAAC;;;;IAIlE,CAAC,CAAC;;;EAGR;;AArZgB,oBAAA,aAAmC;EACjD,WAAW,EAAE,OAAO,KAAI;EACxB,SAAS,EAAE,OAAO,KAAI;;AAER,oBAAA,SAAS;EACvB;EACA;EACA;EACAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AATE,IAAO,qBAAP;AAyZN,IAAI,CAAC,eAAe,IAAI,oBAAoB,GAAG;AAC7C,iBAAe,OAAO,sBAAsB,kBAAkB;AAChE;;;AC5bA,SAAS,OAAAC,MAAK,QAAAC,OAAM,WAAAC,gBAAe;AAM5B,IAAM,sBAAsB;AAE7B,IAAO,oBAAP,MAAO,0BAAyB,cAA8B;EAyFzD,SAAM;AACb,UAAM,OAAO,KAAK;AAClB,QAAI,CAAC;AAAM,aAAOC;AAClB,WAAOC,wDAAuD,KAAK,KAAK,aAAa;2CAC9C,KAAK,SAAS,SAAS,KAAK,KAAK,CAAC;;UAEnE,KAAK,MAAM,IAAI,CAAC,SAAQ;AACxB,YAAM,OAAO,SAAS,KAAK,OAAO;AAClC,aAAOA;;;8BAGa,KAAK,SAAQ,CAAE;uBACtB,MAAM,KAAK,QAAQ,EAAE,MAAM,QAAQ,SAAS,KAAK,QAAO,CAAE,CAAC;;;kBAGhE,QAAQ,KAAK,QAAQ,aAAa,KAAK,OAAO,KAAK,KAAK,CAAC;;wCAEnC,KAAK,KAAK;oDACE,IAAI,IAAI,KAAK,SAAS,MAAM,KAAK,MAAM,CAAC;;oCAExD,KAAK,QAAQ,IAAI,CAACC,OAAMD,iBAAgBC,GAAE,KAAK,QAAQA,GAAE,KAAK,KAAK,CAAC;kBACtF,KAAK,SAASD,4CAA2C,KAAK,OAAO,IAAI,YAAYD,QAAO;;;;IAItG,CAAC,CAAC;;EAER;;AAnHgB,kBAAA,SAAS;EACvB;EACAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAHE,IAAO,mBAAP;AAuHN,IAAI,CAAC,eAAe,IAAI,mBAAmB,GAAG;AAC5C,iBAAe,OAAO,qBAAqB,gBAAgB;AAC7D;;;ACrHO,IAAM,oBAA2D;EACtE,oBAAoB;EACpB,eAAe;EACf,SAAS;EACT,WAAW;EACX,gBAAgB;;AAGZ,SAAU,gBAAgB,MAAY;AAE1C,SAAO,OAAO,OAAO,mBAAmB,IAAI,IACtC,kBAAyD,IAAI,KAAK,OACpE;AACN;;;AC6BO,IAAM,aAAa;AACnB,IAAM,WAAW;AACjB,IAAM,WAAW;AACjB,IAAM,YAAY;AAClB,IAAM,sBAAsB;AACnC,IAAM,UAAU;AAEhB,IAAM,UAAU;AAChB,IAAM,cAAc;AAGd,SAAU,iBAAc;AAC5B,QAAM,QAAQ,IAAI,WAAW,EAAE;AAC/B,SAAO,gBAAgB,KAAK;AAC5B,SAAO,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAAE,KAAK,EAAE;AAC1E;AAEA,SAAS,UAAU,QAAmB;AACpC,QAAM,QAAQ,CAAC,QAAQ,OAAO,IAAI,IAAI,SAAS,OAAO,KAAK,EAAE;AAC7D,MAAI,OAAO;AAAS,UAAM,KAAK,iBAAiB;AAChD,MAAI,OAAO,QAAQ,QAAQ,KAAK,OAAO,IAAI;AAAG,UAAM,KAAK,QAAQ,OAAO,IAAI,EAAE;AAC9E,MAAI,OAAO,QAAQ,OAAO,KAAK,UAAU,eAAe,QAAQ,KAAK,OAAO,IAAI,GAAG;AACjF,UAAM,KAAK,QAAQ,mBAAmB,OAAO,IAAI,CAAC,EAAE;EACtD;AACA,SAAO;AACT;AAEO,IAAM,mBAAmB;AAM1B,SAAU,kBAAkB,SAAkC;AAClE,QAAM,WAAW,OAAO,KAAK,iBAAiB;AAC9C,QAAM,QAAQ,UAAU,SAAS,OAAO,CAAC,SAAS,QAAQ,SAAS,IAAI,CAAC,IAAI;AAC5E,SAAO,MAAM,KAAI,EAAG,KAAK,GAAG;AAC9B;AAEA,SAAS,QAAQ,QAAmB;AAClC,SAAO;IACL,CAAC,mBAAmB,GAAG,OAAO;IAC9B,CAAC,gBAAgB,GAAG,kBAAkB,OAAO,gBAAgB;;AAEjE;AAGM,SAAU,aACd,MACA,QAAmB;AAEnB,QAAM,MAAM,KAAK,SAAS,GAAG,IAAI,MAAM;AACvC,SAAO,EAAE,MAAM,GAAG,IAAI,GAAG,GAAG,GAAG,UAAU,MAAM,EAAE,KAAK,GAAG,CAAC,IAAI,SAAS,QAAQ,MAAM,EAAC;AACxF;AAEA,SAAS,OAAO,MAAa;AAC3B,QAAM,IAAI;AACV,SACE,CAAC,CAAC,MACD,EAAE,UAAU,cAAc,EAAE,UAAU,aACvC,MAAM,QAAQ,EAAE,MAAM,KACtB,MAAM,QAAQ,EAAE,IAAI;AAExB;AAEA,eAAe,KACb,SACA,MACA,MACA,OAAkB;AAElB,MAAI;AACJ,MAAI;AACF,UAAM,MAAM,QAAQ,MAAM,IAAI;EAChC,QAAQ;AACN,WAAO,EAAE,MAAM,cAAa;EAC9B;AACA,MAAI,IAAI,WAAW;AAAK,WAAO;AAC/B,MAAI,CAAC,IAAI;AAAI,WAAO,EAAE,MAAM,cAAa;AACzC,QAAM,OAAgB,MAAM,IAAI,KAAI,EAAG,MAAM,MAAM,IAAI;AACvD,SAAO,OAAO,IAAI,IAAI,EAAE,MAAM,QAAQ,MAAM,KAAI,IAAK,EAAE,MAAM,cAAa;AAC5E;AAGM,SAAU,UACd,SACA,QACA,QAAkB,CAAA,GAAE;AAEpB,QAAM,QAAQ,CAAC,GAAG,UAAU,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,MAAM,QAAQ,mBAAmB,CAAC,CAAC,EAAE,CAAC;AACzF,SAAO,KACL,SACA,GAAG,UAAU,IAAI,MAAM,KAAK,GAAG,CAAC,IAChC,EAAE,QAAQ,OAAO,SAAS,QAAQ,MAAM,EAAC,GACzC,EAAE,MAAM,cAAa,CAAE;AAE3B;AAEM,SAAU,QACd,SACA,KACA,QAAmB;AAEnB,SAAO,KACL,SACA,GAAG,QAAQ,IAAI,UAAU,MAAM,EAAE,KAAK,GAAG,CAAC,IAC1C,EAAE,QAAQ,QAAQ,MAAM,KAAK,UAAU,GAAG,GAAG,SAAS,QAAQ,MAAM,EAAC,GACrE,EAAE,MAAM,WAAU,CAAE;AAExB;AAGM,SAAU,YACd,SACA,MACA,QAAmB;AAEnB,SAAO,KACL,SACA,GAAG,QAAQ,IAAI,UAAU,MAAM,EAAE,KAAK,GAAG,CAAC,IAC1C,EAAE,QAAQ,QAAQ,MAAM,KAAK,UAAU,EAAE,KAAI,CAAE,GAAG,SAAS,QAAQ,MAAM,EAAC,GAC1E,EAAE,MAAM,WAAU,CAAE;AAExB;AAGA,eAAsB,SACpB,SACA,QACA,QACA,YAAY,OAAK;AAEjB,MAAI;AACF,UAAM,QAAQ,GAAG,SAAS,GAAG,OAAO,UAAU,qBAAqB,EAAE,IAAI;MACvE,QAAQ;MACR,MAAM,KAAK,UAAU,MAAM;MAC3B,SAAS,QAAQ,MAAM;MACvB;KACD;EACH,QAAQ;AAEN,YAAQ,KAAK,yDAAyD;EACxE;AACF;;;AC3LA,IAAM,YAAY;AAqBZ,SAAU,WAAW,MAAY;AACrC,MAAI,CAAC,wBAAwB,KAAK,IAAI;AAAG,WAAO;AAChD,SAAO,CAAC,CAAC,GAAG,IAAI,EAAE,KAAK,CAACC,OAAMA,GAAE,WAAW,CAAC,IAAI,MAAQA,GAAE,WAAW,CAAC,MAAM,GAAI;AAClF;AAEA,SAAS,KAAK,OAAc;AAC1B,QAAMC,SAAS,QAA2C,SAAS;AACnE,SAAOA,UAAS,OAAOA,WAAU,WAAYA,SAAwB;AACvE;AAEA,SAAS,WACP,MACA,QACA,QAAqB;AAErB,MAAI,CAAC;AAAQ,WAAO;AACpB,SACE,CAAC,GAAG,KAAK,iBAA8B,cAAc,CAAC,EAAE,KACtD,CAACC,OAAMA,GAAE,QAAQ,WAAW,WAAW,WAAW,QAAQA,GAAE,QAAQ,WAAW,OAAO,KACnF;AAET;AAGA,SAAS,OAAO,UAAgB,MAAiB;AAC/C,MAAIC,KAAiB;AACrB,SAAOA,IAAG;AACR,QAAIA,OAAM;AAAU,aAAO;AAC3B,IAAAA,KAAIA,GAAE,eAAeA,cAAa,aAAaA,GAAE,OAAO;EAC1D;AACA,SAAO;AACT;AAEA,SAAS,KAAK,SAA2B;AACvC,QAAM,UACJ,OAAO,eAAe,cAAc,WAAW,kCAAkC,EAAE;AACrF,WAAS,eAAe,EAAE,OAAO,SAAS,UAAU,UAAU,SAAS,SAAQ,CAAE;AACnF;AAEM,IAAO,uBAAP,MAA2B;EAQ/B,YACmB,MACA,MAA6B;AAD7B,SAAA,OAAA;AACA,SAAA,OAAA;AATX,SAAA,WAA0B;AAC1B,SAAA,WAA+B;AAG/B,SAAA,eAAmC;AAC1B,SAAA,WAAW,oBAAI,IAAG;AAyB3B,SAAA,WAAW,CAAC,UAAsB;AACxC,WAAK,eACH,MACG,aAAY,EACZ,KACC,CAACA,OAAwBA,cAAa,eAAeA,GAAE,QAAQ,wBAAwB,CAAC,KACrF;IACX;AAEQ,SAAA,SAAS,CAAC,UAAsB;AACtC,YAAM,MAAO,MAAyC;AACtD,UAAI,KAAK,SAAS;AAAW;AAC7B,YAAM,OAAO,MAAM,aAAY;AAC/B,YAAM,UAAU,KAAK,KACnB,CAACA,OAAwBA,cAAa,eAAeA,GAAE,QAAQ,cAAc,CAAC;AAEhF,YAAM,UAAU,KAAK;AACrB,YAAM,SAAS,KAAK,CAAC,aAAa,cAAc,KAAK,CAAC,IAAI;AAC1D,WAAK,WAAW;QACd,QAAQ,SAAS,QAAQ,UAAU;QACnC,QAAQ,SAAS,QAAQ,UAAU;QACnC,QAAQ,WAAW,WAAW,OAAO,SAAS,OAAO,IAAI,UAAU;;IAEvE;AAEQ,SAAA,UAAU,CAAC,UAAsB;AACvC,YAAM,SAAS,KAAK;AACpB,WAAK,WAAW;AAChB,YAAM,SAAU,MAA2C,QAAQ;AACnE,YAAMC,QAAO,KAAK,KAAI;AACtB,UAAI,CAAC,UAAU,CAAC,UAAU,CAACA;AAAM;AACjC,YAAM,UAAU,CAAC,GAAGA,MAAK,iBAA8B,cAAc,CAAC,EAAE,KACtE,CAACF,OAAMA,GAAE,QAAQ,WAAW,UAAUA,GAAE,QAAQ,aAAa,gBAAgB;AAE/E,YAAM,SAAS,SAAS;AACxB,YAAM,OAAO,QAAQ,MAAM,SAAS;AACpC,UAAI,CAAC,WAAW,CAAC,UAAU,CAAC;AAAM;AAClC,UAAI,CAAC,WAAW,IAAI,GAAG;AACrB,gBAAQ,MAAM,wEAAwE,IAAI;AAC1F;MACF;AACA,YAAMD,SAAqB;QACzB;QACA,QAAQ,QAAQ,QAAQ,UAAU;QAClC,cAAc,OAAO;QACrB,cAAc,OAAO;;AAEvB,cAAQ,UAAU,EAAE,CAAC,SAAS,GAAGA,OAAK,GAAI,IAAI,IAAI;AAClD,WAAK,SAAS,IAAI,GAAGA,OAAM,MAAM,IAAIA,OAAM,MAAM,IAAI,OAAO,MAAM;AAClE,WAAK,WAAWA;AAChB,WAAK,OAAO,eAAc;IAC5B;AAEQ,SAAA,SAAS,CAAC,UAA8B;AAC9C,YAAMG,QAAO,KAAK,KAAI;AACtB,UAAI,CAACA;AAAM;AACX,YAAMH,SAAQ,KAAK,MAAM,KAAK;AAC9B,UAAIA,QAAO;AACT,aAAK,WAAWA;AAChB,aAAK,WAAWG,OAAMH,OAAM,QAAQA,OAAM,MAAM,CAAC;AACjD;MACF;AACA,YAAM,OAAO,KAAK;AAClB,WAAK,WAAW;AAChB,UAAI,CAAC;AAAM;AACX,WAAK,WAAWG,OAAM,KAAK,cAAc,KAAK,YAAY,CAAC;AAC3D,YAAM,SAAS,KAAK,SAAS,IAAI,GAAG,KAAK,MAAM,IAAI,KAAK,MAAM,EAAE;AAChE,UAAI,CAAC,QAAQ;AAAa;AAE1B,UAAI,OAAO;AAAc,aAAK,OAAO,aAAY;;AAC5C,eAAO,MAAM,EAAE,eAAe,KAAI,CAAE;IAC3C;AAEQ,SAAA,UAAU,MAAW;AAC3B,UAAI,KAAK,QAAQ,KAAK;AAAG,gBAAQ,KAAI;IACvC;EA/FG;EAEH,SAAM;AACJ,SAAK,KAAK,iBAAiB,SAAS,KAAK,UAAU,IAAI;AACvD,SAAK,KAAK,iBAAiB,iBAAiB,KAAK,MAAM;AACvD,SAAK,KAAK,iBAAiB,kBAAkB,KAAK,OAAO;AACzD,SAAK,KAAK,iBAAiB,yBAAyB,KAAK,OAAO;AAChE,WAAO,iBAAiB,YAAY,KAAK,MAAM;EACjD;EAEA,SAAM;AACJ,SAAK,KAAK,oBAAoB,SAAS,KAAK,UAAU,IAAI;AAC1D,SAAK,KAAK,oBAAoB,iBAAiB,KAAK,MAAM;AAC1D,SAAK,KAAK,oBAAoB,kBAAkB,KAAK,OAAO;AAC5D,SAAK,KAAK,oBAAoB,yBAAyB,KAAK,OAAO;AACnE,WAAO,oBAAoB,YAAY,KAAK,MAAM;AAClD,SAAK,WAAW;AAChB,SAAK,eAAe;EACtB;;;;ACnFK,IAAM,aAAa;AACnB,IAAM,iBAAiB;AAC9B,IAAM,aAAa,MAAM,KAAK,EAAE,QAAQ,GAAE,GAAI,CAAC,GAAGC,OAAMA,KAAI,EAAE;AAqB9D,IAAM,kBAAmC,CAAC,IAAI,SAAS,IAAI,qBAAqB,IAAI,IAAI;AAElF,SAAU,aACdC,QAAgG;AAEhG,QAAM,QAAQA,OAAM,iBAAiB;AACrC,QAAM,MAAMA,OAAM,mBAAmB;AACrC,QAAM,OAAO,KAAK,IAAI,KAAKA,OAAM,YAAY,UAAU,GAAG;AAC1D,SAAO,OAAO,IAAI,QAAQ,OAAO;AACnC;AAEM,IAAO,cAAP,MAAkB;EAQtB,YACE,MACA,MACA,MAAoB,MAAM,YAAY,IAAG,GACzC,UAAkC,OAAO,yBAAyB,aAC9D,kBACA,MAAI;AAbO,SAAA,SAAS,oBAAI,IAAG;AAChB,SAAA,QAAQ,oBAAI,IAAG;AA4DxB,SAAA,aACN,CAAC,SACD,CAAC,YAA8C;AAC7C,iBAAWA,UAAS,SAAS;AAC3B,cAAM,QAAQ,KAAK,OAAO,IAAIA,OAAM,MAAM;AAC1C,YAAI,CAAC,SAAS,MAAM;AAAM;AAC1B,cAAM,IAAI,IAAIA,OAAM,kBAAkB,aAAaA,MAAK,KAAK;AAC7D,cAAM,OAAO,MAAM,UAAU,MAAM;AACnC,YAAI,QAAQ,MAAM,UAAU;AAAM,gBAAM,QAAQ,KAAK,KAAI;iBAChD,CAAC,QAAQ,MAAM,UAAU;AAAM,eAAK,QAAQA,OAAM,QAAQ,OAAO,KAAK;MACjF;IACF;AAzDA,SAAK,QAAQ;AACb,SAAK,OAAO;AACZ,SAAK,YAAY,UACb,QAAQ,KAAK,WAAW,QAAQ,GAAG,EAAE,MAAM,WAAW,WAAU,CAAE,IAClE;AACJ,SAAK,UAAU,UACX,QAAQ,KAAK,WAAW,UAAU,GAAG,EAAE,MAAM,MAAM,WAAW,WAAU,CAAE,IAC1E;AACJ,QAAI,CAAC,KAAK,WAAW;AACnB,cAAQ,KACN,sFAAsF;IAE1F;EACF;EAEA,QAAQ,SAAoB;AAC1B,UAAM,SAAS,QAAQ,QAAQ,UAAU;AACzC,UAAM,SAAS,QAAQ,QAAQ,UAAU;AACzC,UAAM,MAAM,GAAG,MAAM,IAAI,MAAM;AAC/B,QAAI,CAAC,UAAU,CAAC,UAAU,KAAK,MAAM,IAAI,GAAG;AAAG;AAC/C,SAAK,MAAM,IAAI,GAAG;AAClB,SAAK,OAAO,IAAI,SAAS;MACvB;MACA;MACA,OAAO;MACP,MAAM;MACN,QAAQ;MACR,UAAU;KACX;AACD,SAAK,WAAW,QAAQ,OAAO;AAC/B,SAAK,SAAS,QAAQ,OAAO;EAC/B;;EAGA,QAAK;AACH,eAAW,CAAC,IAAI,KAAK,KAAK,KAAK,QAAQ;AACrC,UAAI,MAAM,UAAU,QAAQ,CAAC,MAAM;AAAM,aAAK,QAAQ,IAAI,OAAO,IAAI;IACvE;EACF;EAEA,aAAU;AACR,SAAK,WAAW,WAAU;AAC1B,SAAK,SAAS,WAAU;AACxB,SAAK,OAAO,MAAK;EACnB;EAeQ,QAAQ,IAAa,OAAc,OAAc;AACvD,UAAM,OAAO;AACb,SAAK,WAAW,UAAU,EAAE;AAC5B,SAAK,SAAS,UAAU,EAAE;AAC1B,UAAM,KAAK,KAAK,MAAM,KAAK,KAAI,KAAM,MAAM,SAAS,KAAK,KAAI,EAAG;AAChE,SAAK,MACH;MACE,SAAS,MAAM;MACf,SAAS,MAAM;MACf,YAAY,KAAK,IAAI,KAAK,IAAI,IAAI,CAAC,GAAG,cAAc;OAEtD,KAAK;EAET;;;;AChJF,SAAS,OAAAC,YAAW;AAOb,IAAM,aAAaA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACKnB,IAAM,aAAa;AAEnB,IAAM,kBAAkB;AAsB/B,IAAM,UAAU,uBAAO,eAAe;AAMtC,SAAS,SAAS,OAAc;AAC9B,SAAO,OAAO,UAAU,YAAY,UAAU;AAChD;AAEA,SAAS,SAAS,OAAc;AAC9B,MAAI,CAAC,SAAS,KAAK;AAAG,WAAO;AAC7B,QAAM,EAAE,SAAS,MAAM,MAAM,KAAI,IAAK;AACtC,MAAI,OAAO,YAAY,YAAY,OAAO,SAAS;AAAU,WAAO;AACpE,MAAI,OAAO,SAAS,YAAY,CAAC;AAAM,WAAO;AAC9C,MAAI,SAAS,WAAW,SAAS;AAAQ,WAAO;AAChD,SAAO,EAAE,SAAS,MAAM,MAAM,KAAI;AACpC;AAGA,SAAS,UAAU,OAAa;AAC9B,SAAO,MACJ,MAAM,IAAI,EACV,OAAO,CAAC,SAAS,KAAK,WAAW,OAAO,CAAC,EACzC,IAAI,CAAC,SAAU,KAAK,WAAW,QAAQ,IAAI,KAAK,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,CAAE,EACzE,KAAK,EAAE;AACZ;AAEA,eAAsB,YACpB,SACA,MACA,SAA2B;AAE3B,QAAM,EAAE,QAAQ,OAAM,IAAK;AAC3B,MAAI,OAAO;AAAS,WAAO;AAC3B,QAAM,QAAQ,IAAI,gBAAe;AACjC,QAAM,UAAU,MAAY,MAAM,MAAK;AACvC,SAAO,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAI,CAAE;AACxD,QAAM,WAAW,WAAW,MAAM,MAAM,MAAK,GAAI,QAAQ,YAAY,eAAe;AACpF,QAAM,UAAU,IAAI,QAAe,CAAC,UAAU,WAAU;AACtD,UAAM,OAAO,iBAAiB,SAAS,MAAM,OAAO,OAAO,GAAG,EAAE,MAAM,KAAI,CAAE;EAC9E,CAAC;AACD,UAAQ,MAAM,MAAK;EAEnB,CAAC;AACD,MAAI,SAAyD;AAC7D,MAAI;AACF,UAAM,MAAM,MAAM,QAAQ,KAAK;MAC7B,QAAQ,MAAM;QACZ,QAAQ;QACR,SAAS,EAAE,QAAQ,qBAAqB,GAAG,QAAQ,QAAO;QAC1D,QAAQ,MAAM;OACf;MACD;KACD;AACD,QAAI,IAAI,WAAW;AAAK,aAAO;AAC/B,QAAI,CAAC,IAAI,MAAM,CAAC,IAAI;AAAM,aAAO;AACjC,aAAS,IAAI,KAAK,UAAS;AAC3B,UAAM,UAAU,IAAI,YAAW;AAC/B,QAAI,SAAS;AACb,eAAS;AACP,YAAM,EAAE,MAAM,MAAK,IAAK,MAAM,QAAQ,KAAK,CAAC,OAAO,KAAI,GAAI,OAAO,CAAC;AACnE,UAAI;AAAM,eAAO;AACjB,gBAAU,QAAQ,OAAO,OAAO,EAAE,QAAQ,KAAI,CAAE;AAChD,eAAS,MAAM,OAAO,QAAQ,MAAM,GAAG,OAAO,GAAG,MAAM,OAAO,QAAQ,MAAM,GAAG;AAC7E,cAAM,OAAO,UAAU,OAAO,MAAM,GAAG,GAAG,CAAC;AAC3C,iBAAS,OAAO,MAAM,MAAM,CAAC;AAC7B,YAAI,CAAC;AAAM;AACX,cAAM,SAAkB,KAAK,MAAM,IAAI;AACvC,YAAI,SAAS,MAAM,KAAK,OAAO,SAAS;AAAM,iBAAO;AACrD,cAAM,QAAQ,SAAS,MAAM;AAC7B,YAAI;AAAO,iBAAO,KAAK;MACzB;IACF;EACF,QAAQ;AACN,WAAO,OAAO,UAAU,YAAY;EACtC;AACE,iBAAa,QAAQ;AACrB,WAAO,oBAAoB,SAAS,OAAO;AAC3C,YAAQ,OAAM,EAAG,MAAM,MAAK;IAE5B,CAAC;EACH;AACF;;;AC1GA,IAAMC,WAAU;AAChB,IAAM,QAAQ;AACd,IAAM,OAAO;AAiBP,UAAW,UAAU,MAAgB;AACzC,aAAW,MAAM,KAAK,iBAAiB,GAAG,GAAG;AAC3C,QAAI,GAAG,aAAa,WAAW;AAAG,YAAM;AACxC,QAAI,GAAG;AAAY,aAAO,UAAU,GAAG,UAAU;EACnD;AACF;AAEA,SAAS,aAAa,MAAkB,QAAc;AACpD,SAAO,CAAC,GAAG,KAAK,iBAA8B,cAAc,CAAC,EAAE,OAC7D,CAACC,OAAMA,GAAE,QAAQ,WAAW,MAAM;AAEtC;AAEM,SAAU,SACd,MACA,QACA,QACA,MAAY;AAEZ,aAAW,WAAW,aAAa,MAAM,MAAM,GAAG;AAChD,QAAI,QAAQ,QAAQ,WAAW;AAAQ;AACvC,eAAW,QAAQ,UAAU,OAAO,GAAG;AACrC,UAAI,KAAK,aAAa,WAAW,MAAM;AAAM,eAAO;IACtD;EACF;AACA,SAAO;AACT;AAOM,SAAU,SAAS,MAAe,MAAY;AAClD,QAAM,QAAQ,CAAC,GAAG,KAAK,UAAU,EAAE,OAAO,CAACC,OAAiBA,GAAE,aAAa,KAAK,SAAS;AACzF,QAAM,SAAS,MAAM,KAAK,CAACC,OAAMA,GAAE,KAAK,KAAI,CAAE,KAAK,MAAM,CAAC;AAC1D,MAAI;AAAQ,WAAO,OAAO;;AACrB,SAAK,OAAO,IAAI;AACvB;AAEA,SAASC,MAAK,MAAe,OAAY;AACvC,QAAM,OAAQ,UAAU,aAAa,KAAK,aAAa,IAAI,KAAM,KAAK,aAAa,KAAK;AACxF,MAAI;AAAM,aAAS,MAAM,IAAI;AAC/B;AAEM,IAAO,kBAAP,MAAsB;EAM1B,YAAY,MAAwB,SAAqB;AAHjD,SAAA,QAAuB;AACvB,SAAA,SAAiC;AA2BjC,SAAA,UAAU,CAAC,UAAsB;AACvC,YAAM,SAAU,MAA4C,QAAQ;AACpE,UAAI,OAAO,WAAW,YAAY,CAACJ,SAAQ,KAAK,MAAM,KAAK,WAAW,KAAK;AAAO;AAClF,WAAK,KAAK,QAAQ,MAAM;IAC1B;AA5BE,SAAK,QAAQ;AACb,SAAK,WAAW;AAChB,SAAK,cAAc,IAAI;EACzB;EAEA,gBAAa;AACX,SAAK,MAAM,iBAAiB,cAAc,KAAK,OAAO;EACxD;EAEA,mBAAgB;AACd,SAAK,MAAM,oBAAoB,cAAc,KAAK,OAAO;AACzD,SAAK,QAAQ,MAAK;AAClB,SAAK,SAAS;AACd,SAAK,QAAQ;EACf;;EAGA,WAAQ;AACN,UAAM,QAAQ,KAAK,SAAS,MAAK;AACjC,eAAW,QAAQ,UAAU,KAAK,MAAM,UAAU,GAAG;AACnD,UAAI,KAAK,aAAa,KAAK;AAAG,QAAAI,MAAK,MAAM,KAAK;IAChD;EACF;EAQQ,MAAM,QAAQ,QAAc;AAClC,SAAK,QAAQ,MAAK;AAClB,UAAM,QAAQ,IAAI,gBAAe;AACjC,SAAK,SAAS;AACd,SAAK,QAAQ;AACb,UAAM,UAAU,KAAK,SAAS,QAAO;AACrC,QAAI,CAAC;AAAS;AACd,UAAM,OAAO,KAAK,MAAM;AACxB,UAAM,OAAO,aAAa,MAAM,MAAM,EAAE,CAAC,GAAG,QAAQ;AACpD,UAAM,YAAsB,CAAA;AAC5B,UAAM,SAAS,CAAC,UAA0B;AACxC,UAAI,KAAK,UAAU;AAAQ;AAC3B,YAAM,OAAO,SAAS,MAAM,QAAQ,MAAM,SAAS,MAAM,IAAI;AAC7D,UAAI,CAAC;AAAM;AACX,WAAK,aAAa,MAAM,SAAS,SAAS,OAAO,OAAO,MAAM,IAAI;AAClE,MAAAA,MAAK,MAAM,KAAK,SAAS,MAAK,CAAE;AAChC,UAAI,MAAM,SAAS,WAAW,MAAM,YAAY;AAAM,kBAAU,KAAK,MAAM,IAAI;IACjF;AACA,UAAM,EAAE,MAAM,SAAAC,SAAO,IAAK,KAAK,SAAS,QAAQ,MAAM;AACtD,UAAM,UAAU,MAAM,YAAY,SAAS,MAAM,EAAE,QAAQ,MAAM,QAAQ,SAAAA,UAAS,OAAM,CAAE;AAC1F,QAAI,KAAK,WAAW;AAAO,WAAK,SAAS;AACzC,QAAI,KAAK,UAAU,UAAU,YAAY,UAAU,UAAU,CAAC,GAAG;AAC/D,WAAK,SAAS,SAAS,UAAU,CAAC,CAAC;IACrC;EACF;;;;AnB/EF,IAAM,eAAe,eAAc;AAcnC,IAAM,OAAO;EACX,YAAY;EACZ,QAAQ;EACR,SAAS;EACT,SAAS;EACT,WAAW;;AAGP,IAAO,qBAAP,MAAO,2BAA0BC,YAAU;EAsD/C,cAAA;AACE,UAAK;AAhBU,SAAA,QAAQ,IAAI,wBAAwB,IAAI;AACjD,SAAA,QAA4B;AAEnB,SAAA,SAAS,IAAI,gBAAgB,MAAM;MAClD,SAAS,MAAM,KAAK;;MAEpB,SAAS,CAAC,WAAW,aAAa,YAAY,KAAK,QAAQ,MAAM,CAAC;MAClE,OAAO,MAAM,KAAK;MAClB,UAAU,CAAC,SAAS,KAAK,UAAU,MAAM,IAAI;KAC9C;AACO,SAAA,cAAiC;AAEjC,SAAA,WAAwC;AAC/B,SAAA,aAAa,oBAAI,IAAG;AAqD7B,SAAA,gBAAgB,MAAW;AACjC,UAAI,SAAS,oBAAoB;AAAU,aAAK,OAAO,MAAK;IAC9D;AAEQ,SAAA,cAAc,MAAY,KAAK,OAAO,MAAK;AAsI3C,SAAA,SAAS,CAAC,UAAsB;AACtC,WAAK,KAAK,KAAM,MAAgC,QAAQ,KAAK,UAAU,KAAK,CAAC;IAC/E;AAEQ,SAAA,UAAU,CAAC,UAAsB;AACvC,YAAM,EAAE,QAAQ,QAAQ,UAAS,IAAM,MAAuC;AAC9E,WAAK,KAAK,MAAM,QAAQ,QAAQ,aAAa,MAAM,KAAK,UAAU,KAAK,CAAC;IAC1E;AAmCQ,SAAA,YAAY,CAAC,UAAsB;AACzC,YAAM,eAAc;AACpB,YAAM,QAAQ,KAAK,WAAW,cAAgC,qBAAqB;AACnF,YAAM,OAAO,OAAO,MAAM,KAAI,KAAM;AACpC,UAAI,CAAC,SAAS,CAAC,QAAQ,KAAK;AAAO;AACnC,WAAK,KAAK,KAAK,MAAM,KAAK;IAC5B;AA3OE,SAAK,cAAc;AACnB,SAAK,UAAU;AACf,SAAK,QAAQ,CAAA;AACb,SAAK,UAAU,MAAM;AACrB,SAAK,YAAY;AACjB,SAAK,QAAQ;AACb,SAAK,mBAAmB;AACxB,SAAK,QAAQ;AACb,SAAK,SAAS,CAAA;AACd,SAAK,UAAU;AACf,SAAK,QAAQ;AACb,SAAK,UAAU;AACf,SAAK,SAAS;EAChB;EAES,oBAAiB;AACxB,UAAM,kBAAiB;AACvB,QAAI,KAAK,cAAc,SAAS;AAC9B,WAAK,WAAW,IAAI,qBAAqB,MAAM,MAAM,KAAK,UAAU;AACpE,WAAK,SAAS,OAAM;IACtB;AACA,aAAS,iBAAiB,oBAAoB,KAAK,aAAa;AAChE,WAAO,iBAAiB,YAAY,KAAK,WAAW;AAEpD,QAAI,KAAK;AAAO;AAChB,SAAK,KAAK,MAAK;EACjB;EAES,uBAAoB;AAC3B,SAAK,UAAU,OAAM;AACrB,SAAK,WAAW;AAChB,aAAS,oBAAoB,oBAAoB,KAAK,aAAa;AACnE,WAAO,oBAAoB,YAAY,KAAK,WAAW;AACvD,SAAK,OAAO,MAAK;AACjB,SAAK,OAAO,WAAU;AACtB,SAAK,QAAQ;AACb,UAAM,qBAAoB;EAC5B;EAEmB,aAAU;AAC3B,SAAK,gBAAgB,aAAa,KAAK,cAAc,MAAM;EAC7D;EAEmB,UAAO;AAExB,QAAI,KAAK,gBAAgB,QAAQ,KAAK,gBAAgB,KAAK;AAAQ,WAAK,OAAO,SAAQ;AACvF,SAAK,cAAc,KAAK;EAC1B;EAQA,IAAY,SAAM;AAChB,WAAO,KAAK,MAAM,UAAU,aAAa,YAAY;EACvD;EAEQ,QAAQ,MAAsB,MAAoB;AACxD,WAAO;MACL,MAAM,KAAK;MACX,OAAO,KAAK;MACZ,SAAS,KAAK,QAAO;MACrB,MAAM,QAAQ;MACd,MAAM,QAAQ;MACd,aAAa;MACb,kBAAkB,KAAK;;EAE3B;EAEQ,cAAW;AACjB,WAAO,KAAK,OAAO,GAAG,EAAE,GAAG,WAAW;EACxC;;;;;;EAOQ,UAAU,MAAc,QAAQ,OAAK;AAC3C,SAAK,UAAU;AACf,SAAK,SAAS;EAChB;EAEQ,MAAM,QAAK;AACjB,QAAI,CAAC,KAAK,aAAa;AACrB,cAAQ,MAAM,qEAAqE;AACnF,WAAK,UAAU,mBAAmB;AAClC;IACF;AACA,SAAK,UAAU;AACf,SAAK,OAAO,MAAM,UAAU,KAAK,aAAa,KAAK,QAAO,GAAI,KAAK,KAAK,GAAG,SAAS;EACtF;;EAGQ,UAAU,OAAgD;AAChE,SAAK,UAAU;AACf,SAAK,cAAc,IAAI,YAAY,OAAO,EAAE,SAAS,MAAM,UAAU,KAAI,CAAE,CAAC;EAC9E;EAEQ,OAAO,QAAqB,OAAY;AAC9C,QAAI,OAAO,SAAS,QAAQ;AAC1B,YAAM,OAAO,OAAO,KAAK;AACzB,UAAI,CAAC,MAAM;AACT,gBAAQ,MAAM,qDAAqD;AACnE,YAAI,KAAK,SAAS,UAAU;AAAW,eAAK,UAAU,KAAK,UAAU;;AAChE,eAAK,UAAU,mBAAmB;AACvC;MACF;AACA,UAAI,KAAK,cAAc;AAAQ,aAAK,OAAO,MAAK;AAChD,YAAM,QAAQ,CAAC,KAAK;AACpB,WAAK,QAAQ,OAAO;AACpB,WAAK,SAAS,KAAK,cAAc,SAAS,CAAC,IAAI,IAAI,CAAC,GAAG,KAAK,QAAQ,IAAI;AACxE,WAAK,UAAU;AACf,UAAI,OAAO;AAGT,aAAK,cAAc,IAAI,YAAY,eAAe,EAAE,SAAS,MAAM,UAAU,KAAI,CAAE,CAAC;MACtF;AACA,WAAK,KAAK,QAAQ,MAAM,KAAK;IAC/B,WAAW,OAAO,SAAS,eAAe;AACxC,WAAK,UAAU,mBAAmB;IACpC,WAAW,OAAO,SAAS,eAAe;AACxC,UAAI,KAAK,SAAS,UAAU;AAAW,aAAK,UAAU,KAAK,UAAU;;AAChE,aAAK,UAAU,mBAAmB;IACzC;EACF;;EAGQ,MAAM,QAAQ,MAAgB,OAAY;AAChD,UAAM,KAAK;AACX,UAAM,OAAO,KAAK,WAAW,cAA2B,OAAO;AAC/D,QAAI,CAAC;AAAM;AACX,SAAK,UAAL,KAAK,QAAU,IAAI,YAAY,MAAM,CAAC,QAAQ,UAAS;AACrD,UAAI,KAAK;AAAa,aAAK,SAAS,KAAK,aAAa,QAAQ,KAAK,QAAO,GAAI,KAAK;IACrF,CAAC;AACD,UAAM,MAAM,CAAC,GAAG,KAAK,iBAA8B,cAAc,CAAC;AAClE,eAAW,WAAW;AAAK,WAAK,MAAM,QAAQ,OAAO;AACrD,UAAM,QAAQ,IAAI,KAAK,CAACC,OAAMA,GAAE,QAAQ,WAAW,KAAK,OAAO;AAC/D,QAAI,SAAS,UAAU,aAAa,KAAK,cAAc;AACrD,WAAK,YAAY,MAAM;AACzB,QAAI,SAAS,UAAU,OAAO;AAC5B,YAAO,MAAM,mBAAqD,eAAc;IAClF;AACA,SAAK,cACH,IAAI,YAA6B,kBAAkB;MACjD,QAAQ,EAAE,QAAQ,KAAK,QAAO;MAC9B,SAAS;MACT,UAAU;KACX,CAAC;EAEN;;EAGU,MAAM,KAAK,KAAe,MAAY;AAC9C,QAAI,KAAK,SAAS,CAAC,KAAK;AAAa,aAAO;AAC5C,SAAK,QAAQ;AACb,SAAK,UAAU;AACf,QAAI;AACF,YAAM,OAAO,MAAM,QAAQ,KAAK,aAAa,KAAK,KAAK,QAAQ,KAAK,MAAM,KAAK,IAAI,CAAC;AAEpF,YAAM,SACJ,KAAK,SAAS,aAAa,MAAM,UAAU,KAAK,aAAa,KAAK,QAAO,CAAE,IAAI;AACjF,WAAK,OAAO,QAAQ,KAAK;AACzB,aAAO,KAAK,SAAS;IACvB;AACE,WAAK,QAAQ;IACf;EACF;;EAGQ,UAAU,OAAY;AAC5B,UAAM,UAAW,MAAM,QAA2B,UAChD,cAAc;AAEhB,WAAO;MACL,MAAM,SAAS,QAAQ,UAAU,KAAK,YAAW;MACjD,MAAM,SAAS,QAAQ,UAAU;;EAErC;;EAGQ,eAAY;AAClB,WAAO,EAAE,MAAM,KAAK,YAAW,GAAI,MAAM,KAAI;EAC/C;EAWQ,MAAM,MACZ,QACA,QACA,WACA,MAAY;AAEZ,QAAI,KAAK;AAAO;AAChB,UAAM,OAAO,KAAK,QAAO;AACzB,QAAI,CAAC,MAAM;AACT,WAAK,UAAU,KAAK,MAAM;AAC1B;IACF;AACA,SAAK,QAAQ;AACb,SAAK,UAAU;AACf,QAAI;AACJ,QAAI;AACF,cAAQ,MAAM,KAAK,UAAU,EAAE,YAAY,QAAQ,YAAY,WAAW,UAAU,EAAC,CAAE;IACzF,QAAQ;AACN,cAAQ,EAAE,IAAI,OAAO,OAAO,EAAE,MAAM,WAAW,SAAS,GAAE,EAAE;IAC9D;AACE,WAAK,QAAQ;IACf;AACA,QAAI,CAAC,MAAM,IAAI;AACb,YAAM,OAAO,MAAM,OAAO;AAC1B,WAAK,UACH,SAAS,QAAQ,KAAK,UAAU,SAAS,YAAY,KAAK,UAAU,KAAK,SAAS;AAEpF;IACF;AACA,UAAM,QAAQ,MAAM,KAAK,KAAK,EAAE,MAAM,QAAQ,SAAS,QAAQ,OAAM,GAAI,IAAI;AAC7E,QAAI,CAAC;AAAO,WAAK,UAAU,OAAO,KAAK,KAAK,EAAE,YAAY;EAC5D;EAUQ,MAAM,KAAK,MAAc,OAAuB;AACtD,QAAI,KAAK,SAAS,CAAC,KAAK;AAAa;AACrC,SAAK,QAAQ;AACb,SAAK,UAAU;AACf,QAAI;AACF,UAAI,SAAS,MAAM,YAAY,KAAK,aAAa,MAAM,KAAK,QAAQ,KAAK,YAAW,CAAE,CAAC;AACvF,UAAI,OAAO,SAAS;AAAY,iBAAS,MAAM,UAAU,KAAK,aAAa,KAAK,QAAO,CAAE;AACzF,UAAI,OAAO,SAAS,UAAW,OAAO,KAA6B,UAAU,OAAO;AAGlF,aAAK,UAAU,OAAO,KAAK,SAAS,KAAK,UAAU;AACnD;MACF;AACA,WAAK,OAAO,QAAQ,KAAK;AACzB,UAAI,OAAO,SAAS;AAAQ,cAAM,QAAQ;IAC5C;AACE,WAAK,QAAQ;IACf;EACF;EAEQ,YAAS;AACf,WAAO,KAAK,QAAQ,SAAS;EAC/B;EAEQ,YAAS;AACf,UAAM,QAAQ,KAAK,cAAc,SAAS,KAAK,OAAO,MAAM,EAAE,IAAI,KAAK;AACvE,UAAM,MAAiB,CAAA;AACvB,eAAW,QAAQ,OAAO;AACxB,YAAM,WAAsB,CAAA;AAC5B,iBAAW,SAAS,CAAC,GAAG,KAAK,KAAK,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,WAAW,EAAE,QAAQ,GAAG;AAC3E,cAAM,MAAM,gBAAgB,MAAM,QAAQ;AAC1C,YAAI;AAAK,mBAAS,KAAK,EAAE,QAAQ,KAAK,SAAS,OAAO,IAAG,CAAE;iBAClD,CAAC,KAAK,WAAW,IAAI,MAAM,QAAQ,GAAG;AAC7C,eAAK,WAAW,IAAI,MAAM,QAAQ;AAClC,kBAAQ,KACN,4CAA4C,MAAM,QAAQ,mBAAmB;QAEjF;MACF;AACA,UAAI,KAAK,GAAI,KAAK,cAAc,SAAS,SAAS,MAAM,GAAG,CAAC,IAAI,QAAS;IAC3E;AACA,WAAO;EACT;EAES,SAAM;AACb,QAAI,KAAK,YAAY;AAAQ,aAAOC;AACpC,UAAM,OAAO,KAAK;AAClB,QAAI,KAAK,YAAY,aAAa,CAAC,MAAM;AACvC,aAAOC,4CAA2C,KAAK,UAAU,IAAI,CAAC;IACxE;AACA,UAAM,OAAO,OAAO,IAAI;AACxB,WAAOA,oDAAmD,KAAK,UAAU;UACnE,KAAK,UAAU,IAAI,CAAC,IAAI,KAAK,OAAO,IAAI,CAAC;uCACZ,KAAK,MAAM,gBAAgB,KAAK,OAAO;YAClE,OACA,KAAK,UAAS,GACd,CAACF,OAAM,GAAGA,GAAE,MAAM,IAAIA,GAAE,MAAM,OAAO,IACrC,CAACA,OAAM,KAAK,SAASA,EAAC,CAAC,CACxB;;mBAEQ,KAAK,SAAS,iBAAiB,QAAQ,kBAAkB,KAAK,WAAWC,QAAO;yCAC1D,KAAK,SAAS;;;;;;yBAM9B,KAAK,eAAe;0BACnB,KAAK,eAAe;4BAClB,KAAK,UAAS,CAAE;;gDAEI,KAAK,UAAS,CAAE;;;EAG9D;EAEQ,UAAU,MAAsB;AACtC,UAAM,OAAO,KAAK,QAAO;AACzB,UAAM,QAAQ,MAAM,cAAc;AAClC,WAAOC;2BACgB,KAAK,KAAK;QAE7B,OACIA;;2BAEe,QAAQ,SAAS,KAAK,gBAAgB,MAAM;uBAChD,MAAM,KAAK,KAAK,SAAQ,CAAE;;oBAE7B,QAAQA,oCAAmC,KAAK,YAAYD,QAAO;yBAE3EA,QACN;;EAEJ;EAEQ,OAAO,MAAe;AAC5B,UAAM,SAAS,KAAK,aAAY;AAChC,WAAOC;+BACoB,OAAO,IAAI,EAAE,WAAW;;UAE7C,KAAK,OAAO,IACZ,CAACF,OAAME;;;8BAGa,KAAK,UAAS,CAAE;2BACnB,UAAUF,GAAE,KAAK,EAAE;uBACvB,MAAM,KAAK,KAAK,KAAK,EAAE,MAAM,SAAS,SAASA,GAAE,QAAO,GAAI,MAAM,CAAC;;yBAEjEA,GAAE,KAAK,QAAQA,GAAE,KAAK;;qDAEMA,GAAE,KAAK;gBAC5C,CACP;UACC,KAAK,QAAQ,IACb,CAAC,MAAME;8DAC6C,GAAG,EAAE,KAAK,kBAAkB;6DAC7B,EAAE,KAAK;gBACpD,CACP;;QAGD,KAAK,OAAO,UAAU,KAAK,UAAU,aACjCA;;8BAEkB,KAAK,UAAS,CAAE;uBACvB,MAAM,KAAK,KAAK,KAAK,EAAE,MAAM,OAAM,GAAI,MAAM,CAAC;;gBAErD,OAAO,IAAI,EAAE,UAAU;yBAE3BD,QACN;;EAEJ;EAEQ,SAASD,IAAU;AAEzB,UAAM,MAAM,aAAaA,GAAE,GAAG;AAC9B,WAAOE;;qBAEUF,GAAE,MAAM;qBACRA,GAAE,MAAM,OAAO;sBACdA,GAAE,MAAM,QAAQ;;QAE9B,cAAc,GAAG;gBACTA,GAAE,MAAM,IAAI;kBACVA,GAAE,MAAM;kBACRA,GAAE,MAAM,OAAO;gBACjB,KAAK,OAAO;gBACZ,KAAK,KAAK;gBACV,OAAO,KAAK,KAAK,CAAC;WACvB,GAAG,GAAG;;EAEf;;AA5bgB,mBAAA,SAAS;AACT,mBAAA,aAAa;EAC3B,aAAa,EAAE,WAAW,MAAK;EAC/B,SAAS,EAAE,WAAW,MAAK;EAC3B,OAAO,EAAE,WAAW,MAAK;EACzB,SAAS,EAAE,WAAW,MAAK;EAC3B,WAAW,EAAE,WAAW,MAAK;EAC7B,OAAO,EAAE,WAAW,MAAK;EACzB,kBAAkB,EAAE,WAAW,MAAK;EACpC,OAAO,EAAE,OAAO,KAAI;EACpB,QAAQ,EAAE,OAAO,KAAI;EACrB,SAAS,EAAE,OAAO,KAAI;EACtB,OAAO,EAAE,OAAO,KAAI;EACpB,SAAS,EAAE,OAAO,KAAI;EACtB,QAAQ,EAAE,OAAO,KAAI;;AAfnB,IAAO,oBAAP;AAgcN,IAAI,CAAC,eAAe,IAAI,qBAAqB,GAAG;AAC9C,iBAAe,OAAO,uBAAuB,iBAAiB;AAChE;;;AoB5fA,SAAS,OAAAG,OAAK,QAAAC,OAAM,cAAAC,aAAY,WAAAC,gBAAe;AAExC,IAAM,iBAAiB;AAEvB,IAAM,kBAAN,cAA8BD,YAAW;AAAA,EAwC9C,cAAc;AACZ,UAAM;AAPR,SAAQ,aAAiC;AACzC,SAAQ,SAAmB,CAAC;AAC5B,SAAQ,SAAS;AAEjB;AAAA,SAAQ,QAAQ;AAuDhB;AAAA,SAAQ,QAAQ,MAAY;AAC1B,WAAK,gBAAgB,cAAc;AAAA,IACrC;AAGA;AAAA,SAAQ,YAAY,MAAY;AAC9B,WAAK,QAAQ;AACb,WAAK,SAAS;AACd,WAAK,gBAAgB,cAAc;AACnC,WAAK,cAAc;AAAA,IACrB;AA7DE,SAAK,YAAY;AAAA,EACnB;AAAA,EAES,oBAA0B;AACjC,UAAM,kBAAkB;AACxB,QAAI,KAAK,cAAc,KAAK,MAAO;AACnC,UAAM,YAAY,OAAO,OAAO,eAAe;AAC/C,QAAI,CAAC,WAAW;AACd,cAAQ,MAAM,qEAAqE;AACnF,WAAK,UAAU;AACf;AAAA,IACF;AACA,QACE,KAAK,aACL,CAAC,KAAK,UAAU,KAAK,CAAC,SAAS,OAAO,KAAK,iBAAiB,EAAE,SAAS,IAAI,CAAC,GAC5E;AACA,cAAQ;AAAA,QACN,0CAA0C,KAAK,UAAU,KAAK,SAAS,CAAC;AAAA,MAC1E;AACA,WAAK,UAAU;AACf;AAAA,IACF;AACA,SAAK,aAAa;AAClB,SAAK,aAAa,gBAAgB,EAAE;AACpC,SAAK,SAAS,aAAa;AAC3B,SAAK,SAAS,UAAU;AACxB,QAAI,CAAC,KAAK,QAAQ;AAChB,cAAQ;AAAA,QACN;AAAA,MACF;AAAA,IACF;AACA,SAAK,cAAc;AAAA,EACrB;AAAA,EAES,SAAS;AAChB,QAAI,KAAK,SAAS,CAAC,KAAK,WAAY,QAAOC;AAC3C,WAAOF;AAAA,qBACU,KAAK,UAAU;AAAA,iBACnB,OAAO;AAAA,eACT,KAAK,MAAM;AAAA,iBACT,cAAc;AAAA,eAChB,KAAK,MAAM;AAAA,mBACP,OAAO;AAAA,0BACA,KAAK,SAAS;AAAA,qBACnB,KAAK,KAAK;AAAA,2BACJ,KAAK,SAAS;AAAA,2BACd,KAAK,SAAS;AAAA;AAAA,EAEvC;AAcF;AAxGa,gBACK,SAASD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AADd,gBA2BK,aAAa;AAAA,EAC3B,WAAW,EAAE,WAAW,MAAM;AAChC;AA6EK,SAAS,2BAAiC;AAC/C,MAAI,OAAO,mBAAmB,eAAe,CAAC,eAAe,IAAI,cAAc,GAAG;AAChF,mBAAe,OAAO,gBAAgB,eAAe;AAAA,EACvD;AACF;AAEA,yBAAyB;;;AC3HlB,IAAM,qBAAqB;AAAA,EAChC,MAAM,WAAwB,QAA8B;AAC1D,UAAM,QAAQ,mBAAiC,UAAU,IAAI;AAC7D,UAAM,UAAU,SAAS,cAAc,cAAc;AACrD,YAAQ,YAAY,MAAM,aAAa;AAGvC,cAAU,MAAM,YAAY;AAC5B,cAAU,YAAY,OAAO;AAC7B,WAAO,MAAM,QAAQ,OAAO;AAAA,EAC9B;AAAA;AAAA,EAGA,OAAO,WAAwB,QAA8B;AAC3D,UAAM,UAAU,UAAU,cAAc,cAAc;AACtD,QAAI,CAAC,QAAS;AACd,UAAM,YAAY,mBAAiC,UAAU,IAAI,EAAE,aAAa;AAChF,QAAI,KAAK,UAAU,SAAS,MAAM,KAAK,UAAU,QAAQ,SAAS;AAChE,cAAQ,YAAY;AAAA,EACxB;AACF;AAEO,IAAM,UAAU;AAAA,EACrB,IAAI;AAAA,EACJ,SAAS;AAAA,EACT,MAAM;AAAA,EACN,aAAa;AAAA;AAAA,EAGb,WAAW,CAAC;AAAA,EAEZ,SAAS;AAAA,IACP;AAAA,MACE,IAAI;AAAA,MACJ,WAAW;AAAA,MACX,UAAU;AAAA,QACR,MAAM;AAAA,QACN,aAAa;AAAA,QACb,MAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACF;AAEA,IAAO,kBAAQ;",
6
+ "names": ["createContext", "key", "e", "i", "c", "s", "entry", "html", "LitElement", "nothing", "css", "html", "nothing", "html", "n", "t", "s", "css", "nothing", "html", "t", "css", "css", "html", "nothing", "css", "nothing", "html", "t", "css", "css", "html", "nothing", "nothing", "html", "c", "i", "css", "css", "html", "nothing", "t", "nothing", "html", "s", "css", "css", "html", "nothing", "nothing", "html", "s", "css", "c", "entry", "s", "n", "root", "i", "entry", "css", "TURN_ID", "s", "c", "t", "show", "headers", "LitElement", "s", "nothing", "html", "css", "html", "LitElement", "nothing"]
7
+ }