{"version":3,"sources":["../src/consent-banner.ts"],"sourcesContent":["/**\n * Crossdeck Consent — the branded, Stripe-premium consent surface.\n *\n * This is the CONSENT-MODE GUEST PRIMITIVE (Path B, CD-175 / CD-185): the\n * consent widget bundled inside the injected SDK on marketplace installs\n * (Webflow, Wix, Framer, WordPress, WooCommerce, Squarespace, Bubble) and the\n * direct install. It renders the signed-off compact bottom-left arrival banner\n * (Accept all / Manage / Reject all) and a four-switch Manage pane — Strictly\n * necessary (locked) · Analytics (anonymous) · Recognize me (identity opt-in) ·\n * Marketing — in a style-isolated Shadow DOM, and records a withdrawable,\n * auditable consent choice that gates all downstream autocapture.\n *\n * Relationship to the rest of the SDK:\n *  - It WIRES to the existing {@link ../consent ConsentManager} (analytics /\n *    marketing dimensions) — it never forks a parallel consent state. The\n *    NEW \"Recognize me\" switch drives an `identityOptIn` boolean that this\n *    widget only RECORDS and EMITS; another module gates `identify()` on it.\n *  - The FULL-CAPABILITY / Path-A direct build is UNAFFECTED by this file.\n *    Error tracking, host-global monkey-patching and the Trust iframe are\n *    deliberately absent here — this guest build is the leaner, review-safe\n *    consent set only (CD-175 #6 / #7, CD-185 Part 2A).\n *\n * Non-negotiables baked in (Stripe / bank grade, `trust-bank-grade-no-cleverness`):\n *  - CONSENT-FIRST — everything defaults OFF except Strictly necessary; nothing\n *    is auto-on. No capture is implied until the visitor makes a choice.\n *  - GENUINE CHOICE, NEVER A DARK PATTERN — refusing (Reject all) is exactly as\n *    prominent and one-click as accepting. No pre-ticked boxes, no nag pill.\n *  - STYLE-ISOLATED — rendered in a Shadow DOM: the host page's CSS cannot leak\n *    in, and our CSS cannot leak out. In-page, NOT an iframe.\n *  - AUDITABLE + WITHDRAWABLE — every choice is persisted with proof\n *    (categories, method, timestamp, policyVersion, id) and re-openable at any\n *    time via `[data-crossdeck-consent]` or `handle.open()`. A policyVersion\n *    bump re-prompts (re-consent).\n *  - POLICY-URL REQUIRED — in managed mode the widget will NOT render without\n *    the site owner's real privacy-policy URL; it surfaces a clear diagnostic.\n *  - DIAGNOSTIC, NEVER SILENT — risky DOM work is wrapped in `safe()`, which\n *    logs a concise `[crossdeck] consent: …` warning rather than swallowing\n *    the failure (Webflow finding #11).\n */\n\nimport type { ConsentManager } from \"./consent\";\n\n/** Informational \"what is Crossdeck Consent\" page — the attribution/acquisition link. */\nexport const CONSENT_INFO_URL = \"https://cross-deck.com/consent\";\n\n/** localStorage key for the auditable consent record. Origin-scoped by the browser. */\nexport const CONSENT_STORAGE_KEY = \"crossdeck.consent.v1\";\n\n/**\n * The consent choice this widget records and emits. Strictly-necessary is always\n * on and is NOT represented here (it is not a toggleable dimension).\n *\n *  - `analytics`      → maps to ConsentManager `analytics`.\n *  - `marketing`      → maps to ConsentManager `marketing`.\n *  - `identityOptIn`  → NEW. The \"Recognize me\" switch. NOT a ConsentManager\n *                       dimension — the site's `identify()` is gated on it by a\n *                       separate module; here it is only recorded + emitted.\n */\nexport interface ConsentBannerState {\n  analytics: boolean;\n  marketing: boolean;\n  identityOptIn: boolean;\n}\n\n/** How the visitor arrived at a choice — part of the auditable record. */\nexport type ConsentMethod = \"accept_all\" | \"reject_all\" | \"custom\";\n\n/**\n * The auditable, withdrawable consent record. Persisted to localStorage so the\n * site owner (the data controller) can demonstrate consent if challenged.\n */\nexport interface ConsentRecord {\n  /** The per-category choice. Strictly-necessary is implicit (always on). */\n  categories: ConsentBannerState;\n  /** Which control produced the choice. */\n  method: ConsentMethod;\n  /** Epoch ms at which the choice was recorded. */\n  timestamp: number;\n  /** The site's policy version in effect when consent was given. */\n  policyVersion: string;\n  /** Opaque unique id for this consent event. */\n  id: string;\n}\n\n/**\n * Which consent regime this install runs. `managed` = Crossdeck renders the\n * banner and REQUIRES the owner's `policyUrl`. Deferral to a host CMP is handled\n * upstream (CD-185 Part 1) — a deferred install simply does not mount this widget.\n */\nexport type ConsentBannerMode = \"managed\";\n\n/** Copy + tag for one category row in the Manage pane. Config is per-build. */\nexport interface ConsentCategoryConfig {\n  /** Row title, e.g. \"Analytics\". */\n  title: string;\n  /** Enumerated body copy — MUST state exactly what is captured and its identity status. */\n  description: string;\n  /** Optional pill next to the title, e.g. { text: \"Anonymous\", kind: \"anon\" }. */\n  tag?: { text: string; kind: \"anon\" | \"locked\" | \"optin\" };\n}\n\n/** The four-category copy set. Defaults match the signed-off Webflow build. */\nexport interface ConsentCategoriesConfig {\n  necessary: ConsentCategoryConfig;\n  analytics: ConsentCategoryConfig;\n  identity: ConsentCategoryConfig;\n  marketing: ConsentCategoryConfig;\n}\n\nexport interface ConsentBannerOptions {\n  /** Where to attach the widget host. Element or selector. Defaults to `document.body`. */\n  target?: HTMLElement | string;\n  /**\n   * The site owner's real privacy/cookie-policy URL. REQUIRED in managed mode —\n   * no dead `#`, no Crossdeck-hosted stand-in. The widget refuses to render\n   * without it and logs a diagnostic (CD-185 Part 2).\n   */\n  policyUrl?: string;\n  /** Per-category copy overrides. Omitted categories fall back to the signed-off defaults. */\n  categories?: Partial<ConsentCategoriesConfig>;\n  /** Called on every recorded choice with the resulting state. */\n  onChange?: (state: ConsentBannerState) => void;\n  /** Consent regime. Only `managed` renders a banner. Default `managed`. */\n  mode?: ConsentBannerMode;\n  /**\n   * A previously known choice (e.g. from the site owner's server). Used to seed\n   * the widget when no localStorage record is present. If it satisfies the\n   * current `policyVersion`, the banner stays hidden and the choice is applied.\n   */\n  existingConsent?: Partial<ConsentBannerState>;\n  /**\n   * The site's current policy version. When it differs from the stored record's\n   * version, the widget re-prompts (re-consent). Defaults to `\"1\"`.\n   */\n  policyVersion?: string;\n  /**\n   * The SDK's {@link ConsentManager} (or anything with a compatible `set`). The\n   * widget calls `.set({ analytics, marketing })` on every choice so autocapture\n   * follows consent live. `identityOptIn` is emitted separately via `onChange`.\n   */\n  consent?: Pick<ConsentManager, \"set\">;\n  /** Override the info/attribution link target (tests / self-host). */\n  infoUrl?: string;\n}\n\nexport interface ConsentBannerHandle {\n  /** The host element carrying the shadow root — for layout only; never reach inside. */\n  readonly host: HTMLElement | null;\n  /** Re-open the Manage pane (the withdrawal affordance). Idempotent, safe post-choice. */\n  open(): void;\n  /** The current persisted consent record, or `null` if none has been recorded. */\n  getRecord(): ConsentRecord | null;\n  /** The current in-memory state (defaults to all-off until a choice is recorded). */\n  getState(): ConsentBannerState;\n  /** Tear down: remove the host, delegated listener, and media subscription. Idempotent. */\n  destroy(): void;\n}\n\n/** The `Crossdeck.consent` widget namespace surfaced on the SDK client. */\nexport interface CrossdeckConsentNamespace {\n  /** Mount the branded consent banner. See {@link mountConsentBanner}. */\n  banner(opts: ConsentBannerOptions): ConsentBannerHandle;\n}\n\n// The gradient-cross brand mark, rendered inline. Uses the shadow-local `#cd`\n// gradient defined once in the shadow root (isolation keeps the id collision-free).\nconst MARK = `<svg class=\"mark\" viewBox=\"0 0 48 48\" aria-hidden=\"true\"><path fill=\"url(#cd)\" d=\"M13.9 8.2a4 4 0 0 0-5.7 5.6L18.3 24 8.2 34.2a4 4 0 1 0 5.7 5.6L24 29.7l10.1 10.1a4 4 0 0 0 5.7-5.6L29.7 24l10.1-10.2a4 4 0 1 0-5.7-5.6L24 18.3 13.9 8.2Z\"/></svg>`;\n\nconst DEFAULT_CATEGORIES: ConsentCategoriesConfig = {\n  necessary: {\n    title: \"Strictly necessary\",\n    description:\n      \"The site's own essential cookies — sign-in, session, core function. Crossdeck sets nothing here.\",\n    tag: { text: \"On\", kind: \"locked\" },\n  },\n  analytics: {\n    title: \"Analytics\",\n    description:\n      'Page views, sessions, product usage — anonymous. Identified only if you turn on \"Recognize me\", never automatically.',\n    tag: { text: \"Anonymous\", kind: \"anon\" },\n  },\n  identity: {\n    title: \"Recognize me\",\n    description:\n      \"Link activity to your account across visits and devices. On = your explicit identity opt-in; off = anonymous even when signed in.\",\n    tag: { text: \"Identity opt-in\", kind: \"optin\" },\n  },\n  marketing: {\n    title: \"Marketing\",\n    description:\n      \"Referrer + ad-click IDs (gclid, fbclid…) for attribution, and a HubSpot arrival signal.\",\n  },\n};\n\nconst DEFAULT_STATE: ConsentBannerState = {\n  analytics: false,\n  marketing: false,\n  identityOptIn: false,\n};\n\nconst STYLE = `\n:host{ all: initial; }\n.cd-root{\n  --stage:#efe9dd; --surface:#ffffff; --surface-2:#faf7f0;\n  --text:#17130f; --muted:#6b6259; --faint:#9a9088;\n  --border:rgba(23,19,17,.11); --hair:rgba(23,19,17,.07);\n  --accent:#ee4f25; --accent-strong:#c23a15;\n  --brand-a:#FF3D2E; --brand-b:#FF9A3D;\n  --track:rgba(23,19,17,.15); --lock:#efe9de;\n  --shadow:0 16px 40px -14px rgba(23,19,17,.30), 0 2px 6px rgba(23,19,17,.05);\n  font-family:'Inter',-apple-system,system-ui,sans-serif;\n  color:var(--text); -webkit-font-smoothing:antialiased;\n}\n.cd-root.dark{\n  --stage:#141110; --surface:#1c1917; --surface-2:#232019;\n  --text:#f4efe8; --muted:#a89f95; --faint:#7c7267;\n  --border:rgba(244,239,232,.13); --hair:rgba(244,239,232,.08);\n  --accent:#ff6a42; --accent-strong:#ff8862;\n  --track:rgba(244,239,232,.17); --lock:#2a2521;\n  --shadow:0 20px 46px -14px rgba(0,0,0,.72), 0 2px 6px rgba(0,0,0,.4);\n}\n.cd-root *{box-sizing:border-box}\n.hidden{display:none!important}\n.mark{flex-shrink:0}\n\n.dock{position:fixed;left:20px;bottom:20px;z-index:2147483000}\n\n/* ---- ARRIVAL — slim compact banner ---- */\n.arr{width:330px;background:var(--surface);border:1px solid var(--border);border-radius:15px;box-shadow:var(--shadow);\n  padding:15px 16px 14px}\n.arr-brand{display:flex;align-items:center;gap:7px;margin-bottom:8px}\n.arr-brand .mark{width:16px;height:16px}\n.arr-brand b{font-size:12.5px;font-weight:640}\n.arr-brand .by{margin-left:auto;font-size:9.5px;font-weight:600;color:var(--faint);display:inline-flex;align-items:center;gap:4px;text-decoration:none}\n.arr-brand .by .mark{width:10px;height:10px}\n.arr-brand .by:hover{color:var(--muted)}\n.arr p{font-size:12px;line-height:1.5;color:var(--muted);margin:0 0 12px}\n.arr p b{color:var(--text);font-weight:600}\n.arr p a{color:var(--accent-strong);font-weight:600;text-decoration:none}\n.arr-btns{display:flex;align-items:center;gap:7px}\n.arr-btns .btn{flex:1}\n\n/* buttons */\n.btn{font:inherit;font-family:inherit;font-size:12px;font-weight:640;padding:8px 12px;border-radius:9px;cursor:pointer;\n  border:1px solid var(--border);background:var(--surface);color:var(--text);transition:transform .1s,border-color .12s}\n.btn:hover{transform:translateY(-1px);border-color:var(--accent)}\n.btn:focus-visible{outline:2px solid var(--accent);outline-offset:2px}\n.btn.primary{border:none;color:#fff;background:linear-gradient(120deg,var(--brand-a),var(--brand-b));box-shadow:0 4px 12px -3px rgba(255,61,46,.5)}\n.btn.primary:hover{border:none}\n\n/* ---- MANAGE — compact, bottom-left, corner-anchored ---- */\n.mg{width:360px;background:var(--surface);border:1px solid var(--border);border-radius:15px;box-shadow:var(--shadow);overflow:hidden}\n.mg-head{padding:15px 16px 12px;border-bottom:1px solid var(--hair)}\n.mg-brand{display:flex;align-items:center;gap:7px;margin-bottom:8px}\n.mg-brand .mark{width:16px;height:16px}.mg-brand b{font-size:12.5px;font-weight:640}\n.mg-brand .env{margin-left:auto;font-size:9.5px;font-weight:600;color:var(--faint)}\n.mg-head strong{font-size:13.5px;font-weight:670;display:block}\n.cats{padding:2px 16px;max-height:250px;overflow-y:auto}\n.cat{padding:11px 0;border-bottom:1px solid var(--hair)}\n.cat:last-child{border-bottom:0}\n.cat-top{display:flex;align-items:flex-start;gap:10px}\n.cat-t{display:flex;flex-direction:column;gap:2px;min-width:0}\n.cat-t strong{font-size:12px;font-weight:640;display:flex;align-items:center;gap:6px;flex-wrap:wrap}\n.tag{font-size:8.5px;font-weight:700;letter-spacing:.03em;text-transform:uppercase;padding:1.5px 5px;border-radius:4px}\n.tag.anon{background:rgba(31,157,85,.12);color:#1f9d55}\n.tag.locked{background:var(--surface-2);color:var(--faint);border:1px solid var(--hair)}\n.tag.optin{background:rgba(238,79,37,.12);color:var(--accent-strong)}\n.cd-root.dark .tag.optin{background:rgba(255,106,66,.16)}\n.cat-t span{font-size:10.5px;line-height:1.45;color:var(--muted)}\n.cat-t span b{color:var(--text)}\n.cat-t span code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:10px}\n.sw{margin-left:auto;margin-top:2px;flex-shrink:0;position:relative;width:36px;height:21px;border-radius:99px;background:var(--track);border:none;cursor:pointer;transition:background .18s;padding:0}\n.sw::after{content:\"\";position:absolute;top:2.5px;left:2.5px;width:16px;height:16px;border-radius:50%;background:#fff;box-shadow:0 1px 2px rgba(0,0,0,.28);transition:transform .18s}\n.sw.on{background:linear-gradient(120deg,var(--brand-a),var(--brand-b))}\n.sw.on::after{transform:translateX(15px)}\n.sw:focus-visible{outline:2px solid var(--accent);outline-offset:2px}\n.sw.locked{background:var(--lock);cursor:not-allowed}\n.sw.locked::after{background:linear-gradient(120deg,var(--brand-a),var(--brand-b));transform:translateX(15px);box-shadow:none}\n.mg-actions{padding:12px 16px;display:flex;gap:7px;background:var(--surface-2);border-top:1px solid var(--hair)}\n.mg-actions .btn{flex:1}\n.mg-foot{padding:9px 16px;display:flex;align-items:center;justify-content:space-between;background:var(--surface-2);border-top:1px solid var(--hair)}\n.mg-foot a{font-size:10px;font-weight:600;color:var(--faint);display:inline-flex;align-items:center;gap:5px;text-decoration:none}\n.mg-foot a .mark{width:11px;height:11px}\n.mg-foot a:hover{color:var(--muted)}\n.mg-foot span{font-size:9px;color:var(--faint)}\n`;\n\n/**\n * Mount the Crossdeck Consent banner. Framework-agnostic and guaranteed not to\n * throw — any construction failure is logged and an inert handle returned so the\n * host app is never destabilised by our consent surface.\n */\nexport function mountConsentBanner(\n  opts: ConsentBannerOptions,\n): ConsentBannerHandle {\n  const mode: ConsentBannerMode = opts.mode || \"managed\";\n  const policyUrl = typeof opts.policyUrl === \"string\" ? opts.policyUrl.trim() : \"\";\n  const policyVersion =\n    typeof opts.policyVersion === \"string\" && opts.policyVersion\n      ? opts.policyVersion\n      : \"1\";\n  const infoUrl = normalizeUrl(opts.infoUrl) || CONSENT_INFO_URL;\n  const cats = mergeCategories(opts.categories);\n\n  // Mutable widget state — the single source of truth for switch positions.\n  const state: ConsentBannerState = { ...DEFAULT_STATE };\n\n  let hostEl: HTMLElement | null = null;\n  let root: ShadowRoot | null = null;\n  let rootEl: HTMLElement | null = null;\n  let arrEl: HTMLElement | null = null;\n  let mgEl: HTMLElement | null = null;\n  let mql: MediaQueryList | null = null;\n  let onScheme: ((e: MediaQueryListEvent) => void) | null = null;\n  let onDelegatedClick: ((e: Event) => void) | null = null;\n  let record: ConsentRecord | null = null;\n  let destroyed = false;\n\n  // --- managed mode requires the owner's real policy URL (CD-185 Part 2) ---\n  if (mode === \"managed\" && !policyUrl) {\n    warn(\n      \"policyUrl is required in managed mode — refusing to render a policy-less banner. Pass opts.policyUrl (the site owner's real privacy-policy URL).\",\n    );\n    return makeInertHandle();\n  }\n\n  if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n    warn(\"no DOM available (SSR / non-browser) — consent banner not mounted.\");\n    return makeInertHandle();\n  }\n\n  // --- resolve prior consent: stored record wins, else existingConsent seed ---\n  record = readRecord();\n  const stale = record !== null && record.policyVersion !== policyVersion;\n  let alreadyConsented = false;\n\n  if (record && !stale) {\n    Object.assign(state, record.categories);\n    alreadyConsented = true;\n  } else if (!record && opts.existingConsent) {\n    // Seed from an integrator-supplied prior choice; treat as current-policy consent.\n    Object.assign(state, sanitizeState(opts.existingConsent));\n    alreadyConsented = true;\n  }\n\n  // Build the shadow-isolated UI up front so `open()` works whether or not the\n  // arrival banner is shown on load.\n  safe(() => build(), \"failed to build widget\");\n\n  if (alreadyConsented) {\n    // Consent already on file for this policy — apply it, keep the surface hidden,\n    // but keep the reopen affordance live so withdrawal stays as easy as granting.\n    applyToManager();\n    emit();\n    hideAll();\n  } else {\n    // Fresh visitor, or the policy changed (re-consent) — prompt.\n    showArrival();\n  }\n\n  bindDelegatedReopen();\n  subscribeScheme();\n\n  return {\n    get host() {\n      return hostEl;\n    },\n    open: openManage,\n    getRecord() {\n      return record ? { ...record, categories: { ...record.categories } } : null;\n    },\n    getState() {\n      return { ...state };\n    },\n    destroy,\n  };\n\n  // ---------------------------------------------------------------- build ----\n\n  function build(): void {\n    hostEl = document.createElement(\"div\");\n    hostEl.setAttribute(\"data-crossdeck-consent-host\", \"\");\n    // Keep the host out of the host page's layout/flow; the dock is fixed.\n    hostEl.style.cssText = \"position:static;all:initial;\";\n\n    root = hostEl.attachShadow({ mode: \"open\" });\n\n    const style = document.createElement(\"style\");\n    style.textContent = STYLE;\n    root.appendChild(style);\n\n    rootEl = document.createElement(\"div\");\n    rootEl.className = \"cd-root\";\n    rootEl.innerHTML = template();\n    root.appendChild(rootEl);\n\n    arrEl = rootEl.querySelector(\".arr\");\n    mgEl = rootEl.querySelector(\".mg\");\n\n    // Fill in owner-configured URLs programmatically (never via innerHTML) so a\n    // hostile URL can't break out of the attribute context.\n    setHref(rootEl.querySelector(\".arr .policy-link\"), policyUrl);\n    setHref(rootEl.querySelector(\".by\"), infoUrl);\n    setHref(rootEl.querySelector(\".mg-foot a\"), infoUrl);\n\n    wireControls();\n    syncSwitches();\n\n    const parent = resolveTarget(opts.target) || document.body;\n    if (!parent) {\n      warn(\"no mount target and no document.body — consent banner not attached.\");\n      return;\n    }\n    parent.appendChild(hostEl);\n  }\n\n  function template(): string {\n    const gradient = `<svg width=\"0\" height=\"0\" style=\"position:absolute\" aria-hidden=\"true\"><defs><linearGradient id=\"cd\" x1=\"8\" y1=\"6\" x2=\"40\" y2=\"42\" gradientUnits=\"userSpaceOnUse\"><stop offset=\"0\" stop-color=\"#FF3D2E\"/><stop offset=\"1\" stop-color=\"#FF9A3D\"/></linearGradient></defs></svg>`;\n    return (\n      gradient +\n      `<div class=\"dock\">` +\n      arrivalMarkup() +\n      manageMarkup() +\n      `</div>`\n    );\n  }\n\n  function arrivalMarkup(): string {\n    return (\n      `<div class=\"arr\" role=\"dialog\" aria-label=\"Your privacy\" aria-modal=\"false\">` +\n      `<div class=\"arr-brand\">${MARK}<b>Your privacy</b>` +\n      `<a class=\"by\" target=\"_blank\" rel=\"noopener\">${MARK}by Crossdeck</a></div>` +\n      `<p><b>You choose what's collected.</b> Nothing but essentials runs until you say so. ` +\n      `<a class=\"policy-link\" target=\"_blank\" rel=\"noopener\">Privacy policy</a>.</p>` +\n      `<div class=\"arr-btns\">` +\n      `<button type=\"button\" class=\"btn primary\" data-act=\"accept_all\">Accept all</button>` +\n      `<button type=\"button\" class=\"btn\" data-act=\"manage\">Manage</button>` +\n      `<button type=\"button\" class=\"btn\" data-act=\"reject_all\">Reject all</button>` +\n      `</div></div>`\n    );\n  }\n\n  function manageMarkup(): string {\n    return (\n      `<div class=\"mg hidden\" role=\"dialog\" aria-label=\"Manage privacy preferences\" aria-modal=\"false\">` +\n      `<div class=\"mg-head\"><div class=\"mg-brand\">${MARK}<b>Your privacy</b>` +\n      `<span class=\"env\">This site uses Crossdeck</span></div>` +\n      `<strong>Manage preferences</strong></div>` +\n      `<div class=\"cats\">` +\n      catRow(cats.necessary, null, true) +\n      catRow(cats.analytics, \"analytics\", false) +\n      catRow(cats.identity, \"identityOptIn\", false) +\n      catRow(cats.marketing, \"marketing\", false) +\n      `</div>` +\n      `<div class=\"mg-actions\">` +\n      `<button type=\"button\" class=\"btn\" data-act=\"reject_all\">Reject all</button>` +\n      `<button type=\"button\" class=\"btn\" data-act=\"accept_all\">Accept all</button>` +\n      `<button type=\"button\" class=\"btn primary\" data-act=\"save\">Save</button>` +\n      `</div>` +\n      `<div class=\"mg-foot\"><a target=\"_blank\" rel=\"noopener\">${MARK}Consent by Crossdeck</a>` +\n      `<span>processes data on this site's behalf</span></div>` +\n      `</div>`\n    );\n  }\n\n  function catRow(\n    cfg: ConsentCategoryConfig,\n    key: keyof ConsentBannerState | null,\n    locked: boolean,\n  ): string {\n    const tag = cfg.tag\n      ? `<span class=\"tag ${cfg.tag.kind}\">${escapeHtml(cfg.tag.text)}</span>`\n      : \"\";\n    const sw = locked\n      ? `<button type=\"button\" class=\"sw locked\" disabled aria-label=\"${escapeHtml(cfg.title)} (always on)\"></button>`\n      : `<button type=\"button\" class=\"sw\" role=\"switch\" aria-checked=\"false\" data-cat=\"${String(key)}\" aria-label=\"${escapeHtml(cfg.title)}\"></button>`;\n    return (\n      `<div class=\"cat\"><div class=\"cat-top\"><div class=\"cat-t\">` +\n      `<strong>${escapeHtml(cfg.title)} ${tag}</strong>` +\n      `<span>${escapeHtml(cfg.description)}</span></div>${sw}</div></div>`\n    );\n  }\n\n  // ------------------------------------------------------------- wiring ----\n\n  function wireControls(): void {\n    if (!rootEl) return;\n    const buttons = rootEl.querySelectorAll<HTMLButtonElement>(\"[data-act]\");\n    buttons.forEach((btn) => {\n      btn.addEventListener(\"click\", () => {\n        const act = btn.getAttribute(\"data-act\");\n        if (act === \"manage\") showManage();\n        else if (act === \"accept_all\") choose(\"accept_all\");\n        else if (act === \"reject_all\") choose(\"reject_all\");\n        else if (act === \"save\") choose(\"custom\");\n      });\n    });\n    const switches = rootEl.querySelectorAll<HTMLButtonElement>(\".sw[data-cat]\");\n    switches.forEach((el) => {\n      el.addEventListener(\"click\", () => {\n        const key = el.getAttribute(\"data-cat\") as keyof ConsentBannerState | null;\n        if (!key) return;\n        state[key] = !state[key];\n        syncSwitches();\n      });\n    });\n  }\n\n  /** Reflect `state` onto the switch DOM (class + aria). */\n  function syncSwitches(): void {\n    if (!rootEl) return;\n    rootEl.querySelectorAll<HTMLButtonElement>(\".sw[data-cat]\").forEach((el) => {\n      const key = el.getAttribute(\"data-cat\") as keyof ConsentBannerState | null;\n      if (!key) return;\n      const on = state[key] === true;\n      el.classList.toggle(\"on\", on);\n      el.setAttribute(\"aria-checked\", on ? \"true\" : \"false\");\n    });\n  }\n\n  // ------------------------------------------------------------ choices ----\n\n  function choose(method: ConsentMethod): void {\n    if (method === \"accept_all\") {\n      state.analytics = true;\n      state.marketing = true;\n      state.identityOptIn = true;\n    } else if (method === \"reject_all\") {\n      state.analytics = false;\n      state.marketing = false;\n      state.identityOptIn = false;\n    }\n    // `custom` keeps the current per-switch state as-is.\n    syncSwitches();\n    persist(method);\n    applyToManager();\n    emit();\n    hideAll();\n  }\n\n  function persist(method: ConsentMethod): void {\n    record = {\n      categories: { ...state },\n      method,\n      timestamp: Date.now(),\n      policyVersion,\n      id: makeId(),\n    };\n    safe(() => {\n      window.localStorage.setItem(CONSENT_STORAGE_KEY, JSON.stringify(record));\n    }, \"could not persist consent record to localStorage\");\n  }\n\n  function applyToManager(): void {\n    if (!opts.consent) return;\n    safe(() => {\n      opts.consent?.set({ analytics: state.analytics, marketing: state.marketing });\n    }, \"could not apply consent to the ConsentManager\");\n  }\n\n  function emit(): void {\n    if (!opts.onChange) return;\n    safe(() => opts.onChange?.({ ...state }), \"onChange callback threw\");\n  }\n\n  // -------------------------------------------------------- visibility ----\n\n  function showArrival(): void {\n    toggle(arrEl, true);\n    toggle(mgEl, false);\n  }\n\n  function showManage(): void {\n    syncSwitches();\n    toggle(arrEl, false);\n    toggle(mgEl, true);\n  }\n\n  function hideAll(): void {\n    // After a choice the widget DISAPPEARS COMPLETELY — no floating pill.\n    toggle(arrEl, false);\n    toggle(mgEl, false);\n  }\n\n  function openManage(): void {\n    if (destroyed) return;\n    safe(() => showManage(), \"could not reopen the Manage pane\");\n  }\n\n  function toggle(el: HTMLElement | null, visible: boolean): void {\n    if (el) el.classList.toggle(\"hidden\", !visible);\n  }\n\n  // ----------------------------------------------------- reopen / theme ----\n\n  function bindDelegatedReopen(): void {\n    onDelegatedClick = (e: Event) => {\n      const path = e.composedPath ? e.composedPath() : [];\n      for (const node of path) {\n        if (node instanceof Element && node.closest(\"[data-crossdeck-consent]\")) {\n          e.preventDefault();\n          openManage();\n          return;\n        }\n      }\n      // Fallback for environments without composedPath.\n      const t = e.target;\n      if (t instanceof Element && t.closest(\"[data-crossdeck-consent]\")) {\n        e.preventDefault();\n        openManage();\n      }\n    };\n    safe(\n      () => document.addEventListener(\"click\", onDelegatedClick as EventListener),\n      \"could not bind [data-crossdeck-consent] reopen listener\",\n    );\n  }\n\n  function subscribeScheme(): void {\n    safe(() => {\n      mql = window.matchMedia(\"(prefers-color-scheme: dark)\");\n      applyScheme(mql.matches);\n      onScheme = (e: MediaQueryListEvent) => applyScheme(e.matches);\n      // addEventListener is the modern API; older Safari only has addListener.\n      if (typeof mql.addEventListener === \"function\") {\n        mql.addEventListener(\"change\", onScheme);\n      } else if (typeof mql.addListener === \"function\") {\n        mql.addListener(onScheme);\n      }\n    }, \"could not subscribe to prefers-color-scheme\");\n  }\n\n  function applyScheme(dark: boolean): void {\n    if (rootEl) rootEl.classList.toggle(\"dark\", dark);\n  }\n\n  // -------------------------------------------------------- lifecycle ----\n\n  function destroy(): void {\n    if (destroyed) return;\n    destroyed = true;\n    if (onDelegatedClick) {\n      safe(\n        () => document.removeEventListener(\"click\", onDelegatedClick as EventListener),\n        \"could not remove reopen listener\",\n      );\n    }\n    if (mql && onScheme) {\n      safe(() => {\n        if (typeof mql?.removeEventListener === \"function\") {\n          mql.removeEventListener(\"change\", onScheme as (e: MediaQueryListEvent) => void);\n        } else if (typeof mql?.removeListener === \"function\") {\n          mql.removeListener(onScheme as (e: MediaQueryListEvent) => void);\n        }\n      }, \"could not remove scheme listener\");\n    }\n    safe(() => hostEl?.parentNode?.removeChild(hostEl as HTMLElement), \"could not remove host node\");\n  }\n\n  // ------------------------------------------------------------ helpers ----\n\n  function readRecord(): ConsentRecord | null {\n    let raw: string | null = null;\n    safe(() => {\n      raw = window.localStorage.getItem(CONSENT_STORAGE_KEY);\n    }, \"could not read consent record from localStorage\");\n    if (!raw) return null;\n    try {\n      const parsed = JSON.parse(raw) as Partial<ConsentRecord>;\n      if (!parsed || typeof parsed !== \"object\" || !parsed.categories) return null;\n      return {\n        categories: sanitizeState(parsed.categories),\n        method: (parsed.method as ConsentMethod) || \"custom\",\n        timestamp: typeof parsed.timestamp === \"number\" ? parsed.timestamp : 0,\n        policyVersion:\n          typeof parsed.policyVersion === \"string\" ? parsed.policyVersion : \"1\",\n        id: typeof parsed.id === \"string\" ? parsed.id : makeId(),\n      };\n    } catch (err) {\n      warn(\"stored consent record was not valid JSON — ignoring it.\", err);\n      return null;\n    }\n  }\n\n  function makeInertHandle(): ConsentBannerHandle {\n    return {\n      host: null,\n      open() {\n        warn(\"open() called but the banner did not mount (see prior warning).\");\n      },\n      getRecord() {\n        return null;\n      },\n      getState() {\n        return { ...DEFAULT_STATE };\n      },\n      destroy() {\n        /* nothing was mounted */\n      },\n    };\n  }\n}\n\n// ============================================================\n// Module-level pure helpers\n// ============================================================\n\nfunction mergeCategories(\n  overrides?: Partial<ConsentCategoriesConfig>,\n): ConsentCategoriesConfig {\n  if (!overrides) return DEFAULT_CATEGORIES;\n  return {\n    necessary: overrides.necessary || DEFAULT_CATEGORIES.necessary,\n    analytics: overrides.analytics || DEFAULT_CATEGORIES.analytics,\n    identity: overrides.identity || DEFAULT_CATEGORIES.identity,\n    marketing: overrides.marketing || DEFAULT_CATEGORIES.marketing,\n  };\n}\n\nfunction sanitizeState(partial: Partial<ConsentBannerState>): ConsentBannerState {\n  return {\n    analytics: partial.analytics === true,\n    marketing: partial.marketing === true,\n    identityOptIn: partial.identityOptIn === true,\n  };\n}\n\nfunction resolveTarget(target?: HTMLElement | string): HTMLElement | null {\n  try {\n    if (!target) return null;\n    if (typeof target === \"string\") return document.querySelector(target);\n    return target;\n  } catch (err) {\n    warn(\"could not resolve mount target\", err);\n    return null;\n  }\n}\n\nfunction setHref(el: Element | null, url: string): void {\n  if (!el) return;\n  const safeUrl = normalizeUrl(url);\n  if (safeUrl) {\n    el.setAttribute(\"href\", safeUrl);\n  } else if (url) {\n    // A non-http(s) or malformed URL was supplied — do not render it as a link.\n    el.removeAttribute(\"href\");\n    warn(\"refusing to set a non-http(s) URL as a link: \" + String(url));\n  }\n}\n\n/** Only http(s) URLs are allowed as rendered links (blocks javascript:/data: etc). */\nfunction normalizeUrl(url?: string): string | null {\n  if (!url || typeof url !== \"string\") return null;\n  try {\n    const u = new URL(url, typeof location !== \"undefined\" ? location.href : undefined);\n    return u.protocol === \"http:\" || u.protocol === \"https:\" ? u.href : null;\n  } catch {\n    return null;\n  }\n}\n\nfunction makeId(): string {\n  try {\n    const c = (globalThis as { crypto?: Crypto }).crypto;\n    if (c && typeof c.randomUUID === \"function\") return c.randomUUID();\n  } catch (err) {\n    warn(\"crypto.randomUUID unavailable — falling back to a random id\", err);\n  }\n  return \"cdc_\" + Math.random().toString(36).slice(2) + Date.now().toString(36);\n}\n\nfunction escapeHtml(s: string): string {\n  return String(s)\n    .replace(/&/g, \"&amp;\")\n    .replace(/</g, \"&lt;\")\n    .replace(/>/g, \"&gt;\")\n    .replace(/\"/g, \"&quot;\")\n    .replace(/'/g, \"&#39;\");\n}\n\n/**\n * Run risky DOM work, surfacing (never swallowing) any failure as a concise\n * `[crossdeck] consent: …` warning. Webflow finding #11: no empty catch blocks.\n */\nfunction safe(fn: () => void, context: string): void {\n  try {\n    fn();\n  } catch (err) {\n    warn(context, err);\n  }\n}\n\nfunction warn(message: string, err?: unknown): void {\n  try {\n    const prefix = \"[crossdeck] consent: \" + message;\n    if (typeof console !== \"undefined\" && typeof console.warn === \"function\") {\n      if (err !== undefined) console.warn(prefix, err);\n      else console.warn(prefix);\n    }\n  } catch {\n    // console itself is unavailable — nothing further we can safely do.\n  }\n}\n"],"mappings":";AA2CO,IAAM,mBAAmB;AAGzB,IAAM,sBAAsB;AAwHnC,IAAM,OAAO;AAEb,IAAM,qBAA8C;AAAA,EAClD,WAAW;AAAA,IACT,OAAO;AAAA,IACP,aACE;AAAA,IACF,KAAK,EAAE,MAAM,MAAM,MAAM,SAAS;AAAA,EACpC;AAAA,EACA,WAAW;AAAA,IACT,OAAO;AAAA,IACP,aACE;AAAA,IACF,KAAK,EAAE,MAAM,aAAa,MAAM,OAAO;AAAA,EACzC;AAAA,EACA,UAAU;AAAA,IACR,OAAO;AAAA,IACP,aACE;AAAA,IACF,KAAK,EAAE,MAAM,mBAAmB,MAAM,QAAQ;AAAA,EAChD;AAAA,EACA,WAAW;AAAA,IACT,OAAO;AAAA,IACP,aACE;AAAA,EACJ;AACF;AAEA,IAAM,gBAAoC;AAAA,EACxC,WAAW;AAAA,EACX,WAAW;AAAA,EACX,eAAe;AACjB;AAEA,IAAM,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA4FP,SAAS,mBACd,MACqB;AACrB,QAAM,OAA0B,KAAK,QAAQ;AAC7C,QAAM,YAAY,OAAO,KAAK,cAAc,WAAW,KAAK,UAAU,KAAK,IAAI;AAC/E,QAAM,gBACJ,OAAO,KAAK,kBAAkB,YAAY,KAAK,gBAC3C,KAAK,gBACL;AACN,QAAM,UAAU,aAAa,KAAK,OAAO,KAAK;AAC9C,QAAM,OAAO,gBAAgB,KAAK,UAAU;AAG5C,QAAM,QAA4B,EAAE,GAAG,cAAc;AAErD,MAAI,SAA6B;AACjC,MAAI,OAA0B;AAC9B,MAAI,SAA6B;AACjC,MAAI,QAA4B;AAChC,MAAI,OAA2B;AAC/B,MAAI,MAA6B;AACjC,MAAI,WAAsD;AAC1D,MAAI,mBAAgD;AACpD,MAAI,SAA+B;AACnC,MAAI,YAAY;AAGhB,MAAI,SAAS,aAAa,CAAC,WAAW;AACpC;AAAA,MACE;AAAA,IACF;AACA,WAAO,gBAAgB;AAAA,EACzB;AAEA,MAAI,OAAO,WAAW,eAAe,OAAO,aAAa,aAAa;AACpE,SAAK,yEAAoE;AACzE,WAAO,gBAAgB;AAAA,EACzB;AAGA,WAAS,WAAW;AACpB,QAAM,QAAQ,WAAW,QAAQ,OAAO,kBAAkB;AAC1D,MAAI,mBAAmB;AAEvB,MAAI,UAAU,CAAC,OAAO;AACpB,WAAO,OAAO,OAAO,OAAO,UAAU;AACtC,uBAAmB;AAAA,EACrB,WAAW,CAAC,UAAU,KAAK,iBAAiB;AAE1C,WAAO,OAAO,OAAO,cAAc,KAAK,eAAe,CAAC;AACxD,uBAAmB;AAAA,EACrB;AAIA,OAAK,MAAM,MAAM,GAAG,wBAAwB;AAE5C,MAAI,kBAAkB;AAGpB,mBAAe;AACf,SAAK;AACL,YAAQ;AAAA,EACV,OAAO;AAEL,gBAAY;AAAA,EACd;AAEA,sBAAoB;AACpB,kBAAgB;AAEhB,SAAO;AAAA,IACL,IAAI,OAAO;AACT,aAAO;AAAA,IACT;AAAA,IACA,MAAM;AAAA,IACN,YAAY;AACV,aAAO,SAAS,EAAE,GAAG,QAAQ,YAAY,EAAE,GAAG,OAAO,WAAW,EAAE,IAAI;AAAA,IACxE;AAAA,IACA,WAAW;AACT,aAAO,EAAE,GAAG,MAAM;AAAA,IACpB;AAAA,IACA;AAAA,EACF;AAIA,WAAS,QAAc;AACrB,aAAS,SAAS,cAAc,KAAK;AACrC,WAAO,aAAa,+BAA+B,EAAE;AAErD,WAAO,MAAM,UAAU;AAEvB,WAAO,OAAO,aAAa,EAAE,MAAM,OAAO,CAAC;AAE3C,UAAM,QAAQ,SAAS,cAAc,OAAO;AAC5C,UAAM,cAAc;AACpB,SAAK,YAAY,KAAK;AAEtB,aAAS,SAAS,cAAc,KAAK;AACrC,WAAO,YAAY;AACnB,WAAO,YAAY,SAAS;AAC5B,SAAK,YAAY,MAAM;AAEvB,YAAQ,OAAO,cAAc,MAAM;AACnC,WAAO,OAAO,cAAc,KAAK;AAIjC,YAAQ,OAAO,cAAc,mBAAmB,GAAG,SAAS;AAC5D,YAAQ,OAAO,cAAc,KAAK,GAAG,OAAO;AAC5C,YAAQ,OAAO,cAAc,YAAY,GAAG,OAAO;AAEnD,iBAAa;AACb,iBAAa;AAEb,UAAM,SAAS,cAAc,KAAK,MAAM,KAAK,SAAS;AACtD,QAAI,CAAC,QAAQ;AACX,WAAK,0EAAqE;AAC1E;AAAA,IACF;AACA,WAAO,YAAY,MAAM;AAAA,EAC3B;AAEA,WAAS,WAAmB;AAC1B,UAAM,WAAW;AACjB,WACE,WACA,uBACA,cAAc,IACd,aAAa,IACb;AAAA,EAEJ;AAEA,WAAS,gBAAwB;AAC/B,WACE,sGAC0B,IAAI,mEACkB,IAAI;AAAA,EASxD;AAEA,WAAS,eAAuB;AAC9B,WACE,8IAC8C,IAAI,0IAIlD,OAAO,KAAK,WAAW,MAAM,IAAI,IACjC,OAAO,KAAK,WAAW,aAAa,KAAK,IACzC,OAAO,KAAK,UAAU,iBAAiB,KAAK,IAC5C,OAAO,KAAK,WAAW,aAAa,KAAK,IACzC,2TAM0D,IAAI;AAAA,EAIlE;AAEA,WAAS,OACP,KACA,KACA,QACQ;AACR,UAAM,MAAM,IAAI,MACZ,oBAAoB,IAAI,IAAI,IAAI,KAAK,WAAW,IAAI,IAAI,IAAI,CAAC,YAC7D;AACJ,UAAM,KAAK,SACP,gEAAgE,WAAW,IAAI,KAAK,CAAC,4BACrF,iFAAiF,OAAO,GAAG,CAAC,iBAAiB,WAAW,IAAI,KAAK,CAAC;AACtI,WACE,oEACW,WAAW,IAAI,KAAK,CAAC,IAAI,GAAG,kBAC9B,WAAW,IAAI,WAAW,CAAC,gBAAgB,EAAE;AAAA,EAE1D;AAIA,WAAS,eAAqB;AAC5B,QAAI,CAAC,OAAQ;AACb,UAAM,UAAU,OAAO,iBAAoC,YAAY;AACvE,YAAQ,QAAQ,CAAC,QAAQ;AACvB,UAAI,iBAAiB,SAAS,MAAM;AAClC,cAAM,MAAM,IAAI,aAAa,UAAU;AACvC,YAAI,QAAQ,SAAU,YAAW;AAAA,iBACxB,QAAQ,aAAc,QAAO,YAAY;AAAA,iBACzC,QAAQ,aAAc,QAAO,YAAY;AAAA,iBACzC,QAAQ,OAAQ,QAAO,QAAQ;AAAA,MAC1C,CAAC;AAAA,IACH,CAAC;AACD,UAAM,WAAW,OAAO,iBAAoC,eAAe;AAC3E,aAAS,QAAQ,CAAC,OAAO;AACvB,SAAG,iBAAiB,SAAS,MAAM;AACjC,cAAM,MAAM,GAAG,aAAa,UAAU;AACtC,YAAI,CAAC,IAAK;AACV,cAAM,GAAG,IAAI,CAAC,MAAM,GAAG;AACvB,qBAAa;AAAA,MACf,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAGA,WAAS,eAAqB;AAC5B,QAAI,CAAC,OAAQ;AACb,WAAO,iBAAoC,eAAe,EAAE,QAAQ,CAAC,OAAO;AAC1E,YAAM,MAAM,GAAG,aAAa,UAAU;AACtC,UAAI,CAAC,IAAK;AACV,YAAM,KAAK,MAAM,GAAG,MAAM;AAC1B,SAAG,UAAU,OAAO,MAAM,EAAE;AAC5B,SAAG,aAAa,gBAAgB,KAAK,SAAS,OAAO;AAAA,IACvD,CAAC;AAAA,EACH;AAIA,WAAS,OAAO,QAA6B;AAC3C,QAAI,WAAW,cAAc;AAC3B,YAAM,YAAY;AAClB,YAAM,YAAY;AAClB,YAAM,gBAAgB;AAAA,IACxB,WAAW,WAAW,cAAc;AAClC,YAAM,YAAY;AAClB,YAAM,YAAY;AAClB,YAAM,gBAAgB;AAAA,IACxB;AAEA,iBAAa;AACb,YAAQ,MAAM;AACd,mBAAe;AACf,SAAK;AACL,YAAQ;AAAA,EACV;AAEA,WAAS,QAAQ,QAA6B;AAC5C,aAAS;AAAA,MACP,YAAY,EAAE,GAAG,MAAM;AAAA,MACvB;AAAA,MACA,WAAW,KAAK,IAAI;AAAA,MACpB;AAAA,MACA,IAAI,OAAO;AAAA,IACb;AACA,SAAK,MAAM;AACT,aAAO,aAAa,QAAQ,qBAAqB,KAAK,UAAU,MAAM,CAAC;AAAA,IACzE,GAAG,kDAAkD;AAAA,EACvD;AAEA,WAAS,iBAAuB;AAC9B,QAAI,CAAC,KAAK,QAAS;AACnB,SAAK,MAAM;AACT,WAAK,SAAS,IAAI,EAAE,WAAW,MAAM,WAAW,WAAW,MAAM,UAAU,CAAC;AAAA,IAC9E,GAAG,+CAA+C;AAAA,EACpD;AAEA,WAAS,OAAa;AACpB,QAAI,CAAC,KAAK,SAAU;AACpB,SAAK,MAAM,KAAK,WAAW,EAAE,GAAG,MAAM,CAAC,GAAG,yBAAyB;AAAA,EACrE;AAIA,WAAS,cAAoB;AAC3B,WAAO,OAAO,IAAI;AAClB,WAAO,MAAM,KAAK;AAAA,EACpB;AAEA,WAAS,aAAmB;AAC1B,iBAAa;AACb,WAAO,OAAO,KAAK;AACnB,WAAO,MAAM,IAAI;AAAA,EACnB;AAEA,WAAS,UAAgB;AAEvB,WAAO,OAAO,KAAK;AACnB,WAAO,MAAM,KAAK;AAAA,EACpB;AAEA,WAAS,aAAmB;AAC1B,QAAI,UAAW;AACf,SAAK,MAAM,WAAW,GAAG,kCAAkC;AAAA,EAC7D;AAEA,WAAS,OAAO,IAAwB,SAAwB;AAC9D,QAAI,GAAI,IAAG,UAAU,OAAO,UAAU,CAAC,OAAO;AAAA,EAChD;AAIA,WAAS,sBAA4B;AACnC,uBAAmB,CAAC,MAAa;AAC/B,YAAM,OAAO,EAAE,eAAe,EAAE,aAAa,IAAI,CAAC;AAClD,iBAAW,QAAQ,MAAM;AACvB,YAAI,gBAAgB,WAAW,KAAK,QAAQ,0BAA0B,GAAG;AACvE,YAAE,eAAe;AACjB,qBAAW;AACX;AAAA,QACF;AAAA,MACF;AAEA,YAAM,IAAI,EAAE;AACZ,UAAI,aAAa,WAAW,EAAE,QAAQ,0BAA0B,GAAG;AACjE,UAAE,eAAe;AACjB,mBAAW;AAAA,MACb;AAAA,IACF;AACA;AAAA,MACE,MAAM,SAAS,iBAAiB,SAAS,gBAAiC;AAAA,MAC1E;AAAA,IACF;AAAA,EACF;AAEA,WAAS,kBAAwB;AAC/B,SAAK,MAAM;AACT,YAAM,OAAO,WAAW,8BAA8B;AACtD,kBAAY,IAAI,OAAO;AACvB,iBAAW,CAAC,MAA2B,YAAY,EAAE,OAAO;AAE5D,UAAI,OAAO,IAAI,qBAAqB,YAAY;AAC9C,YAAI,iBAAiB,UAAU,QAAQ;AAAA,MACzC,WAAW,OAAO,IAAI,gBAAgB,YAAY;AAChD,YAAI,YAAY,QAAQ;AAAA,MAC1B;AAAA,IACF,GAAG,6CAA6C;AAAA,EAClD;AAEA,WAAS,YAAY,MAAqB;AACxC,QAAI,OAAQ,QAAO,UAAU,OAAO,QAAQ,IAAI;AAAA,EAClD;AAIA,WAAS,UAAgB;AACvB,QAAI,UAAW;AACf,gBAAY;AACZ,QAAI,kBAAkB;AACpB;AAAA,QACE,MAAM,SAAS,oBAAoB,SAAS,gBAAiC;AAAA,QAC7E;AAAA,MACF;AAAA,IACF;AACA,QAAI,OAAO,UAAU;AACnB,WAAK,MAAM;AACT,YAAI,OAAO,KAAK,wBAAwB,YAAY;AAClD,cAAI,oBAAoB,UAAU,QAA4C;AAAA,QAChF,WAAW,OAAO,KAAK,mBAAmB,YAAY;AACpD,cAAI,eAAe,QAA4C;AAAA,QACjE;AAAA,MACF,GAAG,kCAAkC;AAAA,IACvC;AACA,SAAK,MAAM,QAAQ,YAAY,YAAY,MAAqB,GAAG,4BAA4B;AAAA,EACjG;AAIA,WAAS,aAAmC;AAC1C,QAAI,MAAqB;AACzB,SAAK,MAAM;AACT,YAAM,OAAO,aAAa,QAAQ,mBAAmB;AAAA,IACvD,GAAG,iDAAiD;AACpD,QAAI,CAAC,IAAK,QAAO;AACjB,QAAI;AACF,YAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,UAAI,CAAC,UAAU,OAAO,WAAW,YAAY,CAAC,OAAO,WAAY,QAAO;AACxE,aAAO;AAAA,QACL,YAAY,cAAc,OAAO,UAAU;AAAA,QAC3C,QAAS,OAAO,UAA4B;AAAA,QAC5C,WAAW,OAAO,OAAO,cAAc,WAAW,OAAO,YAAY;AAAA,QACrE,eACE,OAAO,OAAO,kBAAkB,WAAW,OAAO,gBAAgB;AAAA,QACpE,IAAI,OAAO,OAAO,OAAO,WAAW,OAAO,KAAK,OAAO;AAAA,MACzD;AAAA,IACF,SAAS,KAAK;AACZ,WAAK,gEAA2D,GAAG;AACnE,aAAO;AAAA,IACT;AAAA,EACF;AAEA,WAAS,kBAAuC;AAC9C,WAAO;AAAA,MACL,MAAM;AAAA,MACN,OAAO;AACL,aAAK,iEAAiE;AAAA,MACxE;AAAA,MACA,YAAY;AACV,eAAO;AAAA,MACT;AAAA,MACA,WAAW;AACT,eAAO,EAAE,GAAG,cAAc;AAAA,MAC5B;AAAA,MACA,UAAU;AAAA,MAEV;AAAA,IACF;AAAA,EACF;AACF;AAMA,SAAS,gBACP,WACyB;AACzB,MAAI,CAAC,UAAW,QAAO;AACvB,SAAO;AAAA,IACL,WAAW,UAAU,aAAa,mBAAmB;AAAA,IACrD,WAAW,UAAU,aAAa,mBAAmB;AAAA,IACrD,UAAU,UAAU,YAAY,mBAAmB;AAAA,IACnD,WAAW,UAAU,aAAa,mBAAmB;AAAA,EACvD;AACF;AAEA,SAAS,cAAc,SAA0D;AAC/E,SAAO;AAAA,IACL,WAAW,QAAQ,cAAc;AAAA,IACjC,WAAW,QAAQ,cAAc;AAAA,IACjC,eAAe,QAAQ,kBAAkB;AAAA,EAC3C;AACF;AAEA,SAAS,cAAc,QAAmD;AACxE,MAAI;AACF,QAAI,CAAC,OAAQ,QAAO;AACpB,QAAI,OAAO,WAAW,SAAU,QAAO,SAAS,cAAc,MAAM;AACpE,WAAO;AAAA,EACT,SAAS,KAAK;AACZ,SAAK,kCAAkC,GAAG;AAC1C,WAAO;AAAA,EACT;AACF;AAEA,SAAS,QAAQ,IAAoB,KAAmB;AACtD,MAAI,CAAC,GAAI;AACT,QAAM,UAAU,aAAa,GAAG;AAChC,MAAI,SAAS;AACX,OAAG,aAAa,QAAQ,OAAO;AAAA,EACjC,WAAW,KAAK;AAEd,OAAG,gBAAgB,MAAM;AACzB,SAAK,kDAAkD,OAAO,GAAG,CAAC;AAAA,EACpE;AACF;AAGA,SAAS,aAAa,KAA6B;AACjD,MAAI,CAAC,OAAO,OAAO,QAAQ,SAAU,QAAO;AAC5C,MAAI;AACF,UAAM,IAAI,IAAI,IAAI,KAAK,OAAO,aAAa,cAAc,SAAS,OAAO,MAAS;AAClF,WAAO,EAAE,aAAa,WAAW,EAAE,aAAa,WAAW,EAAE,OAAO;AAAA,EACtE,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,SAAiB;AACxB,MAAI;AACF,UAAM,IAAK,WAAmC;AAC9C,QAAI,KAAK,OAAO,EAAE,eAAe,WAAY,QAAO,EAAE,WAAW;AAAA,EACnE,SAAS,KAAK;AACZ,SAAK,oEAA+D,GAAG;AAAA,EACzE;AACA,SAAO,SAAS,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,CAAC,IAAI,KAAK,IAAI,EAAE,SAAS,EAAE;AAC9E;AAEA,SAAS,WAAW,GAAmB;AACrC,SAAO,OAAO,CAAC,EACZ,QAAQ,MAAM,OAAO,EACrB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,QAAQ,EACtB,QAAQ,MAAM,OAAO;AAC1B;AAMA,SAAS,KAAK,IAAgB,SAAuB;AACnD,MAAI;AACF,OAAG;AAAA,EACL,SAAS,KAAK;AACZ,SAAK,SAAS,GAAG;AAAA,EACnB;AACF;AAEA,SAAS,KAAK,SAAiB,KAAqB;AAClD,MAAI;AACF,UAAM,SAAS,0BAA0B;AACzC,QAAI,OAAO,YAAY,eAAe,OAAO,QAAQ,SAAS,YAAY;AACxE,UAAI,QAAQ,OAAW,SAAQ,KAAK,QAAQ,GAAG;AAAA,UAC1C,SAAQ,KAAK,MAAM;AAAA,IAC1B;AAAA,EACF,QAAQ;AAAA,EAER;AACF;","names":[]}