@lime-bundles/widget 2.4.1 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/lime-bundle.ts","../src/renderers/fixed.ts","../src/renderers/pricing.ts","../src/renderers/countdown.ts","../src/renderers/cta-button.ts","../src/renderers/dom.ts","../src/renderers/image.ts","../src/renderers/mix-match.ts","../src/renderers/volume.ts","../src/utils/input-mode.ts","../src/styles/bundle-css.ts","../src/index.ts"],"sourcesContent":["/**\n * <lime-bundle> Web Component — renders Lime Bundles on any storefront.\n *\n * ## The two modes\n *\n * Single-bundle (pinned):\n * <lime-bundle\n * shop-domain=\"my-shop.myshopify.com\"\n * storefront-token=\"shpat_...\"\n * bundle-gid=\"gid://shopify/Metaobject/42\"\n * ></lime-bundle>\n *\n * Product-aware (matches classic Liquid theme block behaviour — one snippet\n * on the product page renders every active bundle configured against that\n * product):\n * <lime-bundle\n * shop-domain=\"my-shop.myshopify.com\"\n * storefront-token=\"shpat_...\"\n * ></lime-bundle>\n *\n * Product resolution cascade (when no `bundle-gid` is set):\n * 1. explicit `product-handle` attribute\n * 2. <meta name=\"shopify:product-handle\" content=\"...\"> on the page\n * 3. /products/<handle> segment of window.location.pathname\n * 4. fallthrough: renders nothing, fires `lime-bundle:error`\n *\n * ## Add-to-cart behaviour\n *\n * Merchants who do nothing get a default: the widget calls Shopify's\n * Storefront Cart API (tokenless — no extra scopes required) and redirects\n * the browser to the returned checkoutUrl. One-click-to-checkout is the\n * right UX for most merchants pasting the widget into Webflow / Wix /\n * Squarespace / static HTML.\n *\n * Merchants with their own cart state (Hydrogen's useCart, a custom cart\n * drawer, etc.) opt out by attaching a listener that calls\n * `event.preventDefault()`:\n *\n * document.querySelector(\"lime-bundle\").addEventListener(\n * \"lime-bundle:add-to-cart\",\n * (ev) => {\n * ev.preventDefault(); // suppress the default redirect\n * myCart.linesAdd(ev.detail.lines);\n * },\n * );\n *\n * The event is always dispatched; only the default action is conditional.\n */\nimport {\n createStorefrontClient,\n BUNDLE_METAOBJECT_QUERY,\n BUNDLES_FOR_PRODUCT_QUERY,\n CART_CREATE_MUTATION,\n CART_LINES_ADD_MUTATION,\n SHOP_CUSTOM_CSS_QUERY,\n parseMetaobjectBundle,\n observeImpression,\n reportImpression,\n reportAddToCart,\n injectCustomCss,\n sanitizeCustomCss,\n getABTestAssignment,\n applyABVariantB,\n type ParsedBundle,\n type BundleMetaobjectResponse,\n type BundlesForProductResponse,\n type CartCreateResponse,\n type CartLinesAddResponse,\n type ShopCustomCssResponse,\n type CartLineInput,\n type StorefrontClient,\n} from \"@lime-bundles/core\";\nimport { renderFixedBundle } from \"./renderers/fixed\";\nimport { renderMixMatchBundle } from \"./renderers/mix-match\";\nimport { renderVolumeBundle } from \"./renderers/volume\";\nimport { applyWidgetConfigVars } from \"@lime-bundles/core\";\nimport { trackInputMode } from \"./utils/input-mode\";\nimport {\n BUNDLE_BASE_CSS,\n BUNDLE_FIXED_CSS,\n BUNDLE_MIX_MATCH_CSS,\n BUNDLE_SKELETON_CSS,\n BUNDLE_VOLUME_CSS,\n} from \"./styles/bundle-css\";\n\n/**\n * Per-shop localStorage key for the active cart id. Scoping by shop domain\n * keeps the cart isolated when a single browser visits multiple Lime-\n * Bundles-powered storefronts (rare, but correct).\n */\nconst cartStorageKey = (shopDomain: string) => `lb_cart_id:${shopDomain}`;\n\n/**\n * Resolve the current product handle from the page. Runs the cascade\n * documented on LimeBundleElement and returns null if no source matches.\n */\nfunction resolveProductHandle(explicit: string | null): string | null {\n if (explicit) return explicit.trim() || null;\n\n if (typeof document !== \"undefined\") {\n const meta = document.querySelector<HTMLMetaElement>(\n 'meta[name=\"shopify:product-handle\"]',\n );\n if (meta?.content) return meta.content.trim() || null;\n }\n\n if (typeof window !== \"undefined\") {\n const match = window.location.pathname.match(/\\/products\\/([^/?#]+)/);\n if (match?.[1]) return decodeURIComponent(match[1]);\n }\n\n return null;\n}\n\nexport class LimeBundleElement extends HTMLElement {\n static observedAttributes = [\n \"shop-domain\",\n \"storefront-token\",\n \"bundle-gid\",\n \"product-handle\",\n \"app-url\",\n \"analytics\",\n \"locale\",\n ];\n\n private shadow: ShadowRoot;\n private bundles: ParsedBundle[] = [];\n private abortController: AbortController | null = null;\n private impressionCleanups: Array<() => void> = [];\n /**\n * Tick / observer / listener cleanups registered by individual renderers\n * (e.g. countdown setInterval handles). Flushed in disconnectedCallback\n * so we never leak timers or event listeners when the widget is removed.\n */\n private renderCleanups: Array<() => void> = [];\n /**\n * Merchant custom CSS fetched from the shop metafield. Injected inside\n * the shadow root alongside the bundle stylesheets so selectors like\n * `.lb-bundle-widget { ... }` reach the widget's DOM.\n */\n private shopCustomCss: string | null = null;\n\n constructor() {\n super();\n this.shadow = this.attachShadow({ mode: \"open\" });\n }\n\n connectedCallback() {\n // `fetchBundle()` calls `renderLoading()` synchronously before any\n // await — the skeleton paints on the first frame, reserving\n // layout space before the Storefront API call resolves.\n this.fetchBundle();\n }\n\n disconnectedCallback() {\n this.abortController?.abort();\n this.teardownImpressions();\n this.teardownRenderers();\n }\n\n private teardownImpressions() {\n for (const cleanup of this.impressionCleanups) cleanup();\n this.impressionCleanups = [];\n }\n\n private teardownRenderers() {\n for (const cleanup of this.renderCleanups) cleanup();\n this.renderCleanups = [];\n }\n\n attributeChangedCallback(\n name: string,\n oldValue: string | null,\n newValue: string | null,\n ) {\n if (oldValue === newValue || !this.isConnected) return;\n if (\n name === \"bundle-gid\" ||\n name === \"product-handle\" ||\n name === \"shop-domain\" ||\n name === \"storefront-token\"\n ) {\n if (this.shopDomain && this.storefrontToken) {\n this.fetchBundle();\n }\n }\n }\n\n private get shopDomain(): string {\n return this.getAttribute(\"shop-domain\") ?? \"\";\n }\n\n private get storefrontToken(): string {\n return this.getAttribute(\"storefront-token\") ?? \"\";\n }\n\n private get bundleGid(): string {\n return this.getAttribute(\"bundle-gid\") ?? \"\";\n }\n\n private get productHandleAttr(): string {\n return this.getAttribute(\"product-handle\") ?? \"\";\n }\n\n private get appUrl(): string {\n return this.getAttribute(\"app-url\") ?? \"\";\n }\n\n private get analyticsEnabled(): boolean {\n return this.getAttribute(\"analytics\") !== \"false\";\n }\n\n private async fetchBundle() {\n if (!this.shopDomain || !this.storefrontToken) {\n this.renderError(\n \"Missing required attributes: shop-domain, storefront-token\",\n );\n return;\n }\n\n this.abortController?.abort();\n const controller = new AbortController();\n this.abortController = controller;\n\n this.renderLoading();\n\n const client = createStorefrontClient({\n shopDomain: this.shopDomain,\n accessToken: this.storefrontToken,\n });\n\n try {\n // Fire bundle query BEFORE CSS so order-sensitive call logs (and any\n // test harness that mocks fetch with sequential mockResolvedValueOnce)\n // see the bundle as call #1, CSS as call #2. Parallelism preserved\n // via Promise.all at the await site below.\n let bundlePromise: Promise<void>;\n let singleBundleMode = false;\n if (this.bundleGid) {\n singleBundleMode = true;\n bundlePromise = this.fetchSingleBundle(client, controller.signal);\n } else {\n const handle = resolveProductHandle(this.productHandleAttr);\n if (!handle) {\n this.teardownImpressions();\n this.renderError(\n \"No bundle-gid or product-handle provided, and the current URL doesn't match /products/<handle>.\",\n );\n return;\n }\n bundlePromise = this.fetchProductBundles(\n client,\n controller.signal,\n handle,\n );\n }\n\n // CSS fetch kicks off AFTER bundle fetch for deterministic call order.\n // Best-effort — widget renders without merchant styling if it fails.\n const cssPromise = client\n .query<ShopCustomCssResponse>(SHOP_CUSTOM_CSS_QUERY, undefined, {\n signal: controller.signal,\n })\n .catch(() => null);\n\n await bundlePromise;\n if (controller.signal.aborted) return;\n\n // Single-bundle mode with an explicit GID that didn't resolve is a\n // genuine error — the merchant pinned a specific bundle and it's\n // missing. Product-handle mode with zero bundles is NOT an error:\n // the product legitimately has no bundles configured; widget stays\n // invisible (mirrors classic-theme Liquid block behaviour).\n if (singleBundleMode && this.bundles.length === 0) {\n this.teardownImpressions();\n this.renderError(\"Bundle not found\");\n return;\n }\n\n const css = await cssPromise;\n if (css?.shop?.metafield?.value) {\n // Classic storefronts get the CSS in document.head; the web\n // component also stores the sanitized CSS for its own shadow\n // root injection below, since shadow DOM blocks inherited styles.\n injectCustomCss(this.shopDomain, css.shop.metafield.value);\n const sanitized = sanitizeCustomCss(css.shop.metafield.value);\n if (sanitized.ok) this.shopCustomCss = sanitized.css;\n }\n\n // Resolve A/B variants for any bundle that's in an active test.\n // Awaited so bundles render with the assigned variant on first\n // paint (no flash of Variant A). Individual failures inside\n // applyABVariants fall back to Variant A silently.\n await this.applyABVariants();\n\n this.renderBundles();\n } catch (err) {\n if (controller.signal.aborted) return;\n this.bundles = [];\n this.teardownImpressions();\n this.renderError(\n err instanceof Error ? err.message : \"Failed to load bundle\",\n );\n }\n }\n\n /**\n * Resolve the visitor's A/B bucket for every bundle with an active test\n * and merge Variant B overrides where applicable. Runs in parallel; any\n * assignment failure logs internally but still renders Variant A (safe\n * default). The `getABTestAssignment` helper also persists the bucket\n * via a first-party cookie + POSTs to /api/ab-assign for server-side\n * analytics.\n */\n private async applyABVariants(): Promise<void> {\n if (!this.appUrl || this.bundles.length === 0) return;\n const results = await Promise.all(\n this.bundles.map(async (bundle) => {\n if (!bundle.abTestId || !bundle.abVariantB) return bundle;\n try {\n const assignment = await getABTestAssignment(\n this.appUrl,\n this.shopDomain,\n bundle.abTestId,\n bundle.id,\n );\n if (assignment?.variant === \"B\") {\n return applyABVariantB(bundle);\n }\n } catch (err) {\n // Surface A/B failures so misconfigured tests aren't invisible.\n // Variant A still renders — the customer is never blocked.\n // eslint-disable-next-line no-console\n console.warn(\n `[lime-bundle] A/B assignment failed for bundle ${bundle.id}; falling back to Variant A.`,\n err,\n );\n }\n return bundle;\n }),\n );\n this.bundles = results;\n }\n\n private async fetchSingleBundle(\n client: StorefrontClient,\n signal: AbortSignal,\n ): Promise<void> {\n const data = await client.query<BundleMetaobjectResponse>(\n BUNDLE_METAOBJECT_QUERY,\n { id: this.bundleGid },\n { signal },\n );\n if (!data.metaobject) {\n this.bundles = [];\n return;\n }\n const parsed = parseMetaobjectBundle(\n data.metaobject.id,\n data.metaobject.fields,\n );\n this.bundles = parsed ? [parsed] : [];\n }\n\n private async fetchProductBundles(\n client: StorefrontClient,\n signal: AbortSignal,\n productHandle: string,\n ): Promise<void> {\n // fetchBundlesForProduct re-creates its own client; skip that indirection\n // and reuse the one we already built so the request shares the same\n // AbortSignal and header config.\n const data = await client.query<BundlesForProductResponse>(\n BUNDLES_FOR_PRODUCT_QUERY,\n { handle: productHandle },\n { signal },\n );\n if (!data.product) {\n this.bundles = [];\n return;\n }\n const refs = data.product.metafield?.references?.nodes ?? [];\n const bundles: ParsedBundle[] = [];\n for (const ref of refs) {\n const parsed = parseMetaobjectBundle(ref.id, ref.fields);\n if (parsed) bundles.push(parsed);\n }\n this.bundles = bundles;\n }\n\n /**\n * Dispatch add-to-cart with a cancelable event, then — unless a listener\n * called preventDefault — execute the default cart-and-checkout flow.\n *\n * `fire-and-forget` against `reportAddToCart` runs regardless so merchants\n * with BYO cart still get analytics.\n */\n private handleAddToCart = async (\n bundle: ParsedBundle,\n lines: CartLineInput[],\n ): Promise<void> => {\n const ev = new CustomEvent(\"lime-bundle:add-to-cart\", {\n detail: { lines },\n bubbles: true,\n composed: true,\n cancelable: true,\n });\n // dispatchEvent returns false if preventDefault() was called on a\n // cancelable event. That's how merchants opt out of the default flow.\n const allowDefault = this.dispatchEvent(ev);\n\n // Analytics fire regardless of which cart path runs.\n this.reportAddToCartEvent(bundle, lines);\n\n if (allowDefault) {\n await this.defaultAddToCart(lines);\n }\n };\n\n /**\n * Default cart flow: Shopify's Storefront Cart API is tokenless, so we\n * don't need any additional scopes. Persist the cart ID in localStorage\n * so subsequent adds on the same browser session join the existing cart\n * instead of creating a new one every click.\n */\n private async defaultAddToCart(lines: CartLineInput[]): Promise<void> {\n if (typeof window === \"undefined\") return;\n\n const client = createStorefrontClient({\n shopDomain: this.shopDomain,\n accessToken: this.storefrontToken,\n });\n const storage = window.localStorage;\n const key = cartStorageKey(this.shopDomain);\n const existingCartId = storage?.getItem(key) ?? null;\n\n try {\n let checkoutUrl: string | null = null;\n\n if (existingCartId) {\n const res = await client.query<CartLinesAddResponse>(\n CART_LINES_ADD_MUTATION,\n { cartId: existingCartId, lines },\n );\n const payload = res.cartLinesAdd;\n if (payload?.userErrors?.length) {\n // Cart GID expired or was merged on Shopify's side — fall back to\n // cartCreate below. This happens after ~10 days of inactivity.\n storage?.removeItem(key);\n } else if (payload?.cart) {\n checkoutUrl = payload.cart.checkoutUrl;\n }\n }\n\n if (!checkoutUrl) {\n const res = await client.query<CartCreateResponse>(\n CART_CREATE_MUTATION,\n { input: { lines } },\n );\n const payload = res.cartCreate;\n if (payload?.cart) {\n storage?.setItem(key, payload.cart.id);\n checkoutUrl = payload.cart.checkoutUrl;\n }\n }\n\n if (checkoutUrl) {\n window.location.assign(checkoutUrl);\n } else {\n this.dispatchEvent(\n new CustomEvent(\"lime-bundle:error\", {\n detail: { message: \"Cart creation failed\", code: \"CART_ERROR\" },\n bubbles: true,\n composed: true,\n }),\n );\n }\n } catch (err) {\n this.dispatchEvent(\n new CustomEvent(\"lime-bundle:error\", {\n detail: {\n message:\n err instanceof Error ? err.message : \"Cart mutation failed\",\n code: \"CART_ERROR\",\n },\n bubbles: true,\n composed: true,\n }),\n );\n }\n }\n\n private reportAddToCartEvent(\n bundle: ParsedBundle,\n lines: CartLineInput[],\n ): void {\n if (!this.analyticsEnabled || !this.appUrl) return;\n\n const quantity = lines.reduce((sum, l) => sum + l.quantity, 0);\n const totalPrice = lines.reduce((sum, line) => {\n const product = bundle.products.find((p) =>\n p.variants.nodes.some((v) => v.id === line.merchandiseId),\n );\n const variant = product?.variants.nodes.find(\n (v) => v.id === line.merchandiseId,\n );\n const price = variant ? parseFloat(variant.price.amount) : 0;\n return sum + price * line.quantity;\n }, 0);\n\n reportAddToCart(\n { shopDomain: this.shopDomain, appUrl: this.appUrl },\n {\n bundleGid: bundle.id,\n bundleType: bundle.bundleType,\n productId: bundle.products[0]?.id ?? \"\",\n quantity,\n totalPrice: Math.round(totalPrice * 100) / 100,\n },\n );\n }\n\n private renderBundles() {\n this.teardownImpressions();\n this.teardownRenderers();\n this.shadow.innerHTML = \"\";\n\n // Bundle the base stylesheet and every type-specific sheet in one <style>.\n // This matches the Liquid theme app block's output (base.css +\n // bundle-{fixed,mix-match,volume}.css) so the shipped widget honours the\n // same selectors a merchant already styled against. Shadow DOM scope\n // keeps these rules from leaking out; the ported styles all target\n // `.lb-bundle-widget` descendants so there's no global bleed.\n const style = document.createElement(\"style\");\n style.textContent = [\n BUNDLE_BASE_CSS,\n BUNDLE_FIXED_CSS,\n BUNDLE_MIX_MATCH_CSS,\n BUNDLE_VOLUME_CSS,\n ].join(\"\\n\");\n this.shadow.appendChild(style);\n\n // Merchant custom CSS — injected AFTER the built-in stylesheets so\n // the merchant's rules override defaults. Sanitized upstream via\n // sanitizeCustomCss (strips < > and script-safety patterns).\n if (this.shopCustomCss) {\n const customStyle = document.createElement(\"style\");\n customStyle.setAttribute(\"data-lime-bundles\", \"shop-custom-css\");\n customStyle.textContent = this.shopCustomCss;\n this.shadow.appendChild(customStyle);\n }\n\n // Empty state: the shadow root holds only the <style> tag — nothing\n // visible. Matches Liquid theme UX where a product with no bundles\n // simply shows no block. We still fire `lime-bundle:loaded` below so\n // consumers know the async work completed (important for test\n // synchronisation and for merchants who want to hide a parent\n // placeholder once the widget has decided whether to render).\n\n for (const bundle of this.bundles) {\n const container = document.createElement(\"div\");\n container.className = \"lb-bundle-widget\";\n container.setAttribute(\"role\", \"region\");\n container.setAttribute(\"aria-label\", bundle.title);\n container.setAttribute(\"data-bundle-type\", bundle.bundleType);\n container.setAttribute(\"data-bundle-gid\", bundle.id);\n\n // Emit every merchant-configurable --lb-* property so the ported CSS\n // renders the widget with the look the merchant set in the admin\n // editor.\n applyWidgetConfigVars(container, bundle.widgetConfig);\n\n // Toggle using-mouse / using-keyboard on this container so the CSS\n // focus-ring rules match the customer's current input device.\n this.renderCleanups.push(trackInputMode(container));\n\n const dispatch = (lines: CartLineInput[]) =>\n this.handleAddToCart(bundle, lines);\n const registerCleanup = (fn: () => void) =>\n this.renderCleanups.push(fn);\n\n switch (bundle.bundleType) {\n case \"fixed\":\n renderFixedBundle(container, bundle, dispatch, registerCleanup);\n break;\n case \"mix_match\":\n renderMixMatchBundle(container, bundle, dispatch, registerCleanup);\n break;\n case \"volume\":\n renderVolumeBundle(container, bundle, dispatch, registerCleanup);\n break;\n }\n\n this.shadow.appendChild(container);\n this.setupImpressionFor(bundle, container);\n }\n\n const first = this.bundles[0];\n this.dispatchEvent(\n new CustomEvent(\"lime-bundle:loaded\", {\n detail: {\n bundleCount: this.bundles.length,\n bundleTypes: this.bundles.map((b) => b.bundleType),\n // Legacy fields — meaningful only in single-bundle mode. Preserved\n // for merchants who attached listeners against the pre-1.0 shape.\n bundleType: first?.bundleType,\n title: first?.title,\n },\n bubbles: true,\n composed: true,\n }),\n );\n }\n\n private setupImpressionFor(bundle: ParsedBundle, element: Element) {\n if (!this.analyticsEnabled || !this.appUrl) return;\n const cleanup = observeImpression(element, () => {\n reportImpression(\n { shopDomain: this.shopDomain, appUrl: this.appUrl },\n {\n bundleGid: bundle.id,\n bundleType: bundle.bundleType,\n },\n );\n });\n this.impressionCleanups.push(cleanup);\n }\n\n private renderLoading() {\n // Synchronous paint on every mount (before the Storefront fetch\n // resolves) so the host element has intrinsic size from render-0\n // and doesn't shift layout when the real bundle swaps in.\n this.shadow.innerHTML = `\n <style>${BUNDLE_BASE_CSS}</style>\n <style>${BUNDLE_SKELETON_CSS}</style>\n <div class=\"lb-bundle-widget lb-bundle-widget--loading\" aria-busy=\"true\" aria-label=\"Loading bundle\">\n <div class=\"lb-skeleton lb-skeleton--title\"></div>\n <div class=\"lb-skeleton--products\">\n <div class=\"lb-skeleton--row\">\n <div class=\"lb-skeleton lb-skeleton--thumb\"></div>\n <div>\n <div class=\"lb-skeleton lb-skeleton--line\"></div>\n <div class=\"lb-skeleton lb-skeleton--line\"></div>\n </div>\n <div class=\"lb-skeleton lb-skeleton--price\"></div>\n </div>\n <div class=\"lb-skeleton--row\">\n <div class=\"lb-skeleton lb-skeleton--thumb\"></div>\n <div>\n <div class=\"lb-skeleton lb-skeleton--line\"></div>\n <div class=\"lb-skeleton lb-skeleton--line\"></div>\n </div>\n <div class=\"lb-skeleton lb-skeleton--price\"></div>\n </div>\n <div class=\"lb-skeleton--row\">\n <div class=\"lb-skeleton lb-skeleton--thumb\"></div>\n <div>\n <div class=\"lb-skeleton lb-skeleton--line\"></div>\n <div class=\"lb-skeleton lb-skeleton--line\"></div>\n </div>\n <div class=\"lb-skeleton lb-skeleton--price\"></div>\n </div>\n </div>\n <div class=\"lb-skeleton--footer\">\n <div class=\"lb-skeleton lb-skeleton--total\"></div>\n <div class=\"lb-skeleton lb-skeleton--cta\"></div>\n </div>\n </div>\n `;\n }\n\n private renderError(message: string) {\n this.shadow.innerHTML = \"\";\n this.dispatchEvent(\n new CustomEvent(\"lime-bundle:error\", {\n detail: { message, code: \"LOAD_ERROR\" },\n bubbles: true,\n composed: true,\n }),\n );\n }\n\n}\n\n// Re-export the helper so consumers of the widget package can import it\n// when they want to share the URL-detection logic with their own code.\n","/**\n * DOM renderer for fixed bundles.\n *\n * Emits the class names and structure of `lb-fixed.liquid` so the ported\n * `bundle-fixed.css` themes it. Features:\n * - Variant dropdown per product (when the product has >1 available\n * variants, filtered by merchant's `selected_variant_ids`). Changing\n * a variant live-updates its row price, the bundle total, the\n * header save badge, and the savings bar.\n * - Merchant `productQuantities` + `variantQuantities` honoured.\n * - Out-of-stock behaviour (`hide` / `show_greyed_out`) applied per\n * product.\n * - Bundle-level guard: fixed bundles are all-or-nothing, so the\n * entire widget hides if any product has no available variants.\n *\n * Pricing matches Shopify's per-unit floor rounding via the `pricing`\n * helper, so what the widget shows equals what the customer pays at\n * checkout.\n */\nimport {\n resolveBundleQty,\n type CartLineInput,\n type FixedBundleData,\n type Product,\n type ProductVariant,\n} from \"@lime-bundles/core\";\nimport {\n computeFixedPricing,\n formatCents,\n formatUnitPrice,\n parseCents,\n} from \"./pricing\";\nimport { renderCountdown } from \"./countdown\";\nimport { buildCtaButton } from \"./cta-button\";\nimport { el } from \"./dom\";\nimport { THUMB_PX, transformImageUrl } from \"./image\";\n\nconst PLACEHOLDER_THUMB_SVG = `\n<svg class=\"lb-bundle-placeholder-icon\" viewBox=\"0 0 28 28\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\">\n <rect x=\"4\" y=\"4\" width=\"20\" height=\"20\" rx=\"3\"></rect>\n <line x1=\"4\" y1=\"20\" x2=\"24\" y2=\"20\"></line>\n <circle cx=\"10\" cy=\"12\" r=\"2\"></circle>\n</svg>`;\n\ninterface ProductRowState {\n product: Product;\n /** Variants eligible for this row (intersection of available + merchant selection). */\n eligibleVariants: ProductVariant[];\n /** Currently selected variant; null when none are available (OOS). */\n selected: ProductVariant | null;\n /** Quantity applied to this row (merchant product/variant qty or 1). */\n qty: number;\n /** Whether the product has zero available variants. */\n isOos: boolean;\n}\n\nexport function renderFixedBundle(\n container: HTMLElement,\n bundle: FixedBundleData,\n onAddToCart: (lines: CartLineInput[]) => void,\n onCleanup?: (fn: () => void) => void,\n) {\n const wc = bundle.widgetConfig;\n\n // Per-variant quantity lookup — delegated to the canonical resolver in\n // `@lime-bundles/core` so the fallback chain stays in lockstep with the\n // React SDK and the server-side discount metafield producer.\n const qtyFor = (productId: string, variantId: string): number =>\n resolveBundleQty(bundle, productId, variantId);\n\n // Build row state honouring merchant selections + OOS behaviour.\n const rows: ProductRowState[] = [];\n let oosCount = 0;\n bundle.products.forEach((product, idx) => {\n const row = buildRowState(bundle, product, idx);\n // Merchant explicitly set productQuantities/variantQuantities to 0 —\n // skip the row entirely. Treated as opt-out, not as a zero-quantity\n // line in pricing.\n if (row.qty === 0) return;\n if (row.isOos) {\n oosCount++;\n if (wc.outOfStockBehavior === \"hide\") return; // skip the row entirely\n }\n rows.push(row);\n });\n\n // Bundle-level guard: fixed bundles are all-or-nothing. Even in\n // \"show_greyed_out\" mode, if any product has no stock, disable the CTA\n // and render a warning. In \"hide\" mode, if we lost any rows, bail out\n // entirely — matches Liquid behaviour.\n if (rows.length === 0) return;\n\n const currency =\n bundle.products[0]?.priceRange.minVariantPrice.currencyCode ?? \"USD\";\n\n const root = el(\"div\", \"lb-fixed\", {\n \"data-discount-type\": bundle.discountConfig.discountType,\n \"data-discount-value\": String(bundle.discountConfig.discountValue),\n });\n\n // --- Header (title + subtitle + save badge) ---\n const headerHandle = renderHeader(bundle, currency);\n root.appendChild(headerHandle.el);\n\n // --- Countdown ---\n if (wc.countdown.showCountdown && bundle.endsAt) {\n const countdown = renderCountdown(bundle.endsAt);\n if (countdown) {\n root.appendChild(countdown.el);\n onCleanup?.(countdown.stop);\n }\n }\n\n // --- Product list ---\n const list = el(\"div\", \"lb-fixed__products\");\n const rowHandles: Array<ReturnType<typeof renderProductRow>> = [];\n rows.forEach((rowState) => {\n const handle = renderProductRow(rowState, currency, qtyFor, () => {\n // Variant change → recompute pricing.\n updatePricing();\n });\n rowHandles.push(handle);\n list.appendChild(handle.el);\n });\n root.appendChild(list);\n\n root.appendChild(el(\"div\", \"lb-bundle-divider\"));\n\n // --- Pricing row + savings bar ---\n const pricingHandle = renderPricingRow(bundle);\n root.appendChild(pricingHandle.el);\n const savingsBarHandle = wc.savingsBar.visible ? renderSavingsBar() : null;\n if (savingsBarHandle) root.appendChild(savingsBarHandle.el);\n\n // --- CTA ---\n const cta = renderCta(bundle, oosCount, () => {\n const lines: CartLineInput[] = rows\n .filter((r) => r.selected)\n .map((r) => ({\n merchandiseId: r.selected!.id,\n quantity: r.qty,\n attributes: [\n { key: \"_lime_bundle_gid\", value: bundle.id },\n { key: \"_lime_bundle_type\", value: bundle.bundleType },\n ],\n }));\n if (lines.length === 0) return;\n onAddToCart(lines);\n });\n root.appendChild(cta);\n\n root.appendChild(\n el(\"p\", \"lb-bundle-error\", { \"data-error\": \"\", \"aria-live\": \"polite\" }),\n );\n root.appendChild(\n el(\"span\", \"lb-visually-hidden\", {\n \"data-status\": \"\",\n \"aria-live\": \"polite\",\n }),\n );\n\n container.appendChild(root);\n\n updatePricing();\n\n function updatePricing() {\n const totalCents = rows.reduce((sum, r) => {\n if (!r.selected) return sum;\n const unit = parseCents(r.selected.price.amount);\n return sum + unit * r.qty;\n }, 0);\n const saleCents = computeSale(totalCents, bundle.discountConfig, rows);\n const savingsCents = Math.max(0, totalCents - saleCents);\n\n pricingHandle.update({ totalCents, saleCents, savingsCents, currency });\n if (savingsBarHandle) {\n savingsBarHandle.update({ savingsCents, currency });\n }\n headerHandle.refresh(\n deriveHeaderBadge(bundle, totalCents, saleCents, currency),\n );\n }\n}\n\n// --- State ---\n\nfunction buildRowState(\n bundle: FixedBundleData,\n product: Product,\n productIndex: number,\n): ProductRowState {\n const selectedVariantIds =\n bundle.selectedVariantIds?.[productIndex] ?? null;\n\n // All variants the merchant scoped into the bundle. Sold-out variants are\n // INCLUDED here so the per-option dropdowns render them as disabled\n // options (same UX as unavailable combinations) rather than hiding them\n // from the picker. The initial `selected` still prefers an in-stock\n // variant so the default shown is purchasable.\n const merchantScoped =\n selectedVariantIds && selectedVariantIds.length > 0\n ? product.variants.nodes.filter((v) => selectedVariantIds.includes(v.id))\n : product.variants.nodes;\n\n const firstInStock = merchantScoped.find((v) => v.availableForSale) ?? null;\n const eligibleVariants = merchantScoped;\n const isOos = !firstInStock;\n const selected = firstInStock ?? merchantScoped[0] ?? null;\n const qty = selected ? resolveBundleQty(bundle, product.id, selected.id) : 1;\n\n return { product, eligibleVariants, selected, qty, isOos };\n}\n\n// --- Section renderers ---\n\ninterface HeaderHandle {\n el: HTMLElement;\n /** Update the save-badge text when pricing changes. */\n refresh: (badgeText: string) => void;\n}\n\nfunction renderHeader(\n bundle: FixedBundleData,\n currency: string,\n): HeaderHandle {\n const wc = bundle.widgetConfig;\n const header = el(\"div\", \"lb-bundle-header\");\n const content = el(\"div\", \"lb-bundle-header__content\");\n\n const title = el(\"h3\", \"lb-bundle-title\");\n title.textContent = bundle.title;\n content.appendChild(title);\n\n if (bundle.description) {\n const subtitle = el(\"p\", \"lb-bundle-subtitle\");\n subtitle.textContent = bundle.description;\n content.appendChild(subtitle);\n }\n header.appendChild(content);\n\n const badgeEl = el(\"span\", \"lb-bundle-header__badge\", {\n \"data-header-badge\": \"\",\n });\n header.appendChild(badgeEl);\n\n // Placeholder initial badge — updated by refresh() from updatePricing().\n const initialPricing = computeFixedPricing(\n bundle,\n bundle.productQuantities,\n wc.pricing.showSaveBadge,\n );\n if (initialPricing.headerBadge) {\n badgeEl.textContent = initialPricing.headerBadge;\n } else {\n badgeEl.style.display = \"none\";\n }\n void currency;\n\n return {\n el: header,\n refresh(badgeText) {\n if (!wc.pricing.showSaveBadge) {\n badgeEl.style.display = \"none\";\n return;\n }\n if (badgeText) {\n badgeEl.textContent = badgeText;\n badgeEl.style.display = \"\";\n } else {\n badgeEl.style.display = \"none\";\n }\n },\n };\n}\n\nfunction deriveHeaderBadge(\n bundle: FixedBundleData,\n totalCents: number,\n saleCents: number,\n currency: string,\n): string {\n if (!bundle.widgetConfig.pricing.showSaveBadge) return \"\";\n const savings = totalCents - saleCents;\n if (savings <= 0) return \"\";\n const dc = bundle.discountConfig;\n if (dc.discountType === \"percentage\" && dc.discountValue > 0) {\n return `-${Math.round(dc.discountValue)}%`;\n }\n if (dc.discountType === \"fixed_amount\" && dc.discountValue > 0) {\n return `-${formatCents(Math.round(dc.discountValue * 100), currency)}`;\n }\n return `-${formatCents(savings, currency)}`;\n}\n\ninterface ProductRowHandle {\n el: HTMLElement;\n state: ProductRowState;\n}\n\nfunction renderProductRow(\n state: ProductRowState,\n currency: string,\n qtyFor: (productId: string, variantId: string) => number,\n onVariantChange: () => void,\n): ProductRowHandle {\n const rowEl = el(\n \"div\",\n state.isOos\n ? \"lb-bundle-product-row lb-bundle-product-row--oos\"\n : \"lb-bundle-product-row\",\n {\n \"data-product-id\": state.product.id.replace(/^.*\\//, \"\"),\n ...(state.isOos ? { \"aria-disabled\": \"true\" } : {}),\n },\n );\n\n // Thumbnail + qty badge\n const thumb = el(\"div\", \"lb-bundle-thumbnail\", { \"data-thumbnail\": \"\" });\n if (state.product.featuredImage) {\n const img = document.createElement(\"img\");\n img.src = transformImageUrl(state.product.featuredImage.url, {\n width: THUMB_PX,\n height: THUMB_PX,\n crop: \"center\",\n });\n img.alt = state.product.featuredImage.altText ?? state.product.title;\n img.width = THUMB_PX;\n img.height = THUMB_PX;\n img.loading = \"lazy\";\n thumb.appendChild(img);\n } else {\n thumb.insertAdjacentHTML(\"beforeend\", PLACEHOLDER_THUMB_SVG);\n }\n // Qty badge — reference is captured so on-variant-change can live-update\n // the count when a merchant set per-variant quantity overrides.\n let qtyBadgeRef: HTMLElement | null = null;\n if (!state.isOos) {\n qtyBadgeRef = el(\"span\", \"lb-bundle-qty-badge\", {\n \"data-qty-badge\": \"\",\n });\n qtyBadgeRef.textContent = String(state.qty);\n thumb.appendChild(qtyBadgeRef);\n }\n rowEl.appendChild(thumb);\n\n // Info column\n const info = el(\"div\", \"lb-bundle-product-info\");\n const name = document.createElement(\"a\");\n name.className = \"lb-bundle-product-name\";\n name.href = `/products/${state.product.handle}`;\n name.textContent = state.product.title;\n info.appendChild(name);\n\n if (state.isOos) {\n const oosLabel = el(\"span\", \"lb-bundle-oos-label\");\n oosLabel.textContent = \"Out of stock\";\n info.appendChild(oosLabel);\n } else if (state.selected) {\n // Price row — updated on variant change.\n const prices = el(\"span\", \"lb-bundle-product-prices\");\n const compare = el(\"span\", \"lb-bundle-product-compare-price\", {\n \"data-product-compare-price\": \"\",\n });\n const priceEl = el(\"span\", \"lb-bundle-product-price\", {\n \"data-product-price\": \"\",\n });\n prices.appendChild(compare);\n prices.appendChild(priceEl);\n info.appendChild(prices);\n\n // Unit price (e.g. \"$0.50/100ml\") — sibling of the price row so it sits\n // on its own line beneath the price. Hidden when the merchant hasn't\n // set unit pricing in the Shopify admin.\n const unitPriceEl = el(\"span\", \"lb-bundle-product-unit-price\", {\n \"data-product-unit-price\": \"\",\n });\n unitPriceEl.setAttribute(\"hidden\", \"\");\n info.appendChild(unitPriceEl);\n\n const applyVariantToRow = (variant: ProductVariant) => {\n const unit = parseCents(variant.price.amount);\n priceEl.textContent = formatCents(unit, currency);\n if (variant.compareAtPrice) {\n const cmp = parseCents(variant.compareAtPrice.amount);\n if (cmp > unit) {\n compare.textContent = formatCents(cmp, currency);\n compare.removeAttribute(\"hidden\");\n } else {\n compare.setAttribute(\"hidden\", \"\");\n }\n } else {\n compare.setAttribute(\"hidden\", \"\");\n }\n const unitText = formatUnitPrice(\n variant.unitPrice,\n variant.unitPriceMeasurement,\n currency,\n );\n if (unitText) {\n unitPriceEl.textContent = unitText;\n unitPriceEl.removeAttribute(\"hidden\");\n } else {\n unitPriceEl.setAttribute(\"hidden\", \"\");\n }\n };\n\n applyVariantToRow(state.selected);\n\n // Per-option dropdowns when more than one eligible variant exists —\n // Shopify's recommended approach via product.options_with_values (here\n // derived from variants[].selectedOptions since the Storefront API gives\n // us that). Values that don't combine with the current selection of other\n // options are disabled (Dawn-style availability) so the customer sees\n // what's possible instead of the variant silently jumping combos.\n if (state.eligibleVariants.length > 1) {\n const optionNames: string[] = state.eligibleVariants[0].selectedOptions.map(\n (o) => o.name,\n );\n const productIdTail = state.product.id.replace(/^.*\\//, \"\");\n const optionSelects: HTMLSelectElement[] = [];\n\n const resolveVariant = (values: string[]) =>\n state.eligibleVariants.find(\n (v) =>\n v.selectedOptions.every((o, i) => o.value === values[i]) &&\n v.selectedOptions.length === values.length,\n ) ?? null;\n\n const syncSelectsToVariant = (variant: ProductVariant) => {\n variant.selectedOptions.forEach((o, i) => {\n const sel = optionSelects[i];\n if (sel && sel.value !== o.value) sel.value = o.value;\n });\n };\n\n const isValueAvailable = (\n optionIndex: number,\n value: string,\n selected: string[],\n ): boolean =>\n state.eligibleVariants.some((v) => {\n if (!v.availableForSale) return false;\n if (v.selectedOptions[optionIndex]?.value !== value) return false;\n return v.selectedOptions.every(\n (o, i) => i === optionIndex || o.value === selected[i],\n );\n });\n\n const recomputeDisabled = (selected: string[]) => {\n optionSelects.forEach((sel, i) => {\n Array.from(sel.options).forEach((opt) => {\n opt.disabled = !isValueAvailable(i, opt.value, selected);\n });\n });\n };\n\n const handleChange = () => {\n const values = optionSelects.map((s) => s.value);\n const variant = resolveVariant(values);\n if (!variant) {\n // Disabled combo reached (keyboard nav edge case) — revert selects\n // to the previously selected variant rather than jumping.\n if (state.selected) {\n syncSelectsToVariant(state.selected);\n recomputeDisabled(state.selected.selectedOptions.map((o) => o.value));\n }\n return;\n }\n state.selected = variant;\n state.qty = qtyFor(state.product.id, variant.id);\n if (qtyBadgeRef) qtyBadgeRef.textContent = String(state.qty);\n applyVariantToRow(variant);\n recomputeDisabled(variant.selectedOptions.map((o) => o.value));\n onVariantChange();\n };\n\n const groupsContainer = el(\"div\", \"lb-bundle-variant-option-groups\");\n optionNames.forEach((name, position) => {\n const group = el(\"div\", \"lb-bundle-variant-option-group\");\n\n const label = el(\"span\", \"lb-bundle-variant-option-label\");\n label.textContent = name;\n group.appendChild(label);\n\n const select = document.createElement(\"select\");\n select.className = \"lb-bundle-variant-select\";\n select.setAttribute(\"data-variant-option\", \"\");\n select.setAttribute(\"data-option-position\", String(position + 1));\n select.name = `lb-variant-${productIdTail}-${position + 1}`;\n select.setAttribute(\"aria-label\", name);\n\n const seen = new Set<string>();\n state.eligibleVariants.forEach((v) => {\n const value = v.selectedOptions[position]?.value;\n if (!value || seen.has(value)) return;\n seen.add(value);\n const opt = document.createElement(\"option\");\n opt.value = value;\n opt.textContent = value;\n if (state.selected?.selectedOptions[position]?.value === value) {\n opt.selected = true;\n }\n select.appendChild(opt);\n });\n\n select.addEventListener(\"change\", handleChange);\n optionSelects.push(select);\n group.appendChild(select);\n groupsContainer.appendChild(group);\n });\n info.appendChild(groupsContainer);\n\n if (state.selected) {\n recomputeDisabled(state.selected.selectedOptions.map((o) => o.value));\n }\n } else if (\n state.eligibleVariants.length === 1 &&\n state.product.variants.nodes.length > 1\n ) {\n // Single allowed variant but the product has multiple — render a\n // read-only badge so the customer sees which one's going in the bundle.\n const badge = el(\"span\", \"lb-bundle-variant-badge\");\n badge.textContent = state.eligibleVariants[0].title;\n info.appendChild(badge);\n }\n }\n\n rowEl.appendChild(info);\n return { el: rowEl, state };\n}\n\ninterface PricingHandle {\n el: HTMLElement;\n update: (p: {\n totalCents: number;\n saleCents: number;\n savingsCents: number;\n currency: string;\n }) => void;\n}\n\nfunction renderPricingRow(bundle: FixedBundleData): PricingHandle {\n const row = el(\"div\", \"lb-bundle-pricing\");\n const label = el(\"span\", \"lb-bundle-pricing__label\");\n label.textContent = \"Bundle price\";\n row.appendChild(label);\n\n const prices = el(\"span\", \"lb-bundle-pricing__prices\");\n const compare = el(\"span\", \"lb-bundle-compare-price\", {\n \"data-compare-price\": \"\",\n });\n compare.style.display = \"none\";\n prices.appendChild(compare);\n const sale = el(\"span\", \"lb-bundle-sale-price\", {\n \"data-sale-price\": \"\",\n });\n prices.appendChild(sale);\n row.appendChild(prices);\n\n return {\n el: row,\n update({ totalCents, saleCents, savingsCents, currency }) {\n sale.textContent = formatCents(saleCents, currency);\n if (bundle.widgetConfig.pricing.showCompareAtPrice && savingsCents > 0) {\n compare.textContent = formatCents(totalCents, currency);\n compare.style.display = \"\";\n } else {\n compare.style.display = \"none\";\n }\n },\n };\n}\n\ninterface SavingsBarHandle {\n el: HTMLElement;\n update: (p: { savingsCents: number; currency: string }) => void;\n}\n\nfunction renderSavingsBar(): SavingsBarHandle {\n const bar = el(\"div\", \"lb-bundle-savings-bar\", {\n \"data-savings-bar\": \"\",\n });\n const label = document.createElement(\"span\");\n label.textContent = \"You save\";\n bar.appendChild(label);\n const amount = el(\"span\", \"\", { \"data-savings-amount\": \"\" });\n bar.appendChild(amount);\n return {\n el: bar,\n update({ savingsCents, currency }) {\n if (savingsCents <= 0) {\n bar.style.display = \"none\";\n return;\n }\n bar.style.display = \"\";\n amount.textContent = formatCents(savingsCents, currency);\n },\n };\n}\n\nfunction renderCta(\n bundle: FixedBundleData,\n oosCount: number,\n onClick: () => void,\n): HTMLElement {\n const label =\n oosCount > 0\n ? `${oosCount} item${oosCount === 1 ? \"\" : \"s\"} out of stock`\n : bundle.widgetConfig.cta.ctaText || \"Add to cart\";\n const button = buildCtaButton(label);\n if (oosCount > 0) {\n button.disabled = true;\n } else {\n button.addEventListener(\"click\", () => {\n if (button.disabled) return;\n onClick();\n });\n }\n return button;\n}\n\n// --- Pricing helpers ---\n\nfunction computeSale(\n totalCents: number,\n discount: FixedBundleData[\"discountConfig\"],\n rows: ProductRowState[],\n): number {\n if (discount.discountType === \"percentage\") {\n // Per-unit floor rounding — matches Shopify Discount Function.\n let saleCents = 0;\n for (const r of rows) {\n if (!r.selected) continue;\n const unit = parseCents(r.selected.price.amount);\n const off = Math.floor((unit * discount.discountValue) / 100);\n const perUnit = Math.max(0, unit - off);\n saleCents += perUnit * r.qty;\n }\n return saleCents;\n }\n // fixed_amount: total minus absolute discount (clamped >= 0).\n return Math.max(0, totalCents - Math.round(discount.discountValue * 100));\n}\n\n","/**\n * Widget-package re-exports of the pricing helpers. The canonical\n * implementation lives in `@lime-bundles/core`; keeping the widget\n * package importing from core avoids duplicating the integer-cent\n * arithmetic across packages and lets DIY React devs reach the same\n * primitives the web component uses.\n */\nexport {\n parseCents,\n formatCents,\n percentageDiscountUnit,\n computeFixedPricing,\n computeBundleSaleCents,\n formatUnitPrice,\n type PricingRow,\n type FixedBundlePricing,\n} from \"@lime-bundles/core\";\n","// Matches the DOM/class structure of the Liquid `bundle-widget.liquid`'s\n// `.lb-bundle-countdown` bar so the shared CSS themes both renderers.\nimport { formatCountdown } from \"@lime-bundles/core\";\n\nexport interface CountdownHandle {\n /** DOM element to append to the widget. */\n el: HTMLElement;\n /** Call on widget disconnect to clear the tick interval. */\n stop: () => void;\n}\n\nexport function renderCountdown(endsAtIso: string): CountdownHandle | null {\n const parsed = parseIso(endsAtIso);\n if (parsed === null) return null;\n // Already expired — caller doesn't append anything.\n if (parsed <= Date.now()) return null;\n const target: number = parsed;\n\n const wrap = document.createElement(\"div\");\n wrap.className = \"lb-bundle-countdown\";\n wrap.setAttribute(\"data-countdown\", \"\");\n\n const labelWrap = document.createElement(\"div\");\n labelWrap.className = \"lb-bundle-countdown__label\";\n const labelText = document.createElement(\"span\");\n labelText.textContent = \"Ends in\";\n labelWrap.appendChild(labelText);\n wrap.appendChild(labelWrap);\n\n const timer = document.createElement(\"span\");\n timer.className = \"lb-bundle-countdown__timer\";\n timer.setAttribute(\"data-countdown-timer\", \"\");\n wrap.appendChild(timer);\n\n let intervalId: ReturnType<typeof setInterval> | null = null;\n\n function tick() {\n const msLeft = target - Date.now();\n if (msLeft <= 0) {\n wrap.style.display = \"none\";\n stop();\n return;\n }\n timer.textContent = formatCountdown(msLeft);\n }\n\n function stop() {\n if (intervalId !== null) {\n clearInterval(intervalId);\n intervalId = null;\n }\n }\n\n tick();\n intervalId = setInterval(tick, 1000);\n\n return { el: wrap, stop };\n}\n\nfunction parseIso(iso: string): number | null {\n const t = Date.parse(iso);\n return Number.isFinite(t) ? t : null;\n}\n","/**\n * Builds the shared `.lb-bundle-cta` button structure used by all three\n * widget renderers (fixed, volume, mix_match).\n *\n * The button has two children in a 1×1 CSS grid (see\n * packages/widget/src/styles/bundle-css.ts):\n *\n * <button class=\"lb-bundle-cta\" data-add-bundle>\n * <span class=\"lb-cta-label\" data-cta-label>{label}</span>\n * <span class=\"lb-cta-spinner\" data-cta-spinner aria-hidden=\"true\">…</span>\n * </button>\n *\n * The spinner is visible only when the button has `data-loading=\"true\"`.\n * The SDK itself doesn't toggle that attribute — merchants opt into the\n * loading-state affordance by setting it during their async cart mutation:\n *\n * el.querySelector('[data-add-bundle]').setAttribute('data-loading', 'true');\n * try { await cart.linesAdd(…); }\n * finally { el.querySelector('[data-add-bundle]').removeAttribute('data-loading'); }\n *\n * Keeping the SDK neutral on timing (BYO-cart contract) means no additive\n * Promise API surface needs to change.\n */\nexport function buildCtaButton(label: string): HTMLButtonElement {\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.className = \"lb-bundle-cta\";\n button.setAttribute(\"data-add-bundle\", \"\");\n\n const labelSpan = document.createElement(\"span\");\n labelSpan.className = \"lb-cta-label\";\n labelSpan.setAttribute(\"data-cta-label\", \"\");\n labelSpan.textContent = label;\n button.appendChild(labelSpan);\n\n const spinnerSpan = document.createElement(\"span\");\n spinnerSpan.className = \"lb-cta-spinner\";\n spinnerSpan.setAttribute(\"data-cta-spinner\", \"\");\n spinnerSpan.setAttribute(\"aria-hidden\", \"true\");\n // Same markup as snippets/lb-cta-spinner.liquid — keep in sync.\n spinnerSpan.innerHTML =\n '<svg viewBox=\"0 0 24 24\" width=\"20\" height=\"20\" fill=\"none\" ' +\n 'stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\">' +\n '<path d=\"M12 2a10 10 0 0 1 10 10\" /></svg>';\n button.appendChild(spinnerSpan);\n\n return button;\n}\n\n/**\n * Updates the visible label text on a button built by `buildCtaButton`.\n * Targets the `[data-cta-label]` span so the sibling spinner isn't\n * clobbered — a plain `button.textContent = \"...\"` would destroy every\n * child, including the spinner span.\n *\n * If `[data-cta-label]` isn't found, this function is a no-op rather than\n * falling back to `button.textContent`. Buttons that omit the label span\n * either (a) also omit the spinner (nothing to destroy — but also nothing\n * the caller needs to worry about; returning early is fine) or (b) were\n * mutated in-flight by an adapter that should have kept the label. Either\n * way, overwriting `button.textContent` is strictly harmful: it either\n * silently nukes a spinner we're trying to preserve, or replaces whatever\n * structure the adapter built. Callers needing a plain-button text update\n * should write `button.textContent = ...` themselves.\n */\nexport function setCtaLabel(button: HTMLButtonElement, text: string): void {\n const label = button.querySelector<HTMLElement>(\"[data-cta-label]\");\n if (label) {\n label.textContent = text;\n }\n}\n","/**\n * Shared DOM helper for the widget renderers. Keep this internal to\n * `packages/widget/src/renderers/`; it intentionally isn't re-exported\n * from the package's public entrypoint.\n */\nexport function el(\n tag: string,\n className: string,\n attrs: Record<string, string> = {},\n): HTMLElement {\n const node = document.createElement(tag);\n if (className) node.className = className;\n for (const [k, v] of Object.entries(attrs)) {\n node.setAttribute(k, v);\n }\n return node;\n}\n","/**\n * Re-export of `transformImageUrl` from core. See\n * `packages/core/src/utils/image.ts` for the implementation.\n */\nexport {\n transformImageUrl,\n THUMB_PX,\n type ImageTransform,\n} from \"@lime-bundles/core\";\n","/**\n * DOM renderer for mix-and-match bundles.\n *\n * Mirrors `extensions/bundle-theme/snippets/lb-mix-match.liquid` and its\n * associated picker-modal JS. Merchant flow:\n *\n * 1. Widget renders `minQuantity` empty slots with a live progress bar.\n * 2. Clicking a slot opens the picker modal with the eligible products.\n * 3. Inside the modal, each product has a quantity stepper and a count\n * bubble. Adjusting stepper values adds or removes slots.\n * 4. Progress bar, slot contents, pricing, and CTA update live.\n * 5. When `minQuantity` is reached, the CTA unlocks. Customer clicks,\n * cart lines dispatch.\n *\n * Class names match the Liquid template one-for-one so the ported\n * bundle-mix-match.css styles this DOM without changes.\n */\nimport type {\n CartLineInput,\n MixMatchBundleData,\n Product,\n ProductVariant,\n} from \"@lime-bundles/core\";\nimport { resolveBundleQty } from \"@lime-bundles/core\";\nimport {\n computeBundleSaleCents,\n formatCents,\n formatUnitPrice,\n parseCents,\n} from \"./pricing\";\nimport { renderCountdown } from \"./countdown\";\nimport { buildCtaButton, setCtaLabel } from \"./cta-button\";\nimport { el } from \"./dom\";\nimport { THUMB_PX, transformImageUrl } from \"./image\";\n\ninterface EligibleProduct {\n product: Product;\n variants: ProductVariant[];\n firstAvailableVariant: ProductVariant | null;\n isOos: boolean;\n}\n\ninterface Selection {\n productId: string;\n productTitle: string;\n variantId: string;\n variantTitle: string;\n imageUrl: string | null;\n priceCents: number;\n compareCents: number | null;\n /** Pre-formatted unit price (\"$0.50/100ml\") for the filled-slot view. */\n unitPriceLabel: string | null;\n /** Merchant-configured per-slot quantity (variantQuantities ?? productQuantities ?? 1). */\n quantity: number;\n}\n\nconst PLACEHOLDER_THUMB_SVG = `\n<svg class=\"lb-bundle-placeholder-icon\" viewBox=\"0 0 28 28\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\">\n <rect x=\"4\" y=\"4\" width=\"20\" height=\"20\" rx=\"3\"></rect>\n <line x1=\"4\" y1=\"20\" x2=\"24\" y2=\"20\"></line>\n <circle cx=\"10\" cy=\"12\" r=\"2\"></circle>\n</svg>`;\n\nconst PLUS_ICON_SVG = `\n<svg width=\"18\" height=\"18\" viewBox=\"0 0 18 18\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <line x1=\"9\" y1=\"3\" x2=\"9\" y2=\"15\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n <line x1=\"3\" y1=\"9\" x2=\"15\" y2=\"9\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n</svg>`;\n\nconst CLOSE_ICON_SVG = `\n<svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <line x1=\"5\" y1=\"5\" x2=\"15\" y2=\"15\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n <line x1=\"15\" y1=\"5\" x2=\"5\" y2=\"15\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n</svg>`;\n\nconst SEARCH_CLEAR_ICON_SVG = `\n<svg width=\"16\" height=\"16\" viewBox=\"0 0 20 20\" fill=\"currentColor\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path d=\"M14.348 5.652a.5.5 0 0 0-.707 0L10 9.293 6.36 5.652a.5.5 0 1 0-.708.707L9.293 10l-3.641 3.641a.5.5 0 0 0 .708.707L10 10.707l3.641 3.641a.5.5 0 0 0 .707-.707L10.707 10l3.641-3.641a.5.5 0 0 0 0-.707z\"/>\n</svg>`;\n\nexport function renderMixMatchBundle(\n container: HTMLElement,\n bundle: MixMatchBundleData,\n onAddToCart: (lines: CartLineInput[]) => void,\n onCleanup?: (fn: () => void) => void,\n) {\n const wc = bundle.widgetConfig;\n const currency =\n bundle.products[0]?.priceRange.minVariantPrice.currencyCode ?? \"USD\";\n const requiredQty = bundle.minQuantity ?? 1;\n const maxQty = bundle.maxQuantity ?? requiredQty;\n\n // Build eligible-products list, honoring outOfStockBehavior.\n const eligible = buildEligibleProducts(bundle, wc.outOfStockBehavior);\n const inStockCount = eligible.filter((e) => !e.isOos).length;\n\n // Bundle visibility guard: if we can't possibly satisfy minQuantity from\n // in-stock products, don't render the widget at all. Matches Liquid.\n if (inStockCount < requiredQty) return;\n\n const selections: Selection[] = [];\n const root = el(\"div\", \"lb-mix-match\", {\n \"data-required-quantity\": String(requiredQty),\n \"data-max-quantity\": String(maxQty),\n });\n\n // --- Header ---\n const header = renderHeader(bundle);\n root.appendChild(header);\n\n // --- Countdown ---\n if (wc.countdown.showCountdown && bundle.endsAt) {\n const countdown = renderCountdown(bundle.endsAt);\n if (countdown) {\n root.appendChild(countdown.el);\n onCleanup?.(countdown.stop);\n }\n }\n\n // --- Progress bar ---\n const progress = renderProgress(requiredQty);\n root.appendChild(progress.el);\n\n // --- Slots ---\n const slotsContainer = el(\"div\", \"lb-mix-match__slots\", {\n \"data-selection-slots\": \"\",\n });\n root.appendChild(slotsContainer);\n\n root.appendChild(el(\"div\", \"lb-bundle-divider\"));\n\n // --- Pricing (hidden until first selection) ---\n const pricingSection = renderPricingSection(wc.pricing.showCompareAtPrice);\n root.appendChild(pricingSection.el);\n\n const savingsBar = wc.savingsBar.visible\n ? renderSavingsBar()\n : null;\n if (savingsBar) root.appendChild(savingsBar.el);\n\n // --- Price placeholder (shown until first selection) ---\n const placeholder = el(\"div\", \"lb-mix-match__price-placeholder\", {\n \"data-price-placeholder\": \"\",\n });\n const placeholderText = el(\n \"span\",\n \"lb-mix-match__price-placeholder-text\",\n );\n placeholderText.textContent = `Select ${requiredQty} items to see price`;\n placeholder.appendChild(placeholderText);\n root.appendChild(placeholder);\n\n // --- Modal overlay ---\n const modal = renderModal(bundle, eligible, currency, {\n showSearch: wc.showSearch,\n onAdd: (product, variant) => addSelection(product, variant),\n onRemove: (productId, variantId) => removeSelection(productId, variantId),\n isOverMax: () => selections.length >= maxQty,\n });\n root.appendChild(modal.el);\n\n // --- CTA ---\n const cta = buildCtaButton(`Select ${requiredQty} items to unlock`);\n cta.disabled = true;\n cta.addEventListener(\"click\", () => {\n if (cta.disabled) return;\n const lines: CartLineInput[] = selections.map((s) => ({\n merchandiseId: s.variantId,\n quantity: s.quantity,\n attributes: [\n { key: \"_lime_bundle_gid\", value: bundle.id },\n { key: \"_lime_bundle_type\", value: bundle.bundleType },\n ],\n }));\n onAddToCart(lines);\n });\n root.appendChild(cta);\n\n root.appendChild(\n el(\"p\", \"lb-bundle-error\", { \"data-error\": \"\", \"aria-live\": \"polite\" }),\n );\n root.appendChild(\n el(\"span\", \"lb-visually-hidden\", {\n \"data-status\": \"\",\n \"aria-live\": \"polite\",\n }),\n );\n\n container.appendChild(root);\n\n // Seed slot 0 with the first eligible in-stock product so the widget\n // opens \"live\" — matches the Liquid theme block's default behaviour.\n // Customers can swap or remove via the picker as usual.\n const firstEligible = eligible.find((ep) => !ep.isOos);\n const firstVariant =\n firstEligible?.firstAvailableVariant ?? firstEligible?.variants[0];\n if (firstEligible && firstVariant) {\n selections.push({\n productId: firstEligible.product.id,\n productTitle: firstEligible.product.title,\n variantId: firstVariant.id,\n variantTitle: firstVariant.title,\n imageUrl: firstEligible.product.featuredImage?.url ?? null,\n priceCents: parseCents(firstVariant.price.amount),\n compareCents: firstVariant.compareAtPrice\n ? parseCents(firstVariant.compareAtPrice.amount)\n : null,\n unitPriceLabel: formatUnitPrice(\n firstVariant.unitPrice,\n firstVariant.unitPriceMeasurement,\n currency,\n ),\n quantity: resolveBundleQty(bundle, firstEligible.product.id, firstVariant.id),\n });\n }\n\n // Initial render — afterMutation() handles slots, progress, pricing,\n // savings bar, placeholder, modal refreshCounts, and CTA in one place.\n // Safe to call here: modal.refreshCounts is a no-op while the picker\n // is closed (productRows is built lazily on first open).\n afterMutation();\n\n // --- Mutation helpers (closures over local state) ---\n\n function addSelection(product: Product, variant: ProductVariant) {\n if (selections.length >= maxQty) return;\n selections.push({\n productId: product.id,\n productTitle: product.title,\n variantId: variant.id,\n variantTitle: variant.title,\n imageUrl: product.featuredImage?.url ?? null,\n priceCents: parseCents(variant.price.amount),\n compareCents: variant.compareAtPrice\n ? parseCents(variant.compareAtPrice.amount)\n : null,\n unitPriceLabel: formatUnitPrice(\n variant.unitPrice,\n variant.unitPriceMeasurement,\n currency,\n ),\n quantity: resolveBundleQty(bundle, product.id, variant.id),\n });\n afterMutation();\n }\n\n function removeSelection(productId: string, variantId: string) {\n const idx = selections.findIndex(\n (s) => s.productId === productId && s.variantId === variantId,\n );\n if (idx === -1) return;\n selections.splice(idx, 1);\n afterMutation();\n }\n\n function removeSlotAt(index: number) {\n if (index < 0 || index >= selections.length) return;\n selections.splice(index, 1);\n afterMutation();\n }\n\n function afterMutation() {\n renderSlots();\n progress.update(selections.length);\n pricingSection.update(selections, bundle, currency);\n if (savingsBar) savingsBar.update(selections, bundle, currency);\n placeholder.style.display = selections.length === 0 ? \"\" : \"none\";\n modal.refreshCounts();\n updateCta();\n }\n\n function renderSlots() {\n slotsContainer.innerHTML = \"\";\n const totalSlots = Math.max(requiredQty, selections.length);\n for (let i = 0; i < totalSlots; i++) {\n const selection = selections[i];\n if (selection) {\n slotsContainer.appendChild(\n renderFilledSlot(selection, i, currency, () => removeSlotAt(i)),\n );\n } else {\n slotsContainer.appendChild(\n renderEmptySlot(i, () => modal.open()),\n );\n }\n }\n }\n\n function updateCta() {\n // Target the label span, not the button itself — replacing the button's\n // textContent would destroy the sibling spinner span built by\n // buildCtaButton. See packages/widget/src/renderers/cta-button.ts.\n const count = selections.length;\n if (count < requiredQty) {\n cta.disabled = true;\n setCtaLabel(cta, `Select ${requiredQty - count} more to unlock`);\n } else {\n cta.disabled = false;\n setCtaLabel(cta, wc.cta.ctaText || \"Add to cart\");\n }\n }\n}\n\n// --- Section renderers ---\n\nfunction renderHeader(bundle: MixMatchBundleData): HTMLElement {\n const wc = bundle.widgetConfig;\n const header = el(\"div\", \"lb-bundle-header\");\n const content = el(\"div\", \"lb-bundle-header__content\");\n const title = el(\"h3\", \"lb-bundle-title\");\n title.textContent = bundle.title;\n content.appendChild(title);\n header.appendChild(content);\n\n if (wc.pricing.showSaveBadge) {\n const { discountType, discountValue } = bundle.discountConfig;\n let label: string | null = null;\n if (discountType === \"percentage\" && discountValue > 0) {\n label = `-${Math.round(discountValue)}%`;\n } else if (discountType === \"fixed_amount\" && discountValue > 0) {\n label = `-${formatCents(\n Math.round(discountValue * 100),\n bundle.products[0]?.priceRange.minVariantPrice.currencyCode ?? \"USD\",\n )}`;\n }\n if (label) {\n const badge = el(\"span\", \"lb-bundle-header__badge\");\n badge.textContent = label;\n header.appendChild(badge);\n }\n }\n return header;\n}\n\nfunction renderProgress(requiredQty: number) {\n const wrap = el(\"div\", \"lb-mix-match__progress\");\n const labels = el(\"div\", \"lb-mix-match__progress-labels\");\n const count = el(\"span\", \"lb-mix-match__progress-count\", {\n \"data-progress-count\": \"\",\n });\n count.textContent = `0 of ${requiredQty} selected`;\n labels.appendChild(count);\n const remaining = el(\"span\", \"lb-mix-match__progress-remaining\", {\n \"data-progress-remaining\": \"\",\n });\n remaining.textContent = `${requiredQty} more to go`;\n labels.appendChild(remaining);\n wrap.appendChild(labels);\n\n const track = el(\"div\", \"lb-mix-match__progress-track\", {\n role: \"progressbar\",\n \"aria-valuenow\": \"0\",\n \"aria-valuemin\": \"0\",\n \"aria-valuemax\": String(requiredQty),\n });\n const fill = el(\"div\", \"lb-mix-match__progress-fill\", {\n \"data-progress-fill\": \"\",\n });\n fill.style.width = \"0%\";\n track.appendChild(fill);\n wrap.appendChild(track);\n\n function update(selected: number) {\n const pct = Math.min(100, (selected / requiredQty) * 100);\n count.textContent = `${selected} of ${requiredQty} selected`;\n if (selected >= requiredQty) {\n remaining.textContent = \"Complete\";\n } else {\n remaining.textContent = `${requiredQty - selected} more to go`;\n }\n fill.style.width = `${pct}%`;\n track.setAttribute(\"aria-valuenow\", String(Math.min(selected, requiredQty)));\n }\n\n return { el: wrap, update };\n}\n\nfunction renderEmptySlot(index: number, onClick: () => void): HTMLElement {\n const slot = el(\n \"div\",\n \"lb-bundle-product-row lb-mix-match__slot lb-mix-match__slot--empty\",\n {\n \"data-slot\": String(index + 1),\n tabindex: \"0\",\n role: \"button\",\n \"aria-label\": \"Add a product to the bundle\",\n },\n );\n const thumb = el(\"div\", \"lb-mix-match__empty-thumb\");\n thumb.innerHTML = PLUS_ICON_SVG;\n slot.appendChild(thumb);\n const text = el(\"span\", \"lb-mix-match__empty-text\");\n text.textContent = \"Choose an item\";\n slot.appendChild(text);\n slot.addEventListener(\"click\", onClick);\n slot.addEventListener(\"keydown\", (e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n onClick();\n }\n });\n return slot;\n}\n\nfunction renderFilledSlot(\n selection: Selection,\n index: number,\n currency: string,\n onRemove: () => void,\n): HTMLElement {\n const slot = el(\n \"div\",\n \"lb-bundle-product-row lb-mix-match__slot lb-mix-match__slot--filled\",\n { \"data-slot\": String(index + 1) },\n );\n const thumb = el(\"div\", \"lb-bundle-thumbnail\", { \"data-thumbnail\": \"\" });\n if (selection.imageUrl) {\n const img = document.createElement(\"img\");\n img.src = transformImageUrl(selection.imageUrl, {\n width: THUMB_PX,\n height: THUMB_PX,\n crop: \"center\",\n });\n img.alt = selection.productTitle;\n img.width = THUMB_PX;\n img.height = THUMB_PX;\n img.loading = \"lazy\";\n thumb.appendChild(img);\n } else {\n thumb.insertAdjacentHTML(\"beforeend\", PLACEHOLDER_THUMB_SVG);\n }\n const qtyBadge = el(\"span\", \"lb-bundle-qty-badge\");\n qtyBadge.textContent = String(selection.quantity);\n thumb.appendChild(qtyBadge);\n slot.appendChild(thumb);\n\n const info = el(\"div\", \"lb-mix-match__filled-info\");\n const title = el(\"span\", \"lb-mix-match__filled-title\");\n title.textContent = selection.productTitle;\n info.appendChild(title);\n if (selection.variantTitle && selection.variantTitle !== \"Default Title\") {\n const variant = el(\"span\", \"lb-mix-match__filled-variant\");\n variant.textContent = selection.variantTitle;\n info.appendChild(variant);\n }\n const linePrice = selection.priceCents * selection.quantity;\n const lineCompare =\n selection.compareCents !== null\n ? selection.compareCents * selection.quantity\n : null;\n const priceWrap = el(\"span\", \"lb-mix-match__filled-price\");\n if (lineCompare !== null && lineCompare > linePrice) {\n const compare = el(\"span\", \"lb-mix-match__filled-compare\");\n compare.textContent = formatCents(lineCompare, currency);\n priceWrap.appendChild(compare);\n }\n const priceEl = document.createElement(\"span\");\n priceEl.textContent = formatCents(linePrice, currency);\n priceWrap.appendChild(priceEl);\n info.appendChild(priceWrap);\n if (selection.unitPriceLabel) {\n const unitPrice = el(\"span\", \"lb-bundle-product-unit-price\");\n unitPrice.textContent = selection.unitPriceLabel;\n info.appendChild(unitPrice);\n }\n slot.appendChild(info);\n\n const remove = document.createElement(\"button\");\n remove.type = \"button\";\n remove.className = \"lb-mix-match__slot-remove\";\n remove.setAttribute(\"aria-label\", `Remove ${selection.productTitle}`);\n remove.innerHTML = CLOSE_ICON_SVG;\n remove.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n onRemove();\n });\n slot.appendChild(remove);\n return slot;\n}\n\nfunction renderPricingSection(showCompareAtPrice: boolean) {\n const wrap = el(\"div\", \"lb-bundle-pricing\", { \"data-pricing-section\": \"\" });\n wrap.style.display = \"none\";\n const label = el(\"span\", \"lb-bundle-pricing__label\");\n label.textContent = \"Bundle price\";\n wrap.appendChild(label);\n const prices = el(\"span\", \"lb-bundle-pricing__prices\");\n const compare = el(\"span\", \"lb-bundle-compare-price\", {\n \"data-compare-price\": \"\",\n });\n if (showCompareAtPrice) prices.appendChild(compare);\n const sale = el(\"span\", \"lb-bundle-sale-price\", { \"data-sale-price\": \"\" });\n prices.appendChild(sale);\n wrap.appendChild(prices);\n\n function update(\n selections: Selection[],\n bundle: MixMatchBundleData,\n currency: string,\n ) {\n if (selections.length === 0) {\n wrap.style.display = \"none\";\n return;\n }\n wrap.style.display = \"\";\n const totalCents = selections.reduce(\n (s, sel) => s + sel.priceCents * sel.quantity,\n 0,\n );\n const saleCents = computeBundleSaleCents(totalCents, bundle.discountConfig);\n if (showCompareAtPrice && totalCents > saleCents) {\n compare.textContent = formatCents(totalCents, currency);\n compare.style.display = \"\";\n } else {\n compare.style.display = \"none\";\n }\n sale.textContent = formatCents(saleCents, currency);\n }\n\n return { el: wrap, update };\n}\n\nfunction renderSavingsBar() {\n const wrap = el(\"div\", \"lb-bundle-savings-bar\", {\n \"data-savings-bar\": \"\",\n });\n wrap.style.display = \"none\";\n const labelEl = document.createElement(\"span\");\n labelEl.textContent = \"You save\";\n wrap.appendChild(labelEl);\n const amount = el(\"span\", \"\", { \"data-savings-amount\": \"\" });\n wrap.appendChild(amount);\n\n function update(\n selections: Selection[],\n bundle: MixMatchBundleData,\n currency: string,\n ) {\n if (selections.length === 0) {\n wrap.style.display = \"none\";\n return;\n }\n const totalCents = selections.reduce(\n (s, sel) => s + sel.priceCents * sel.quantity,\n 0,\n );\n const saleCents = computeBundleSaleCents(totalCents, bundle.discountConfig);\n const savings = Math.max(0, totalCents - saleCents);\n if (savings <= 0) {\n wrap.style.display = \"none\";\n return;\n }\n wrap.style.display = \"\";\n amount.textContent = formatCents(savings, currency);\n }\n\n return { el: wrap, update };\n}\n\n// --- Picker modal ---\n\ninterface ModalHandlers {\n showSearch: boolean;\n onAdd: (product: Product, variant: ProductVariant) => void;\n onRemove: (productId: string, variantId: string) => void;\n isOverMax: () => boolean;\n}\n\nfunction renderModal(\n bundle: MixMatchBundleData,\n eligible: EligibleProduct[],\n currency: string,\n handlers: ModalHandlers,\n) {\n const overlay = el(\"div\", \"lb-mix-match__modal-overlay\", {\n \"data-modal-overlay\": \"\",\n \"data-bundle-gid\": bundle.id,\n });\n overlay.style.display = \"none\";\n\n const modal = el(\"div\", \"lb-mix-match__modal\", {\n role: \"dialog\",\n \"aria-modal\": \"true\",\n \"aria-labelledby\": `lb-modal-title-${sanitizeId(bundle.id)}`,\n tabindex: \"-1\",\n });\n\n // Header\n const modalHeader = el(\"div\", \"lb-mix-match__modal-header\");\n const modalTitle = el(\"h4\", \"lb-mix-match__modal-title\", {\n id: `lb-modal-title-${sanitizeId(bundle.id)}`,\n });\n modalTitle.textContent = \"Pick an item\";\n modalHeader.appendChild(modalTitle);\n const closeBtn = document.createElement(\"button\");\n closeBtn.type = \"button\";\n closeBtn.className = \"lb-mix-match__modal-close\";\n closeBtn.setAttribute(\"data-modal-close\", \"\");\n closeBtn.setAttribute(\"aria-label\", \"Close\");\n closeBtn.innerHTML = CLOSE_ICON_SVG;\n closeBtn.addEventListener(\"click\", close);\n modalHeader.appendChild(closeBtn);\n modal.appendChild(modalHeader);\n\n // Search\n let searchInput: HTMLInputElement | null = null;\n let searchClearBtn: HTMLButtonElement | null = null;\n if (handlers.showSearch) {\n const searchWrap = el(\"div\", \"lb-mix-match__modal-search\");\n searchInput = document.createElement(\"input\");\n searchInput.type = \"text\";\n searchInput.className = \"lb-mix-match__modal-search-input\";\n searchInput.setAttribute(\"data-modal-search\", \"\");\n searchInput.setAttribute(\"role\", \"searchbox\");\n searchInput.setAttribute(\"aria-label\", \"Search products\");\n searchInput.setAttribute(\"placeholder\", \"Search products\");\n searchInput.autocomplete = \"off\";\n searchInput.addEventListener(\"input\", () => applySearch());\n searchWrap.appendChild(searchInput);\n\n searchClearBtn = document.createElement(\"button\");\n searchClearBtn.type = \"button\";\n searchClearBtn.className = \"lb-mix-match__modal-search-clear\";\n searchClearBtn.setAttribute(\"data-modal-search-clear\", \"\");\n searchClearBtn.setAttribute(\"aria-label\", \"Clear search\");\n searchClearBtn.style.display = \"none\";\n searchClearBtn.innerHTML = SEARCH_CLEAR_ICON_SVG;\n searchClearBtn.addEventListener(\"click\", () => {\n if (!searchInput) return;\n searchInput.value = \"\";\n applySearch();\n searchInput.focus();\n });\n searchWrap.appendChild(searchClearBtn);\n modal.appendChild(searchWrap);\n }\n\n // Product list\n const list = el(\"div\", \"lb-mix-match__modal-list\", {\n \"data-modal-list\": \"\",\n });\n modal.appendChild(list);\n\n const empty = el(\"div\", \"lb-mix-match__modal-empty\", {\n \"data-modal-empty\": \"\",\n });\n empty.style.display = \"none\";\n const emptyText = document.createElement(\"p\");\n emptyText.textContent = \"No products match your search.\";\n empty.appendChild(emptyText);\n modal.appendChild(empty);\n\n const live = el(\"span\", \"lb-visually-hidden\", {\n \"data-modal-live\": \"\",\n \"aria-live\": \"polite\",\n });\n modal.appendChild(live);\n\n overlay.appendChild(modal);\n\n // Build product rows lazily on first open.\n let rowsBuilt = false;\n const productRows: Array<{\n el: HTMLElement;\n product: Product;\n variant: ProductVariant;\n updateCount: () => void;\n }> = [];\n\n function buildRows() {\n if (rowsBuilt) return;\n rowsBuilt = true;\n list.innerHTML = \"\";\n\n eligible.forEach((ep) => {\n // Mirrors the Liquid picker-modal pattern in bundle-mix-match.js:\n // - qty badge lives INSIDE the thumb (positioned corner), not the\n // add button, so it overlays the product image.\n // - title/price/unit-price are <p> elements so they stack as blocks.\n // <span> would flow inline against .lb-mix-match__modal-product-info\n // which has no flex-direction set.\n // - <select> appears for products with >1 available variant; the add\n // button dispatches the currently-selected variant, not a frozen\n // initial one.\n // - Removal is driven by the filled-slot × in the main widget — the\n // modal row only has the Add button.\n // Include sold-out variants in the picker so they render as disabled\n // options (matches the unavailable-combo UX). The initial variant\n // still prefers an in-stock one so the default price + Add click\n // target a purchasable variant.\n const availableVariants = ep.variants;\n const firstAvailVariant =\n ep.variants.find((v) => v.availableForSale) ??\n ep.firstAvailableVariant ??\n ep.variants[0];\n if (!firstAvailVariant) return;\n\n let currentVariant = firstAvailVariant;\n\n const productEl = el(\n \"div\",\n ep.isOos\n ? \"lb-mix-match__modal-product lb-mix-match__modal-product--sold-out\"\n : \"lb-mix-match__modal-product\",\n { \"data-product-id\": ep.product.id.replace(/^.*\\//, \"\") },\n );\n\n const thumb = el(\"div\", \"lb-mix-match__modal-product-thumb\");\n if (ep.product.featuredImage) {\n const img = document.createElement(\"img\");\n img.src = transformImageUrl(ep.product.featuredImage.url, {\n width: THUMB_PX,\n height: THUMB_PX,\n crop: \"center\",\n });\n img.alt = ep.product.featuredImage.altText ?? ep.product.title;\n img.width = THUMB_PX;\n img.height = THUMB_PX;\n img.loading = \"lazy\";\n thumb.appendChild(img);\n } else {\n thumb.insertAdjacentHTML(\"beforeend\", PLACEHOLDER_THUMB_SVG);\n }\n const countBadge = el(\"span\", \"lb-bundle-qty-badge\");\n countBadge.textContent = String(\n resolveBundleQty(bundle, ep.product.id, currentVariant.id),\n );\n thumb.appendChild(countBadge);\n productEl.appendChild(thumb);\n\n const info = el(\"div\", \"lb-mix-match__modal-product-info\");\n const title = el(\"p\", \"lb-mix-match__modal-product-title\");\n title.textContent = ep.product.title;\n info.appendChild(title);\n\n const price = el(\"p\", \"lb-mix-match__modal-product-price\");\n price.textContent = formatCents(\n parseCents(currentVariant.price.amount) *\n resolveBundleQty(bundle, ep.product.id, currentVariant.id),\n currency,\n );\n info.appendChild(price);\n\n const unitPrice = el(\n \"p\",\n \"lb-mix-match__modal-product-unit-price lb-bundle-product-unit-price\",\n );\n const initialUnitText = formatUnitPrice(\n currentVariant.unitPrice,\n currentVariant.unitPriceMeasurement,\n currency,\n );\n if (initialUnitText) {\n unitPrice.textContent = initialUnitText;\n } else {\n unitPrice.hidden = true;\n }\n info.appendChild(unitPrice);\n\n // Declared up-front so the select-change handler can keep\n // `row.variant` in sync with the closure `currentVariant`. Anything\n // that reads productRows[i].variant (search filters, future analytics\n // hooks) sees the live selection rather than the initial variant.\n const row = {\n el: productEl,\n product: ep.product,\n variant: firstAvailVariant,\n updateCount: () => {},\n };\n\n // Per-option dropdowns (Shopify's recommended pattern — one <select>\n // per product option). Values that don't combine with the currently\n // selected values for other options are disabled, so the customer gets\n // clear feedback instead of the variant silently jumping combos.\n if (availableVariants.length > 1) {\n const optionNames: string[] = availableVariants[0].selectedOptions.map(\n (o) => o.name,\n );\n const productIdTail = ep.product.id.replace(/^.*\\//, \"\");\n const optionSelects: HTMLSelectElement[] = [];\n\n const resolveVariant = (values: string[]): ProductVariant | null =>\n availableVariants.find(\n (v) =>\n v.selectedOptions.length === values.length &&\n v.selectedOptions.every((o, i) => o.value === values[i]),\n ) ?? null;\n\n const syncSelectsToVariant = (v: ProductVariant) => {\n v.selectedOptions.forEach((o, i) => {\n const sel = optionSelects[i];\n if (sel && sel.value !== o.value) sel.value = o.value;\n });\n };\n\n const isValueAvailable = (\n optionIndex: number,\n value: string,\n selected: string[],\n ): boolean =>\n availableVariants.some((v) => {\n if (!v.availableForSale) return false;\n if (v.selectedOptions[optionIndex]?.value !== value) return false;\n return v.selectedOptions.every(\n (o, i) => i === optionIndex || o.value === selected[i],\n );\n });\n\n const recomputeDisabled = (selected: string[]) => {\n optionSelects.forEach((sel, i) => {\n Array.from(sel.options).forEach((opt) => {\n opt.disabled = !isValueAvailable(i, opt.value, selected);\n });\n });\n };\n\n const handleChange = () => {\n const values = optionSelects.map((s) => s.value);\n const next = resolveVariant(values);\n if (!next) {\n // Disabled combo reached via keyboard — revert selects to the\n // currently selected variant rather than jumping.\n syncSelectsToVariant(currentVariant);\n recomputeDisabled(\n currentVariant.selectedOptions.map((o) => o.value),\n );\n return;\n }\n currentVariant = next;\n row.variant = next;\n const nextQty = resolveBundleQty(\n bundle,\n ep.product.id,\n currentVariant.id,\n );\n price.textContent = formatCents(\n parseCents(currentVariant.price.amount) * nextQty,\n currency,\n );\n const nextUnitText = formatUnitPrice(\n currentVariant.unitPrice,\n currentVariant.unitPriceMeasurement,\n currency,\n );\n if (nextUnitText) {\n unitPrice.textContent = nextUnitText;\n unitPrice.hidden = false;\n } else {\n unitPrice.textContent = \"\";\n unitPrice.hidden = true;\n }\n recomputeDisabled(next.selectedOptions.map((o) => o.value));\n rowUpdateCount();\n };\n\n const groupsContainer = el(\"div\", \"lb-bundle-variant-option-groups\");\n optionNames.forEach((name, position) => {\n const group = el(\"div\", \"lb-bundle-variant-option-group\");\n\n const label = el(\"span\", \"lb-bundle-variant-option-label\");\n label.textContent = name;\n group.appendChild(label);\n\n const select = document.createElement(\"select\");\n select.className = \"lb-mix-match__variant-select\";\n select.setAttribute(\"data-variant-option\", \"\");\n select.setAttribute(\"data-option-position\", String(position + 1));\n select.name = `lb-variant-${productIdTail}-${position + 1}`;\n select.setAttribute(\"aria-label\", name);\n\n const seen = new Set<string>();\n availableVariants.forEach((v) => {\n const value = v.selectedOptions[position]?.value;\n if (!value || seen.has(value)) return;\n seen.add(value);\n const opt = document.createElement(\"option\");\n opt.value = value;\n opt.textContent = value;\n if (firstAvailVariant.selectedOptions[position]?.value === value) {\n opt.selected = true;\n }\n select.appendChild(opt);\n });\n\n select.addEventListener(\"change\", handleChange);\n optionSelects.push(select);\n group.appendChild(select);\n groupsContainer.appendChild(group);\n });\n info.appendChild(groupsContainer);\n\n recomputeDisabled(\n firstAvailVariant.selectedOptions.map((o) => o.value),\n );\n } else if (\n availableVariants.length === 1 &&\n firstAvailVariant.title !== \"Default Title\"\n ) {\n const variantLabel = el(\"span\", \"lb-mix-match__filled-variant\");\n variantLabel.textContent = firstAvailVariant.title;\n info.appendChild(variantLabel);\n }\n\n if (ep.isOos) {\n const soldOut = el(\"span\", \"lb-mix-match__modal-sold-out-label\");\n soldOut.textContent = \"Sold out\";\n info.appendChild(soldOut);\n }\n productEl.appendChild(info);\n\n // Closure captures currentVariant by reference — variant-change handler\n // mutates it, and both addBtn click + rowUpdateCount read the latest.\n // Badge shows the merchant-configured per-slot qty for the CURRENT\n // variant, matching Liquid picker-modal semantics (mirrors\n // `bundle-mix-match.js:359-364`).\n const rowUpdateCount = () => {\n countBadge.textContent = String(\n resolveBundleQty(bundle, ep.product.id, currentVariant.id),\n );\n };\n\n if (!ep.isOos) {\n const addBtn = document.createElement(\"button\");\n addBtn.type = \"button\";\n addBtn.className = \"lb-mix-match__modal-add\";\n addBtn.textContent = \"Add\";\n addBtn.addEventListener(\"click\", () => {\n if (handlers.isOverMax()) return;\n handlers.onAdd(ep.product, currentVariant);\n close();\n });\n productEl.appendChild(addBtn);\n }\n\n // One productRows entry per product — OOS rows are included so the\n // search filter iterates over both available and sold-out uniformly;\n // updateCount is a no-op for OOS (no badge to toggle).\n row.updateCount = ep.isOos ? () => {} : rowUpdateCount;\n productRows.push(row);\n\n list.appendChild(productEl);\n });\n\n refreshCounts();\n }\n\n function applySearch() {\n if (!searchInput) return;\n const query = searchInput.value.trim().toLowerCase();\n if (searchClearBtn) {\n searchClearBtn.style.display = query ? \"\" : \"none\";\n }\n let visibleCount = 0;\n productRows.forEach((row) => {\n const match = !query || row.product.title.toLowerCase().includes(query);\n row.el.style.display = match ? \"\" : \"none\";\n if (match) visibleCount++;\n });\n empty.style.display = visibleCount === 0 && query ? \"\" : \"none\";\n }\n\n // Focus trap + keyboard handling\n let lastFocused: Element | null = null;\n function onKeydown(e: KeyboardEvent) {\n if (e.key === \"Escape\") {\n e.preventDefault();\n close();\n return;\n }\n if (e.key === \"Tab\") {\n trapFocus(e, modal);\n }\n }\n\n let isOpen = false;\n\n function open() {\n if (isOpen) return;\n if (handlers.isOverMax()) return;\n isOpen = true;\n buildRows();\n lastFocused = (overlay.getRootNode() as Document | ShadowRoot)\n .activeElement;\n overlay.style.display = \"\";\n overlay.classList.add(\"lb-mix-match__modal-overlay--open\");\n modal.focus();\n document.addEventListener(\"keydown\", onKeydown);\n overlay.addEventListener(\"click\", onOverlayClick);\n }\n\n function close() {\n if (!isOpen) return;\n isOpen = false;\n overlay.classList.remove(\"lb-mix-match__modal-overlay--open\");\n overlay.style.display = \"none\";\n document.removeEventListener(\"keydown\", onKeydown);\n overlay.removeEventListener(\"click\", onOverlayClick);\n if (lastFocused instanceof HTMLElement) {\n lastFocused.focus();\n }\n }\n\n function onOverlayClick(e: MouseEvent) {\n if (e.target === overlay) close();\n }\n\n function refreshCounts() {\n productRows.forEach((r) => r.updateCount());\n }\n\n return { el: overlay, open, close, refreshCounts };\n}\n\n// --- Helpers ---\n\nfunction buildEligibleProducts(\n bundle: MixMatchBundleData,\n oosBehavior: \"show_greyed_out\" | \"hide\",\n): EligibleProduct[] {\n const result: EligibleProduct[] = [];\n const seen = new Set<string>();\n for (const product of bundle.products) {\n if (seen.has(product.id)) continue;\n seen.add(product.id);\n const available = product.variants.nodes.filter((v) => v.availableForSale);\n const isOos = available.length === 0;\n if (isOos && oosBehavior === \"hide\") continue;\n result.push({\n product,\n variants: product.variants.nodes,\n firstAvailableVariant: available[0] ?? null,\n isOos,\n });\n }\n return result;\n}\n\nfunction trapFocus(e: KeyboardEvent, container: HTMLElement) {\n const focusables = container.querySelectorAll<HTMLElement>(\n 'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])',\n );\n if (focusables.length === 0) return;\n const first = focusables[0];\n const last = focusables[focusables.length - 1];\n const active = (container.getRootNode() as Document | ShadowRoot)\n .activeElement;\n if (e.shiftKey && active === first) {\n e.preventDefault();\n last.focus();\n } else if (!e.shiftKey && active === last) {\n e.preventDefault();\n first.focus();\n }\n}\n\nfunction sanitizeId(gid: string): string {\n return gid.replace(/[^a-zA-Z0-9_-]/g, \"-\");\n}\n\n","/**\n * DOM renderer for volume bundles.\n *\n * Mirrors `extensions/bundle-theme/snippets/lb-volume.liquid`. Each tier is a\n * radio-styled card; clicking one updates the pricing row and recalculates\n * the total. Add bundle dispatches the active tier's quantity for the first\n * available variant.\n */\nimport type {\n CartLineInput,\n DiscountConfig,\n VolumeBundleData,\n VolumeTier,\n} from \"@lime-bundles/core\";\nimport { formatCents, parseCents } from \"./pricing\";\nimport { renderCountdown } from \"./countdown\";\nimport { buildCtaButton } from \"./cta-button\";\nimport { el } from \"./dom\";\n\ninterface ResolvedTier {\n tier: VolumeTier;\n index: number;\n qty: number;\n /** Per-unit price after applying this tier's discount, in cents. */\n pricePerUnitCents: number;\n /** Pre-discount per-unit baseline in cents. */\n basePricePerUnitCents: number;\n}\n\nexport function renderVolumeBundle(\n container: HTMLElement,\n bundle: VolumeBundleData,\n onAddToCart: (lines: CartLineInput[]) => void,\n onCleanup?: (fn: () => void) => void,\n) {\n const wc = bundle.widgetConfig;\n const product = bundle.products[0];\n const variant = product?.variants.nodes.find((v) => v.availableForSale);\n\n // Bundle visibility guard: in \"hide\" mode, don't render the widget at\n // all if the product has no available variants. Matches Liquid behaviour.\n if (!variant && wc.outOfStockBehavior === \"hide\") return;\n\n const basePriceCents = variant ? parseCents(variant.price.amount) : 0;\n const currency = variant?.price.currencyCode ?? \"USD\";\n\n // Discount shape: bundle.discountConfig.discountType selects which field\n // on each tier carries the magnitude. \"percentage\" → tier.percentage (a\n // whole-number e.g. 10); \"fixed_amount\" → tier.amount (currency units\n // e.g. 5.00). Missing fields fall through to zero (tier renders at base\n // price — merchant config error, not a crash path).\n const discountType = bundle.discountConfig.discountType;\n\n const resolved = bundle.volumeTiers.map<ResolvedTier>((tier, index) => {\n let perUnit: number;\n if (discountType === \"fixed_amount\") {\n const amt = Math.round((tier.amount ?? 0) * 100);\n perUnit = Math.max(0, basePriceCents - amt);\n } else {\n const pct = tier.percentage ?? 0;\n const discount = Math.floor((basePriceCents * pct) / 100);\n perUnit = Math.max(0, basePriceCents - discount);\n }\n return {\n tier,\n index,\n qty: tier.minQuantity,\n pricePerUnitCents: perUnit,\n basePricePerUnitCents: basePriceCents,\n };\n });\n\n const bestTierIndex = pickBestTierIndex(resolved);\n let selectedIndex = wc.defaultTier === \"best_value\" ? bestTierIndex : 0;\n if (typeof wc.defaultTier === \"number\") {\n selectedIndex = clamp(wc.defaultTier, 0, resolved.length - 1);\n }\n\n const popularIndex =\n wc.popularBadge.tierIndex !== undefined\n ? clamp(wc.popularBadge.tierIndex, 0, resolved.length - 1)\n : bestTierIndex;\n\n const root = el(\"div\", \"lb-volume\");\n root.appendChild(\n renderHeader(bundle, resolved, selectedIndex, currency, discountType),\n );\n\n if (wc.countdown.showCountdown && bundle.endsAt) {\n const countdown = renderCountdown(bundle.endsAt);\n if (countdown) {\n root.appendChild(countdown.el);\n onCleanup?.(countdown.stop);\n }\n }\n\n const tierGroup = el(\"div\", \"lb-volume__tiers\", {\n role: \"radiogroup\",\n \"aria-label\": \"Quantity tiers\",\n \"data-tier-group\": \"\",\n });\n\n resolved.forEach((r) => {\n const tierEl = renderTierCard(\n r,\n r.index === selectedIndex,\n currency,\n wc.popularBadge.visible && r.index === popularIndex\n ? wc.popularBadge.text\n : null,\n wc.pricing.showComparePrice,\n wc.pricing.showPerUnitPrice,\n );\n tierEl.addEventListener(\"click\", () => selectTier(r.index));\n // Radiogroup keyboard contract: arrow keys move focus + selection\n // between siblings; Space/Enter activates the focused tier.\n tierEl.addEventListener(\"keydown\", (e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n selectTier(r.index);\n return;\n }\n if (\n e.key === \"ArrowDown\" ||\n e.key === \"ArrowRight\" ||\n e.key === \"ArrowUp\" ||\n e.key === \"ArrowLeft\"\n ) {\n e.preventDefault();\n const delta =\n e.key === \"ArrowDown\" || e.key === \"ArrowRight\" ? 1 : -1;\n const next = (r.index + delta + resolved.length) % resolved.length;\n selectTier(next);\n const target = tierGroup.children[next] as HTMLElement | undefined;\n target?.focus();\n }\n });\n tierGroup.appendChild(tierEl);\n });\n\n root.appendChild(tierGroup);\n root.appendChild(el(\"div\", \"lb-bundle-divider\"));\n\n let pricingEl = renderPricingRow(\n resolved,\n selectedIndex,\n currency,\n wc.pricing.showItemCount,\n wc.pricing.showCompareAtPrice,\n );\n root.appendChild(pricingEl);\n\n let savingsBarEl: HTMLElement | null = wc.savingsBar.visible\n ? renderSavingsBar(resolved, selectedIndex, currency)\n : null;\n if (savingsBarEl) root.appendChild(savingsBarEl);\n\n const cta = renderCta(bundle, () => {\n if (!variant) return;\n const r = resolved[selectedIndex];\n if (!r) return;\n onAddToCart([\n {\n merchandiseId: variant.id,\n quantity: r.qty,\n attributes: [\n { key: \"_lime_bundle_gid\", value: bundle.id },\n { key: \"_lime_bundle_type\", value: bundle.bundleType },\n ],\n },\n ]);\n });\n root.appendChild(cta);\n\n root.appendChild(\n el(\"p\", \"lb-bundle-error\", { \"data-error\": \"\", \"aria-live\": \"polite\" }),\n );\n root.appendChild(\n el(\"span\", \"lb-visually-hidden\", {\n \"data-status\": \"\",\n \"aria-live\": \"polite\",\n }),\n );\n\n container.appendChild(root);\n\n function selectTier(idx: number) {\n if (idx === selectedIndex || idx < 0 || idx >= resolved.length) return;\n selectedIndex = idx;\n Array.from(tierGroup.children).forEach((card, i) => {\n card.setAttribute(\"aria-checked\", String(i === idx));\n (card as HTMLElement).tabIndex = i === idx ? 0 : -1;\n });\n const newPricing = renderPricingRow(\n resolved,\n idx,\n currency,\n wc.pricing.showItemCount,\n wc.pricing.showCompareAtPrice,\n );\n pricingEl.replaceWith(newPricing);\n pricingEl = newPricing;\n if (savingsBarEl) {\n const newBar = renderSavingsBar(resolved, idx, currency);\n savingsBarEl.replaceWith(newBar);\n savingsBarEl = newBar;\n }\n // Keep the header save-badge text in sync with the selected tier.\n // Hidden when showSaveBadge is off (the element doesn't exist), or\n // when the tier has no discount (badgeFor returns null).\n const badgeEl = root.querySelector<HTMLElement>(\"[data-header-badge]\");\n if (badgeEl) {\n const label = badgeFor(resolved[idx], currency, discountType);\n if (label) {\n badgeEl.textContent = label;\n badgeEl.style.display = \"\";\n } else {\n badgeEl.style.display = \"none\";\n }\n }\n }\n}\n\n// --- Section renderers ---\n\nfunction renderHeader(\n bundle: VolumeBundleData,\n resolved: ResolvedTier[],\n selectedIndex: number,\n currency: string,\n discountType: DiscountConfig[\"discountType\"],\n): HTMLElement {\n const wc = bundle.widgetConfig;\n const header = el(\"div\", \"lb-bundle-header\");\n const content = el(\"div\", \"lb-bundle-header__content\");\n const title = el(\"h3\", \"lb-bundle-title\");\n title.textContent = bundle.title;\n content.appendChild(title);\n header.appendChild(content);\n\n if (wc.pricing.showSaveBadge) {\n const badge = badgeFor(resolved[selectedIndex], currency, discountType);\n if (badge) {\n const badgeEl = el(\"span\", \"lb-bundle-header__badge\", {\n \"data-header-badge\": \"\",\n });\n badgeEl.textContent = badge;\n header.appendChild(badgeEl);\n }\n }\n return header;\n}\n\nfunction renderTierCard(\n r: ResolvedTier,\n isSelected: boolean,\n currency: string,\n popularLabel: string | null,\n showComparePrice: boolean,\n showPerUnitPrice: boolean,\n): HTMLElement {\n const tier = el(\"div\", \"lb-volume__tier\", {\n role: \"radio\",\n \"aria-checked\": String(isSelected),\n tabindex: isSelected ? \"0\" : \"-1\",\n \"data-tier-index\": String(r.index),\n \"data-tier-qty\": String(r.qty),\n });\n\n const radio = el(\"span\", \"lb-volume__radio\");\n radio.appendChild(el(\"span\", \"lb-volume__radio-dot\"));\n tier.appendChild(radio);\n\n const grid = el(\"span\", \"lb-volume__tier-grid\");\n const label = el(\"span\", \"lb-volume__tier-label\");\n label.textContent = `Buy ${r.qty}`;\n grid.appendChild(label);\n\n const price = el(\"span\", \"lb-volume__tier-price\");\n if (showComparePrice && r.pricePerUnitCents < r.basePricePerUnitCents) {\n const compare = el(\"span\", \"lb-volume__tier-compare\");\n compare.textContent = formatCents(r.basePricePerUnitCents, currency);\n price.appendChild(compare);\n }\n if (showPerUnitPrice) {\n const each = document.createElement(\"span\");\n each.setAttribute(\"data-tier-price-each\", \"\");\n each.textContent = formatCents(r.pricePerUnitCents, currency);\n price.appendChild(each);\n const unit = el(\"span\", \"lb-volume__tier-unit\");\n unit.textContent = \" each\";\n price.appendChild(unit);\n }\n grid.appendChild(price);\n tier.appendChild(grid);\n\n const badge = el(\"span\", \"lb-volume__tier-badge\");\n if (popularLabel) {\n badge.textContent = popularLabel;\n } else {\n badge.style.display = \"none\";\n }\n tier.appendChild(badge);\n\n return tier;\n}\n\nfunction renderPricingRow(\n resolved: ResolvedTier[],\n selectedIndex: number,\n currency: string,\n showItemCount: boolean,\n showCompareAtPrice: boolean,\n): HTMLElement {\n const r = resolved[selectedIndex];\n const totalCents = r ? r.pricePerUnitCents * r.qty : 0;\n const undiscountedCents = r ? r.basePricePerUnitCents * r.qty : 0;\n const savings = Math.max(0, undiscountedCents - totalCents);\n\n const row = el(\"div\", \"lb-bundle-pricing\");\n const label = el(\"span\", \"lb-bundle-pricing__label\", {\n \"data-total-label\": \"\",\n });\n label.textContent = \"Total\";\n if (showItemCount && r) {\n const count = document.createElement(\"span\");\n count.setAttribute(\"data-item-count\", \"\");\n count.textContent = ` (${r.qty} item${r.qty === 1 ? \"\" : \"s\"})`;\n label.appendChild(count);\n }\n row.appendChild(label);\n\n const prices = el(\"span\", \"lb-bundle-pricing__prices\");\n if (showCompareAtPrice && savings > 0) {\n const compare = el(\"span\", \"lb-bundle-compare-price\", {\n \"data-compare-price\": \"\",\n });\n compare.textContent = formatCents(undiscountedCents, currency);\n prices.appendChild(compare);\n }\n const sale = el(\"span\", \"lb-bundle-sale-price\", { \"data-total-price\": \"\" });\n sale.textContent = formatCents(totalCents, currency);\n prices.appendChild(sale);\n row.appendChild(prices);\n return row;\n}\n\nfunction renderSavingsBar(\n resolved: ResolvedTier[],\n selectedIndex: number,\n currency: string,\n): HTMLElement {\n const r = resolved[selectedIndex];\n const totalCents = r ? r.pricePerUnitCents * r.qty : 0;\n const undiscountedCents = r ? r.basePricePerUnitCents * r.qty : 0;\n const savings = Math.max(0, undiscountedCents - totalCents);\n\n const bar = el(\"div\", \"lb-bundle-savings-bar\", { \"data-savings-bar\": \"\" });\n if (savings <= 0) bar.style.display = \"none\";\n const labelEl = document.createElement(\"span\");\n labelEl.textContent = \"You save\";\n bar.appendChild(labelEl);\n const amount = el(\"span\", \"\", { \"data-savings-amount\": \"\" });\n amount.textContent = formatCents(savings, currency);\n bar.appendChild(amount);\n return bar;\n}\n\nfunction renderCta(\n bundle: VolumeBundleData,\n onClick: () => void,\n): HTMLElement {\n const product = bundle.products[0];\n const isAvailable = product?.variants.nodes.some((v) => v.availableForSale);\n const label = isAvailable\n ? bundle.widgetConfig.cta.ctaText || \"Add to cart\"\n : \"Sold out\";\n const button = buildCtaButton(label);\n if (!isAvailable) button.disabled = true;\n button.addEventListener(\"click\", () => {\n if (button.disabled) return;\n onClick();\n });\n return button;\n}\n\n// --- Helpers ---\n\nfunction badgeFor(\n resolved: ResolvedTier | undefined,\n currency: string,\n discountType: DiscountConfig[\"discountType\"],\n): string | null {\n if (!resolved) return null;\n const { tier } = resolved;\n if (discountType === \"fixed_amount\") {\n const amount = tier.amount ?? 0;\n if (amount > 0) return `-${formatCents(Math.round(amount * 100), currency)}`;\n return null;\n }\n if (discountType === \"percentage\") {\n const pct = tier.percentage ?? 0;\n if (pct > 0) return `-${Math.round(pct)}%`;\n return null;\n }\n return null;\n}\n\nfunction pickBestTierIndex(resolved: ResolvedTier[]): number {\n let bestSavings = 0;\n let bestIndex = 0;\n resolved.forEach((r, i) => {\n const savings = r.basePricePerUnitCents - r.pricePerUnitCents;\n if (savings > bestSavings) {\n bestSavings = savings;\n bestIndex = i;\n }\n });\n return bestIndex;\n}\n\nfunction clamp(n: number, min: number, max: number): number {\n return Math.max(min, Math.min(max, n));\n}\n\n","/**\n * Input-mode tracker — toggles `using-mouse` / `using-keyboard` classes on a\n * target element so CSS can scope focus styles by the customer's current\n * input device. Default is mouse; the first keyboard-navigation keypress\n * (Tab, arrow keys, Enter, Space, Escape, Home/End/PageUp/PageDown) flips\n * to keyboard mode, and the next pointer click flips back.\n *\n * Multiple targets share one pair of document-level listeners — installed\n * on the first `trackInputMode` call, removed when the last target is\n * released. Safe to call across every widget instance on a page without\n * stacking listeners.\n *\n * CSS shape (see bundle-base.css):\n * .using-mouse .lb-bundle-widget :focus { outline: none; }\n *\n * The Liquid theme mirrors this behaviour from `bundle-widget.js` against\n * `document.documentElement` so classic and headless storefronts render the\n * same focus rings.\n */\n\nconst NAV_KEYS = new Set([\n \"Tab\",\n \"ArrowUp\",\n \"ArrowDown\",\n \"ArrowLeft\",\n \"ArrowRight\",\n \"Home\",\n \"End\",\n \"PageUp\",\n \"PageDown\",\n \"Enter\",\n \" \",\n \"Escape\",\n]);\n\nconst targets = new Set<HTMLElement>();\nlet listenersAttached = false;\n\nfunction setAll(on: \"using-mouse\" | \"using-keyboard\"): void {\n const off = on === \"using-mouse\" ? \"using-keyboard\" : \"using-mouse\";\n for (const el of targets) {\n el.classList.add(on);\n el.classList.remove(off);\n }\n}\n\nfunction onKeyDown(e: KeyboardEvent): void {\n if (NAV_KEYS.has(e.key)) setAll(\"using-keyboard\");\n}\n\nfunction onPointerDown(): void {\n setAll(\"using-mouse\");\n}\n\nfunction attachListeners(): void {\n if (listenersAttached) return;\n listenersAttached = true;\n document.addEventListener(\"keydown\", onKeyDown, true);\n document.addEventListener(\"pointerdown\", onPointerDown, true);\n}\n\nfunction detachListeners(): void {\n if (!listenersAttached) return;\n listenersAttached = false;\n document.removeEventListener(\"keydown\", onKeyDown, true);\n document.removeEventListener(\"pointerdown\", onPointerDown, true);\n}\n\nexport function trackInputMode(target: HTMLElement): () => void {\n target.classList.add(\"using-mouse\");\n target.classList.remove(\"using-keyboard\");\n targets.add(target);\n attachListeners();\n\n return () => {\n targets.delete(target);\n target.classList.remove(\"using-mouse\");\n target.classList.remove(\"using-keyboard\");\n if (targets.size === 0) detachListeners();\n };\n}\n","/**\n * Base + type-specific widget CSS, inlined as template literals so the web\n * component can dump them into its shadow root. Source of truth for these\n * rules is `extensions/bundle-theme/assets/*.css` — the classic Shopify\n * theme app block reads the same files. Keep the two in lockstep; the\n * bundle-css-parity.test.ts golden test enforces byte equality.\n *\n * `BUNDLE_SKELETON_CSS` (at the bottom of this file) is intentionally\n * web-component-only. The theme app block never renders a loading\n * state — its Liquid render is synchronous on the server — so the\n * skeleton styles would be dead rules there. Excluding from parity.\n */\nexport const BUNDLE_BASE_CSS = `/* Lime Bundles — shared base styles for all bundle widget types */\n\n.lb-bundle-widget.lb-bundle-widget,\n.lb-bundle-widget.lb-bundle-widget * {\n line-height: normal;\n}\n\n.lb-bundle-widget {\n /* Internal CSS-only vars (not merchant-configurable). */\n --lb-thumbnail-bg: #F0F0F0;\n --lb-widget-pad: 20px;\n --lb-progress-color: var(--lb-primary-color);\n\n font-family: inherit;\n font-size: 16px;\n background: var(--lb-bg);\n border: var(--lb-border-width) solid var(--lb-border);\n border-radius: var(--lb-radius);\n padding: var(--lb-widget-pad) var(--lb-widget-pad) 20px;\n box-sizing: border-box;\n /* Cap the widget at a comfortable reading width on desktop. Below\n 440px viewports the container is already narrower than the cap,\n so the rule is inert on mobile. */\n max-width: 440px;\n}\n\n/* Countdown timer bar — sits below the gradient header */\n.lb-bundle-countdown {\n margin: 0 calc(-1 * var(--lb-widget-pad)) 20px;\n padding: 12px 20px;\n background: var(--lb-countdown-bg);\n border-top: 1px solid color-mix(in srgb, var(--lb-text) 6%, transparent);\n display: flex;\n align-items: center;\n justify-content: space-between;\n}\n\n.lb-bundle-countdown__label {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.lb-bundle-countdown__label svg {\n width: 16px;\n height: 16px;\n flex-shrink: 0;\n color: var(--lb-countdown-text);\n}\n\n.lb-bundle-countdown__label span {\n font-size: 12px;\n font-weight: 500;\n line-height: 1;\n color: var(--lb-countdown-text);\n}\n\n.lb-bundle-countdown__timer {\n font-family: 'SF Mono', 'Roboto Mono', ui-monospace, monospace;\n font-size: 12px;\n font-weight: 600;\n line-height: 1;\n color: var(--lb-countdown-text);\n letter-spacing: 0.02em;\n}\n\n/* Hide wrapper when the inner snippet rendered nothing (product OOS / unfulfillable) */\n.lb-bundle-widget:not(:has(.lb-fixed, .lb-mix-match, .lb-volume)) {\n display: none;\n}\n\n/* Sibling widget spacing — separates multiple bundles on the same product page.\n Uses \\`~\\` (general sibling) rather than \\`+\\` (adjacent) because each Liquid\n loop iteration emits a {% style %} block before its widget div, so the\n rendered DOM alternates <style><widget><style><widget>. The \\`+\\` combinator\n requires immediate adjacency and would match nothing; \\`~\\` matches every\n widget after the first regardless of elements between. Single-widget pages\n stay unaffected (no prior \\`.lb-bundle-widget\\` sibling to match against).\n Only \\`margin-top\\` — do NOT override \\`padding-top\\` here. The gradient header\n uses \\`margin-top: calc(-1 * var(--lb-widget-pad))\\` to reach the widget's\n inner border edge, assuming padding-top == --lb-widget-pad. Changing\n padding-top on the subsequent widget breaks that math and leaves a visible\n gap above the header. */\n.lb-bundle-widget ~ .lb-bundle-widget {\n margin-top: 24px;\n}\n\n/* Header — gradient banner with title + savings badge */\n.lb-bundle-header {\n margin: calc(-1 * var(--lb-widget-pad)) calc(-1 * var(--lb-widget-pad)) 20px;\n padding: 20px 20px;\n background: var(--lb-header-bg);\n /* Match the widget's inner border curve so there's no background gap at the top corners. */\n border-radius: max(0px, calc(var(--lb-radius) - var(--lb-border-width))) max(0px, calc(var(--lb-radius) - var(--lb-border-width))) 0 0;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 16px;\n}\n\n/* When countdown follows header, remove header bottom margin */\n.lb-bundle-header:has(+ .lb-bundle-countdown) {\n margin-bottom: 0;\n}\n\n.lb-bundle-header__content {\n flex: 1;\n min-width: 0;\n}\n\n.lb-bundle-title {\n font-size: 20px;\n font-weight: 700;\n line-height: 28px;\n letter-spacing: -0.02em;\n color: var(--lb-text);\n margin: 0;\n}\n\n.lb-bundle-header .lb-bundle-title {\n color: var(--lb-header-text);\n}\n\n.lb-bundle-subtitle {\n font-size: 16px;\n font-weight: 400;\n line-height: 20px;\n color: color-mix(in srgb, var(--lb-text) 60%, transparent);\n margin: 4px 0 0;\n}\n\n.lb-bundle-header .lb-bundle-subtitle {\n color: var(--lb-header-text);\n opacity: 0.85;\n margin-top: 8px;\n}\n\n.lb-bundle-header:has(.lb-bundle-subtitle) {\n align-items: flex-start;\n}\n\n.lb-bundle-header__badge {\n background: var(--lb-save-badge-bg);\n color: var(--lb-save-badge-text);\n border: var(--lb-save-badge-border-width) solid var(--lb-save-badge-border-color);\n font-size: 16px;\n font-weight: 700;\n line-height: 1;\n padding: 4px 12px;\n border-radius: var(--lb-save-badge-radius);\n white-space: nowrap;\n flex-shrink: 0;\n}\n\n/* Override Dawn's \\`div:empty { display: none }\\` reset for decorative elements */\n.lb-bundle-divider:empty,\n.lb-mix-match__progress-fill:empty {\n display: block;\n}\n\n/* Divider */\n.lb-bundle-divider {\n height: 1px;\n background: color-mix(in srgb, var(--lb-text) 7%, transparent);\n margin: 16px 0;\n}\n\n/* Product rows */\n.lb-bundle-product-row {\n display: flex;\n gap: 20px;\n padding: 12px 0;\n}\n\n.lb-bundle-thumbnail {\n position: relative;\n width: 48px;\n min-width: 48px;\n /* Aspect-ratio comes from the merchant \\`thumbnailRatio\\` enum via Liquid;\n \"original\" sets it to \\`auto\\` so the box sizes to the image's intrinsic\n ratio. Default keeps the historical 1:1 behaviour. */\n aspect-ratio: var(--lb-thumbnail-aspect-ratio, 1 / 1);\n background: var(--lb-thumbnail-bg);\n border-radius: 8px;\n overflow: hidden;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.lb-bundle-thumbnail img {\n width: 100%;\n /* Height + fit come from the same merchant enum; \"original\" sets them to\n \\`auto\\` / \\`contain\\` so the image renders uncropped at intrinsic ratio. */\n height: var(--lb-thumbnail-img-height, 100%);\n object-fit: var(--lb-thumbnail-img-fit, cover);\n}\n\n.lb-bundle-thumbnail svg {\n width: 28px;\n height: 28px;\n color: color-mix(in srgb, var(--lb-text) 35%, transparent);\n}\n\n.lb-bundle-product-info {\n flex: 1;\n min-width: 0;\n}\n\n.lb-bundle-product-name {\n font-size: 16px;\n font-weight: 700;\n line-height: 20px;\n color: var(--lb-text);\n margin: 0;\n text-decoration: none;\n display: block;\n}\n\n.lb-bundle-product-name:hover {\n text-decoration: underline;\n}\n\n.lb-bundle-product-price {\n /* --lb-product-price-display fallback is the \"on\" branch; Liquid sets 'none' when merchant disables. */\n display: var(--lb-product-price-display, block);\n font-size: 14px;\n font-weight: 400;\n line-height: 20px;\n color: var(--lb-text);\n}\n\n.lb-bundle-product-prices {\n display: flex;\n align-items: baseline;\n gap: 6px;\n flex-wrap: wrap;\n margin-top: 2px;\n}\n\n.lb-bundle-product-compare-price {\n /* --lb-product-compare-display fallback is the \"on\" branch; Liquid sets 'none' when merchant disables. */\n display: var(--lb-product-compare-display, inline);\n font-size: 14px;\n font-weight: 400;\n line-height: 20px;\n color: color-mix(in srgb, var(--lb-text) 60%, transparent);\n text-decoration: line-through;\n}\n\n/* Setting \\`display\\` above outranks the UA [hidden] rule — restore it so rows\n without a compare-at price don't leave a phantom flex item + gap. */\n.lb-bundle-product-compare-price[hidden] {\n display: none;\n}\n\n/* Unit price (e.g. \"$0.50/100ml\") — only rendered when the merchant has\n configured unit pricing on the variant in the Shopify admin. No merchant\n toggle: present in admin → shown; absent → hidden. Styled as muted\n secondary text beneath the price row so it doesn't compete visually. */\n.lb-bundle-product-unit-price {\n display: block;\n font-size: 12px;\n line-height: 16px;\n color: color-mix(in srgb, var(--lb-text) 60%, transparent);\n margin-top: 2px;\n}\n\n.lb-bundle-product-unit-price[hidden] {\n display: none;\n}\n\n.lb-bundle-variant-badge {\n display: inline-block;\n border: var(--lb-variant-border-width) solid var(--lb-variant-border-color);\n border-radius: var(--lb-variant-radius);\n padding: 8px 12px;\n font-size: 12px;\n line-height: 16px;\n color: color-mix(in srgb, var(--lb-text) 60%, transparent);\n margin-top: 8px;\n}\n\n/* Per-option variant pickers. Each option's label + select sit inside a\n .lb-bundle-variant-option-group (flex column, 2px gap between label and\n select); the groups stack inside a .lb-bundle-variant-option-groups\n parent (flex column, 12px gap between groups). The parent owns the top\n offset from the preceding unit-price line, so individual labels and\n selects don't carry their own vertical margins. */\n.lb-bundle-variant-option-groups {\n display: flex;\n flex-direction: column;\n gap: 12px;\n margin-top: 8px;\n}\n\n.lb-bundle-variant-option-group {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.lb-bundle-variant-option-label {\n display: block;\n margin: 0;\n font-size: 12px;\n line-height: 16px;\n font-weight: 600;\n letter-spacing: 0.05em;\n text-transform: uppercase;\n color: color-mix(in srgb, var(--lb-text) 60%, transparent);\n}\n\n.lb-mix-match__modal-product-info .lb-bundle-variant-option-groups {\n margin-top: 4px;\n}\n\n/* Focus-ring suppression for mouse users. A small JS helper — see\n packages/widget/src/utils/input-mode.ts and bundle-widget.js — toggles\n .using-mouse / .using-keyboard on the widget root (or html in the Liquid\n path) based on the customer's current input device. Default is mouse, so\n click-to-focus doesn't leave a keyboard-style ring. The modal overlay\n gets its own selector because the Liquid path reparents it to body,\n outside the widget root. */\n.using-mouse .lb-bundle-widget :focus,\n.using-mouse .lb-bundle-widget :focus-visible,\n.using-mouse .lb-mix-match__modal-overlay :focus,\n.using-mouse .lb-mix-match__modal-overlay :focus-visible {\n outline: none;\n outline-offset: 0;\n box-shadow: none;\n}\n\n.lb-bundle-quantity {\n font-size: 12px;\n line-height: 16px;\n color: color-mix(in srgb, var(--lb-text) 60%, transparent);\n margin-left: 8px;\n white-space: nowrap;\n}\n\n/* Pricing row — label left, prices right */\n.lb-bundle-pricing {\n display: flex;\n align-items: baseline;\n justify-content: space-between;\n padding: 4px 0 8px;\n gap: 12px;\n}\n\n.lb-bundle-pricing__label {\n font-size: 16px;\n font-weight: 400;\n line-height: 20px;\n color: var(--lb-text);\n white-space: nowrap;\n}\n\n.lb-bundle-pricing__prices {\n display: flex;\n align-items: baseline;\n gap: 8px;\n}\n\n.lb-bundle-sale-price {\n font-size: 20px;\n font-weight: 700;\n line-height: 1;\n letter-spacing: -0.02em;\n color: var(--lb-text);\n}\n\n.lb-bundle-compare-price {\n font-size: 16px;\n font-weight: 400;\n line-height: 20px;\n color: color-mix(in srgb, var(--lb-text) 60%, transparent);\n text-decoration: line-through;\n}\n\n/* Savings bar — green banner below pricing */\n.lb-bundle-savings-bar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n background: var(--lb-savings-bar-bg);\n color: var(--lb-savings-bar-text);\n border: var(--lb-savings-bar-border-width) solid var(--lb-savings-bar-border-color);\n font-size: 16px;\n font-weight: 600;\n line-height: 20px;\n padding: 8px 12px;\n border-radius: var(--lb-savings-bar-radius);\n margin-bottom: 12px;\n}\n\n/* Quantity badge — overlay on thumbnail top-right */\n.lb-bundle-qty-badge.lb-bundle-qty-badge {\n position: absolute;\n top: -8px;\n right: -8px;\n /* --lb-qty-badge-display fallback is the \"on\" branch; Liquid sets 'none' when merchant disables. */\n display: var(--lb-qty-badge-display, flex);\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 24px;\n border-radius: 50%;\n background: var(--lb-qty-badge-bg);\n border: var(--lb-image-border-width) solid var(--lb-image-border-color);\n color: var(--lb-qty-badge-color);\n font-size: 12px;\n font-weight: 700;\n line-height: 0;\n text-align: center;\n z-index: 1;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);\n}\n\n/* Override Dawn's \\`div:empty\\` for savings bar when hidden */\n.lb-bundle-savings-bar:empty {\n display: none;\n}\n\n/* CTA button.\n * Label and spinner share a single 1×1 grid cell so the button's intrinsic\n * width/height stays fixed when swapping between them — no layout shift when\n * entering the loading state. Visibility (not display) is used so the hidden\n * child still contributes to the cell's min-content sizing. See the\n * [data-loading=\"true\"] rules below. */\n.lb-bundle-cta {\n display: grid;\n grid-template-rows: 1fr;\n grid-template-columns: 1fr;\n width: 100%;\n padding: 12px 16px;\n border: var(--lb-cta-border-width) solid var(--lb-cta-border-color);\n border-radius: var(--lb-cta-radius);\n font-size: 16px;\n font-weight: 600;\n line-height: 20px;\n cursor: pointer;\n text-align: center;\n transition: opacity 0.15s ease;\n font-family: inherit;\n}\n\n.lb-bundle-cta:not(:disabled) {\n background: var(--lb-primary-color);\n color: var(--lb-btn-text);\n}\n\n.lb-bundle-cta:not(:disabled):hover {\n opacity: 0.9;\n}\n\n.lb-bundle-cta:disabled {\n background: color-mix(in srgb, var(--lb-primary-color) 35%, var(--lb-bg));\n color: color-mix(in srgb, var(--lb-btn-text) 85%, transparent);\n cursor: not-allowed;\n}\n\n/* Both the label and the spinner occupy grid cell (1, 1). Only one is\n * visible at a time; the other keeps its box for sizing but is invisible. */\n.lb-cta-label,\n.lb-cta-spinner {\n grid-row: 1;\n grid-column: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n min-width: 0;\n}\n\n.lb-cta-spinner {\n visibility: hidden;\n}\n\n.lb-cta-spinner svg {\n width: 20px;\n height: 20px;\n animation: lb-cta-spin 0.8s linear infinite;\n}\n\n@keyframes lb-cta-spin {\n to { transform: rotate(360deg); }\n}\n\n.lb-bundle-cta[data-loading=\"true\"] {\n cursor: wait;\n pointer-events: none;\n}\n\n.lb-bundle-cta[data-loading=\"true\"] .lb-cta-label {\n visibility: hidden;\n}\n\n.lb-bundle-cta[data-loading=\"true\"] .lb-cta-spinner {\n visibility: visible;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .lb-cta-spinner svg {\n animation-duration: 2.5s;\n }\n}\n\n/* Error message */\n.lb-bundle-error {\n font-size: 16px;\n color: #D72C0D;\n margin-top: 8px;\n display: none;\n}\n\n.lb-bundle-error[data-visible=\"true\"] {\n display: block;\n}\n\n/* Visually hidden — accessible to screen readers only */\n.lb-visually-hidden {\n position: absolute !important;\n width: 1px !important;\n height: 1px !important;\n padding: 0 !important;\n margin: -1px !important;\n overflow: hidden !important;\n clip-path: inset(50%) !important;\n white-space: nowrap !important;\n border: 0 !important;\n}\n\n/* Placeholder SVG icon for missing images */\n.lb-bundle-placeholder-icon {\n width: 28px;\n height: 28px;\n stroke: color-mix(in srgb, var(--lb-text) 35%, transparent);\n stroke-width: 1.5;\n fill: none;\n}\n\n/* Out-of-stock product row */\n.lb-bundle-product-row--oos {\n opacity: 0.5;\n}\n\n.lb-bundle-oos-label {\n font-size: 12px;\n font-weight: 500;\n color: #D72C0D;\n white-space: nowrap;\n margin-left: auto;\n}\n\n/* A/B test: hide save badge until JS swaps the label (prevents flash of default) */\n.lb-ab-pending {\n visibility: hidden;\n}\n`;\nexport const BUNDLE_FIXED_CSS = `/* Lime Bundles — Fixed bundle styles */\n\n.lb-fixed__products {\n display: flex;\n flex-direction: column;\n gap: 0;\n margin: 0;\n}\n\n/* Fixed bundles: product rows */\n.lb-fixed .lb-bundle-product-row {\n gap: 20px;\n align-items: center;\n}\n\n/* Fixed bundles: larger thumbnails. Height comes from --lb-thumbnail-aspect-ratio\n (set on .lb-bundle-widget via Liquid / applyWidgetConfigVars). */\n.lb-fixed .lb-bundle-thumbnail {\n width: 60px;\n min-width: 60px;\n border-radius: var(--lb-image-border-radius);\n border: var(--lb-image-border-width) solid var(--lb-image-border-color);\n box-sizing: border-box;\n overflow: visible;\n}\n\n.lb-fixed .lb-bundle-thumbnail img {\n border-radius: max(0px, calc(var(--lb-image-border-radius) - var(--lb-image-border-width)));\n border: none;\n}\n\n/* Variant picker select — styled to match the variant badge aesthetic.\n Sits inside .lb-bundle-variant-option-group so vertical spacing is owned\n by the group/groups flex gap, not the select itself. */\n.lb-bundle-variant-select {\n display: inline-block;\n border: var(--lb-variant-border-width) solid var(--lb-variant-border-color);\n border-radius: var(--lb-variant-radius);\n padding: 8px 32px 8px 12px;\n font-size: 12px;\n line-height: 16px;\n color: var(--lb-text);\n background: var(--lb-bg);\n font-family: inherit;\n cursor: pointer;\n appearance: none;\n -webkit-appearance: none;\n background-image: var(--lb-variant-chevron);\n background-repeat: no-repeat;\n background-position: right 8px center;\n background-size: 12px;\n width: 50%;\n max-width: 50%;\n}\n\n.lb-bundle-variant-select:focus-visible {\n outline: 2px solid var(--lb-primary-color);\n outline-offset: 2px;\n}\n`;\nexport const BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles — Mix & Match styles */\n\n/* === Progress Bar === */\n.lb-mix-match__progress {\n margin-bottom: 16px;\n}\n\n.lb-mix-match__progress-labels {\n display: flex;\n justify-content: space-between;\n margin-bottom: 8px;\n}\n\n.lb-mix-match__progress-count {\n font-size: 12px;\n font-weight: 500;\n line-height: 16px;\n color: color-mix(in srgb, var(--lb-text) 60%, transparent);\n}\n\n.lb-mix-match__progress-remaining {\n font-size: 12px;\n font-weight: 500;\n line-height: 16px;\n color: var(--lb-text);\n}\n\n.lb-mix-match__progress-track {\n width: 100%;\n height: 4px;\n background: color-mix(in srgb, var(--lb-text) 10%, transparent);\n border-radius: 4px;\n overflow: hidden;\n}\n\n.lb-mix-match__progress-fill {\n height: 100%;\n background: var(--lb-text);\n border-radius: 4px;\n transition: width 0.3s ease;\n}\n\n/* === Slots === */\n.lb-mix-match__slot {\n cursor: pointer;\n}\n\n.lb-mix-match .lb-bundle-product-row {\n align-items: center;\n}\n\n.lb-mix-match__slot--empty:focus-visible {\n outline: 2px solid var(--lb-primary-color);\n outline-offset: 2px;\n border-radius: 8px;\n}\n\n.lb-mix-match__slot--empty .lb-mix-match__empty-thumb {\n width: 60px;\n height: 60px;\n min-width: 60px;\n /* 2px border is intentionally independent of --lb-image-border-width —\n empty slots always need a visible dashed outline as an affordance,\n regardless of how the merchant has styled populated thumbnails. */\n border: 2px dashed color-mix(in srgb, var(--lb-text) 35%, transparent);\n border-radius: var(--lb-image-border-radius);\n box-sizing: border-box;\n display: flex;\n align-items: center;\n justify-content: center;\n color: color-mix(in srgb, var(--lb-text) 35%, transparent);\n}\n\n.lb-mix-match__empty-text {\n font-size: 16px;\n line-height: 20px;\n color: color-mix(in srgb, var(--lb-text) 45%, transparent);\n}\n\n/* Filled slot */\n.lb-mix-match__slot--filled {\n cursor: default;\n}\n\n/* Mix-match thumbnails. Height comes from --lb-thumbnail-aspect-ratio\n (set on .lb-bundle-widget via Liquid / applyWidgetConfigVars). */\n.lb-mix-match .lb-bundle-thumbnail {\n width: 60px;\n min-width: 60px;\n border-radius: var(--lb-image-border-radius);\n border: var(--lb-image-border-width) solid var(--lb-image-border-color);\n box-sizing: border-box;\n overflow: visible;\n}\n\n.lb-mix-match .lb-bundle-thumbnail img {\n border-radius: max(0px, calc(var(--lb-image-border-radius) - var(--lb-image-border-width)));\n border: none;\n}\n\n.lb-mix-match__slot--filled .lb-mix-match__filled-info {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n}\n\n.lb-mix-match__slot--filled .lb-mix-match__filled-title {\n font-size: 16px;\n font-weight: 700;\n line-height: 20px;\n color: var(--lb-text);\n text-decoration: none;\n overflow-wrap: break-word;\n}\n\n.lb-mix-match__slot--filled a.lb-mix-match__filled-title:hover {\n text-decoration: underline;\n}\n\n.lb-mix-match__slot--filled .lb-mix-match__filled-variant {\n font-size: 12px;\n line-height: 20px;\n margin-top: 2px;\n color: color-mix(in srgb, var(--lb-text) 60%, transparent);\n}\n\n.lb-mix-match__filled-price {\n /* --lb-product-price-display fallback is the \"on\" branch; Liquid sets 'none' when merchant disables. */\n display: var(--lb-product-price-display, block);\n font-size: 12px;\n line-height: 20px;\n margin-top: 4px;\n color: var(--lb-text);\n font-weight: 500;\n}\n\n.lb-mix-match__filled-compare {\n /* --lb-product-compare-display fallback is the \"on\" branch; Liquid sets 'none' when merchant disables. */\n display: var(--lb-product-compare-display, inline);\n font-size: 12px;\n font-weight: 400;\n color: color-mix(in srgb, var(--lb-text) 60%, transparent);\n text-decoration: line-through;\n margin-right: 4px;\n}\n\n.lb-mix-match__slot-remove {\n min-width: 44px;\n min-height: 44px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: none;\n border: none;\n cursor: pointer;\n color: var(--lb-text);\n padding: 0;\n margin-left: auto;\n border-radius: 8px;\n}\n\n.lb-mix-match__slot-remove:hover {\n color: var(--lb-text);\n}\n\n.lb-mix-match__slot-remove:focus-visible {\n outline: 2px solid var(--lb-primary-color);\n outline-offset: 2px;\n}\n\n/* Price placeholder */\n.lb-mix-match__price-placeholder {\n padding: 4px 0 16px;\n text-align: center;\n}\n\n.lb-mix-match__price-placeholder-text {\n font-size: 16px;\n color: var(--lb-text);\n}\n\n/* === Modal Overlay === */\n.lb-mix-match__modal-overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n z-index: 9999;\n display: flex;\n align-items: center;\n justify-content: center;\n opacity: 0;\n transition: opacity 0.2s ease-out;\n}\n\n.lb-mix-match__modal-overlay--open {\n opacity: 1;\n}\n\n/* === Modal Panel === */\n.lb-mix-match__modal {\n background: var(--lb-picker-bg);\n color: var(--lb-picker-text);\n border-radius: var(--lb-picker-radius);\n border: var(--lb-picker-border-width) solid var(--lb-picker-border-color);\n box-sizing: border-box;\n width: 100%;\n max-width: 480px;\n max-height: 70vh;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.16);\n transform: translateY(24px);\n transition: transform 0.25s ease-out;\n will-change: transform;\n}\n\n.lb-mix-match__modal-overlay--open .lb-mix-match__modal {\n transform: translateY(0);\n}\n\n/* === Modal Header === */\n.lb-mix-match__modal-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 20px 20px 12px;\n flex-shrink: 0;\n}\n\n.lb-mix-match__modal-title {\n font-size: 20px;\n font-weight: 600;\n line-height: 24px;\n margin: 0;\n color: inherit;\n}\n\n.lb-mix-match__modal-close {\n min-width: 44px;\n min-height: 44px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: none;\n border: none;\n cursor: pointer;\n color: var(--lb-picker-text);\n border-radius: 8px;\n padding: 0;\n margin: -12px -12px -12px 0;\n}\n\n.lb-mix-match__modal-close:focus-visible {\n outline: 2px solid var(--lb-primary-color);\n outline-offset: 2px;\n}\n\n/* === Modal Search === */\n.lb-mix-match__modal-search {\n padding: 0 20px 12px;\n position: relative;\n flex-shrink: 0;\n}\n\n.lb-mix-match__modal-search-input {\n width: 100%;\n padding: 12px 40px 12px 16px;\n border: var(--lb-picker-search-border-width) solid var(--lb-picker-search-border-color);\n border-radius: var(--lb-picker-search-radius);\n font-size: 16px;\n line-height: 20px;\n color: var(--lb-picker-text);\n background: var(--lb-picker-bg);\n box-sizing: border-box;\n -webkit-appearance: none;\n appearance: none;\n}\n\n.lb-mix-match__modal-search-input::placeholder {\n color: color-mix(in srgb, var(--lb-picker-text) 50%, transparent);\n}\n\n.lb-mix-match__modal-search-input:focus {\n outline: none;\n box-shadow: 0 0 0 1px var(--lb-primary-color);\n}\n\n.lb-mix-match__modal-search-clear {\n position: absolute;\n right: 32px;\n /* Anchor to the input area only — parent has padding-bottom: 12px which would\n otherwise push a top:50% center down by 6px. */\n top: 0;\n bottom: 12px;\n min-width: 32px;\n min-height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: none;\n border: none;\n cursor: pointer;\n color: var(--lb-picker-text);\n padding: 0;\n}\n\n/* === Modal Product List === */\n.lb-mix-match__modal-list {\n overflow-y: auto;\n flex: 1;\n padding: 0 20px;\n -webkit-overflow-scrolling: touch;\n}\n\n.lb-mix-match__modal-product {\n display: flex;\n align-items: center;\n gap: 20px;\n padding: 12px 0;\n border-bottom: 1px solid color-mix(in srgb, var(--lb-picker-text) 7%, transparent);\n}\n\n.lb-mix-match__modal-product:last-child {\n border-bottom: none;\n}\n\n.lb-mix-match__modal-product-thumb {\n position: relative;\n width: 48px;\n min-width: 48px;\n /* Modal picker thumbs follow the merchant's pickerThumbnailRatio —\n independent from the main widget's thumbnailRatio so a merchant can\n e.g. show tall picker thumbs with square main thumbs. */\n aspect-ratio: var(--lb-picker-thumbnail-aspect-ratio, 1 / 1);\n border-radius: var(--lb-picker-product-radius);\n border: var(--lb-picker-product-border-width) solid var(--lb-picker-product-border-color);\n box-sizing: border-box;\n overflow: visible;\n background: var(--lb-thumbnail-bg);\n}\n\n/* Qty badge inside the picker modal inherits picker-product border (width + color) plus\n inverted picker bg/text for clear contrast against the modal — always stays round\n (the badge shape is independent of the thumbnail shape). */\n.lb-mix-match__modal-product-thumb .lb-bundle-qty-badge.lb-bundle-qty-badge {\n /* --lb-picker-qty-badge-display fallback is the \"on\" branch; Liquid sets 'none' when merchant disables. */\n display: var(--lb-picker-qty-badge-display, flex);\n background: var(--lb-picker-qty-badge-bg);\n color: var(--lb-picker-qty-badge-color);\n border: var(--lb-picker-product-border-width) solid var(--lb-picker-product-border-color);\n}\n\n.lb-mix-match__modal-product-thumb img {\n width: 100%;\n /* Height + fit come from pickerThumbnailRatio — \"original\" sets both\n to auto/contain so the image renders uncropped at intrinsic ratio. */\n height: var(--lb-picker-thumbnail-img-height, 100%);\n object-fit: var(--lb-picker-thumbnail-img-fit, cover);\n border-radius: max(0px, calc(var(--lb-picker-product-radius) - var(--lb-picker-product-border-width)));\n}\n\n.lb-mix-match__modal-product-info {\n flex: 1;\n min-width: 0;\n}\n\n.lb-mix-match__modal-product-title {\n font-size: 16px;\n font-weight: 700;\n line-height: 20px;\n color: inherit;\n margin: 0;\n}\n\n.lb-mix-match__modal-product-price {\n font-size: 12px;\n line-height: 20px;\n color: inherit;\n margin: 4px 0 0;\n}\n\n.lb-mix-match__modal-product-unit-price {\n font-size: 11px;\n line-height: 16px;\n color: color-mix(in srgb, var(--lb-text) 60%, transparent);\n margin: 2px 0 0;\n}\n\n.lb-mix-match__modal-product-unit-price[hidden] {\n display: none;\n}\n\n.lb-mix-match__variant-select {\n font-size: 12px;\n padding: 4px 24px 4px 8px;\n border: var(--lb-picker-variant-border-width) solid var(--lb-picker-variant-border-color);\n border-radius: var(--lb-picker-variant-radius);\n background-color: var(--lb-picker-bg);\n background-image: var(--lb-picker-variant-chevron);\n background-repeat: no-repeat;\n background-position: right 6px center;\n background-size: 12px;\n color: var(--lb-picker-text);\n font-family: inherit;\n min-height: 32px;\n cursor: pointer;\n width: 50%;\n max-width: 50%;\n appearance: none;\n -webkit-appearance: none;\n}\n\n.lb-mix-match__variant-select:focus-visible {\n outline: 2px solid var(--lb-primary-color);\n outline-offset: 2px;\n}\n\n.lb-mix-match__modal-add {\n padding: 8px 20px;\n background: var(--lb-picker-add-bg);\n color: var(--lb-picker-add-label);\n border: var(--lb-picker-add-border-width) solid var(--lb-picker-add-border-color);\n border-radius: var(--lb-picker-add-radius);\n box-sizing: border-box;\n font-size: 12px;\n font-weight: 600;\n cursor: pointer;\n white-space: nowrap;\n flex-shrink: 0;\n}\n\n.lb-mix-match__modal-add:hover {\n opacity: 0.9;\n}\n\n.lb-mix-match__modal-add:focus-visible {\n outline: 2px solid var(--lb-primary-color);\n outline-offset: 2px;\n}\n\n.lb-mix-match__modal-add:disabled {\n background: color-mix(in srgb, var(--lb-picker-add-bg) 35%, var(--lb-picker-bg));\n color: color-mix(in srgb, var(--lb-picker-add-label) 85%, transparent);\n cursor: not-allowed;\n}\n\n/* Sold out product row */\n.lb-mix-match__modal-product--sold-out {\n opacity: 0.5;\n}\n\n.lb-mix-match__modal-product--sold-out .lb-mix-match__modal-sold-out-label {\n font-size: 12px;\n color: inherit;\n font-weight: 500;\n white-space: nowrap;\n}\n\n/* === Modal Empty State === */\n.lb-mix-match__modal-empty {\n padding: 32px 20px;\n text-align: center;\n}\n\n.lb-mix-match__modal-empty p {\n margin: 0;\n font-size: 16px;\n color: var(--lb-picker-text);\n}\n\n/* Hidden utility for search filtering */\n.lb-hidden {\n display: none !important;\n}\n\n/* === Mobile Full-Screen Modal === */\n@media (max-width: 767px) {\n .lb-mix-match__modal-overlay {\n align-items: flex-end;\n }\n\n .lb-mix-match__modal {\n max-width: 100%;\n max-height: 90vh;\n border-radius: 16px 16px 0 0;\n transform: translateY(100%);\n }\n\n .lb-mix-match__modal-overlay--open .lb-mix-match__modal {\n transform: translateY(0);\n }\n\n .lb-mix-match__variant-select {\n width: 80%;\n max-width: 80%;\n }\n}\n\n/* === Reduced Motion === */\n@media (prefers-reduced-motion: reduce) {\n .lb-mix-match__modal-overlay,\n .lb-mix-match__modal,\n .lb-mix-match__progress-fill {\n transition: none;\n }\n}\n`;\nexport const BUNDLE_VOLUME_CSS = `/* Lime Bundles — Volume / Quantity Breaks styles */\n\n.lb-volume__tiers {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n.lb-volume__tier {\n display: flex;\n align-items: center;\n gap: 12px;\n border: var(--lb-tier-border-width) solid var(--lb-tier-border-color);\n border-radius: var(--lb-tier-radius);\n padding: 12px 16px;\n cursor: pointer;\n position: relative;\n transition: border-color 0.15s ease;\n}\n\n.lb-volume__tier:hover {\n border-color: color-mix(in srgb, var(--lb-tier-border-color) 50%, black);\n}\n\n.lb-volume__tier:focus-visible {\n outline: 2px solid var(--lb-primary-color);\n outline-offset: 2px;\n}\n\n.lb-volume__tier[aria-checked=\"true\"] {\n border-color: var(--lb-tier-selected-border-color);\n outline: var(--lb-tier-selected-border-width) solid var(--lb-tier-selected-border-color);\n outline-offset: calc(-1 * var(--lb-tier-selected-border-width));\n}\n\n.lb-volume__radio {\n width: 20px;\n height: 20px;\n min-width: 20px;\n border: 2px solid var(--lb-text);\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: border-color 0.15s ease;\n}\n\n.lb-volume__tier[aria-checked=\"true\"] .lb-volume__radio {\n border-color: var(--lb-text);\n}\n\n.lb-volume__radio-dot {\n width: 12px;\n height: 12px;\n border-radius: 50%;\n background: transparent;\n transition: background 0.15s ease;\n}\n\n.lb-volume__tier[aria-checked=\"true\"] .lb-volume__radio-dot {\n background: var(--lb-text);\n}\n\n/* Tier content: 2x2 grid layout */\n.lb-volume__tier-grid {\n flex: 1;\n display: grid;\n row-gap: 4px;\n align-items: center;\n}\n\n.lb-volume__tier-label {\n font-size: 16px;\n font-weight: 700;\n color: var(--lb-text);\n}\n\n.lb-volume__tier-badge {\n flex-shrink: 0;\n font-size: 12px;\n line-height: 16px;\n color: var(--lb-popular-badge-text);\n background: var(--lb-popular-badge-bg);\n border: var(--lb-popular-badge-border-width) solid var(--lb-popular-badge-border-color);\n border-radius: var(--lb-popular-badge-radius);\n padding: 4px 8px;\n}\n\n.lb-volume__tier-price {\n grid-column: 1 / -1;\n font-size: 14px;\n font-weight: 500;\n color: var(--lb-text);\n}\n\n.lb-volume__tier-unit {\n font-size: 12px;\n color: var(--lb-text);\n}\n\n/* Compare-at (strikethrough) price */\n.lb-volume__tier-compare {\n font-size: 14px;\n line-height: 16px;\n color: color-mix(in srgb, var(--lb-text) 60%, transparent);\n text-decoration: line-through;\n margin-right: 4px;\n}\n\n\n`;\n\n/**\n * Skeleton styles for the web component's loading state. Rendered\n * synchronously in connectedCallback → renderLoading() so the host\n * element has intrinsic size from render-0 and doesn't shift layout\n * when the real bundle paints. Web-component only (see file header).\n */\nexport const BUNDLE_SKELETON_CSS = `/* Lime Bundles — web-component loading skeleton (not mirrored to theme assets) */\n\n.lb-bundle-widget--loading {\n display: block;\n padding: var(--lb-widget-pad, 20px);\n border: 1px solid var(--lb-border, #E5E5E5);\n border-radius: var(--lb-radius, 12px);\n background: var(--lb-bg, #FFFFFF);\n /* Contain layout/paint so the skeleton doesn't influence ancestor\n layout once the real content swaps in. */\n contain: layout paint;\n}\n\n.lb-bundle-widget--loading .lb-skeleton {\n background: linear-gradient(\n 90deg,\n rgba(0, 0, 0, 0.06) 0%,\n rgba(0, 0, 0, 0.10) 50%,\n rgba(0, 0, 0, 0.06) 100%\n );\n background-size: 200% 100%;\n border-radius: 6px;\n animation: lb-skeleton-pulse 1.4s ease-in-out infinite;\n}\n\n.lb-bundle-widget--loading .lb-skeleton--title {\n height: 28px;\n width: 60%;\n margin-bottom: 16px;\n}\n\n.lb-bundle-widget--loading .lb-skeleton--products {\n display: grid;\n gap: 12px;\n margin-bottom: 16px;\n}\n\n.lb-bundle-widget--loading .lb-skeleton--row {\n display: grid;\n grid-template-columns: 56px 1fr 60px;\n gap: 12px;\n align-items: center;\n}\n\n.lb-bundle-widget--loading .lb-skeleton--thumb {\n height: 56px;\n width: 56px;\n border-radius: 8px;\n}\n\n.lb-bundle-widget--loading .lb-skeleton--line {\n height: 14px;\n border-radius: 4px;\n}\n\n.lb-bundle-widget--loading .lb-skeleton--line + .lb-skeleton--line {\n margin-top: 8px;\n width: 70%;\n}\n\n.lb-bundle-widget--loading .lb-skeleton--price {\n height: 20px;\n width: 60px;\n}\n\n.lb-bundle-widget--loading .lb-skeleton--footer {\n margin-top: 16px;\n padding-top: 16px;\n border-top: 1px solid var(--lb-border, #E5E5E5);\n display: grid;\n grid-template-columns: 1fr auto;\n gap: 12px;\n align-items: center;\n}\n\n.lb-bundle-widget--loading .lb-skeleton--total {\n height: 24px;\n width: 40%;\n}\n\n.lb-bundle-widget--loading .lb-skeleton--cta {\n height: 44px;\n width: 140px;\n border-radius: var(--lb-cta-radius, 8px);\n}\n\n@keyframes lb-skeleton-pulse {\n 0% { background-position: 0% 50%; }\n 100% { background-position: -200% 50%; }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .lb-bundle-widget--loading .lb-skeleton {\n animation: none;\n }\n}\n`;\n\n","/**\n * @lime-bundles/widget — Vanilla JS Web Component for headless storefronts.\n *\n * Usage:\n * <script src=\"https://unpkg.com/@lime-bundles/widget/dist/lime-bundle.js\"></script>\n * <lime-bundle shop-domain=\"...\" storefront-token=\"...\" bundle-gid=\"...\"></lime-bundle>\n */\nimport { LimeBundleElement } from \"./lime-bundle\";\n\n// Register custom element\nif (\n typeof customElements !== \"undefined\" &&\n !customElements.get(\"lime-bundle\")\n) {\n customElements.define(\"lime-bundle\", LimeBundleElement);\n}\n\nexport { LimeBundleElement };\nexport { trackInputMode } from \"./utils/input-mode\";\n"],"mappings":"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AA;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;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;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;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;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;AA+iBxB,IAAM,mBAAmB;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;AA4DzB,IAAM,uBAAuB;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;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;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;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;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;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;AAAA;AAggB7B,IAAM,oBAAoB;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;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;AAsH1B,IAAM,sBAAsB;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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA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","names":["name","resolveBundleQty","PLACEHOLDER_THUMB_SVG","renderHeader","renderSavingsBar","resolveBundleQty","renderHeader","renderPricingRow","renderSavingsBar","renderCta","el"]}
1
+ {"version":3,"sources":["../src/lime-bundle.ts","../src/renderers/fixed.ts","../src/dropdown/bind-dropdown.ts","../src/renderers/pricing.ts","../src/renderers/countdown.ts","../src/renderers/cta-button.ts","../src/renderers/dom.ts","../src/renderers/image.ts","../src/renderers/mix-match.ts","../src/renderers/volume.ts","../src/utils/input-mode.ts","../src/styles/bundle-css.ts","../src/index.ts"],"sourcesContent":["/**\n * <lime-bundle> Web Component — renders Lime Bundles on any storefront.\n *\n * ## The two modes\n *\n * Single-bundle (pinned):\n * <lime-bundle\n * shop-domain=\"my-shop.myshopify.com\"\n * storefront-token=\"shpat_...\"\n * bundle-gid=\"gid://shopify/Metaobject/42\"\n * ></lime-bundle>\n *\n * Product-aware (matches classic Liquid theme block behaviour — one snippet\n * on the product page renders every active bundle configured against that\n * product):\n * <lime-bundle\n * shop-domain=\"my-shop.myshopify.com\"\n * storefront-token=\"shpat_...\"\n * ></lime-bundle>\n *\n * Product resolution cascade (when no `bundle-gid` is set):\n * 1. explicit `product-handle` attribute\n * 2. <meta name=\"shopify:product-handle\" content=\"...\"> on the page\n * 3. /products/<handle> segment of window.location.pathname\n * 4. fallthrough: renders nothing, fires `lime-bundle:error`\n *\n * ## Add-to-cart behaviour\n *\n * Merchants who do nothing get a default: the widget calls Shopify's\n * Storefront Cart API (tokenless — no extra scopes required) and redirects\n * the browser to the returned checkoutUrl. One-click-to-checkout is the\n * right UX for most merchants pasting the widget into Webflow / Wix /\n * Squarespace / static HTML.\n *\n * Merchants with their own cart state (Hydrogen's useCart, a custom cart\n * drawer, etc.) opt out by attaching a listener that calls\n * `event.preventDefault()`:\n *\n * document.querySelector(\"lime-bundle\").addEventListener(\n * \"lime-bundle:add-to-cart\",\n * (ev) => {\n * ev.preventDefault(); // suppress the default redirect\n * myCart.linesAdd(ev.detail.lines);\n * },\n * );\n *\n * The event is always dispatched; only the default action is conditional.\n */\nimport {\n createStorefrontClient,\n BUNDLE_METAOBJECT_QUERY,\n BUNDLES_FOR_PRODUCT_QUERY,\n CART_CREATE_MUTATION,\n CART_LINES_ADD_MUTATION,\n SHOP_CUSTOM_CSS_QUERY,\n parseMetaobjectBundle,\n observeImpression,\n reportImpression,\n reportAddToCart,\n injectCustomCss,\n sanitizeCustomCss,\n getABTestAssignment,\n applyABVariantB,\n type ParsedBundle,\n type BundleMetaobjectResponse,\n type BundlesForProductResponse,\n type CartCreateResponse,\n type CartLinesAddResponse,\n type ShopCustomCssResponse,\n type CartLineInput,\n type StorefrontClient,\n} from \"@lime-bundles/core\";\nimport { renderFixedBundle } from \"./renderers/fixed\";\nimport { renderMixMatchBundle } from \"./renderers/mix-match\";\nimport { renderVolumeBundle } from \"./renderers/volume\";\nimport { applyWidgetConfigVars } from \"@lime-bundles/core\";\nimport { trackInputMode } from \"./utils/input-mode\";\nimport {\n BUNDLE_BASE_CSS,\n BUNDLE_DROPDOWN_CSS,\n BUNDLE_FIXED_CSS,\n BUNDLE_MIX_MATCH_CSS,\n BUNDLE_SKELETON_CSS,\n BUNDLE_VOLUME_CSS,\n} from \"./styles/bundle-css\";\n\n/**\n * Per-shop localStorage key for the active cart id. Scoping by shop domain\n * keeps the cart isolated when a single browser visits multiple Lime-\n * Bundles-powered storefronts (rare, but correct).\n */\nconst cartStorageKey = (shopDomain: string) => `lb_cart_id:${shopDomain}`;\n\n/**\n * Resolve the current product handle from the page. Runs the cascade\n * documented on LimeBundleElement and returns null if no source matches.\n */\nfunction resolveProductHandle(explicit: string | null): string | null {\n if (explicit) return explicit.trim() || null;\n\n if (typeof document !== \"undefined\") {\n const meta = document.querySelector<HTMLMetaElement>(\n 'meta[name=\"shopify:product-handle\"]',\n );\n if (meta?.content) return meta.content.trim() || null;\n }\n\n if (typeof window !== \"undefined\") {\n const match = window.location.pathname.match(/\\/products\\/([^/?#]+)/);\n if (match?.[1]) return decodeURIComponent(match[1]);\n }\n\n return null;\n}\n\nexport class LimeBundleElement extends HTMLElement {\n static observedAttributes = [\n \"shop-domain\",\n \"storefront-token\",\n \"bundle-gid\",\n \"product-handle\",\n \"app-url\",\n \"analytics\",\n \"locale\",\n ];\n\n private shadow: ShadowRoot;\n private bundles: ParsedBundle[] = [];\n private abortController: AbortController | null = null;\n private impressionCleanups: Array<() => void> = [];\n /**\n * Tick / observer / listener cleanups registered by individual renderers\n * (e.g. countdown setInterval handles). Flushed in disconnectedCallback\n * so we never leak timers or event listeners when the widget is removed.\n */\n private renderCleanups: Array<() => void> = [];\n /**\n * Merchant custom CSS fetched from the shop metafield. Injected inside\n * the shadow root alongside the bundle stylesheets so selectors like\n * `.lb-bundle-widget { ... }` reach the widget's DOM.\n */\n private shopCustomCss: string | null = null;\n\n constructor() {\n super();\n this.shadow = this.attachShadow({ mode: \"open\" });\n }\n\n connectedCallback() {\n // `fetchBundle()` calls `renderLoading()` synchronously before any\n // await — the skeleton paints on the first frame, reserving\n // layout space before the Storefront API call resolves.\n this.fetchBundle();\n }\n\n disconnectedCallback() {\n this.abortController?.abort();\n this.teardownImpressions();\n this.teardownRenderers();\n }\n\n private teardownImpressions() {\n for (const cleanup of this.impressionCleanups) cleanup();\n this.impressionCleanups = [];\n }\n\n private teardownRenderers() {\n for (const cleanup of this.renderCleanups) cleanup();\n this.renderCleanups = [];\n }\n\n attributeChangedCallback(\n name: string,\n oldValue: string | null,\n newValue: string | null,\n ) {\n if (oldValue === newValue || !this.isConnected) return;\n if (\n name === \"bundle-gid\" ||\n name === \"product-handle\" ||\n name === \"shop-domain\" ||\n name === \"storefront-token\"\n ) {\n if (this.shopDomain && this.storefrontToken) {\n this.fetchBundle();\n }\n }\n }\n\n private get shopDomain(): string {\n return this.getAttribute(\"shop-domain\") ?? \"\";\n }\n\n private get storefrontToken(): string {\n return this.getAttribute(\"storefront-token\") ?? \"\";\n }\n\n private get bundleGid(): string {\n return this.getAttribute(\"bundle-gid\") ?? \"\";\n }\n\n private get productHandleAttr(): string {\n return this.getAttribute(\"product-handle\") ?? \"\";\n }\n\n private get appUrl(): string {\n return this.getAttribute(\"app-url\") ?? \"\";\n }\n\n private get analyticsEnabled(): boolean {\n return this.getAttribute(\"analytics\") !== \"false\";\n }\n\n private async fetchBundle() {\n if (!this.shopDomain || !this.storefrontToken) {\n this.renderError(\n \"Missing required attributes: shop-domain, storefront-token\",\n );\n return;\n }\n\n this.abortController?.abort();\n const controller = new AbortController();\n this.abortController = controller;\n\n this.renderLoading();\n\n const client = createStorefrontClient({\n shopDomain: this.shopDomain,\n accessToken: this.storefrontToken,\n });\n\n try {\n // Fire bundle query BEFORE CSS so order-sensitive call logs (and any\n // test harness that mocks fetch with sequential mockResolvedValueOnce)\n // see the bundle as call #1, CSS as call #2. Parallelism preserved\n // via Promise.all at the await site below.\n let bundlePromise: Promise<void>;\n let singleBundleMode = false;\n if (this.bundleGid) {\n singleBundleMode = true;\n bundlePromise = this.fetchSingleBundle(client, controller.signal);\n } else {\n const handle = resolveProductHandle(this.productHandleAttr);\n if (!handle) {\n this.teardownImpressions();\n this.renderError(\n \"No bundle-gid or product-handle provided, and the current URL doesn't match /products/<handle>.\",\n );\n return;\n }\n bundlePromise = this.fetchProductBundles(\n client,\n controller.signal,\n handle,\n );\n }\n\n // CSS fetch kicks off AFTER bundle fetch for deterministic call order.\n // Best-effort — widget renders without merchant styling if it fails.\n const cssPromise = client\n .query<ShopCustomCssResponse>(SHOP_CUSTOM_CSS_QUERY, undefined, {\n signal: controller.signal,\n })\n .catch(() => null);\n\n await bundlePromise;\n if (controller.signal.aborted) return;\n\n // Single-bundle mode with an explicit GID that didn't resolve is a\n // genuine error — the merchant pinned a specific bundle and it's\n // missing. Product-handle mode with zero bundles is NOT an error:\n // the product legitimately has no bundles configured; widget stays\n // invisible (mirrors classic-theme Liquid block behaviour).\n if (singleBundleMode && this.bundles.length === 0) {\n this.teardownImpressions();\n this.renderError(\"Bundle not found\");\n return;\n }\n\n const css = await cssPromise;\n if (css?.shop?.metafield?.value) {\n // Classic storefronts get the CSS in document.head; the web\n // component also stores the sanitized CSS for its own shadow\n // root injection below, since shadow DOM blocks inherited styles.\n injectCustomCss(this.shopDomain, css.shop.metafield.value);\n const sanitized = sanitizeCustomCss(css.shop.metafield.value);\n if (sanitized.ok) this.shopCustomCss = sanitized.css;\n }\n\n // Resolve A/B variants for any bundle that's in an active test.\n // Awaited so bundles render with the assigned variant on first\n // paint (no flash of Variant A). Individual failures inside\n // applyABVariants fall back to Variant A silently.\n await this.applyABVariants();\n\n this.renderBundles();\n } catch (err) {\n if (controller.signal.aborted) return;\n this.bundles = [];\n this.teardownImpressions();\n this.renderError(\n err instanceof Error ? err.message : \"Failed to load bundle\",\n );\n }\n }\n\n /**\n * Resolve the visitor's A/B bucket for every bundle with an active test\n * and merge Variant B overrides where applicable. Runs in parallel; any\n * assignment failure logs internally but still renders Variant A (safe\n * default). The `getABTestAssignment` helper also persists the bucket\n * via a first-party cookie + POSTs to /api/ab-assign for server-side\n * analytics.\n */\n private async applyABVariants(): Promise<void> {\n if (!this.appUrl || this.bundles.length === 0) return;\n const results = await Promise.all(\n this.bundles.map(async (bundle) => {\n if (!bundle.abTestId || !bundle.abVariantB) return bundle;\n try {\n const assignment = await getABTestAssignment(\n this.appUrl,\n this.shopDomain,\n bundle.abTestId,\n bundle.id,\n );\n if (assignment?.variant === \"B\") {\n return applyABVariantB(bundle);\n }\n } catch (err) {\n // Surface A/B failures so misconfigured tests aren't invisible.\n // Variant A still renders — the customer is never blocked.\n // eslint-disable-next-line no-console\n console.warn(\n `[lime-bundle] A/B assignment failed for bundle ${bundle.id}; falling back to Variant A.`,\n err,\n );\n }\n return bundle;\n }),\n );\n this.bundles = results;\n }\n\n private async fetchSingleBundle(\n client: StorefrontClient,\n signal: AbortSignal,\n ): Promise<void> {\n const data = await client.query<BundleMetaobjectResponse>(\n BUNDLE_METAOBJECT_QUERY,\n { id: this.bundleGid },\n { signal },\n );\n if (!data.metaobject) {\n this.bundles = [];\n return;\n }\n const parsed = parseMetaobjectBundle(\n data.metaobject.id,\n data.metaobject.fields,\n );\n this.bundles = parsed ? [parsed] : [];\n }\n\n private async fetchProductBundles(\n client: StorefrontClient,\n signal: AbortSignal,\n productHandle: string,\n ): Promise<void> {\n // fetchBundlesForProduct re-creates its own client; skip that indirection\n // and reuse the one we already built so the request shares the same\n // AbortSignal and header config.\n const data = await client.query<BundlesForProductResponse>(\n BUNDLES_FOR_PRODUCT_QUERY,\n { handle: productHandle },\n { signal },\n );\n if (!data.product) {\n this.bundles = [];\n return;\n }\n const refs = data.product.metafield?.references?.nodes ?? [];\n const bundles: ParsedBundle[] = [];\n for (const ref of refs) {\n const parsed = parseMetaobjectBundle(ref.id, ref.fields);\n if (parsed) bundles.push(parsed);\n }\n this.bundles = bundles;\n }\n\n /**\n * Dispatch add-to-cart with a cancelable event, then — unless a listener\n * called preventDefault — execute the default cart-and-checkout flow.\n *\n * `fire-and-forget` against `reportAddToCart` runs regardless so merchants\n * with BYO cart still get analytics.\n */\n private handleAddToCart = async (\n bundle: ParsedBundle,\n lines: CartLineInput[],\n ): Promise<void> => {\n const ev = new CustomEvent(\"lime-bundle:add-to-cart\", {\n detail: { lines },\n bubbles: true,\n composed: true,\n cancelable: true,\n });\n // dispatchEvent returns false if preventDefault() was called on a\n // cancelable event. That's how merchants opt out of the default flow.\n const allowDefault = this.dispatchEvent(ev);\n\n // Analytics fire regardless of which cart path runs.\n this.reportAddToCartEvent(bundle, lines);\n\n if (allowDefault) {\n await this.defaultAddToCart(lines);\n }\n };\n\n /**\n * Default cart flow: Shopify's Storefront Cart API is tokenless, so we\n * don't need any additional scopes. Persist the cart ID in localStorage\n * so subsequent adds on the same browser session join the existing cart\n * instead of creating a new one every click.\n */\n private async defaultAddToCart(lines: CartLineInput[]): Promise<void> {\n if (typeof window === \"undefined\") return;\n\n const client = createStorefrontClient({\n shopDomain: this.shopDomain,\n accessToken: this.storefrontToken,\n });\n const storage = window.localStorage;\n const key = cartStorageKey(this.shopDomain);\n const existingCartId = storage?.getItem(key) ?? null;\n\n try {\n let checkoutUrl: string | null = null;\n\n if (existingCartId) {\n const res = await client.query<CartLinesAddResponse>(\n CART_LINES_ADD_MUTATION,\n { cartId: existingCartId, lines },\n );\n const payload = res.cartLinesAdd;\n if (payload?.userErrors?.length) {\n // Cart GID expired or was merged on Shopify's side — fall back to\n // cartCreate below. This happens after ~10 days of inactivity.\n storage?.removeItem(key);\n } else if (payload?.cart) {\n checkoutUrl = payload.cart.checkoutUrl;\n }\n }\n\n if (!checkoutUrl) {\n const res = await client.query<CartCreateResponse>(\n CART_CREATE_MUTATION,\n { input: { lines } },\n );\n const payload = res.cartCreate;\n if (payload?.cart) {\n storage?.setItem(key, payload.cart.id);\n checkoutUrl = payload.cart.checkoutUrl;\n }\n }\n\n if (checkoutUrl) {\n window.location.assign(checkoutUrl);\n } else {\n this.dispatchEvent(\n new CustomEvent(\"lime-bundle:error\", {\n detail: { message: \"Cart creation failed\", code: \"CART_ERROR\" },\n bubbles: true,\n composed: true,\n }),\n );\n }\n } catch (err) {\n this.dispatchEvent(\n new CustomEvent(\"lime-bundle:error\", {\n detail: {\n message:\n err instanceof Error ? err.message : \"Cart mutation failed\",\n code: \"CART_ERROR\",\n },\n bubbles: true,\n composed: true,\n }),\n );\n }\n }\n\n private reportAddToCartEvent(\n bundle: ParsedBundle,\n lines: CartLineInput[],\n ): void {\n if (!this.analyticsEnabled || !this.appUrl) return;\n\n const quantity = lines.reduce((sum, l) => sum + l.quantity, 0);\n const totalPrice = lines.reduce((sum, line) => {\n const product = bundle.products.find((p) =>\n p.variants.nodes.some((v) => v.id === line.merchandiseId),\n );\n const variant = product?.variants.nodes.find(\n (v) => v.id === line.merchandiseId,\n );\n const price = variant ? parseFloat(variant.price.amount) : 0;\n return sum + price * line.quantity;\n }, 0);\n\n reportAddToCart(\n { shopDomain: this.shopDomain, appUrl: this.appUrl },\n {\n bundleGid: bundle.id,\n bundleType: bundle.bundleType,\n productId: bundle.products[0]?.id ?? \"\",\n quantity,\n totalPrice: Math.round(totalPrice * 100) / 100,\n },\n );\n }\n\n private renderBundles() {\n this.teardownImpressions();\n this.teardownRenderers();\n this.shadow.innerHTML = \"\";\n\n // Bundle the base stylesheet and every type-specific sheet in one <style>.\n // This matches the Liquid theme app block's output (base.css +\n // bundle-{fixed,mix-match,volume}.css) so the shipped widget honours the\n // same selectors a merchant already styled against. Shadow DOM scope\n // keeps these rules from leaking out; the ported styles all target\n // `.lb-bundle-widget` descendants so there's no global bleed.\n const style = document.createElement(\"style\");\n style.textContent = [\n BUNDLE_BASE_CSS,\n BUNDLE_FIXED_CSS,\n BUNDLE_MIX_MATCH_CSS,\n BUNDLE_VOLUME_CSS,\n BUNDLE_DROPDOWN_CSS,\n ].join(\"\\n\");\n this.shadow.appendChild(style);\n\n // Merchant custom CSS — injected AFTER the built-in stylesheets so\n // the merchant's rules override defaults. Sanitized upstream via\n // sanitizeCustomCss (strips < > and script-safety patterns).\n if (this.shopCustomCss) {\n const customStyle = document.createElement(\"style\");\n customStyle.setAttribute(\"data-lime-bundles\", \"shop-custom-css\");\n customStyle.textContent = this.shopCustomCss;\n this.shadow.appendChild(customStyle);\n }\n\n // Empty state: the shadow root holds only the <style> tag — nothing\n // visible. Matches Liquid theme UX where a product with no bundles\n // simply shows no block. We still fire `lime-bundle:loaded` below so\n // consumers know the async work completed (important for test\n // synchronisation and for merchants who want to hide a parent\n // placeholder once the widget has decided whether to render).\n\n for (const bundle of this.bundles) {\n const container = document.createElement(\"div\");\n container.className = \"lb-bundle-widget\";\n container.setAttribute(\"role\", \"region\");\n container.setAttribute(\"aria-label\", bundle.title);\n container.setAttribute(\"data-bundle-type\", bundle.bundleType);\n container.setAttribute(\"data-bundle-gid\", bundle.id);\n\n // Emit every merchant-configurable --lb-* property so the ported CSS\n // renders the widget with the look the merchant set in the admin\n // editor.\n applyWidgetConfigVars(container, bundle.widgetConfig);\n\n // Toggle using-mouse / using-keyboard on this container so the CSS\n // focus-ring rules match the customer's current input device.\n this.renderCleanups.push(trackInputMode(container));\n\n const dispatch = (lines: CartLineInput[]) =>\n this.handleAddToCart(bundle, lines);\n const registerCleanup = (fn: () => void) =>\n this.renderCleanups.push(fn);\n\n switch (bundle.bundleType) {\n case \"fixed\":\n renderFixedBundle(container, bundle, dispatch, registerCleanup);\n break;\n case \"mix_match\":\n renderMixMatchBundle(container, bundle, dispatch, registerCleanup);\n break;\n case \"volume\":\n renderVolumeBundle(container, bundle, dispatch, registerCleanup);\n break;\n }\n\n this.shadow.appendChild(container);\n this.setupImpressionFor(bundle, container);\n }\n\n const first = this.bundles[0];\n this.dispatchEvent(\n new CustomEvent(\"lime-bundle:loaded\", {\n detail: {\n bundleCount: this.bundles.length,\n bundleTypes: this.bundles.map((b) => b.bundleType),\n // Legacy fields — meaningful only in single-bundle mode. Preserved\n // for merchants who attached listeners against the pre-1.0 shape.\n bundleType: first?.bundleType,\n title: first?.title,\n },\n bubbles: true,\n composed: true,\n }),\n );\n }\n\n private setupImpressionFor(bundle: ParsedBundle, element: Element) {\n if (!this.analyticsEnabled || !this.appUrl) return;\n const cleanup = observeImpression(element, () => {\n reportImpression(\n { shopDomain: this.shopDomain, appUrl: this.appUrl },\n {\n bundleGid: bundle.id,\n bundleType: bundle.bundleType,\n },\n );\n });\n this.impressionCleanups.push(cleanup);\n }\n\n private renderLoading() {\n // Synchronous paint on every mount (before the Storefront fetch\n // resolves) so the host element has intrinsic size from render-0\n // and doesn't shift layout when the real bundle swaps in.\n this.shadow.innerHTML = `\n <style>${BUNDLE_BASE_CSS}</style>\n <style>${BUNDLE_SKELETON_CSS}</style>\n <div class=\"lb-bundle-widget lb-bundle-widget--loading\" aria-busy=\"true\" aria-label=\"Loading bundle\">\n <div class=\"lb-skeleton lb-skeleton--title\"></div>\n <div class=\"lb-skeleton--products\">\n <div class=\"lb-skeleton--row\">\n <div class=\"lb-skeleton lb-skeleton--thumb\"></div>\n <div>\n <div class=\"lb-skeleton lb-skeleton--line\"></div>\n <div class=\"lb-skeleton lb-skeleton--line\"></div>\n </div>\n <div class=\"lb-skeleton lb-skeleton--price\"></div>\n </div>\n <div class=\"lb-skeleton--row\">\n <div class=\"lb-skeleton lb-skeleton--thumb\"></div>\n <div>\n <div class=\"lb-skeleton lb-skeleton--line\"></div>\n <div class=\"lb-skeleton lb-skeleton--line\"></div>\n </div>\n <div class=\"lb-skeleton lb-skeleton--price\"></div>\n </div>\n <div class=\"lb-skeleton--row\">\n <div class=\"lb-skeleton lb-skeleton--thumb\"></div>\n <div>\n <div class=\"lb-skeleton lb-skeleton--line\"></div>\n <div class=\"lb-skeleton lb-skeleton--line\"></div>\n </div>\n <div class=\"lb-skeleton lb-skeleton--price\"></div>\n </div>\n </div>\n <div class=\"lb-skeleton--footer\">\n <div class=\"lb-skeleton lb-skeleton--total\"></div>\n <div class=\"lb-skeleton lb-skeleton--cta\"></div>\n </div>\n </div>\n `;\n }\n\n private renderError(message: string) {\n this.shadow.innerHTML = \"\";\n this.dispatchEvent(\n new CustomEvent(\"lime-bundle:error\", {\n detail: { message, code: \"LOAD_ERROR\" },\n bubbles: true,\n composed: true,\n }),\n );\n }\n\n}\n\n// Re-export the helper so consumers of the widget package can import it\n// when they want to share the URL-detection logic with their own code.\n","/**\n * DOM renderer for fixed bundles.\n *\n * Emits the class names and structure of `lb-fixed.liquid` so the ported\n * `bundle-fixed.css` themes it. Features:\n * - Variant dropdown per product (when the product has >1 available\n * variants, filtered by merchant's `selected_variant_ids`). Changing\n * a variant live-updates its row price, the bundle total, the\n * header save badge, and the savings bar.\n * - Merchant `productQuantities` + `variantQuantities` honoured.\n * - Out-of-stock behaviour (`hide` / `show_greyed_out`) applied per\n * product.\n * - Bundle-level guard: fixed bundles are all-or-nothing, so the\n * entire widget hides if any product has no available variants.\n *\n * Pricing matches Shopify's per-unit floor rounding via the `pricing`\n * helper, so what the widget shows equals what the customer pays at\n * checkout.\n */\nimport {\n resolveBundleQty,\n type CartLineInput,\n type FixedBundleData,\n type Product,\n type ProductVariant,\n} from \"@lime-bundles/core\";\nimport { bindAllDropdowns, unbindAllDropdowns } from \"../dropdown/bind-dropdown\";\nimport {\n computeFixedPricing,\n formatCents,\n formatUnitPrice,\n parseCents,\n} from \"./pricing\";\nimport { renderCountdown } from \"./countdown\";\nimport { buildCtaButton } from \"./cta-button\";\nimport { el } from \"./dom\";\nimport { THUMB_PX, transformImageUrl } from \"./image\";\n\nconst PLACEHOLDER_THUMB_SVG = `\n<svg class=\"lb-bundle-placeholder-icon\" viewBox=\"0 0 28 28\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\">\n <rect x=\"4\" y=\"4\" width=\"20\" height=\"20\" rx=\"3\"></rect>\n <line x1=\"4\" y1=\"20\" x2=\"24\" y2=\"20\"></line>\n <circle cx=\"10\" cy=\"12\" r=\"2\"></circle>\n</svg>`;\n\ninterface ProductRowState {\n product: Product;\n /** Variants eligible for this row (intersection of available + merchant selection). */\n eligibleVariants: ProductVariant[];\n /** Currently selected variant; null when none are available (OOS). */\n selected: ProductVariant | null;\n /** Quantity applied to this row (merchant product/variant qty or 1). */\n qty: number;\n /** Whether the product has zero available variants. */\n isOos: boolean;\n}\n\nexport function renderFixedBundle(\n container: HTMLElement,\n bundle: FixedBundleData,\n onAddToCart: (lines: CartLineInput[]) => void,\n onCleanup?: (fn: () => void) => void,\n) {\n const wc = bundle.widgetConfig;\n\n // Per-variant quantity lookup — delegated to the canonical resolver in\n // `@lime-bundles/core` so the fallback chain stays in lockstep with the\n // React SDK and the server-side discount metafield producer.\n const qtyFor = (productId: string, variantId: string): number =>\n resolveBundleQty(bundle, productId, variantId);\n\n // Build row state honouring merchant selections + OOS behaviour.\n const rows: ProductRowState[] = [];\n let oosCount = 0;\n bundle.products.forEach((product, idx) => {\n const row = buildRowState(bundle, product, idx);\n // Merchant explicitly set productQuantities/variantQuantities to 0 —\n // skip the row entirely. Treated as opt-out, not as a zero-quantity\n // line in pricing.\n if (row.qty === 0) return;\n if (row.isOos) {\n oosCount++;\n if (wc.outOfStockBehavior === \"hide\") return; // skip the row entirely\n }\n rows.push(row);\n });\n\n // Bundle-level guard: fixed bundles are all-or-nothing. Even in\n // \"show_greyed_out\" mode, if any product has no stock, disable the CTA\n // and render a warning. In \"hide\" mode, if we lost any rows, bail out\n // entirely — matches Liquid behaviour.\n if (rows.length === 0) return;\n\n const currency =\n bundle.products[0]?.priceRange.minVariantPrice.currencyCode ?? \"USD\";\n\n const root = el(\"div\", \"lb-fixed\", {\n \"data-discount-type\": bundle.discountConfig.discountType,\n \"data-discount-value\": String(bundle.discountConfig.discountValue),\n });\n\n // --- Header (title + subtitle + save badge) ---\n const headerHandle = renderHeader(bundle, currency);\n root.appendChild(headerHandle.el);\n\n // --- Countdown ---\n if (wc.countdown.showCountdown && bundle.endsAt) {\n const countdown = renderCountdown(bundle.endsAt);\n if (countdown) {\n root.appendChild(countdown.el);\n onCleanup?.(countdown.stop);\n }\n }\n\n // --- Product list ---\n const list = el(\"div\", \"lb-fixed__products\");\n const rowHandles: Array<ReturnType<typeof renderProductRow>> = [];\n rows.forEach((rowState) => {\n const handle = renderProductRow(rowState, currency, qtyFor, () => {\n // Variant change → recompute pricing.\n updatePricing();\n });\n rowHandles.push(handle);\n list.appendChild(handle.el);\n });\n root.appendChild(list);\n\n root.appendChild(el(\"div\", \"lb-bundle-divider\"));\n\n // --- Pricing row + savings bar ---\n const pricingHandle = renderPricingRow(bundle);\n root.appendChild(pricingHandle.el);\n const savingsBarHandle = wc.savingsBar.visible ? renderSavingsBar() : null;\n if (savingsBarHandle) root.appendChild(savingsBarHandle.el);\n\n // --- CTA ---\n const cta = renderCta(bundle, oosCount, () => {\n const lines: CartLineInput[] = rows\n .filter((r) => r.selected)\n .map((r) => ({\n merchandiseId: r.selected!.id,\n quantity: r.qty,\n attributes: [\n { key: \"_lime_bundle_gid\", value: bundle.id },\n { key: \"_lime_bundle_type\", value: bundle.bundleType },\n ],\n }));\n if (lines.length === 0) return;\n onAddToCart(lines);\n });\n root.appendChild(cta);\n\n root.appendChild(\n el(\"p\", \"lb-bundle-error\", { \"data-error\": \"\", \"aria-live\": \"polite\" }),\n );\n root.appendChild(\n el(\"span\", \"lb-visually-hidden\", {\n \"data-status\": \"\",\n \"aria-live\": \"polite\",\n }),\n );\n\n container.appendChild(root);\n\n // Replace native <select> popup chrome with our accessible custom dropdown.\n // Native <select> stays in DOM as state holder; the change handler attached\n // above continues to fire on commit.\n bindAllDropdowns(root);\n onCleanup?.(() => unbindAllDropdowns(root));\n\n updatePricing();\n\n function updatePricing() {\n const totalCents = rows.reduce((sum, r) => {\n if (!r.selected) return sum;\n const unit = parseCents(r.selected.price.amount);\n return sum + unit * r.qty;\n }, 0);\n const saleCents = computeSale(totalCents, bundle.discountConfig, rows);\n const savingsCents = Math.max(0, totalCents - saleCents);\n\n pricingHandle.update({ totalCents, saleCents, savingsCents, currency });\n if (savingsBarHandle) {\n savingsBarHandle.update({ savingsCents, currency });\n }\n headerHandle.refresh(\n deriveHeaderBadge(bundle, totalCents, saleCents, currency),\n );\n }\n}\n\n// --- State ---\n\nfunction buildRowState(\n bundle: FixedBundleData,\n product: Product,\n productIndex: number,\n): ProductRowState {\n const selectedVariantIds =\n bundle.selectedVariantIds?.[productIndex] ?? null;\n\n // All variants the merchant scoped into the bundle. Sold-out variants are\n // INCLUDED here so the per-option dropdowns render them as disabled\n // options (same UX as unavailable combinations) rather than hiding them\n // from the picker. The initial `selected` still prefers an in-stock\n // variant so the default shown is purchasable.\n const merchantScoped =\n selectedVariantIds && selectedVariantIds.length > 0\n ? product.variants.nodes.filter((v) => selectedVariantIds.includes(v.id))\n : product.variants.nodes;\n\n const firstInStock = merchantScoped.find((v) => v.availableForSale) ?? null;\n const eligibleVariants = merchantScoped;\n const isOos = !firstInStock;\n const selected = firstInStock ?? merchantScoped[0] ?? null;\n const qty = selected ? resolveBundleQty(bundle, product.id, selected.id) : 1;\n\n return { product, eligibleVariants, selected, qty, isOos };\n}\n\n// --- Section renderers ---\n\ninterface HeaderHandle {\n el: HTMLElement;\n /** Update the save-badge text when pricing changes. */\n refresh: (badgeText: string) => void;\n}\n\nfunction renderHeader(\n bundle: FixedBundleData,\n currency: string,\n): HeaderHandle {\n const wc = bundle.widgetConfig;\n const header = el(\"div\", \"lb-bundle-header\");\n const content = el(\"div\", \"lb-bundle-header__content\");\n\n const title = el(\"h3\", \"lb-bundle-title\");\n title.textContent = bundle.title;\n content.appendChild(title);\n\n if (bundle.description) {\n const subtitle = el(\"p\", \"lb-bundle-subtitle\");\n subtitle.textContent = bundle.description;\n content.appendChild(subtitle);\n }\n header.appendChild(content);\n\n const badgeEl = el(\"span\", \"lb-bundle-header__badge\", {\n \"data-header-badge\": \"\",\n });\n header.appendChild(badgeEl);\n\n // Placeholder initial badge — updated by refresh() from updatePricing().\n const initialPricing = computeFixedPricing(\n bundle,\n bundle.productQuantities,\n wc.pricing.showSaveBadge,\n );\n if (initialPricing.headerBadge) {\n badgeEl.textContent = initialPricing.headerBadge;\n } else {\n badgeEl.style.display = \"none\";\n }\n void currency;\n\n return {\n el: header,\n refresh(badgeText) {\n if (!wc.pricing.showSaveBadge) {\n badgeEl.style.display = \"none\";\n return;\n }\n if (badgeText) {\n badgeEl.textContent = badgeText;\n badgeEl.style.display = \"\";\n } else {\n badgeEl.style.display = \"none\";\n }\n },\n };\n}\n\nfunction deriveHeaderBadge(\n bundle: FixedBundleData,\n totalCents: number,\n saleCents: number,\n currency: string,\n): string {\n if (!bundle.widgetConfig.pricing.showSaveBadge) return \"\";\n const savings = totalCents - saleCents;\n if (savings <= 0) return \"\";\n const dc = bundle.discountConfig;\n if (dc.discountType === \"percentage\" && dc.discountValue > 0) {\n return `-${Math.round(dc.discountValue)}%`;\n }\n if (dc.discountType === \"fixed_amount\" && dc.discountValue > 0) {\n return `-${formatCents(Math.round(dc.discountValue * 100), currency)}`;\n }\n return `-${formatCents(savings, currency)}`;\n}\n\ninterface ProductRowHandle {\n el: HTMLElement;\n state: ProductRowState;\n}\n\nfunction renderProductRow(\n state: ProductRowState,\n currency: string,\n qtyFor: (productId: string, variantId: string) => number,\n onVariantChange: () => void,\n): ProductRowHandle {\n const rowEl = el(\n \"div\",\n state.isOos\n ? \"lb-bundle-product-row lb-bundle-product-row--oos\"\n : \"lb-bundle-product-row\",\n {\n \"data-product-id\": state.product.id.replace(/^.*\\//, \"\"),\n ...(state.isOos ? { \"aria-disabled\": \"true\" } : {}),\n },\n );\n\n // Prefer the selected variant's image so the thumb tracks colour-swatch\n // selections; falls back to product hero, then placeholder.\n const thumb = el(\"div\", \"lb-bundle-thumbnail\", { \"data-thumbnail\": \"\" });\n const initialThumbImage =\n state.selected?.image ?? state.product.featuredImage ?? null;\n let thumbImg: HTMLImageElement | null = null;\n if (initialThumbImage) {\n thumbImg = document.createElement(\"img\");\n thumbImg.src = transformImageUrl(initialThumbImage.url, {\n width: THUMB_PX,\n height: THUMB_PX,\n crop: \"center\",\n });\n thumbImg.alt = initialThumbImage.altText ?? state.product.title;\n thumbImg.width = THUMB_PX;\n thumbImg.height = THUMB_PX;\n thumbImg.loading = \"lazy\";\n thumb.appendChild(thumbImg);\n } else {\n thumb.insertAdjacentHTML(\"beforeend\", PLACEHOLDER_THUMB_SVG);\n }\n let qtyBadgeRef: HTMLElement | null = null;\n if (!state.isOos) {\n qtyBadgeRef = el(\"span\", \"lb-bundle-qty-badge\", {\n \"data-qty-badge\": \"\",\n });\n qtyBadgeRef.textContent = String(state.qty);\n thumb.appendChild(qtyBadgeRef);\n }\n rowEl.appendChild(thumb);\n\n // Info column\n const info = el(\"div\", \"lb-bundle-product-info\");\n const name = document.createElement(\"a\");\n name.className = \"lb-bundle-product-name\";\n name.href = `/products/${state.product.handle}`;\n name.textContent = state.product.title;\n info.appendChild(name);\n\n if (state.isOos) {\n const oosLabel = el(\"span\", \"lb-bundle-oos-label\");\n oosLabel.textContent = \"Out of stock\";\n info.appendChild(oosLabel);\n } else if (state.selected) {\n // Price row — updated on variant change.\n const prices = el(\"span\", \"lb-bundle-product-prices\");\n const compare = el(\"span\", \"lb-bundle-product-compare-price\", {\n \"data-product-compare-price\": \"\",\n });\n const priceEl = el(\"span\", \"lb-bundle-product-price\", {\n \"data-product-price\": \"\",\n });\n prices.appendChild(compare);\n prices.appendChild(priceEl);\n info.appendChild(prices);\n\n // Unit price (e.g. \"$0.50/100ml\") — sibling of the price row so it sits\n // on its own line beneath the price. Hidden when the merchant hasn't\n // set unit pricing in the Shopify admin.\n const unitPriceEl = el(\"span\", \"lb-bundle-product-unit-price\", {\n \"data-product-unit-price\": \"\",\n });\n unitPriceEl.setAttribute(\"hidden\", \"\");\n info.appendChild(unitPriceEl);\n\n const applyVariantToRow = (variant: ProductVariant) => {\n const unit = parseCents(variant.price.amount);\n priceEl.textContent = formatCents(unit, currency);\n if (variant.compareAtPrice) {\n const cmp = parseCents(variant.compareAtPrice.amount);\n if (cmp > unit) {\n compare.textContent = formatCents(cmp, currency);\n compare.removeAttribute(\"hidden\");\n } else {\n compare.setAttribute(\"hidden\", \"\");\n }\n } else {\n compare.setAttribute(\"hidden\", \"\");\n }\n const unitText = formatUnitPrice(\n variant.unitPrice,\n variant.unitPriceMeasurement,\n currency,\n );\n if (unitText) {\n unitPriceEl.textContent = unitText;\n unitPriceEl.removeAttribute(\"hidden\");\n } else {\n unitPriceEl.setAttribute(\"hidden\", \"\");\n }\n const nextImage = variant.image ?? state.product.featuredImage;\n if (thumbImg && nextImage) {\n thumbImg.src = transformImageUrl(nextImage.url, {\n width: THUMB_PX,\n height: THUMB_PX,\n crop: \"center\",\n });\n thumbImg.alt = nextImage.altText ?? state.product.title;\n }\n };\n\n applyVariantToRow(state.selected);\n\n // Per-option dropdowns when more than one eligible variant exists —\n // Shopify's recommended approach via product.options_with_values (here\n // derived from variants[].selectedOptions since the Storefront API gives\n // us that). Values that don't combine with the current selection of other\n // options are disabled (Dawn-style availability) so the customer sees\n // what's possible instead of the variant silently jumping combos.\n if (state.eligibleVariants.length > 1) {\n const optionNames: string[] = state.eligibleVariants[0].selectedOptions.map(\n (o) => o.name,\n );\n const productIdTail = state.product.id.replace(/^.*\\//, \"\");\n const optionSelects: HTMLSelectElement[] = [];\n\n const resolveVariant = (values: string[]) =>\n state.eligibleVariants.find(\n (v) =>\n v.selectedOptions.every((o, i) => o.value === values[i]) &&\n v.selectedOptions.length === values.length,\n ) ?? null;\n\n const syncSelectsToVariant = (variant: ProductVariant) => {\n variant.selectedOptions.forEach((o, i) => {\n const sel = optionSelects[i];\n if (sel && sel.value !== o.value) sel.value = o.value;\n });\n };\n\n const isValueAvailable = (\n optionIndex: number,\n value: string,\n selected: string[],\n ): boolean =>\n state.eligibleVariants.some((v) => {\n if (!v.availableForSale) return false;\n if (v.selectedOptions[optionIndex]?.value !== value) return false;\n return v.selectedOptions.every(\n (o, i) => i === optionIndex || o.value === selected[i],\n );\n });\n\n const recomputeDisabled = (selected: string[]) => {\n optionSelects.forEach((sel, i) => {\n Array.from(sel.options).forEach((opt) => {\n opt.disabled = !isValueAvailable(i, opt.value, selected);\n });\n });\n };\n\n const handleChange = () => {\n const values = optionSelects.map((s) => s.value);\n const variant = resolveVariant(values);\n if (!variant) {\n // Disabled combo reached (keyboard nav edge case) — revert selects\n // to the previously selected variant rather than jumping.\n if (state.selected) {\n syncSelectsToVariant(state.selected);\n recomputeDisabled(state.selected.selectedOptions.map((o) => o.value));\n }\n return;\n }\n state.selected = variant;\n state.qty = qtyFor(state.product.id, variant.id);\n if (qtyBadgeRef) qtyBadgeRef.textContent = String(state.qty);\n applyVariantToRow(variant);\n recomputeDisabled(variant.selectedOptions.map((o) => o.value));\n onVariantChange();\n };\n\n const groupsContainer = el(\"div\", \"lb-bundle-variant-option-groups\");\n optionNames.forEach((name, position) => {\n const group = el(\"div\", \"lb-bundle-variant-option-group\");\n\n const label = el(\"span\", \"lb-bundle-variant-option-label\");\n label.textContent = name;\n group.appendChild(label);\n\n const select = document.createElement(\"select\");\n select.className = \"lb-bundle-variant-select\";\n select.setAttribute(\"data-variant-option\", \"\");\n select.setAttribute(\"data-option-position\", String(position + 1));\n select.name = `lb-variant-${productIdTail}-${position + 1}`;\n select.setAttribute(\"aria-label\", name);\n\n const seen = new Set<string>();\n state.eligibleVariants.forEach((v) => {\n const value = v.selectedOptions[position]?.value;\n if (!value || seen.has(value)) return;\n seen.add(value);\n const opt = document.createElement(\"option\");\n opt.value = value;\n opt.textContent = value;\n if (state.selected?.selectedOptions[position]?.value === value) {\n opt.selected = true;\n }\n select.appendChild(opt);\n });\n\n select.addEventListener(\"change\", handleChange);\n optionSelects.push(select);\n group.appendChild(select);\n groupsContainer.appendChild(group);\n });\n info.appendChild(groupsContainer);\n\n if (state.selected) {\n recomputeDisabled(state.selected.selectedOptions.map((o) => o.value));\n }\n } else if (\n state.eligibleVariants.length === 1 &&\n state.product.variants.nodes.length > 1\n ) {\n // Single allowed variant but the product has multiple — render a\n // read-only badge so the customer sees which one's going in the bundle.\n const badge = el(\"span\", \"lb-bundle-variant-badge\");\n badge.textContent = state.eligibleVariants[0].title;\n info.appendChild(badge);\n }\n }\n\n rowEl.appendChild(info);\n return { el: rowEl, state };\n}\n\ninterface PricingHandle {\n el: HTMLElement;\n update: (p: {\n totalCents: number;\n saleCents: number;\n savingsCents: number;\n currency: string;\n }) => void;\n}\n\nfunction renderPricingRow(bundle: FixedBundleData): PricingHandle {\n const row = el(\"div\", \"lb-bundle-pricing\");\n const label = el(\"span\", \"lb-bundle-pricing__label\");\n label.textContent = \"Bundle price\";\n row.appendChild(label);\n\n const prices = el(\"span\", \"lb-bundle-pricing__prices\");\n const compare = el(\"span\", \"lb-bundle-compare-price\", {\n \"data-compare-price\": \"\",\n });\n compare.style.display = \"none\";\n prices.appendChild(compare);\n const sale = el(\"span\", \"lb-bundle-sale-price\", {\n \"data-sale-price\": \"\",\n });\n prices.appendChild(sale);\n row.appendChild(prices);\n\n return {\n el: row,\n update({ totalCents, saleCents, savingsCents, currency }) {\n sale.textContent = formatCents(saleCents, currency);\n if (bundle.widgetConfig.pricing.showCompareAtPrice && savingsCents > 0) {\n compare.textContent = formatCents(totalCents, currency);\n compare.style.display = \"\";\n } else {\n compare.style.display = \"none\";\n }\n },\n };\n}\n\ninterface SavingsBarHandle {\n el: HTMLElement;\n update: (p: { savingsCents: number; currency: string }) => void;\n}\n\nfunction renderSavingsBar(): SavingsBarHandle {\n const bar = el(\"div\", \"lb-bundle-savings-bar\", {\n \"data-savings-bar\": \"\",\n });\n const label = document.createElement(\"span\");\n label.textContent = \"You save\";\n bar.appendChild(label);\n const amount = el(\"span\", \"\", { \"data-savings-amount\": \"\" });\n bar.appendChild(amount);\n return {\n el: bar,\n update({ savingsCents, currency }) {\n if (savingsCents <= 0) {\n bar.style.display = \"none\";\n return;\n }\n bar.style.display = \"\";\n amount.textContent = formatCents(savingsCents, currency);\n },\n };\n}\n\nfunction renderCta(\n bundle: FixedBundleData,\n oosCount: number,\n onClick: () => void,\n): HTMLElement {\n const label =\n oosCount > 0\n ? `${oosCount} item${oosCount === 1 ? \"\" : \"s\"} out of stock`\n : bundle.widgetConfig.cta.ctaText || \"Add to cart\";\n const button = buildCtaButton(label);\n if (oosCount > 0) {\n button.disabled = true;\n } else {\n button.addEventListener(\"click\", () => {\n if (button.disabled) return;\n onClick();\n });\n }\n return button;\n}\n\n// --- Pricing helpers ---\n\nfunction computeSale(\n totalCents: number,\n discount: FixedBundleData[\"discountConfig\"],\n rows: ProductRowState[],\n): number {\n if (discount.discountType === \"percentage\") {\n // Per-unit floor rounding — matches Shopify Discount Function.\n let saleCents = 0;\n for (const r of rows) {\n if (!r.selected) continue;\n const unit = parseCents(r.selected.price.amount);\n const off = Math.floor((unit * discount.discountValue) / 100);\n const perUnit = Math.max(0, unit - off);\n saleCents += perUnit * r.qty;\n }\n return saleCents;\n }\n // fixed_amount: total minus absolute discount (clamped >= 0).\n return Math.max(0, totalCents - Math.round(discount.discountValue * 100));\n}\n\n","/**\n * TypeScript bind helper for the custom variant-picker dropdown inside the\n * `<lime-bundle>` web component. Uses pure algorithms from\n * `@lime-bundles/core/dropdown` and adds the DOM glue for shadow-DOM use.\n *\n * Mirrors the contract of the vanilla theme asset\n * `extensions/bundle-theme/assets/bundle-dropdown.js`. The native `<select>`\n * stays in DOM as the canonical state holder; the custom UI dispatches\n * synthetic `change` events on commit so existing renderer change-handlers\n * work unchanged.\n */\nimport { dropdown } from \"@lime-bundles/core\";\n\nconst { computePosition, emptyTypeAheadState, handleKey, pushTypeAheadChar } =\n dropdown;\n\n// Layout-tuning constants for panel-height calculation. Inlined here\n// rather than imported from core so they stay tweakable without locking\n// numeric values into the public SDK contract.\nconst ITEM_HEIGHT_PX = 32;\nconst LIST_PAD_Y = 8;\nconst MAX_VISIBLE_ITEMS = 8;\ntype DropdownAction = dropdown.DropdownAction;\ntype TypeAheadState = dropdown.TypeAheadState;\n\nexport interface DropdownInstance {\n readonly shell: HTMLElement;\n readonly listbox: HTMLElement;\n readonly select: HTMLSelectElement;\n close(): void;\n destroy(): void;\n}\n\n// Single module-level set of currently open dropdowns. Document-level\n// listeners are attached on the 0→1 transition and detached on 1→0.\n// `composedPath()` lets one listener correctly identify hits across any\n// number of shadow roots — events bubble out of shadow with retargeted\n// `event.target`, but composedPath still surfaces the original element.\n// `bind-dropdown` keeps a module-level list of currently-open instances so\n// document-level listeners are reference-counted (one set of listeners\n// across N dropdowns). Note for test authors: a test that opens a\n// dropdown without calling `inst.destroy()` in cleanup will leak document\n// listeners across cases — call __resetDropdownsForTest() in beforeEach\n// or always tear down via the returned instance.\nconst openInstances: DropdownInstance[] = [];\n\n// Outside-click and ancestor-scroll both close any open dropdown whose\n// shell + listbox aren't in the event path. Same handler for both.\nfunction closeOutsideEvent(event: Event) {\n const path = event.composedPath();\n for (let i = openInstances.length - 1; i >= 0; i--) {\n const inst = openInstances[i];\n if (!path.includes(inst.shell) && !path.includes(inst.listbox)) {\n inst.close();\n }\n }\n}\n\nfunction onDocResize() {\n for (let i = openInstances.length - 1; i >= 0; i--) openInstances[i].close();\n}\n\nlet docListenersAttached = false;\nfunction attachDocumentListeners() {\n if (docListenersAttached) return;\n document.addEventListener(\"pointerdown\", closeOutsideEvent, true);\n window.addEventListener(\"scroll\", closeOutsideEvent, true);\n window.addEventListener(\"resize\", onDocResize);\n docListenersAttached = true;\n}\n\nfunction detachDocumentListeners() {\n if (!docListenersAttached || openInstances.length > 0) return;\n document.removeEventListener(\"pointerdown\", closeOutsideEvent, true);\n window.removeEventListener(\"scroll\", closeOutsideEvent, true);\n window.removeEventListener(\"resize\", onDocResize);\n docListenersAttached = false;\n}\n\n/** Test-only reset hook. Vitest caches module imports across cases; a\n * test that opens a dropdown without destroying it would leak document\n * listeners and stale entries in openInstances into subsequent tests.\n * Call this in beforeEach when a test exercises bindDropdown directly. */\nexport function __resetDropdownsForTest(): void {\n while (openInstances.length > 0) {\n openInstances[openInstances.length - 1].destroy();\n }\n detachDocumentListeners();\n}\n\ntype OptionState = dropdown.TypeAheadOption;\n\nfunction readOptions(select: HTMLSelectElement): OptionState[] {\n const out: OptionState[] = [];\n for (let i = 0; i < select.options.length; i++) {\n const o = select.options[i];\n out.push({ disabled: o.disabled, label: o.textContent || o.value });\n }\n return out;\n}\n\nfunction firstEnabled(opts: OptionState[]): number {\n for (let i = 0; i < opts.length; i++) if (!opts[i].disabled) return i;\n return -1;\n}\n\ntype SelectWithInstance = HTMLSelectElement & {\n __lbDropdownInstance?: DropdownInstance;\n};\n\nconst VARIANT_SELECT_CLASSES = [\n \"lb-bundle-variant-select\",\n \"lb-mix-match__variant-select\",\n] as const;\n\nconst BIND_SELECTOR = VARIANT_SELECT_CLASSES.map(\n (c) => `select.${c}:not(.lb-dropdown-state)`,\n).join(\", \");\n\nexport function bindDropdown(\n select: HTMLSelectElement,\n): DropdownInstance | null {\n const slot = select as SelectWithInstance;\n if (select.classList.contains(\"lb-dropdown-state\")) {\n return slot.__lbDropdownInstance ?? null;\n }\n\n const doc = select.ownerDocument;\n const rootNode = select.getRootNode() as ShadowRoot | Document;\n const labelText = select.getAttribute(\"aria-label\") ?? \"\";\n const idBase = `lb-dd-${Math.random().toString(36).slice(2, 9)}`;\n\n select.classList.add(\"lb-dropdown-state\");\n select.setAttribute(\"aria-hidden\", \"true\");\n select.setAttribute(\"tabindex\", \"-1\");\n\n const shell = doc.createElement(\"div\");\n shell.className = \"lb-dropdown\";\n shell.setAttribute(\"data-lb-dropdown\", \"\");\n\n const trigger = doc.createElement(\"button\");\n trigger.type = \"button\";\n trigger.className = \"lb-dropdown-trigger\";\n trigger.setAttribute(\"role\", \"combobox\");\n trigger.setAttribute(\"aria-haspopup\", \"listbox\");\n trigger.setAttribute(\"aria-expanded\", \"false\");\n const listboxId = `${idBase}-listbox`;\n trigger.setAttribute(\"aria-controls\", listboxId);\n if (labelText) trigger.setAttribute(\"aria-label\", labelText);\n\n const triggerLabel = doc.createElement(\"span\");\n triggerLabel.className = \"lb-dropdown-trigger-value\";\n\n const chevron = doc.createElement(\"span\");\n chevron.className = \"lb-dropdown-chevron\";\n chevron.setAttribute(\"aria-hidden\", \"true\");\n\n trigger.appendChild(triggerLabel);\n trigger.appendChild(chevron);\n\n const listbox = doc.createElement(\"ul\");\n listbox.id = listboxId;\n listbox.className = \"lb-dropdown-listbox\";\n listbox.setAttribute(\"role\", \"listbox\");\n if (labelText) listbox.setAttribute(\"aria-label\", labelText);\n listbox.hidden = true;\n\n shell.appendChild(trigger);\n select.parentNode?.insertBefore(shell, select.nextSibling);\n // The mix-match modal applies translateY for its slide-in animation,\n // which turns position:fixed into relative-to-modal. Portal the\n // listbox up to the overlay (carries per-bundle --lb-* variables AND\n // has no transform of its own) only when the trigger is inside a\n // modal. For the main widget, leave the listbox inside the shell —\n // there's no transformed ancestor to escape.\n const modalOverlay = select.closest(\"[data-modal-overlay]\");\n if (modalOverlay) {\n modalOverlay.appendChild(listbox);\n listbox.setAttribute(\"data-lb-dropdown-portal\", \"\");\n } else {\n shell.appendChild(listbox);\n }\n\n let isOpen = false;\n let activeIndex = -1;\n let typeAhead: TypeAheadState = emptyTypeAheadState();\n let optionEls: HTMLLIElement[] = [];\n let instance: DropdownInstance; // eslint-disable-line prefer-const\n\n function syncFromSelect() {\n const opts = readOptions(select);\n const idx = select.selectedIndex;\n triggerLabel.textContent = idx >= 0 && opts[idx] ? opts[idx].label : \"\";\n\n while (listbox.firstChild) listbox.removeChild(listbox.firstChild);\n optionEls = [];\n\n for (let i = 0; i < opts.length; i++) {\n const li = doc.createElement(\"li\");\n li.id = `${idBase}-opt-${i}`;\n li.className = \"lb-dropdown-option\";\n li.setAttribute(\"role\", \"option\");\n li.setAttribute(\"aria-selected\", i === idx ? \"true\" : \"false\");\n if (opts[i].disabled) li.setAttribute(\"aria-disabled\", \"true\");\n li.setAttribute(\"data-value\", select.options[i].value);\n li.setAttribute(\"data-index\", String(i));\n li.textContent = opts[i].label;\n listbox.appendChild(li);\n optionEls.push(li);\n }\n }\n\n function setActive(newIndex: number) {\n if (activeIndex >= 0 && optionEls[activeIndex]) {\n optionEls[activeIndex].classList.remove(\"is-active\");\n }\n activeIndex = newIndex;\n if (newIndex >= 0 && optionEls[newIndex]) {\n const li = optionEls[newIndex];\n li.classList.add(\"is-active\");\n trigger.setAttribute(\"aria-activedescendant\", li.id);\n // Scroll within the listbox only — Element.scrollIntoView falls\n // through to the document scroll when the listbox itself isn't\n // overflowing, which can yank the page when the active option's\n // viewport position differs from its offsetTop-relative position.\n const liTop = li.offsetTop;\n const liBottom = liTop + li.offsetHeight;\n const visTop = listbox.scrollTop;\n const visBottom = visTop + listbox.clientHeight;\n if (liTop < visTop) {\n listbox.scrollTop = liTop;\n } else if (liBottom > visBottom) {\n listbox.scrollTop = liBottom - listbox.clientHeight;\n }\n } else {\n trigger.setAttribute(\"aria-activedescendant\", \"\");\n }\n }\n\n function position(): boolean {\n const rect = trigger.getBoundingClientRect();\n if (rect.width === 0) return false;\n const visibleCount = Math.min(optionEls.length || 1, MAX_VISIBLE_ITEMS);\n const desiredHeight = visibleCount * ITEM_HEIGHT_PX + LIST_PAD_Y;\n const result = computePosition({\n trigger: {\n top: rect.top,\n bottom: rect.bottom,\n left: rect.left,\n width: rect.width,\n },\n viewportHeight: window.innerHeight,\n desiredHeight,\n });\n listbox.setAttribute(\"data-placement\", result.placement);\n listbox.style.maxHeight = `${result.maxHeight}px`;\n // In-shell (main widget) case: CSS [data-placement] selectors\n // anchor the listbox against the position:relative shell. Only the\n // portaled (modal) case needs inline viewport coords.\n if (listbox.hasAttribute(\"data-lb-dropdown-portal\")) {\n listbox.style.top = `${result.offsetTop}px`;\n listbox.style.left = `${result.offsetLeft}px`;\n listbox.style.width = `${result.width}px`;\n }\n return true;\n }\n\n function open() {\n if (isOpen) return;\n // Close any other open dropdown first — single-open semantics.\n for (let i = openInstances.length - 1; i >= 0; i--) {\n if (openInstances[i] !== instance) openInstances[i].close();\n }\n isOpen = true;\n listbox.hidden = false;\n trigger.setAttribute(\"aria-expanded\", \"true\");\n if (!position()) {\n requestAnimationFrame(() => position());\n }\n const opts = readOptions(select);\n const selIdx = select.selectedIndex;\n if (selIdx >= 0 && opts[selIdx] && !opts[selIdx].disabled) {\n setActive(selIdx);\n } else {\n setActive(firstEnabled(opts));\n }\n openInstances.push(instance);\n if (openInstances.length === 1) attachDocumentListeners();\n }\n\n function close(restoreFocus: boolean) {\n if (!isOpen) return;\n isOpen = false;\n listbox.hidden = true;\n trigger.setAttribute(\"aria-expanded\", \"false\");\n trigger.setAttribute(\"aria-activedescendant\", \"\");\n if (activeIndex >= 0 && optionEls[activeIndex]) {\n optionEls[activeIndex].classList.remove(\"is-active\");\n }\n activeIndex = -1;\n const idx = openInstances.indexOf(instance);\n if (idx >= 0) openInstances.splice(idx, 1);\n if (openInstances.length === 0) detachDocumentListeners();\n if (restoreFocus) trigger.focus();\n }\n\n function commit(index: number) {\n const opt = select.options[index];\n if (!opt || opt.disabled) return;\n if (select.value !== opt.value) {\n select.value = opt.value;\n const event = new Event(\"change\", { bubbles: true });\n select.dispatchEvent(event);\n }\n syncFromSelect();\n close(true);\n }\n\n function applyAction(action: DropdownAction) {\n switch (action.type) {\n case \"open\":\n open();\n if (action.activeIndex >= 0) setActive(action.activeIndex);\n return;\n case \"close\":\n close(action.restoreFocus);\n return;\n case \"move-active\":\n setActive(action.activeIndex);\n return;\n case \"commit\":\n commit(action.index);\n return;\n case \"type-ahead\": {\n const opts = readOptions(select);\n const result = pushTypeAheadChar(\n typeAhead,\n action.char,\n Date.now(),\n opts,\n );\n typeAhead = result.newState;\n if (result.matchedIndex !== null) {\n if (!isOpen) open();\n setActive(result.matchedIndex);\n }\n return;\n }\n case \"passthrough\":\n return;\n default: {\n const _exhaustive: never = action;\n void _exhaustive;\n }\n }\n }\n\n function onKeydown(event: KeyboardEvent) {\n const opts = readOptions(select);\n const action = handleKey(\n {\n key: event.key,\n ctrlKey: event.ctrlKey,\n metaKey: event.metaKey,\n altKey: event.altKey,\n shiftKey: event.shiftKey,\n },\n {\n isOpen,\n activeIndex,\n selectedIndex: select.selectedIndex,\n options: opts,\n },\n );\n if (action.preventDefault) event.preventDefault();\n applyAction(action);\n }\n\n function onTriggerClick(event: MouseEvent) {\n event.preventDefault();\n if (isOpen) close(false);\n else open();\n }\n\n function onListboxClick(event: MouseEvent) {\n let target = event.target as HTMLElement | null;\n while (target && target !== listbox) {\n if (target.classList?.contains(\"lb-dropdown-option\")) {\n const idx = parseInt(target.getAttribute(\"data-index\") ?? \"\", 10);\n if (!Number.isNaN(idx)) {\n commit(idx);\n return;\n }\n }\n target = target.parentElement;\n }\n }\n\n function onListboxMousemove(event: MouseEvent) {\n let target = event.target as HTMLElement | null;\n while (target && target !== listbox) {\n if (target.classList?.contains(\"lb-dropdown-option\")) {\n if (target.getAttribute(\"aria-disabled\") === \"true\") return;\n const idx = parseInt(target.getAttribute(\"data-index\") ?? \"\", 10);\n if (!Number.isNaN(idx) && idx !== activeIndex) setActive(idx);\n return;\n }\n target = target.parentElement;\n }\n }\n\n function onShellFocusout() {\n // In shadow DOM, document.activeElement returns the shadow host;\n // rootNode.activeElement returns the actual focused element inside\n // the shadow tree. Falls back to document.activeElement in the\n // light-DOM (non-shadow) case.\n setTimeout(() => {\n if (!isOpen) return;\n const active = rootNode.activeElement ?? doc.activeElement;\n if (!shell.contains(active)) close(false);\n }, 0);\n }\n\n function onSelectChange() {\n syncFromSelect();\n }\n\n const observer = new MutationObserver(() => {\n syncFromSelect();\n });\n observer.observe(select, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: [\"disabled\", \"value\", \"selected\"],\n });\n\n // Prevent mousedown on a non-focusable <li> from blurring the trigger.\n // Without this, focusout fires on the shell and queues a setTimeout(0)\n // that closes the dropdown — and on desktop the close runs before the\n // synthesized click event, so onListboxClick never sees the option and\n // commit never runs. Mobile is unaffected because touchstart doesn't\n // shift focus.\n const onListboxMousedown = (event: MouseEvent) => event.preventDefault();\n\n trigger.addEventListener(\"click\", onTriggerClick);\n trigger.addEventListener(\"keydown\", onKeydown);\n shell.addEventListener(\"focusout\", onShellFocusout);\n listbox.addEventListener(\"mousedown\", onListboxMousedown);\n listbox.addEventListener(\"click\", onListboxClick);\n listbox.addEventListener(\"mousemove\", onListboxMousemove);\n select.addEventListener(\"change\", onSelectChange);\n\n function destroy() {\n if (isOpen) close(false);\n observer.disconnect();\n trigger.removeEventListener(\"click\", onTriggerClick);\n trigger.removeEventListener(\"keydown\", onKeydown);\n shell.removeEventListener(\"focusout\", onShellFocusout);\n listbox.removeEventListener(\"mousedown\", onListboxMousedown);\n listbox.removeEventListener(\"click\", onListboxClick);\n listbox.removeEventListener(\"mousemove\", onListboxMousemove);\n select.removeEventListener(\"change\", onSelectChange);\n if (shell.parentNode) shell.parentNode.removeChild(shell);\n if (listbox.parentNode) listbox.parentNode.removeChild(listbox);\n select.classList.remove(\"lb-dropdown-state\");\n select.removeAttribute(\"aria-hidden\");\n select.removeAttribute(\"tabindex\");\n delete slot.__lbDropdownInstance;\n }\n\n instance = {\n shell,\n listbox,\n select,\n close: () => close(false),\n destroy,\n };\n slot.__lbDropdownInstance = instance;\n\n syncFromSelect();\n return instance;\n}\n\nexport function bindAllDropdowns(root: ParentNode): DropdownInstance[] {\n const selects = root.querySelectorAll(BIND_SELECTOR);\n const instances: DropdownInstance[] = [];\n selects.forEach((sel) => {\n const inst = bindDropdown(sel as HTMLSelectElement);\n if (inst) instances.push(inst);\n });\n return instances;\n}\n\nexport function unbindAllDropdowns(root: ParentNode): void {\n const bound = root.querySelectorAll(\"select.lb-dropdown-state\");\n bound.forEach((sel) => {\n const inst = (sel as SelectWithInstance).__lbDropdownInstance;\n if (inst) inst.destroy();\n });\n}\n","/**\n * Widget-package re-exports of the pricing helpers. The canonical\n * implementation lives in `@lime-bundles/core`; keeping the widget\n * package importing from core avoids duplicating the integer-cent\n * arithmetic across packages and lets DIY React devs reach the same\n * primitives the web component uses.\n */\nexport {\n parseCents,\n formatCents,\n percentageDiscountUnit,\n computeFixedPricing,\n computeBundleSaleCents,\n formatUnitPrice,\n type PricingRow,\n type FixedBundlePricing,\n} from \"@lime-bundles/core\";\n","// Matches the DOM/class structure of the Liquid `bundle-widget.liquid`'s\n// `.lb-bundle-countdown` bar so the shared CSS themes both renderers.\nimport { formatCountdown } from \"@lime-bundles/core\";\n\nexport interface CountdownHandle {\n /** DOM element to append to the widget. */\n el: HTMLElement;\n /** Call on widget disconnect to clear the tick interval. */\n stop: () => void;\n}\n\nexport function renderCountdown(endsAtIso: string): CountdownHandle | null {\n const parsed = parseIso(endsAtIso);\n if (parsed === null) return null;\n // Already expired — caller doesn't append anything.\n if (parsed <= Date.now()) return null;\n const target: number = parsed;\n\n const wrap = document.createElement(\"div\");\n wrap.className = \"lb-bundle-countdown\";\n wrap.setAttribute(\"data-countdown\", \"\");\n\n const labelWrap = document.createElement(\"div\");\n labelWrap.className = \"lb-bundle-countdown__label\";\n const labelText = document.createElement(\"span\");\n labelText.textContent = \"Ends in\";\n labelWrap.appendChild(labelText);\n wrap.appendChild(labelWrap);\n\n const timer = document.createElement(\"span\");\n timer.className = \"lb-bundle-countdown__timer\";\n timer.setAttribute(\"data-countdown-timer\", \"\");\n wrap.appendChild(timer);\n\n let intervalId: ReturnType<typeof setInterval> | null = null;\n\n function tick() {\n const msLeft = target - Date.now();\n if (msLeft <= 0) {\n wrap.style.display = \"none\";\n stop();\n return;\n }\n timer.textContent = formatCountdown(msLeft);\n }\n\n function stop() {\n if (intervalId !== null) {\n clearInterval(intervalId);\n intervalId = null;\n }\n }\n\n tick();\n intervalId = setInterval(tick, 1000);\n\n return { el: wrap, stop };\n}\n\nfunction parseIso(iso: string): number | null {\n const t = Date.parse(iso);\n return Number.isFinite(t) ? t : null;\n}\n","/**\n * Builds the shared `.lb-bundle-cta` button structure used by all three\n * widget renderers (fixed, volume, mix_match).\n *\n * The button has two children in a 1×1 CSS grid (see\n * packages/widget/src/styles/bundle-css.ts):\n *\n * <button class=\"lb-bundle-cta\" data-add-bundle>\n * <span class=\"lb-cta-label\" data-cta-label>{label}</span>\n * <span class=\"lb-cta-spinner\" data-cta-spinner aria-hidden=\"true\">…</span>\n * </button>\n *\n * The spinner is visible only when the button has `data-loading=\"true\"`.\n * The SDK itself doesn't toggle that attribute — merchants opt into the\n * loading-state affordance by setting it during their async cart mutation:\n *\n * el.querySelector('[data-add-bundle]').setAttribute('data-loading', 'true');\n * try { await cart.linesAdd(…); }\n * finally { el.querySelector('[data-add-bundle]').removeAttribute('data-loading'); }\n *\n * Keeping the SDK neutral on timing (BYO-cart contract) means no additive\n * Promise API surface needs to change.\n */\nexport function buildCtaButton(label: string): HTMLButtonElement {\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.className = \"lb-bundle-cta\";\n button.setAttribute(\"data-add-bundle\", \"\");\n\n const labelSpan = document.createElement(\"span\");\n labelSpan.className = \"lb-cta-label\";\n labelSpan.setAttribute(\"data-cta-label\", \"\");\n labelSpan.textContent = label;\n button.appendChild(labelSpan);\n\n const spinnerSpan = document.createElement(\"span\");\n spinnerSpan.className = \"lb-cta-spinner\";\n spinnerSpan.setAttribute(\"data-cta-spinner\", \"\");\n spinnerSpan.setAttribute(\"aria-hidden\", \"true\");\n // Same markup as snippets/lb-cta-spinner.liquid — keep in sync.\n spinnerSpan.innerHTML =\n '<svg viewBox=\"0 0 24 24\" width=\"20\" height=\"20\" fill=\"none\" ' +\n 'stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\">' +\n '<path d=\"M12 2a10 10 0 0 1 10 10\" /></svg>';\n button.appendChild(spinnerSpan);\n\n return button;\n}\n\n/**\n * Updates the visible label text on a button built by `buildCtaButton`.\n * Targets the `[data-cta-label]` span so the sibling spinner isn't\n * clobbered — a plain `button.textContent = \"...\"` would destroy every\n * child, including the spinner span.\n *\n * If `[data-cta-label]` isn't found, this function is a no-op rather than\n * falling back to `button.textContent`. Buttons that omit the label span\n * either (a) also omit the spinner (nothing to destroy — but also nothing\n * the caller needs to worry about; returning early is fine) or (b) were\n * mutated in-flight by an adapter that should have kept the label. Either\n * way, overwriting `button.textContent` is strictly harmful: it either\n * silently nukes a spinner we're trying to preserve, or replaces whatever\n * structure the adapter built. Callers needing a plain-button text update\n * should write `button.textContent = ...` themselves.\n */\nexport function setCtaLabel(button: HTMLButtonElement, text: string): void {\n const label = button.querySelector<HTMLElement>(\"[data-cta-label]\");\n if (label) {\n label.textContent = text;\n }\n}\n","/**\n * Shared DOM helper for the widget renderers. Keep this internal to\n * `packages/widget/src/renderers/`; it intentionally isn't re-exported\n * from the package's public entrypoint.\n */\nexport function el(\n tag: string,\n className: string,\n attrs: Record<string, string> = {},\n): HTMLElement {\n const node = document.createElement(tag);\n if (className) node.className = className;\n for (const [k, v] of Object.entries(attrs)) {\n node.setAttribute(k, v);\n }\n return node;\n}\n","/**\n * Re-export of `transformImageUrl` from core. See\n * `packages/core/src/utils/image.ts` for the implementation.\n */\nexport {\n transformImageUrl,\n THUMB_PX,\n type ImageTransform,\n} from \"@lime-bundles/core\";\n","/**\n * DOM renderer for mix-and-match bundles.\n *\n * Mirrors `extensions/bundle-theme/snippets/lb-mix-match.liquid` and its\n * associated picker-modal JS. Merchant flow:\n *\n * 1. Widget renders `minQuantity` empty slots with a live progress bar.\n * 2. Clicking a slot opens the picker modal with the eligible products.\n * 3. Inside the modal, each product has a quantity stepper and a count\n * bubble. Adjusting stepper values adds or removes slots.\n * 4. Progress bar, slot contents, pricing, and CTA update live.\n * 5. When `minQuantity` is reached, the CTA unlocks. Customer clicks,\n * cart lines dispatch.\n *\n * Class names match the Liquid template one-for-one so the ported\n * bundle-mix-match.css styles this DOM without changes.\n */\nimport type {\n CartLineInput,\n MixMatchBundleData,\n Product,\n ProductVariant,\n} from \"@lime-bundles/core\";\nimport { resolveBundleQty } from \"@lime-bundles/core\";\nimport {\n computeBundleSaleCents,\n formatCents,\n formatUnitPrice,\n parseCents,\n} from \"./pricing\";\nimport { bindAllDropdowns, unbindAllDropdowns } from \"../dropdown/bind-dropdown\";\nimport { renderCountdown } from \"./countdown\";\nimport { buildCtaButton, setCtaLabel } from \"./cta-button\";\nimport { el } from \"./dom\";\nimport { THUMB_PX, transformImageUrl } from \"./image\";\n\ninterface EligibleProduct {\n product: Product;\n variants: ProductVariant[];\n firstAvailableVariant: ProductVariant | null;\n isOos: boolean;\n}\n\ninterface Selection {\n productId: string;\n productTitle: string;\n variantId: string;\n variantTitle: string;\n imageUrl: string | null;\n priceCents: number;\n compareCents: number | null;\n /** Pre-formatted unit price (\"$0.50/100ml\") for the filled-slot view. */\n unitPriceLabel: string | null;\n /** Merchant-configured per-slot quantity (variantQuantities ?? productQuantities ?? 1). */\n quantity: number;\n}\n\nconst PLACEHOLDER_THUMB_SVG = `\n<svg class=\"lb-bundle-placeholder-icon\" viewBox=\"0 0 28 28\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" focusable=\"false\">\n <rect x=\"4\" y=\"4\" width=\"20\" height=\"20\" rx=\"3\"></rect>\n <line x1=\"4\" y1=\"20\" x2=\"24\" y2=\"20\"></line>\n <circle cx=\"10\" cy=\"12\" r=\"2\"></circle>\n</svg>`;\n\nconst PLUS_ICON_SVG = `\n<svg width=\"18\" height=\"18\" viewBox=\"0 0 18 18\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <line x1=\"9\" y1=\"3\" x2=\"9\" y2=\"15\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n <line x1=\"3\" y1=\"9\" x2=\"15\" y2=\"9\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n</svg>`;\n\nconst CLOSE_ICON_SVG = `\n<svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <line x1=\"5\" y1=\"5\" x2=\"15\" y2=\"15\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n <line x1=\"15\" y1=\"5\" x2=\"5\" y2=\"15\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n</svg>`;\n\nconst SEARCH_CLEAR_ICON_SVG = `\n<svg width=\"16\" height=\"16\" viewBox=\"0 0 20 20\" fill=\"currentColor\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path d=\"M14.348 5.652a.5.5 0 0 0-.707 0L10 9.293 6.36 5.652a.5.5 0 1 0-.708.707L9.293 10l-3.641 3.641a.5.5 0 0 0 .708.707L10 10.707l3.641 3.641a.5.5 0 0 0 .707-.707L10.707 10l3.641-3.641a.5.5 0 0 0 0-.707z\"/>\n</svg>`;\n\nexport function renderMixMatchBundle(\n container: HTMLElement,\n bundle: MixMatchBundleData,\n onAddToCart: (lines: CartLineInput[]) => void,\n onCleanup?: (fn: () => void) => void,\n) {\n const wc = bundle.widgetConfig;\n const currency =\n bundle.products[0]?.priceRange.minVariantPrice.currencyCode ?? \"USD\";\n const requiredQty = bundle.minQuantity ?? 1;\n const maxQty = bundle.maxQuantity ?? requiredQty;\n\n // Build eligible-products list, honoring outOfStockBehavior.\n const eligible = buildEligibleProducts(bundle, wc.outOfStockBehavior);\n const inStockCount = eligible.filter((e) => !e.isOos).length;\n\n // Bundle visibility guard: if we can't possibly satisfy minQuantity from\n // in-stock products, don't render the widget at all. Matches Liquid.\n if (inStockCount < requiredQty) return;\n\n const selections: Selection[] = [];\n const root = el(\"div\", \"lb-mix-match\", {\n \"data-required-quantity\": String(requiredQty),\n \"data-max-quantity\": String(maxQty),\n });\n\n // --- Header ---\n const header = renderHeader(bundle);\n root.appendChild(header);\n\n // --- Countdown ---\n if (wc.countdown.showCountdown && bundle.endsAt) {\n const countdown = renderCountdown(bundle.endsAt);\n if (countdown) {\n root.appendChild(countdown.el);\n onCleanup?.(countdown.stop);\n }\n }\n\n // --- Progress bar ---\n const progress = renderProgress(requiredQty);\n root.appendChild(progress.el);\n\n // --- Slots ---\n const slotsContainer = el(\"div\", \"lb-mix-match__slots\", {\n \"data-selection-slots\": \"\",\n });\n root.appendChild(slotsContainer);\n\n root.appendChild(el(\"div\", \"lb-bundle-divider\"));\n\n // --- Pricing (hidden until first selection) ---\n const pricingSection = renderPricingSection(wc.pricing.showCompareAtPrice);\n root.appendChild(pricingSection.el);\n\n const savingsBar = wc.savingsBar.visible\n ? renderSavingsBar()\n : null;\n if (savingsBar) root.appendChild(savingsBar.el);\n\n // --- Price placeholder (shown until first selection) ---\n const placeholder = el(\"div\", \"lb-mix-match__price-placeholder\", {\n \"data-price-placeholder\": \"\",\n });\n const placeholderText = el(\n \"span\",\n \"lb-mix-match__price-placeholder-text\",\n );\n placeholderText.textContent = `Select ${requiredQty} items to see price`;\n placeholder.appendChild(placeholderText);\n root.appendChild(placeholder);\n\n // --- Modal overlay ---\n const modal = renderModal(bundle, eligible, currency, {\n showSearch: wc.showSearch,\n onAdd: (product, variant) => addSelection(product, variant),\n onRemove: (productId, variantId) => removeSelection(productId, variantId),\n isOverMax: () => selections.length >= maxQty,\n });\n root.appendChild(modal.el);\n\n // --- CTA ---\n const cta = buildCtaButton(`Select ${requiredQty} items to unlock`);\n cta.disabled = true;\n cta.addEventListener(\"click\", () => {\n if (cta.disabled) return;\n const lines: CartLineInput[] = selections.map((s) => ({\n merchandiseId: s.variantId,\n quantity: s.quantity,\n attributes: [\n { key: \"_lime_bundle_gid\", value: bundle.id },\n { key: \"_lime_bundle_type\", value: bundle.bundleType },\n ],\n }));\n onAddToCart(lines);\n });\n root.appendChild(cta);\n\n root.appendChild(\n el(\"p\", \"lb-bundle-error\", { \"data-error\": \"\", \"aria-live\": \"polite\" }),\n );\n root.appendChild(\n el(\"span\", \"lb-visually-hidden\", {\n \"data-status\": \"\",\n \"aria-live\": \"polite\",\n }),\n );\n\n container.appendChild(root);\n\n // Custom dropdown teardown — bind happens lazily inside buildRows().\n onCleanup?.(() => unbindAllDropdowns(root));\n\n // Seed slot 0 with the first eligible in-stock product so the widget\n // opens \"live\" — matches the Liquid theme block's default behaviour.\n // Customers can swap or remove via the picker as usual.\n const firstEligible = eligible.find((ep) => !ep.isOos);\n const firstVariant =\n firstEligible?.firstAvailableVariant ?? firstEligible?.variants[0];\n if (firstEligible && firstVariant) {\n selections.push({\n productId: firstEligible.product.id,\n productTitle: firstEligible.product.title,\n variantId: firstVariant.id,\n variantTitle: firstVariant.title,\n imageUrl:\n firstVariant.image?.url ??\n firstEligible.product.featuredImage?.url ??\n null,\n priceCents: parseCents(firstVariant.price.amount),\n compareCents: firstVariant.compareAtPrice\n ? parseCents(firstVariant.compareAtPrice.amount)\n : null,\n unitPriceLabel: formatUnitPrice(\n firstVariant.unitPrice,\n firstVariant.unitPriceMeasurement,\n currency,\n ),\n quantity: resolveBundleQty(bundle, firstEligible.product.id, firstVariant.id),\n });\n }\n\n // Initial render — afterMutation() handles slots, progress, pricing,\n // savings bar, placeholder, modal refreshCounts, and CTA in one place.\n // Safe to call here: modal.refreshCounts is a no-op while the picker\n // is closed (productRows is built lazily on first open).\n afterMutation();\n\n // --- Mutation helpers (closures over local state) ---\n\n function addSelection(product: Product, variant: ProductVariant) {\n if (selections.length >= maxQty) return;\n selections.push({\n productId: product.id,\n productTitle: product.title,\n variantId: variant.id,\n variantTitle: variant.title,\n imageUrl: variant.image?.url ?? product.featuredImage?.url ?? null,\n priceCents: parseCents(variant.price.amount),\n compareCents: variant.compareAtPrice\n ? parseCents(variant.compareAtPrice.amount)\n : null,\n unitPriceLabel: formatUnitPrice(\n variant.unitPrice,\n variant.unitPriceMeasurement,\n currency,\n ),\n quantity: resolveBundleQty(bundle, product.id, variant.id),\n });\n afterMutation();\n }\n\n function removeSelection(productId: string, variantId: string) {\n const idx = selections.findIndex(\n (s) => s.productId === productId && s.variantId === variantId,\n );\n if (idx === -1) return;\n selections.splice(idx, 1);\n afterMutation();\n }\n\n function removeSlotAt(index: number) {\n if (index < 0 || index >= selections.length) return;\n selections.splice(index, 1);\n afterMutation();\n }\n\n function afterMutation() {\n renderSlots();\n progress.update(selections.length);\n pricingSection.update(selections, bundle, currency);\n if (savingsBar) savingsBar.update(selections, bundle, currency);\n placeholder.style.display = selections.length === 0 ? \"\" : \"none\";\n modal.refreshCounts();\n updateCta();\n }\n\n function renderSlots() {\n slotsContainer.innerHTML = \"\";\n const totalSlots = Math.max(requiredQty, selections.length);\n for (let i = 0; i < totalSlots; i++) {\n const selection = selections[i];\n if (selection) {\n slotsContainer.appendChild(\n renderFilledSlot(selection, i, currency, () => removeSlotAt(i)),\n );\n } else {\n slotsContainer.appendChild(\n renderEmptySlot(i, () => modal.open()),\n );\n }\n }\n }\n\n function updateCta() {\n // Target the label span, not the button itself — replacing the button's\n // textContent would destroy the sibling spinner span built by\n // buildCtaButton. See packages/widget/src/renderers/cta-button.ts.\n const count = selections.length;\n if (count < requiredQty) {\n cta.disabled = true;\n setCtaLabel(cta, `Select ${requiredQty - count} more to unlock`);\n } else {\n cta.disabled = false;\n setCtaLabel(cta, wc.cta.ctaText || \"Add to cart\");\n }\n }\n}\n\n// --- Section renderers ---\n\nfunction renderHeader(bundle: MixMatchBundleData): HTMLElement {\n const wc = bundle.widgetConfig;\n const header = el(\"div\", \"lb-bundle-header\");\n const content = el(\"div\", \"lb-bundle-header__content\");\n const title = el(\"h3\", \"lb-bundle-title\");\n title.textContent = bundle.title;\n content.appendChild(title);\n header.appendChild(content);\n\n if (wc.pricing.showSaveBadge) {\n const { discountType, discountValue } = bundle.discountConfig;\n let label: string | null = null;\n if (discountType === \"percentage\" && discountValue > 0) {\n label = `-${Math.round(discountValue)}%`;\n } else if (discountType === \"fixed_amount\" && discountValue > 0) {\n label = `-${formatCents(\n Math.round(discountValue * 100),\n bundle.products[0]?.priceRange.minVariantPrice.currencyCode ?? \"USD\",\n )}`;\n }\n if (label) {\n const badge = el(\"span\", \"lb-bundle-header__badge\");\n badge.textContent = label;\n header.appendChild(badge);\n }\n }\n return header;\n}\n\nfunction renderProgress(requiredQty: number) {\n const wrap = el(\"div\", \"lb-mix-match__progress\");\n const labels = el(\"div\", \"lb-mix-match__progress-labels\");\n const count = el(\"span\", \"lb-mix-match__progress-count\", {\n \"data-progress-count\": \"\",\n });\n count.textContent = `0 of ${requiredQty} selected`;\n labels.appendChild(count);\n const remaining = el(\"span\", \"lb-mix-match__progress-remaining\", {\n \"data-progress-remaining\": \"\",\n });\n remaining.textContent = `${requiredQty} more to go`;\n labels.appendChild(remaining);\n wrap.appendChild(labels);\n\n const track = el(\"div\", \"lb-mix-match__progress-track\", {\n role: \"progressbar\",\n \"aria-valuenow\": \"0\",\n \"aria-valuemin\": \"0\",\n \"aria-valuemax\": String(requiredQty),\n });\n const fill = el(\"div\", \"lb-mix-match__progress-fill\", {\n \"data-progress-fill\": \"\",\n });\n fill.style.width = \"0%\";\n track.appendChild(fill);\n wrap.appendChild(track);\n\n function update(selected: number) {\n const pct = Math.min(100, (selected / requiredQty) * 100);\n count.textContent = `${selected} of ${requiredQty} selected`;\n if (selected >= requiredQty) {\n remaining.textContent = \"Complete\";\n } else {\n remaining.textContent = `${requiredQty - selected} more to go`;\n }\n fill.style.width = `${pct}%`;\n track.setAttribute(\"aria-valuenow\", String(Math.min(selected, requiredQty)));\n }\n\n return { el: wrap, update };\n}\n\nfunction renderEmptySlot(index: number, onClick: () => void): HTMLElement {\n const slot = el(\n \"div\",\n \"lb-bundle-product-row lb-mix-match__slot lb-mix-match__slot--empty\",\n {\n \"data-slot\": String(index + 1),\n tabindex: \"0\",\n role: \"button\",\n \"aria-label\": \"Add a product to the bundle\",\n },\n );\n const thumb = el(\"div\", \"lb-mix-match__empty-thumb\");\n thumb.innerHTML = PLUS_ICON_SVG;\n slot.appendChild(thumb);\n const text = el(\"span\", \"lb-mix-match__empty-text\");\n text.textContent = \"Choose an item\";\n slot.appendChild(text);\n slot.addEventListener(\"click\", onClick);\n slot.addEventListener(\"keydown\", (e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n onClick();\n }\n });\n return slot;\n}\n\nfunction renderFilledSlot(\n selection: Selection,\n index: number,\n currency: string,\n onRemove: () => void,\n): HTMLElement {\n const slot = el(\n \"div\",\n \"lb-bundle-product-row lb-mix-match__slot lb-mix-match__slot--filled\",\n { \"data-slot\": String(index + 1) },\n );\n const thumb = el(\"div\", \"lb-bundle-thumbnail\", { \"data-thumbnail\": \"\" });\n if (selection.imageUrl) {\n const img = document.createElement(\"img\");\n img.src = transformImageUrl(selection.imageUrl, {\n width: THUMB_PX,\n height: THUMB_PX,\n crop: \"center\",\n });\n img.alt = selection.productTitle;\n img.width = THUMB_PX;\n img.height = THUMB_PX;\n img.loading = \"lazy\";\n thumb.appendChild(img);\n } else {\n thumb.insertAdjacentHTML(\"beforeend\", PLACEHOLDER_THUMB_SVG);\n }\n const qtyBadge = el(\"span\", \"lb-bundle-qty-badge\");\n qtyBadge.textContent = String(selection.quantity);\n thumb.appendChild(qtyBadge);\n slot.appendChild(thumb);\n\n const info = el(\"div\", \"lb-mix-match__filled-info\");\n const title = el(\"span\", \"lb-mix-match__filled-title\");\n title.textContent = selection.productTitle;\n info.appendChild(title);\n if (selection.variantTitle && selection.variantTitle !== \"Default Title\") {\n const variant = el(\"span\", \"lb-mix-match__filled-variant\");\n variant.textContent = selection.variantTitle;\n info.appendChild(variant);\n }\n const linePrice = selection.priceCents * selection.quantity;\n const lineCompare =\n selection.compareCents !== null\n ? selection.compareCents * selection.quantity\n : null;\n const priceWrap = el(\"span\", \"lb-mix-match__filled-price\");\n if (lineCompare !== null && lineCompare > linePrice) {\n const compare = el(\"span\", \"lb-mix-match__filled-compare\");\n compare.textContent = formatCents(lineCompare, currency);\n priceWrap.appendChild(compare);\n }\n const priceEl = document.createElement(\"span\");\n priceEl.textContent = formatCents(linePrice, currency);\n priceWrap.appendChild(priceEl);\n info.appendChild(priceWrap);\n if (selection.unitPriceLabel) {\n const unitPrice = el(\"span\", \"lb-bundle-product-unit-price\");\n unitPrice.textContent = selection.unitPriceLabel;\n info.appendChild(unitPrice);\n }\n slot.appendChild(info);\n\n const remove = document.createElement(\"button\");\n remove.type = \"button\";\n remove.className = \"lb-mix-match__slot-remove\";\n remove.setAttribute(\"aria-label\", `Remove ${selection.productTitle}`);\n remove.innerHTML = CLOSE_ICON_SVG;\n remove.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n onRemove();\n });\n slot.appendChild(remove);\n return slot;\n}\n\nfunction renderPricingSection(showCompareAtPrice: boolean) {\n const wrap = el(\"div\", \"lb-bundle-pricing\", { \"data-pricing-section\": \"\" });\n wrap.style.display = \"none\";\n const label = el(\"span\", \"lb-bundle-pricing__label\");\n label.textContent = \"Bundle price\";\n wrap.appendChild(label);\n const prices = el(\"span\", \"lb-bundle-pricing__prices\");\n const compare = el(\"span\", \"lb-bundle-compare-price\", {\n \"data-compare-price\": \"\",\n });\n if (showCompareAtPrice) prices.appendChild(compare);\n const sale = el(\"span\", \"lb-bundle-sale-price\", { \"data-sale-price\": \"\" });\n prices.appendChild(sale);\n wrap.appendChild(prices);\n\n function update(\n selections: Selection[],\n bundle: MixMatchBundleData,\n currency: string,\n ) {\n if (selections.length === 0) {\n wrap.style.display = \"none\";\n return;\n }\n wrap.style.display = \"\";\n const totalCents = selections.reduce(\n (s, sel) => s + sel.priceCents * sel.quantity,\n 0,\n );\n const saleCents = computeBundleSaleCents(totalCents, bundle.discountConfig);\n if (showCompareAtPrice && totalCents > saleCents) {\n compare.textContent = formatCents(totalCents, currency);\n compare.style.display = \"\";\n } else {\n compare.style.display = \"none\";\n }\n sale.textContent = formatCents(saleCents, currency);\n }\n\n return { el: wrap, update };\n}\n\nfunction renderSavingsBar() {\n const wrap = el(\"div\", \"lb-bundle-savings-bar\", {\n \"data-savings-bar\": \"\",\n });\n wrap.style.display = \"none\";\n const labelEl = document.createElement(\"span\");\n labelEl.textContent = \"You save\";\n wrap.appendChild(labelEl);\n const amount = el(\"span\", \"\", { \"data-savings-amount\": \"\" });\n wrap.appendChild(amount);\n\n function update(\n selections: Selection[],\n bundle: MixMatchBundleData,\n currency: string,\n ) {\n if (selections.length === 0) {\n wrap.style.display = \"none\";\n return;\n }\n const totalCents = selections.reduce(\n (s, sel) => s + sel.priceCents * sel.quantity,\n 0,\n );\n const saleCents = computeBundleSaleCents(totalCents, bundle.discountConfig);\n const savings = Math.max(0, totalCents - saleCents);\n if (savings <= 0) {\n wrap.style.display = \"none\";\n return;\n }\n wrap.style.display = \"\";\n amount.textContent = formatCents(savings, currency);\n }\n\n return { el: wrap, update };\n}\n\n// --- Picker modal ---\n\ninterface ModalHandlers {\n showSearch: boolean;\n onAdd: (product: Product, variant: ProductVariant) => void;\n onRemove: (productId: string, variantId: string) => void;\n isOverMax: () => boolean;\n}\n\nfunction renderModal(\n bundle: MixMatchBundleData,\n eligible: EligibleProduct[],\n currency: string,\n handlers: ModalHandlers,\n) {\n const overlay = el(\"div\", \"lb-mix-match__modal-overlay\", {\n \"data-modal-overlay\": \"\",\n \"data-bundle-gid\": bundle.id,\n });\n overlay.style.display = \"none\";\n\n const modal = el(\"div\", \"lb-mix-match__modal\", {\n role: \"dialog\",\n \"aria-modal\": \"true\",\n \"aria-labelledby\": `lb-modal-title-${sanitizeId(bundle.id)}`,\n tabindex: \"-1\",\n });\n\n // Header\n const modalHeader = el(\"div\", \"lb-mix-match__modal-header\");\n const modalTitle = el(\"h4\", \"lb-mix-match__modal-title\", {\n id: `lb-modal-title-${sanitizeId(bundle.id)}`,\n });\n modalTitle.textContent = \"Pick an item\";\n modalHeader.appendChild(modalTitle);\n const closeBtn = document.createElement(\"button\");\n closeBtn.type = \"button\";\n closeBtn.className = \"lb-mix-match__modal-close\";\n closeBtn.setAttribute(\"data-modal-close\", \"\");\n closeBtn.setAttribute(\"aria-label\", \"Close\");\n closeBtn.innerHTML = CLOSE_ICON_SVG;\n closeBtn.addEventListener(\"click\", close);\n modalHeader.appendChild(closeBtn);\n modal.appendChild(modalHeader);\n\n // Search\n let searchInput: HTMLInputElement | null = null;\n let searchClearBtn: HTMLButtonElement | null = null;\n if (handlers.showSearch) {\n const searchWrap = el(\"div\", \"lb-mix-match__modal-search\");\n searchInput = document.createElement(\"input\");\n searchInput.type = \"text\";\n searchInput.className = \"lb-mix-match__modal-search-input\";\n searchInput.setAttribute(\"data-modal-search\", \"\");\n searchInput.setAttribute(\"role\", \"searchbox\");\n searchInput.setAttribute(\"aria-label\", \"Search products\");\n searchInput.setAttribute(\"placeholder\", \"Search products\");\n searchInput.autocomplete = \"off\";\n searchInput.addEventListener(\"input\", () => applySearch());\n searchWrap.appendChild(searchInput);\n\n searchClearBtn = document.createElement(\"button\");\n searchClearBtn.type = \"button\";\n searchClearBtn.className = \"lb-mix-match__modal-search-clear\";\n searchClearBtn.setAttribute(\"data-modal-search-clear\", \"\");\n searchClearBtn.setAttribute(\"aria-label\", \"Clear search\");\n searchClearBtn.style.display = \"none\";\n searchClearBtn.innerHTML = SEARCH_CLEAR_ICON_SVG;\n searchClearBtn.addEventListener(\"click\", () => {\n if (!searchInput) return;\n searchInput.value = \"\";\n applySearch();\n searchInput.focus();\n });\n searchWrap.appendChild(searchClearBtn);\n modal.appendChild(searchWrap);\n }\n\n // Product list\n const list = el(\"div\", \"lb-mix-match__modal-list\", {\n \"data-modal-list\": \"\",\n });\n modal.appendChild(list);\n\n const empty = el(\"div\", \"lb-mix-match__modal-empty\", {\n \"data-modal-empty\": \"\",\n });\n empty.style.display = \"none\";\n const emptyText = document.createElement(\"p\");\n emptyText.textContent = \"No products match your search.\";\n empty.appendChild(emptyText);\n modal.appendChild(empty);\n\n const live = el(\"span\", \"lb-visually-hidden\", {\n \"data-modal-live\": \"\",\n \"aria-live\": \"polite\",\n });\n modal.appendChild(live);\n\n overlay.appendChild(modal);\n\n // Build product rows lazily on first open.\n let rowsBuilt = false;\n const productRows: Array<{\n el: HTMLElement;\n product: Product;\n variant: ProductVariant;\n updateCount: () => void;\n }> = [];\n\n function buildRows() {\n if (rowsBuilt) return;\n rowsBuilt = true;\n list.innerHTML = \"\";\n\n eligible.forEach((ep) => {\n // Mirrors the Liquid picker-modal pattern in bundle-mix-match.js:\n // - qty badge lives INSIDE the thumb (positioned corner), not the\n // add button, so it overlays the product image.\n // - title/price/unit-price are <p> elements so they stack as blocks.\n // <span> would flow inline against .lb-mix-match__modal-product-info\n // which has no flex-direction set.\n // - <select> appears for products with >1 available variant; the add\n // button dispatches the currently-selected variant, not a frozen\n // initial one.\n // - Removal is driven by the filled-slot × in the main widget — the\n // modal row only has the Add button.\n // Include sold-out variants in the picker so they render as disabled\n // options (matches the unavailable-combo UX). The initial variant\n // still prefers an in-stock one so the default price + Add click\n // target a purchasable variant.\n const availableVariants = ep.variants;\n const firstAvailVariant =\n ep.variants.find((v) => v.availableForSale) ??\n ep.firstAvailableVariant ??\n ep.variants[0];\n if (!firstAvailVariant) return;\n\n let currentVariant = firstAvailVariant;\n\n const productEl = el(\n \"div\",\n ep.isOos\n ? \"lb-mix-match__modal-product lb-mix-match__modal-product--sold-out\"\n : \"lb-mix-match__modal-product\",\n { \"data-product-id\": ep.product.id.replace(/^.*\\//, \"\") },\n );\n\n const thumb = el(\"div\", \"lb-mix-match__modal-product-thumb\");\n const initialThumbVariant =\n ep.variants.find((v) => v.availableForSale) ?? ep.variants[0] ?? null;\n const initialThumbImage =\n initialThumbVariant?.image ?? ep.product.featuredImage ?? null;\n let thumbImg: HTMLImageElement | null = null;\n if (initialThumbImage) {\n thumbImg = document.createElement(\"img\");\n thumbImg.src = transformImageUrl(initialThumbImage.url, {\n width: THUMB_PX,\n height: THUMB_PX,\n crop: \"center\",\n });\n thumbImg.alt = initialThumbImage.altText ?? ep.product.title;\n thumbImg.width = THUMB_PX;\n thumbImg.height = THUMB_PX;\n thumbImg.loading = \"lazy\";\n thumb.appendChild(thumbImg);\n } else {\n thumb.insertAdjacentHTML(\"beforeend\", PLACEHOLDER_THUMB_SVG);\n }\n const countBadge = el(\"span\", \"lb-bundle-qty-badge\");\n countBadge.textContent = String(\n resolveBundleQty(bundle, ep.product.id, currentVariant.id),\n );\n thumb.appendChild(countBadge);\n productEl.appendChild(thumb);\n\n const info = el(\"div\", \"lb-mix-match__modal-product-info\");\n const title = el(\"p\", \"lb-mix-match__modal-product-title\");\n title.textContent = ep.product.title;\n info.appendChild(title);\n\n const price = el(\"p\", \"lb-mix-match__modal-product-price\");\n price.textContent = formatCents(\n parseCents(currentVariant.price.amount) *\n resolveBundleQty(bundle, ep.product.id, currentVariant.id),\n currency,\n );\n info.appendChild(price);\n\n const unitPrice = el(\n \"p\",\n \"lb-mix-match__modal-product-unit-price lb-bundle-product-unit-price\",\n );\n const initialUnitText = formatUnitPrice(\n currentVariant.unitPrice,\n currentVariant.unitPriceMeasurement,\n currency,\n );\n if (initialUnitText) {\n unitPrice.textContent = initialUnitText;\n } else {\n unitPrice.hidden = true;\n }\n info.appendChild(unitPrice);\n\n // Declared up-front so the select-change handler can keep\n // `row.variant` in sync with the closure `currentVariant`. Anything\n // that reads productRows[i].variant (search filters, future analytics\n // hooks) sees the live selection rather than the initial variant.\n const row = {\n el: productEl,\n product: ep.product,\n variant: firstAvailVariant,\n updateCount: () => {},\n };\n\n // Per-option dropdowns (Shopify's recommended pattern — one <select>\n // per product option). Values that don't combine with the currently\n // selected values for other options are disabled, so the customer gets\n // clear feedback instead of the variant silently jumping combos.\n if (availableVariants.length > 1) {\n const optionNames: string[] = availableVariants[0].selectedOptions.map(\n (o) => o.name,\n );\n const productIdTail = ep.product.id.replace(/^.*\\//, \"\");\n const optionSelects: HTMLSelectElement[] = [];\n\n const resolveVariant = (values: string[]): ProductVariant | null =>\n availableVariants.find(\n (v) =>\n v.selectedOptions.length === values.length &&\n v.selectedOptions.every((o, i) => o.value === values[i]),\n ) ?? null;\n\n const syncSelectsToVariant = (v: ProductVariant) => {\n v.selectedOptions.forEach((o, i) => {\n const sel = optionSelects[i];\n if (sel && sel.value !== o.value) sel.value = o.value;\n });\n };\n\n const isValueAvailable = (\n optionIndex: number,\n value: string,\n selected: string[],\n ): boolean =>\n availableVariants.some((v) => {\n if (!v.availableForSale) return false;\n if (v.selectedOptions[optionIndex]?.value !== value) return false;\n return v.selectedOptions.every(\n (o, i) => i === optionIndex || o.value === selected[i],\n );\n });\n\n const recomputeDisabled = (selected: string[]) => {\n optionSelects.forEach((sel, i) => {\n Array.from(sel.options).forEach((opt) => {\n opt.disabled = !isValueAvailable(i, opt.value, selected);\n });\n });\n };\n\n const handleChange = () => {\n const values = optionSelects.map((s) => s.value);\n const next = resolveVariant(values);\n if (!next) {\n // Disabled combo reached via keyboard — revert selects to the\n // currently selected variant rather than jumping.\n syncSelectsToVariant(currentVariant);\n recomputeDisabled(\n currentVariant.selectedOptions.map((o) => o.value),\n );\n return;\n }\n currentVariant = next;\n row.variant = next;\n const nextQty = resolveBundleQty(\n bundle,\n ep.product.id,\n currentVariant.id,\n );\n price.textContent = formatCents(\n parseCents(currentVariant.price.amount) * nextQty,\n currency,\n );\n const nextUnitText = formatUnitPrice(\n currentVariant.unitPrice,\n currentVariant.unitPriceMeasurement,\n currency,\n );\n if (nextUnitText) {\n unitPrice.textContent = nextUnitText;\n unitPrice.hidden = false;\n } else {\n unitPrice.textContent = \"\";\n unitPrice.hidden = true;\n }\n // Swap the row thumbnail to the picked variant's image when it\n // has one. Falls back to the product's featured image so a\n // variant without its own image doesn't blank the thumb out.\n const nextImage = currentVariant.image ?? ep.product.featuredImage;\n if (thumbImg && nextImage) {\n thumbImg.src = transformImageUrl(nextImage.url, {\n width: THUMB_PX,\n height: THUMB_PX,\n crop: \"center\",\n });\n thumbImg.alt = nextImage.altText ?? ep.product.title;\n }\n recomputeDisabled(next.selectedOptions.map((o) => o.value));\n rowUpdateCount();\n };\n\n const groupsContainer = el(\"div\", \"lb-bundle-variant-option-groups\");\n optionNames.forEach((name, position) => {\n const group = el(\"div\", \"lb-bundle-variant-option-group\");\n\n const label = el(\"span\", \"lb-bundle-variant-option-label\");\n label.textContent = name;\n group.appendChild(label);\n\n const select = document.createElement(\"select\");\n select.className = \"lb-mix-match__variant-select\";\n select.setAttribute(\"data-variant-option\", \"\");\n select.setAttribute(\"data-option-position\", String(position + 1));\n select.name = `lb-variant-${productIdTail}-${position + 1}`;\n select.setAttribute(\"aria-label\", name);\n\n const seen = new Set<string>();\n availableVariants.forEach((v) => {\n const value = v.selectedOptions[position]?.value;\n if (!value || seen.has(value)) return;\n seen.add(value);\n const opt = document.createElement(\"option\");\n opt.value = value;\n opt.textContent = value;\n if (firstAvailVariant.selectedOptions[position]?.value === value) {\n opt.selected = true;\n }\n select.appendChild(opt);\n });\n\n select.addEventListener(\"change\", handleChange);\n optionSelects.push(select);\n group.appendChild(select);\n groupsContainer.appendChild(group);\n });\n info.appendChild(groupsContainer);\n\n recomputeDisabled(\n firstAvailVariant.selectedOptions.map((o) => o.value),\n );\n } else if (\n availableVariants.length === 1 &&\n firstAvailVariant.title !== \"Default Title\"\n ) {\n const variantLabel = el(\"span\", \"lb-mix-match__filled-variant\");\n variantLabel.textContent = firstAvailVariant.title;\n info.appendChild(variantLabel);\n }\n\n if (ep.isOos) {\n const soldOut = el(\"span\", \"lb-mix-match__modal-sold-out-label\");\n soldOut.textContent = \"Sold out\";\n info.appendChild(soldOut);\n }\n productEl.appendChild(info);\n\n // Closure captures currentVariant by reference — variant-change handler\n // mutates it, and both addBtn click + rowUpdateCount read the latest.\n // Badge shows the merchant-configured per-slot qty for the CURRENT\n // variant, matching Liquid picker-modal semantics (mirrors\n // `bundle-mix-match.js:359-364`).\n const rowUpdateCount = () => {\n countBadge.textContent = String(\n resolveBundleQty(bundle, ep.product.id, currentVariant.id),\n );\n };\n\n if (!ep.isOos) {\n const addBtn = document.createElement(\"button\");\n addBtn.type = \"button\";\n addBtn.className = \"lb-mix-match__modal-add\";\n addBtn.textContent = \"Add\";\n addBtn.addEventListener(\"click\", () => {\n if (handlers.isOverMax()) return;\n handlers.onAdd(ep.product, currentVariant);\n close();\n });\n productEl.appendChild(addBtn);\n }\n\n // One productRows entry per product — OOS rows are included so the\n // search filter iterates over both available and sold-out uniformly;\n // updateCount is a no-op for OOS (no badge to toggle).\n row.updateCount = ep.isOos ? () => {} : rowUpdateCount;\n productRows.push(row);\n\n list.appendChild(productEl);\n });\n\n // Replace native <select> popup chrome with our accessible custom dropdown.\n bindAllDropdowns(list);\n\n refreshCounts();\n }\n\n function applySearch() {\n if (!searchInput) return;\n const query = searchInput.value.trim().toLowerCase();\n if (searchClearBtn) {\n searchClearBtn.style.display = query ? \"\" : \"none\";\n }\n let visibleCount = 0;\n productRows.forEach((row) => {\n const match = !query || row.product.title.toLowerCase().includes(query);\n row.el.style.display = match ? \"\" : \"none\";\n if (match) visibleCount++;\n });\n empty.style.display = visibleCount === 0 && query ? \"\" : \"none\";\n }\n\n // Focus trap + keyboard handling\n let lastFocused: Element | null = null;\n function onKeydown(e: KeyboardEvent) {\n if (e.key === \"Escape\") {\n e.preventDefault();\n close();\n return;\n }\n if (e.key === \"Tab\") {\n trapFocus(e, modal);\n }\n }\n\n let isOpen = false;\n\n function open() {\n if (isOpen) return;\n if (handlers.isOverMax()) return;\n isOpen = true;\n buildRows();\n lastFocused = (overlay.getRootNode() as Document | ShadowRoot)\n .activeElement;\n overlay.style.display = \"\";\n overlay.classList.add(\"lb-mix-match__modal-overlay--open\");\n modal.focus();\n document.addEventListener(\"keydown\", onKeydown);\n overlay.addEventListener(\"click\", onOverlayClick);\n }\n\n function close() {\n if (!isOpen) return;\n isOpen = false;\n overlay.classList.remove(\"lb-mix-match__modal-overlay--open\");\n overlay.style.display = \"none\";\n document.removeEventListener(\"keydown\", onKeydown);\n overlay.removeEventListener(\"click\", onOverlayClick);\n if (lastFocused instanceof HTMLElement) {\n lastFocused.focus();\n }\n }\n\n function onOverlayClick(e: MouseEvent) {\n if (e.target === overlay) close();\n }\n\n function refreshCounts() {\n productRows.forEach((r) => r.updateCount());\n }\n\n return { el: overlay, open, close, refreshCounts };\n}\n\n// --- Helpers ---\n\nfunction buildEligibleProducts(\n bundle: MixMatchBundleData,\n oosBehavior: \"show_greyed_out\" | \"hide\",\n): EligibleProduct[] {\n const result: EligibleProduct[] = [];\n const seen = new Set<string>();\n for (const product of bundle.products) {\n if (seen.has(product.id)) continue;\n seen.add(product.id);\n const available = product.variants.nodes.filter((v) => v.availableForSale);\n const isOos = available.length === 0;\n if (isOos && oosBehavior === \"hide\") continue;\n result.push({\n product,\n variants: product.variants.nodes,\n firstAvailableVariant: available[0] ?? null,\n isOos,\n });\n }\n return result;\n}\n\nfunction trapFocus(e: KeyboardEvent, container: HTMLElement) {\n const focusables = container.querySelectorAll<HTMLElement>(\n 'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])',\n );\n if (focusables.length === 0) return;\n const first = focusables[0];\n const last = focusables[focusables.length - 1];\n const active = (container.getRootNode() as Document | ShadowRoot)\n .activeElement;\n if (e.shiftKey && active === first) {\n e.preventDefault();\n last.focus();\n } else if (!e.shiftKey && active === last) {\n e.preventDefault();\n first.focus();\n }\n}\n\nfunction sanitizeId(gid: string): string {\n return gid.replace(/[^a-zA-Z0-9_-]/g, \"-\");\n}\n\n","/**\n * DOM renderer for volume bundles.\n *\n * Mirrors `extensions/bundle-theme/snippets/lb-volume.liquid`. Each tier is a\n * radio-styled card; clicking one updates the pricing row and recalculates\n * the total. Add bundle dispatches the active tier's quantity for the first\n * available variant.\n */\nimport type {\n CartLineInput,\n DiscountConfig,\n VolumeBundleData,\n VolumeTier,\n} from \"@lime-bundles/core\";\nimport { formatCents, parseCents } from \"./pricing\";\nimport { renderCountdown } from \"./countdown\";\nimport { buildCtaButton } from \"./cta-button\";\nimport { el } from \"./dom\";\n\ninterface ResolvedTier {\n tier: VolumeTier;\n index: number;\n qty: number;\n /** Per-unit price after applying this tier's discount, in cents. */\n pricePerUnitCents: number;\n /** Pre-discount per-unit baseline in cents. */\n basePricePerUnitCents: number;\n}\n\nexport function renderVolumeBundle(\n container: HTMLElement,\n bundle: VolumeBundleData,\n onAddToCart: (lines: CartLineInput[]) => void,\n onCleanup?: (fn: () => void) => void,\n) {\n const wc = bundle.widgetConfig;\n const product = bundle.products[0];\n const variant = product?.variants.nodes.find((v) => v.availableForSale);\n\n // Bundle visibility guard: in \"hide\" mode, don't render the widget at\n // all if the product has no available variants. Matches Liquid behaviour.\n if (!variant && wc.outOfStockBehavior === \"hide\") return;\n\n const basePriceCents = variant ? parseCents(variant.price.amount) : 0;\n const currency = variant?.price.currencyCode ?? \"USD\";\n\n // Discount shape: bundle.discountConfig.discountType selects which field\n // on each tier carries the magnitude. \"percentage\" → tier.percentage (a\n // whole-number e.g. 10); \"fixed_amount\" → tier.amount (currency units\n // e.g. 5.00). Missing fields fall through to zero (tier renders at base\n // price — merchant config error, not a crash path).\n const discountType = bundle.discountConfig.discountType;\n\n const resolved = bundle.volumeTiers.map<ResolvedTier>((tier, index) => {\n let perUnit: number;\n if (discountType === \"fixed_amount\") {\n const amt = Math.round((tier.amount ?? 0) * 100);\n perUnit = Math.max(0, basePriceCents - amt);\n } else {\n const pct = tier.percentage ?? 0;\n const discount = Math.floor((basePriceCents * pct) / 100);\n perUnit = Math.max(0, basePriceCents - discount);\n }\n return {\n tier,\n index,\n qty: tier.minQuantity,\n pricePerUnitCents: perUnit,\n basePricePerUnitCents: basePriceCents,\n };\n });\n\n const bestTierIndex = pickBestTierIndex(resolved);\n let selectedIndex = wc.defaultTier === \"best_value\" ? bestTierIndex : 0;\n if (typeof wc.defaultTier === \"number\") {\n selectedIndex = clamp(wc.defaultTier, 0, resolved.length - 1);\n }\n\n const popularIndex =\n wc.popularBadge.tierIndex !== undefined\n ? clamp(wc.popularBadge.tierIndex, 0, resolved.length - 1)\n : bestTierIndex;\n\n const root = el(\"div\", \"lb-volume\");\n root.appendChild(\n renderHeader(bundle, resolved, selectedIndex, currency, discountType),\n );\n\n if (wc.countdown.showCountdown && bundle.endsAt) {\n const countdown = renderCountdown(bundle.endsAt);\n if (countdown) {\n root.appendChild(countdown.el);\n onCleanup?.(countdown.stop);\n }\n }\n\n const tierGroup = el(\"div\", \"lb-volume__tiers\", {\n role: \"radiogroup\",\n \"aria-label\": \"Quantity tiers\",\n \"data-tier-group\": \"\",\n });\n\n resolved.forEach((r) => {\n const tierEl = renderTierCard(\n r,\n r.index === selectedIndex,\n currency,\n wc.popularBadge.visible && r.index === popularIndex\n ? wc.popularBadge.text\n : null,\n wc.pricing.showComparePrice,\n wc.pricing.showPerUnitPrice,\n );\n tierEl.addEventListener(\"click\", () => selectTier(r.index));\n // Radiogroup keyboard contract: arrow keys move focus + selection\n // between siblings; Space/Enter activates the focused tier.\n tierEl.addEventListener(\"keydown\", (e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n selectTier(r.index);\n return;\n }\n if (\n e.key === \"ArrowDown\" ||\n e.key === \"ArrowRight\" ||\n e.key === \"ArrowUp\" ||\n e.key === \"ArrowLeft\"\n ) {\n e.preventDefault();\n const delta =\n e.key === \"ArrowDown\" || e.key === \"ArrowRight\" ? 1 : -1;\n const next = (r.index + delta + resolved.length) % resolved.length;\n selectTier(next);\n const target = tierGroup.children[next] as HTMLElement | undefined;\n target?.focus();\n }\n });\n tierGroup.appendChild(tierEl);\n });\n\n root.appendChild(tierGroup);\n root.appendChild(el(\"div\", \"lb-bundle-divider\"));\n\n let pricingEl = renderPricingRow(\n resolved,\n selectedIndex,\n currency,\n wc.pricing.showItemCount,\n wc.pricing.showCompareAtPrice,\n );\n root.appendChild(pricingEl);\n\n let savingsBarEl: HTMLElement | null = wc.savingsBar.visible\n ? renderSavingsBar(resolved, selectedIndex, currency)\n : null;\n if (savingsBarEl) root.appendChild(savingsBarEl);\n\n const cta = renderCta(bundle, () => {\n if (!variant) return;\n const r = resolved[selectedIndex];\n if (!r) return;\n onAddToCart([\n {\n merchandiseId: variant.id,\n quantity: r.qty,\n attributes: [\n { key: \"_lime_bundle_gid\", value: bundle.id },\n { key: \"_lime_bundle_type\", value: bundle.bundleType },\n ],\n },\n ]);\n });\n root.appendChild(cta);\n\n root.appendChild(\n el(\"p\", \"lb-bundle-error\", { \"data-error\": \"\", \"aria-live\": \"polite\" }),\n );\n root.appendChild(\n el(\"span\", \"lb-visually-hidden\", {\n \"data-status\": \"\",\n \"aria-live\": \"polite\",\n }),\n );\n\n container.appendChild(root);\n\n function selectTier(idx: number) {\n if (idx === selectedIndex || idx < 0 || idx >= resolved.length) return;\n selectedIndex = idx;\n Array.from(tierGroup.children).forEach((card, i) => {\n card.setAttribute(\"aria-checked\", String(i === idx));\n (card as HTMLElement).tabIndex = i === idx ? 0 : -1;\n });\n const newPricing = renderPricingRow(\n resolved,\n idx,\n currency,\n wc.pricing.showItemCount,\n wc.pricing.showCompareAtPrice,\n );\n pricingEl.replaceWith(newPricing);\n pricingEl = newPricing;\n if (savingsBarEl) {\n const newBar = renderSavingsBar(resolved, idx, currency);\n savingsBarEl.replaceWith(newBar);\n savingsBarEl = newBar;\n }\n // Keep the header save-badge text in sync with the selected tier.\n // Hidden when showSaveBadge is off (the element doesn't exist), or\n // when the tier has no discount (badgeFor returns null).\n const badgeEl = root.querySelector<HTMLElement>(\"[data-header-badge]\");\n if (badgeEl) {\n const label = badgeFor(resolved[idx], currency, discountType);\n if (label) {\n badgeEl.textContent = label;\n badgeEl.style.display = \"\";\n } else {\n badgeEl.style.display = \"none\";\n }\n }\n }\n}\n\n// --- Section renderers ---\n\nfunction renderHeader(\n bundle: VolumeBundleData,\n resolved: ResolvedTier[],\n selectedIndex: number,\n currency: string,\n discountType: DiscountConfig[\"discountType\"],\n): HTMLElement {\n const wc = bundle.widgetConfig;\n const header = el(\"div\", \"lb-bundle-header\");\n const content = el(\"div\", \"lb-bundle-header__content\");\n const title = el(\"h3\", \"lb-bundle-title\");\n title.textContent = bundle.title;\n content.appendChild(title);\n header.appendChild(content);\n\n if (wc.pricing.showSaveBadge) {\n const badge = badgeFor(resolved[selectedIndex], currency, discountType);\n if (badge) {\n const badgeEl = el(\"span\", \"lb-bundle-header__badge\", {\n \"data-header-badge\": \"\",\n });\n badgeEl.textContent = badge;\n header.appendChild(badgeEl);\n }\n }\n return header;\n}\n\nfunction renderTierCard(\n r: ResolvedTier,\n isSelected: boolean,\n currency: string,\n popularLabel: string | null,\n showComparePrice: boolean,\n showPerUnitPrice: boolean,\n): HTMLElement {\n const tier = el(\"div\", \"lb-volume__tier\", {\n role: \"radio\",\n \"aria-checked\": String(isSelected),\n tabindex: isSelected ? \"0\" : \"-1\",\n \"data-tier-index\": String(r.index),\n \"data-tier-qty\": String(r.qty),\n });\n\n const radio = el(\"span\", \"lb-volume__radio\");\n radio.appendChild(el(\"span\", \"lb-volume__radio-dot\"));\n tier.appendChild(radio);\n\n const grid = el(\"span\", \"lb-volume__tier-grid\");\n const label = el(\"span\", \"lb-volume__tier-label\");\n label.textContent = `Buy ${r.qty}`;\n grid.appendChild(label);\n\n const price = el(\"span\", \"lb-volume__tier-price\");\n if (showComparePrice && r.pricePerUnitCents < r.basePricePerUnitCents) {\n const compare = el(\"span\", \"lb-volume__tier-compare\");\n compare.textContent = formatCents(r.basePricePerUnitCents, currency);\n price.appendChild(compare);\n }\n if (showPerUnitPrice) {\n const each = document.createElement(\"span\");\n each.setAttribute(\"data-tier-price-each\", \"\");\n each.textContent = formatCents(r.pricePerUnitCents, currency);\n price.appendChild(each);\n const unit = el(\"span\", \"lb-volume__tier-unit\");\n unit.textContent = \" each\";\n price.appendChild(unit);\n }\n grid.appendChild(price);\n tier.appendChild(grid);\n\n const badge = el(\"span\", \"lb-volume__tier-badge\");\n if (popularLabel) {\n badge.textContent = popularLabel;\n } else {\n badge.style.display = \"none\";\n }\n tier.appendChild(badge);\n\n return tier;\n}\n\nfunction renderPricingRow(\n resolved: ResolvedTier[],\n selectedIndex: number,\n currency: string,\n showItemCount: boolean,\n showCompareAtPrice: boolean,\n): HTMLElement {\n const r = resolved[selectedIndex];\n const totalCents = r ? r.pricePerUnitCents * r.qty : 0;\n const undiscountedCents = r ? r.basePricePerUnitCents * r.qty : 0;\n const savings = Math.max(0, undiscountedCents - totalCents);\n\n const row = el(\"div\", \"lb-bundle-pricing\");\n const label = el(\"span\", \"lb-bundle-pricing__label\", {\n \"data-total-label\": \"\",\n });\n label.textContent = \"Total\";\n if (showItemCount && r) {\n const count = document.createElement(\"span\");\n count.setAttribute(\"data-item-count\", \"\");\n count.textContent = ` (${r.qty} item${r.qty === 1 ? \"\" : \"s\"})`;\n label.appendChild(count);\n }\n row.appendChild(label);\n\n const prices = el(\"span\", \"lb-bundle-pricing__prices\");\n if (showCompareAtPrice && savings > 0) {\n const compare = el(\"span\", \"lb-bundle-compare-price\", {\n \"data-compare-price\": \"\",\n });\n compare.textContent = formatCents(undiscountedCents, currency);\n prices.appendChild(compare);\n }\n const sale = el(\"span\", \"lb-bundle-sale-price\", { \"data-total-price\": \"\" });\n sale.textContent = formatCents(totalCents, currency);\n prices.appendChild(sale);\n row.appendChild(prices);\n return row;\n}\n\nfunction renderSavingsBar(\n resolved: ResolvedTier[],\n selectedIndex: number,\n currency: string,\n): HTMLElement {\n const r = resolved[selectedIndex];\n const totalCents = r ? r.pricePerUnitCents * r.qty : 0;\n const undiscountedCents = r ? r.basePricePerUnitCents * r.qty : 0;\n const savings = Math.max(0, undiscountedCents - totalCents);\n\n const bar = el(\"div\", \"lb-bundle-savings-bar\", { \"data-savings-bar\": \"\" });\n if (savings <= 0) bar.style.display = \"none\";\n const labelEl = document.createElement(\"span\");\n labelEl.textContent = \"You save\";\n bar.appendChild(labelEl);\n const amount = el(\"span\", \"\", { \"data-savings-amount\": \"\" });\n amount.textContent = formatCents(savings, currency);\n bar.appendChild(amount);\n return bar;\n}\n\nfunction renderCta(\n bundle: VolumeBundleData,\n onClick: () => void,\n): HTMLElement {\n const product = bundle.products[0];\n const isAvailable = product?.variants.nodes.some((v) => v.availableForSale);\n const label = isAvailable\n ? bundle.widgetConfig.cta.ctaText || \"Add to cart\"\n : \"Sold out\";\n const button = buildCtaButton(label);\n if (!isAvailable) button.disabled = true;\n button.addEventListener(\"click\", () => {\n if (button.disabled) return;\n onClick();\n });\n return button;\n}\n\n// --- Helpers ---\n\nfunction badgeFor(\n resolved: ResolvedTier | undefined,\n currency: string,\n discountType: DiscountConfig[\"discountType\"],\n): string | null {\n if (!resolved) return null;\n const { tier } = resolved;\n if (discountType === \"fixed_amount\") {\n const amount = tier.amount ?? 0;\n if (amount > 0) return `-${formatCents(Math.round(amount * 100), currency)}`;\n return null;\n }\n if (discountType === \"percentage\") {\n const pct = tier.percentage ?? 0;\n if (pct > 0) return `-${Math.round(pct)}%`;\n return null;\n }\n return null;\n}\n\nfunction pickBestTierIndex(resolved: ResolvedTier[]): number {\n let bestSavings = 0;\n let bestIndex = 0;\n resolved.forEach((r, i) => {\n const savings = r.basePricePerUnitCents - r.pricePerUnitCents;\n if (savings > bestSavings) {\n bestSavings = savings;\n bestIndex = i;\n }\n });\n return bestIndex;\n}\n\nfunction clamp(n: number, min: number, max: number): number {\n return Math.max(min, Math.min(max, n));\n}\n\n","/**\n * Input-mode tracker — toggles `using-mouse` / `using-keyboard` classes on a\n * target element so CSS can scope focus styles by the customer's current\n * input device. Default is mouse; the first keyboard-navigation keypress\n * (Tab, arrow keys, Enter, Space, Escape, Home/End/PageUp/PageDown) flips\n * to keyboard mode, and the next pointer click flips back.\n *\n * Multiple targets share one pair of document-level listeners — installed\n * on the first `trackInputMode` call, removed when the last target is\n * released. Safe to call across every widget instance on a page without\n * stacking listeners.\n *\n * CSS shape (see bundle-base.css):\n * .using-mouse .lb-bundle-widget :focus { outline: none; }\n *\n * The Liquid theme mirrors this behaviour from `bundle-widget.js` against\n * `document.documentElement` so classic and headless storefronts render the\n * same focus rings.\n */\n\nconst NAV_KEYS = new Set([\n \"Tab\",\n \"ArrowUp\",\n \"ArrowDown\",\n \"ArrowLeft\",\n \"ArrowRight\",\n \"Home\",\n \"End\",\n \"PageUp\",\n \"PageDown\",\n \"Enter\",\n \" \",\n \"Escape\",\n]);\n\nconst targets = new Set<HTMLElement>();\nlet listenersAttached = false;\n\nfunction setAll(on: \"using-mouse\" | \"using-keyboard\"): void {\n const off = on === \"using-mouse\" ? \"using-keyboard\" : \"using-mouse\";\n for (const el of targets) {\n el.classList.add(on);\n el.classList.remove(off);\n }\n}\n\nfunction onKeyDown(e: KeyboardEvent): void {\n if (NAV_KEYS.has(e.key)) setAll(\"using-keyboard\");\n}\n\nfunction onPointerDown(): void {\n setAll(\"using-mouse\");\n}\n\nfunction attachListeners(): void {\n if (listenersAttached) return;\n listenersAttached = true;\n document.addEventListener(\"keydown\", onKeyDown, true);\n document.addEventListener(\"pointerdown\", onPointerDown, true);\n}\n\nfunction detachListeners(): void {\n if (!listenersAttached) return;\n listenersAttached = false;\n document.removeEventListener(\"keydown\", onKeyDown, true);\n document.removeEventListener(\"pointerdown\", onPointerDown, true);\n}\n\nexport function trackInputMode(target: HTMLElement): () => void {\n target.classList.add(\"using-mouse\");\n target.classList.remove(\"using-keyboard\");\n targets.add(target);\n attachListeners();\n\n return () => {\n targets.delete(target);\n target.classList.remove(\"using-mouse\");\n target.classList.remove(\"using-keyboard\");\n if (targets.size === 0) detachListeners();\n };\n}\n","/**\n * Base + type-specific widget CSS, inlined as template literals so the web\n * component can dump them into its shadow root. Source of truth for these\n * rules is `extensions/bundle-theme/assets/*.css` — the classic Shopify\n * theme app block reads the same files. Keep the two in lockstep; the\n * bundle-css-parity.test.ts golden test enforces byte equality.\n *\n * `BUNDLE_SKELETON_CSS` (at the bottom of this file) is intentionally\n * web-component-only. The theme app block never renders a loading\n * state — its Liquid render is synchronous on the server — so the\n * skeleton styles would be dead rules there. Excluding from parity.\n */\nexport const BUNDLE_BASE_CSS = `/* Lime Bundles — shared base styles for all bundle widget types */\n\n.lb-bundle-widget.lb-bundle-widget,\n.lb-bundle-widget.lb-bundle-widget * {\n line-height: normal;\n}\n\n.lb-bundle-widget {\n /* Internal CSS-only vars (not merchant-configurable). */\n --lb-thumbnail-bg: #F0F0F0;\n --lb-widget-pad: 20px;\n --lb-progress-color: var(--lb-primary-color);\n /* Cap on the per-bundle product/slot/tier list height — keeps long\n bundles from pushing the CTA off-screen. The list scrolls\n internally with the same custom 4px scrollbar as the variant\n dropdown when content exceeds this. */\n --lb-list-max-height: 360px;\n\n font-family: inherit;\n font-size: 16px;\n background: var(--lb-bg);\n border: var(--lb-border-width) solid var(--lb-border);\n border-radius: var(--lb-radius);\n padding: var(--lb-widget-pad) var(--lb-widget-pad) 20px;\n box-sizing: border-box;\n /* Cap the widget at a comfortable reading width on desktop. Below\n 440px viewports the container is already narrower than the cap,\n so the rule is inert on mobile. */\n max-width: 440px;\n}\n\n/* Countdown timer bar — sits below the gradient header */\n.lb-bundle-countdown {\n margin: 0 calc(-1 * var(--lb-widget-pad)) 20px;\n padding: 12px 20px;\n background: var(--lb-countdown-bg);\n border-top: 1px solid color-mix(in srgb, var(--lb-text) 6%, transparent);\n display: flex;\n align-items: center;\n justify-content: space-between;\n}\n\n.lb-bundle-countdown__label {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.lb-bundle-countdown__label svg {\n width: 16px;\n height: 16px;\n flex-shrink: 0;\n color: var(--lb-countdown-text);\n}\n\n.lb-bundle-countdown__label span {\n font-size: 12px;\n font-weight: 500;\n line-height: 1;\n color: var(--lb-countdown-text);\n}\n\n.lb-bundle-countdown__timer {\n font-family: 'SF Mono', 'Roboto Mono', ui-monospace, monospace;\n font-size: 12px;\n font-weight: 600;\n line-height: 1;\n color: var(--lb-countdown-text);\n letter-spacing: 0.02em;\n}\n\n/* Hide wrapper when the inner snippet rendered nothing (product OOS / unfulfillable) */\n.lb-bundle-widget:not(:has(.lb-fixed, .lb-mix-match, .lb-volume)) {\n display: none;\n}\n\n/* Sibling widget spacing — separates multiple bundles on the same product page.\n Uses \\`~\\` (general sibling) rather than \\`+\\` (adjacent) because each Liquid\n loop iteration emits a {% style %} block before its widget div, so the\n rendered DOM alternates <style><widget><style><widget>. The \\`+\\` combinator\n requires immediate adjacency and would match nothing; \\`~\\` matches every\n widget after the first regardless of elements between. Single-widget pages\n stay unaffected (no prior \\`.lb-bundle-widget\\` sibling to match against).\n Only \\`margin-top\\` — do NOT override \\`padding-top\\` here. The gradient header\n uses \\`margin-top: calc(-1 * var(--lb-widget-pad))\\` to reach the widget's\n inner border edge, assuming padding-top == --lb-widget-pad. Changing\n padding-top on the subsequent widget breaks that math and leaves a visible\n gap above the header. */\n.lb-bundle-widget ~ .lb-bundle-widget {\n margin-top: 24px;\n}\n\n/* Header — gradient banner with title + savings badge */\n.lb-bundle-header {\n margin: calc(-1 * var(--lb-widget-pad)) calc(-1 * var(--lb-widget-pad)) 20px;\n padding: 20px 20px;\n background: var(--lb-header-bg);\n /* Match the widget's inner border curve so there's no background gap at the top corners. */\n border-radius: max(0px, calc(var(--lb-radius) - var(--lb-border-width))) max(0px, calc(var(--lb-radius) - var(--lb-border-width))) 0 0;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 16px;\n}\n\n/* When countdown follows header, remove header bottom margin */\n.lb-bundle-header:has(+ .lb-bundle-countdown) {\n margin-bottom: 0;\n}\n\n.lb-bundle-header__content {\n flex: 1;\n min-width: 0;\n}\n\n.lb-bundle-title {\n font-size: 20px;\n font-weight: 700;\n line-height: 28px;\n letter-spacing: -0.02em;\n color: var(--lb-text);\n margin: 0;\n}\n\n.lb-bundle-header .lb-bundle-title {\n color: var(--lb-header-text);\n}\n\n.lb-bundle-subtitle {\n font-size: 16px;\n font-weight: 400;\n line-height: 20px;\n color: color-mix(in srgb, var(--lb-text) 60%, transparent);\n margin: 4px 0 0;\n}\n\n.lb-bundle-header .lb-bundle-subtitle {\n color: var(--lb-header-text);\n opacity: 0.85;\n margin-top: 8px;\n}\n\n.lb-bundle-header:has(.lb-bundle-subtitle) {\n align-items: flex-start;\n}\n\n.lb-bundle-header__badge {\n background: var(--lb-save-badge-bg);\n color: var(--lb-save-badge-text);\n border: var(--lb-save-badge-border-width) solid var(--lb-save-badge-border-color);\n font-size: 16px;\n font-weight: 700;\n line-height: 1;\n padding: 4px 12px;\n border-radius: var(--lb-save-badge-radius);\n white-space: nowrap;\n flex-shrink: 0;\n}\n\n/* Override Dawn's \\`div:empty { display: none }\\` reset for decorative elements */\n.lb-bundle-divider:empty,\n.lb-mix-match__progress-fill:empty {\n display: block;\n}\n\n/* Divider */\n.lb-bundle-divider {\n height: 1px;\n background: color-mix(in srgb, var(--lb-text) 7%, transparent);\n margin: 16px 0;\n}\n\n/* Product rows */\n.lb-bundle-product-row {\n display: flex;\n gap: 20px;\n padding: 12px 0;\n}\n\n.lb-bundle-thumbnail {\n position: relative;\n width: 48px;\n min-width: 48px;\n /* Aspect-ratio comes from the merchant \\`thumbnailRatio\\` enum via Liquid;\n \"original\" sets it to \\`auto\\` so the box sizes to the image's intrinsic\n ratio. Default keeps the historical 1:1 behaviour. */\n aspect-ratio: var(--lb-thumbnail-aspect-ratio, 1 / 1);\n background: var(--lb-thumbnail-bg);\n border-radius: 8px;\n overflow: hidden;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.lb-bundle-thumbnail img {\n width: 100%;\n /* Height + fit come from the same merchant enum; \"original\" sets them to\n \\`auto\\` / \\`contain\\` so the image renders uncropped at intrinsic ratio. */\n height: var(--lb-thumbnail-img-height, 100%);\n object-fit: var(--lb-thumbnail-img-fit, cover);\n}\n\n.lb-bundle-thumbnail svg {\n width: 28px;\n height: 28px;\n color: color-mix(in srgb, var(--lb-text) 35%, transparent);\n}\n\n.lb-bundle-product-info {\n flex: 1;\n min-width: 0;\n}\n\n.lb-bundle-product-name {\n font-size: 16px;\n font-weight: 700;\n line-height: 20px;\n color: var(--lb-text);\n margin: 0;\n text-decoration: none;\n display: block;\n}\n\n.lb-bundle-product-name:hover {\n text-decoration: underline;\n}\n\n.lb-bundle-product-price {\n /* --lb-product-price-display fallback is the \"on\" branch; Liquid sets 'none' when merchant disables. */\n display: var(--lb-product-price-display, block);\n font-size: 14px;\n font-weight: 400;\n line-height: 20px;\n color: var(--lb-text);\n}\n\n.lb-bundle-product-prices {\n display: flex;\n align-items: baseline;\n gap: 6px;\n flex-wrap: wrap;\n margin-top: 2px;\n}\n\n.lb-bundle-product-compare-price {\n /* --lb-product-compare-display fallback is the \"on\" branch; Liquid sets 'none' when merchant disables. */\n display: var(--lb-product-compare-display, inline);\n font-size: 14px;\n font-weight: 400;\n line-height: 20px;\n color: color-mix(in srgb, var(--lb-text) 60%, transparent);\n text-decoration: line-through;\n}\n\n/* Setting \\`display\\` above outranks the UA [hidden] rule — restore it so rows\n without a compare-at price don't leave a phantom flex item + gap. */\n.lb-bundle-product-compare-price[hidden] {\n display: none;\n}\n\n/* Unit price (e.g. \"$0.50/100ml\") — only rendered when the merchant has\n configured unit pricing on the variant in the Shopify admin. No merchant\n toggle: present in admin → shown; absent → hidden. Styled as muted\n secondary text beneath the price row so it doesn't compete visually. */\n.lb-bundle-product-unit-price {\n display: block;\n font-size: 12px;\n line-height: 16px;\n color: color-mix(in srgb, var(--lb-text) 60%, transparent);\n margin-top: 2px;\n}\n\n.lb-bundle-product-unit-price[hidden] {\n display: none;\n}\n\n.lb-bundle-variant-badge {\n display: inline-block;\n border: var(--lb-variant-border-width) solid var(--lb-variant-border-color);\n border-radius: var(--lb-variant-radius);\n padding: 8px 12px;\n font-size: 12px;\n line-height: 16px;\n color: color-mix(in srgb, var(--lb-text) 60%, transparent);\n margin-top: 8px;\n}\n\n/* Per-option variant pickers. Each option's label + select sit inside a\n .lb-bundle-variant-option-group (flex column, 2px gap between label and\n select); the groups stack inside a .lb-bundle-variant-option-groups\n parent (flex column, 12px gap between groups). The parent owns the top\n offset from the preceding unit-price line, so individual labels and\n selects don't carry their own vertical margins. */\n.lb-bundle-variant-option-groups {\n display: flex;\n flex-direction: column;\n gap: 12px;\n margin-top: 8px;\n}\n\n.lb-bundle-variant-option-group {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.lb-bundle-variant-option-label {\n display: block;\n margin: 0;\n font-size: 12px;\n line-height: 16px;\n font-weight: 600;\n letter-spacing: 0.05em;\n text-transform: uppercase;\n color: color-mix(in srgb, var(--lb-text) 60%, transparent);\n}\n\n.lb-mix-match__modal-product-info .lb-bundle-variant-option-groups {\n margin-top: 4px;\n}\n\n/* Focus-ring suppression for mouse users. A small JS helper — see\n packages/widget/src/utils/input-mode.ts and bundle-widget.js — toggles\n .using-mouse / .using-keyboard on the widget root (or html in the Liquid\n path) based on the customer's current input device. Default is mouse, so\n click-to-focus doesn't leave a keyboard-style ring. The modal overlay\n gets its own selector because the Liquid path reparents it to body,\n outside the widget root. */\n.using-mouse .lb-bundle-widget :focus,\n.using-mouse .lb-bundle-widget :focus-visible,\n.using-mouse .lb-mix-match__modal-overlay :focus,\n.using-mouse .lb-mix-match__modal-overlay :focus-visible {\n outline: none;\n outline-offset: 0;\n box-shadow: none;\n}\n\n.lb-bundle-quantity {\n font-size: 12px;\n line-height: 16px;\n color: color-mix(in srgb, var(--lb-text) 60%, transparent);\n margin-left: 8px;\n white-space: nowrap;\n}\n\n/* Pricing row — label left, prices right */\n.lb-bundle-pricing {\n display: flex;\n align-items: baseline;\n justify-content: space-between;\n padding: 4px 0 8px;\n gap: 12px;\n}\n\n.lb-bundle-pricing__label {\n font-size: 16px;\n font-weight: 400;\n line-height: 20px;\n color: var(--lb-text);\n white-space: nowrap;\n}\n\n.lb-bundle-pricing__prices {\n display: flex;\n align-items: baseline;\n gap: 8px;\n}\n\n.lb-bundle-sale-price {\n font-size: 20px;\n font-weight: 700;\n line-height: 1;\n letter-spacing: -0.02em;\n color: var(--lb-text);\n}\n\n.lb-bundle-compare-price {\n font-size: 16px;\n font-weight: 400;\n line-height: 20px;\n color: color-mix(in srgb, var(--lb-text) 60%, transparent);\n text-decoration: line-through;\n}\n\n/* Savings bar — green banner below pricing */\n.lb-bundle-savings-bar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n background: var(--lb-savings-bar-bg);\n color: var(--lb-savings-bar-text);\n border: var(--lb-savings-bar-border-width) solid var(--lb-savings-bar-border-color);\n font-size: 16px;\n font-weight: 600;\n line-height: 20px;\n padding: 8px 12px;\n border-radius: var(--lb-savings-bar-radius);\n margin-bottom: 12px;\n}\n\n/* Quantity badge — overlay on thumbnail top-right */\n.lb-bundle-qty-badge.lb-bundle-qty-badge {\n position: absolute;\n top: -8px;\n right: -8px;\n /* --lb-qty-badge-display fallback is the \"on\" branch; Liquid sets 'none' when merchant disables. */\n display: var(--lb-qty-badge-display, flex);\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 24px;\n border-radius: 50%;\n background: var(--lb-qty-badge-bg);\n border: var(--lb-image-border-width) solid var(--lb-image-border-color);\n color: var(--lb-qty-badge-color);\n font-size: 12px;\n font-weight: 700;\n line-height: 0;\n text-align: center;\n z-index: 1;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);\n}\n\n/* Override Dawn's \\`div:empty\\` for savings bar when hidden */\n.lb-bundle-savings-bar:empty {\n display: none;\n}\n\n/* CTA button.\n * Label and spinner share a single 1×1 grid cell so the button's intrinsic\n * width/height stays fixed when swapping between them — no layout shift when\n * entering the loading state. Visibility (not display) is used so the hidden\n * child still contributes to the cell's min-content sizing. See the\n * [data-loading=\"true\"] rules below. */\n.lb-bundle-cta {\n display: grid;\n grid-template-rows: 1fr;\n grid-template-columns: 1fr;\n width: 100%;\n padding: 12px 16px;\n border: var(--lb-cta-border-width) solid var(--lb-cta-border-color);\n border-radius: var(--lb-cta-radius);\n font-size: 16px;\n font-weight: 600;\n line-height: 20px;\n cursor: pointer;\n text-align: center;\n transition: opacity 0.15s ease;\n font-family: inherit;\n}\n\n.lb-bundle-cta:not(:disabled) {\n background: var(--lb-primary-color);\n color: var(--lb-btn-text);\n}\n\n.lb-bundle-cta:not(:disabled):hover {\n opacity: 0.9;\n}\n\n.lb-bundle-cta:disabled {\n background: color-mix(in srgb, var(--lb-primary-color) 35%, var(--lb-bg));\n color: color-mix(in srgb, var(--lb-btn-text) 85%, transparent);\n cursor: not-allowed;\n}\n\n/* Both the label and the spinner occupy grid cell (1, 1). Only one is\n * visible at a time; the other keeps its box for sizing but is invisible. */\n.lb-cta-label,\n.lb-cta-spinner {\n grid-row: 1;\n grid-column: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n min-width: 0;\n}\n\n.lb-cta-spinner {\n visibility: hidden;\n}\n\n.lb-cta-spinner svg {\n width: 20px;\n height: 20px;\n animation: lb-cta-spin 0.8s linear infinite;\n}\n\n@keyframes lb-cta-spin {\n to { transform: rotate(360deg); }\n}\n\n.lb-bundle-cta[data-loading=\"true\"] {\n cursor: wait;\n pointer-events: none;\n}\n\n.lb-bundle-cta[data-loading=\"true\"] .lb-cta-label {\n visibility: hidden;\n}\n\n.lb-bundle-cta[data-loading=\"true\"] .lb-cta-spinner {\n visibility: visible;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .lb-cta-spinner svg {\n animation-duration: 2.5s;\n }\n}\n\n/* Error message */\n.lb-bundle-error {\n font-size: 16px;\n color: #D72C0D;\n margin-top: 8px;\n display: none;\n}\n\n.lb-bundle-error[data-visible=\"true\"] {\n display: block;\n}\n\n/* Visually hidden — accessible to screen readers only */\n.lb-visually-hidden {\n position: absolute !important;\n width: 1px !important;\n height: 1px !important;\n padding: 0 !important;\n margin: -1px !important;\n overflow: hidden !important;\n clip-path: inset(50%) !important;\n white-space: nowrap !important;\n border: 0 !important;\n}\n\n/* Placeholder SVG icon for missing images */\n.lb-bundle-placeholder-icon {\n width: 28px;\n height: 28px;\n stroke: color-mix(in srgb, var(--lb-text) 35%, transparent);\n stroke-width: 1.5;\n fill: none;\n}\n\n/* Out-of-stock product row */\n.lb-bundle-product-row--oos {\n opacity: 0.5;\n}\n\n.lb-bundle-oos-label {\n font-size: 12px;\n font-weight: 500;\n color: #D72C0D;\n white-space: nowrap;\n margin-left: auto;\n}\n\n/* A/B test: hide save badge until JS swaps the label (prevents flash of default) */\n.lb-ab-pending {\n visibility: hidden;\n}\n`;\nexport const BUNDLE_FIXED_CSS = `/* Lime Bundles — Fixed bundle styles */\n\n.lb-fixed__products {\n display: flex;\n flex-direction: column;\n gap: 0;\n margin: 0;\n max-height: var(--lb-list-max-height);\n overflow-y: auto;\n /* Custom scrollbar — text colour at 15% opacity (thumb) and 5% (track). */\n scrollbar-width: thin;\n scrollbar-color: color-mix(in srgb, var(--lb-text) 15%, transparent)\n color-mix(in srgb, var(--lb-text) 2%, transparent);\n}\n\n.lb-fixed__products::-webkit-scrollbar { width: 4px; }\n.lb-fixed__products::-webkit-scrollbar-track {\n background: color-mix(in srgb, var(--lb-text) 2%, transparent);\n border-radius: 2px;\n}\n.lb-fixed__products::-webkit-scrollbar-thumb {\n background: color-mix(in srgb, var(--lb-text) 15%, transparent);\n border-radius: 2px;\n}\n\n/* Fixed bundles: product rows */\n.lb-fixed .lb-bundle-product-row {\n gap: 20px;\n align-items: center;\n}\n\n/* Fixed bundles: larger thumbnails. Height comes from --lb-thumbnail-aspect-ratio\n (set on .lb-bundle-widget via Liquid / applyWidgetConfigVars). */\n.lb-fixed .lb-bundle-thumbnail {\n width: 60px;\n min-width: 60px;\n border-radius: var(--lb-image-border-radius);\n border: var(--lb-image-border-width) solid var(--lb-image-border-color);\n box-sizing: border-box;\n overflow: visible;\n}\n\n.lb-fixed .lb-bundle-thumbnail img {\n border-radius: max(0px, calc(var(--lb-image-border-radius) - var(--lb-image-border-width)));\n border: none;\n}\n\n/* Variant picker select — styled to match the variant badge aesthetic.\n Sits inside .lb-bundle-variant-option-group so vertical spacing is owned\n by the group/groups flex gap, not the select itself. */\n.lb-bundle-variant-select {\n display: inline-block;\n border: var(--lb-variant-border-width) solid var(--lb-variant-border-color);\n border-radius: var(--lb-variant-radius);\n padding: 8px 32px 8px 12px;\n font-size: 12px;\n line-height: 16px;\n color: var(--lb-text);\n background: var(--lb-bg);\n font-family: inherit;\n cursor: pointer;\n appearance: none;\n -webkit-appearance: none;\n background-image: var(--lb-variant-chevron);\n background-repeat: no-repeat;\n background-position: right 8px center;\n background-size: 12px;\n width: 50%;\n max-width: 50%;\n}\n\n.lb-bundle-variant-select:focus-visible {\n outline: 2px solid var(--lb-primary-color);\n outline-offset: 2px;\n}\n`;\nexport const BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles — Mix & Match styles */\n\n/* === Slot list ============================================================\n Caps the height of the slot stack so long bundles don't push the CTA off\n the page. Internal scroll with the same custom 4px scrollbar as the\n variant dropdown panel. */\n.lb-mix-match__slots {\n max-height: var(--lb-list-max-height);\n overflow-y: auto;\n scrollbar-width: thin;\n scrollbar-color: color-mix(in srgb, var(--lb-text) 15%, transparent)\n color-mix(in srgb, var(--lb-text) 2%, transparent);\n}\n\n.lb-mix-match__slots::-webkit-scrollbar { width: 4px; }\n.lb-mix-match__slots::-webkit-scrollbar-track {\n background: color-mix(in srgb, var(--lb-text) 2%, transparent);\n border-radius: 2px;\n}\n.lb-mix-match__slots::-webkit-scrollbar-thumb {\n background: color-mix(in srgb, var(--lb-text) 15%, transparent);\n border-radius: 2px;\n}\n\n/* === Progress Bar === */\n.lb-mix-match__progress {\n margin-bottom: 16px;\n}\n\n.lb-mix-match__progress-labels {\n display: flex;\n justify-content: space-between;\n margin-bottom: 8px;\n}\n\n.lb-mix-match__progress-count {\n font-size: 12px;\n font-weight: 500;\n line-height: 16px;\n color: color-mix(in srgb, var(--lb-text) 60%, transparent);\n}\n\n.lb-mix-match__progress-remaining {\n font-size: 12px;\n font-weight: 500;\n line-height: 16px;\n color: var(--lb-text);\n}\n\n.lb-mix-match__progress-track {\n width: 100%;\n height: 4px;\n background: color-mix(in srgb, var(--lb-text) 10%, transparent);\n border-radius: 4px;\n overflow: hidden;\n}\n\n.lb-mix-match__progress-fill {\n height: 100%;\n background: var(--lb-text);\n border-radius: 4px;\n transition: width 0.3s ease;\n}\n\n/* === Slots === */\n.lb-mix-match__slot {\n cursor: pointer;\n}\n\n.lb-mix-match .lb-bundle-product-row {\n align-items: center;\n}\n\n.lb-mix-match__slot--empty:focus-visible {\n outline: 2px solid var(--lb-primary-color);\n outline-offset: 2px;\n border-radius: 8px;\n}\n\n.lb-mix-match__slot--empty .lb-mix-match__empty-thumb {\n width: 60px;\n height: 60px;\n min-width: 60px;\n /* 2px border is intentionally independent of --lb-image-border-width —\n empty slots always need a visible dashed outline as an affordance,\n regardless of how the merchant has styled populated thumbnails. */\n border: 2px dashed color-mix(in srgb, var(--lb-text) 35%, transparent);\n border-radius: var(--lb-image-border-radius);\n box-sizing: border-box;\n display: flex;\n align-items: center;\n justify-content: center;\n color: color-mix(in srgb, var(--lb-text) 35%, transparent);\n}\n\n.lb-mix-match__empty-text {\n font-size: 16px;\n line-height: 20px;\n color: color-mix(in srgb, var(--lb-text) 45%, transparent);\n}\n\n/* Filled slot */\n.lb-mix-match__slot--filled {\n cursor: default;\n}\n\n/* Mix-match thumbnails. Height comes from --lb-thumbnail-aspect-ratio\n (set on .lb-bundle-widget via Liquid / applyWidgetConfigVars). */\n.lb-mix-match .lb-bundle-thumbnail {\n width: 60px;\n min-width: 60px;\n border-radius: var(--lb-image-border-radius);\n border: var(--lb-image-border-width) solid var(--lb-image-border-color);\n box-sizing: border-box;\n overflow: visible;\n}\n\n.lb-mix-match .lb-bundle-thumbnail img {\n border-radius: max(0px, calc(var(--lb-image-border-radius) - var(--lb-image-border-width)));\n border: none;\n}\n\n.lb-mix-match__slot--filled .lb-mix-match__filled-info {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n}\n\n.lb-mix-match__slot--filled .lb-mix-match__filled-title {\n font-size: 16px;\n font-weight: 700;\n line-height: 20px;\n color: var(--lb-text);\n text-decoration: none;\n overflow-wrap: break-word;\n}\n\n.lb-mix-match__slot--filled a.lb-mix-match__filled-title:hover {\n text-decoration: underline;\n}\n\n.lb-mix-match__slot--filled .lb-mix-match__filled-variant {\n font-size: 12px;\n line-height: 20px;\n margin-top: 2px;\n color: color-mix(in srgb, var(--lb-text) 60%, transparent);\n}\n\n.lb-mix-match__filled-price {\n /* --lb-product-price-display fallback is the \"on\" branch; Liquid sets 'none' when merchant disables. */\n display: var(--lb-product-price-display, block);\n font-size: 12px;\n line-height: 20px;\n margin-top: 4px;\n color: var(--lb-text);\n font-weight: 500;\n}\n\n.lb-mix-match__filled-compare {\n /* --lb-product-compare-display fallback is the \"on\" branch; Liquid sets 'none' when merchant disables. */\n display: var(--lb-product-compare-display, inline);\n font-size: 12px;\n font-weight: 400;\n color: color-mix(in srgb, var(--lb-text) 60%, transparent);\n text-decoration: line-through;\n margin-right: 4px;\n}\n\n.lb-mix-match__slot-remove {\n min-width: 44px;\n min-height: 44px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: none;\n border: none;\n cursor: pointer;\n color: var(--lb-text);\n padding: 0;\n margin-left: auto;\n border-radius: 8px;\n}\n\n.lb-mix-match__slot-remove:hover {\n color: var(--lb-text);\n}\n\n.lb-mix-match__slot-remove:focus-visible {\n outline: 2px solid var(--lb-primary-color);\n outline-offset: 2px;\n}\n\n/* Price placeholder */\n.lb-mix-match__price-placeholder {\n padding: 4px 0 16px;\n text-align: center;\n}\n\n.lb-mix-match__price-placeholder-text {\n font-size: 16px;\n color: var(--lb-text);\n}\n\n/* === Modal Overlay === */\n.lb-mix-match__modal-overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n z-index: 9999;\n display: flex;\n align-items: center;\n justify-content: center;\n opacity: 0;\n transition: opacity 0.2s ease-out;\n}\n\n.lb-mix-match__modal-overlay--open {\n opacity: 1;\n}\n\n/* === Modal Panel === */\n.lb-mix-match__modal {\n background: var(--lb-picker-bg);\n color: var(--lb-picker-text);\n border-radius: var(--lb-picker-radius);\n border: var(--lb-picker-border-width) solid var(--lb-picker-border-color);\n box-sizing: border-box;\n width: 100%;\n max-width: 480px;\n max-height: 70vh;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.16);\n transform: translateY(24px);\n transition: transform 0.25s ease-out;\n will-change: transform;\n}\n\n.lb-mix-match__modal-overlay--open .lb-mix-match__modal {\n transform: translateY(0);\n}\n\n/* === Modal Header === */\n.lb-mix-match__modal-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 20px 20px 12px;\n flex-shrink: 0;\n}\n\n.lb-mix-match__modal-title {\n font-size: 20px;\n font-weight: 600;\n line-height: 24px;\n margin: 0;\n color: inherit;\n}\n\n.lb-mix-match__modal-close {\n min-width: 44px;\n min-height: 44px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: none;\n border: none;\n cursor: pointer;\n color: var(--lb-picker-text);\n border-radius: 8px;\n padding: 0;\n margin: -12px -12px -12px 0;\n}\n\n.lb-mix-match__modal-close:focus-visible {\n outline: 2px solid var(--lb-primary-color);\n outline-offset: 2px;\n}\n\n/* === Modal Search === */\n.lb-mix-match__modal-search {\n padding: 0 20px 12px;\n position: relative;\n flex-shrink: 0;\n}\n\n.lb-mix-match__modal-search-input {\n width: 100%;\n padding: 12px 40px 12px 16px;\n border: var(--lb-picker-search-border-width) solid var(--lb-picker-search-border-color);\n border-radius: var(--lb-picker-search-radius);\n font-size: 16px;\n line-height: 20px;\n color: var(--lb-picker-text);\n background: var(--lb-picker-bg);\n box-sizing: border-box;\n -webkit-appearance: none;\n appearance: none;\n}\n\n.lb-mix-match__modal-search-input::placeholder {\n color: color-mix(in srgb, var(--lb-picker-text) 50%, transparent);\n}\n\n.lb-mix-match__modal-search-input:focus {\n outline: none;\n box-shadow: 0 0 0 1px var(--lb-primary-color);\n}\n\n.lb-mix-match__modal-search-clear {\n position: absolute;\n right: 32px;\n /* Anchor to the input area only — parent has padding-bottom: 12px which would\n otherwise push a top:50% center down by 6px. */\n top: 0;\n bottom: 12px;\n min-width: 32px;\n min-height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: none;\n border: none;\n cursor: pointer;\n color: var(--lb-picker-text);\n padding: 0;\n}\n\n/* === Modal Product List === */\n.lb-mix-match__modal-list {\n overflow-y: auto;\n flex: 1;\n padding: 0 20px;\n -webkit-overflow-scrolling: touch;\n}\n\n.lb-mix-match__modal-product {\n display: flex;\n align-items: center;\n gap: 20px;\n padding: 12px 0;\n border-bottom: 1px solid color-mix(in srgb, var(--lb-picker-text) 7%, transparent);\n}\n\n.lb-mix-match__modal-product:last-child {\n border-bottom: none;\n}\n\n.lb-mix-match__modal-product-thumb {\n position: relative;\n width: 48px;\n min-width: 48px;\n /* Modal picker thumbs follow the merchant's pickerThumbnailRatio —\n independent from the main widget's thumbnailRatio so a merchant can\n e.g. show tall picker thumbs with square main thumbs. */\n aspect-ratio: var(--lb-picker-thumbnail-aspect-ratio, 1 / 1);\n border-radius: var(--lb-picker-product-radius);\n border: var(--lb-picker-product-border-width) solid var(--lb-picker-product-border-color);\n box-sizing: border-box;\n overflow: visible;\n background: var(--lb-thumbnail-bg);\n}\n\n/* Qty badge inside the picker modal inherits picker-product border (width + color) plus\n inverted picker bg/text for clear contrast against the modal — always stays round\n (the badge shape is independent of the thumbnail shape). */\n.lb-mix-match__modal-product-thumb .lb-bundle-qty-badge.lb-bundle-qty-badge {\n /* --lb-picker-qty-badge-display fallback is the \"on\" branch; Liquid sets 'none' when merchant disables. */\n display: var(--lb-picker-qty-badge-display, flex);\n background: var(--lb-picker-qty-badge-bg);\n color: var(--lb-picker-qty-badge-color);\n border: var(--lb-picker-product-border-width) solid var(--lb-picker-product-border-color);\n}\n\n.lb-mix-match__modal-product-thumb img {\n width: 100%;\n /* Height + fit come from pickerThumbnailRatio — \"original\" sets both\n to auto/contain so the image renders uncropped at intrinsic ratio. */\n height: var(--lb-picker-thumbnail-img-height, 100%);\n object-fit: var(--lb-picker-thumbnail-img-fit, cover);\n border-radius: max(0px, calc(var(--lb-picker-product-radius) - var(--lb-picker-product-border-width)));\n}\n\n.lb-mix-match__modal-product-info {\n flex: 1;\n min-width: 0;\n}\n\n.lb-mix-match__modal-product-title {\n font-size: 16px;\n font-weight: 700;\n line-height: 20px;\n color: inherit;\n margin: 0;\n}\n\n.lb-mix-match__modal-product-price {\n font-size: 12px;\n line-height: 20px;\n color: inherit;\n margin: 4px 0 0;\n}\n\n.lb-mix-match__modal-product-unit-price {\n font-size: 11px;\n line-height: 16px;\n color: color-mix(in srgb, var(--lb-text) 60%, transparent);\n margin: 2px 0 0;\n}\n\n.lb-mix-match__modal-product-unit-price[hidden] {\n display: none;\n}\n\n.lb-mix-match__variant-select {\n font-size: 12px;\n padding: 4px 24px 4px 8px;\n border: var(--lb-picker-variant-border-width) solid var(--lb-picker-variant-border-color);\n border-radius: var(--lb-picker-variant-radius);\n background-color: var(--lb-picker-bg);\n background-image: var(--lb-picker-variant-chevron);\n background-repeat: no-repeat;\n background-position: right 6px center;\n background-size: 12px;\n color: var(--lb-picker-text);\n font-family: inherit;\n min-height: 32px;\n cursor: pointer;\n width: 50%;\n max-width: 50%;\n appearance: none;\n -webkit-appearance: none;\n}\n\n.lb-mix-match__variant-select:focus-visible {\n outline: 2px solid var(--lb-primary-color);\n outline-offset: 2px;\n}\n\n.lb-mix-match__modal-add {\n padding: 8px 20px;\n background: var(--lb-picker-add-bg);\n color: var(--lb-picker-add-label);\n border: var(--lb-picker-add-border-width) solid var(--lb-picker-add-border-color);\n border-radius: var(--lb-picker-add-radius);\n box-sizing: border-box;\n font-size: 12px;\n font-weight: 600;\n cursor: pointer;\n white-space: nowrap;\n flex-shrink: 0;\n}\n\n.lb-mix-match__modal-add:hover {\n opacity: 0.9;\n}\n\n.lb-mix-match__modal-add:focus-visible {\n outline: 2px solid var(--lb-primary-color);\n outline-offset: 2px;\n}\n\n.lb-mix-match__modal-add:disabled {\n background: color-mix(in srgb, var(--lb-picker-add-bg) 35%, var(--lb-picker-bg));\n color: color-mix(in srgb, var(--lb-picker-add-label) 85%, transparent);\n cursor: not-allowed;\n}\n\n/* Sold out product row */\n.lb-mix-match__modal-product--sold-out {\n opacity: 0.5;\n}\n\n.lb-mix-match__modal-product--sold-out .lb-mix-match__modal-sold-out-label {\n font-size: 12px;\n color: inherit;\n font-weight: 500;\n white-space: nowrap;\n}\n\n/* === Modal Empty State === */\n.lb-mix-match__modal-empty {\n padding: 32px 20px;\n text-align: center;\n}\n\n.lb-mix-match__modal-empty p {\n margin: 0;\n font-size: 16px;\n color: var(--lb-picker-text);\n}\n\n/* Hidden utility for search filtering */\n.lb-hidden {\n display: none !important;\n}\n\n/* === Mobile Full-Screen Modal === */\n@media (max-width: 767px) {\n .lb-mix-match__modal-overlay {\n align-items: flex-end;\n }\n\n .lb-mix-match__modal {\n max-width: 100%;\n max-height: 90vh;\n border-radius: 16px 16px 0 0;\n transform: translateY(100%);\n }\n\n .lb-mix-match__modal-overlay--open .lb-mix-match__modal {\n transform: translateY(0);\n }\n\n .lb-mix-match__variant-select {\n width: 80%;\n max-width: 80%;\n }\n}\n\n/* === Reduced Motion === */\n@media (prefers-reduced-motion: reduce) {\n .lb-mix-match__modal-overlay,\n .lb-mix-match__modal,\n .lb-mix-match__progress-fill {\n transition: none;\n }\n}\n`;\nexport const BUNDLE_VOLUME_CSS = `/* Lime Bundles — Volume / Quantity Breaks styles */\n\n.lb-volume__tiers {\n display: flex;\n flex-direction: column;\n gap: 12px;\n max-height: var(--lb-list-max-height);\n overflow-y: auto;\n /* Custom scrollbar — text colour at 15% opacity (thumb) and 5% (track). */\n scrollbar-width: thin;\n scrollbar-color: color-mix(in srgb, var(--lb-text) 15%, transparent)\n color-mix(in srgb, var(--lb-text) 2%, transparent);\n}\n\n.lb-volume__tiers::-webkit-scrollbar { width: 4px; }\n.lb-volume__tiers::-webkit-scrollbar-track {\n background: color-mix(in srgb, var(--lb-text) 2%, transparent);\n border-radius: 2px;\n}\n.lb-volume__tiers::-webkit-scrollbar-thumb {\n background: color-mix(in srgb, var(--lb-text) 15%, transparent);\n border-radius: 2px;\n}\n\n.lb-volume__tier {\n display: flex;\n align-items: center;\n gap: 12px;\n border: var(--lb-tier-border-width) solid var(--lb-tier-border-color);\n border-radius: var(--lb-tier-radius);\n padding: 12px 16px;\n cursor: pointer;\n position: relative;\n transition: border-color 0.15s ease;\n}\n\n.lb-volume__tier:hover {\n border-color: color-mix(in srgb, var(--lb-tier-border-color) 50%, black);\n}\n\n.lb-volume__tier:focus-visible {\n outline: 2px solid var(--lb-primary-color);\n outline-offset: 2px;\n}\n\n.lb-volume__tier[aria-checked=\"true\"] {\n border-color: var(--lb-tier-selected-border-color);\n outline: var(--lb-tier-selected-border-width) solid var(--lb-tier-selected-border-color);\n outline-offset: calc(-1 * var(--lb-tier-selected-border-width));\n}\n\n.lb-volume__radio {\n width: 20px;\n height: 20px;\n min-width: 20px;\n border: 2px solid var(--lb-text);\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: border-color 0.15s ease;\n}\n\n.lb-volume__tier[aria-checked=\"true\"] .lb-volume__radio {\n border-color: var(--lb-text);\n}\n\n.lb-volume__radio-dot {\n width: 12px;\n height: 12px;\n border-radius: 50%;\n background: transparent;\n transition: background 0.15s ease;\n}\n\n.lb-volume__tier[aria-checked=\"true\"] .lb-volume__radio-dot {\n background: var(--lb-text);\n}\n\n/* Tier content: 2x2 grid layout */\n.lb-volume__tier-grid {\n flex: 1;\n display: grid;\n row-gap: 4px;\n align-items: center;\n}\n\n.lb-volume__tier-label {\n font-size: 16px;\n font-weight: 700;\n color: var(--lb-text);\n}\n\n.lb-volume__tier-badge {\n flex-shrink: 0;\n font-size: 12px;\n line-height: 16px;\n color: var(--lb-popular-badge-text);\n background: var(--lb-popular-badge-bg);\n border: var(--lb-popular-badge-border-width) solid var(--lb-popular-badge-border-color);\n border-radius: var(--lb-popular-badge-radius);\n padding: 4px 8px;\n}\n\n.lb-volume__tier-price {\n grid-column: 1 / -1;\n font-size: 14px;\n font-weight: 500;\n color: var(--lb-text);\n}\n\n.lb-volume__tier-unit {\n font-size: 12px;\n color: var(--lb-text);\n}\n\n/* Compare-at (strikethrough) price */\n.lb-volume__tier-compare {\n font-size: 14px;\n line-height: 16px;\n color: color-mix(in srgb, var(--lb-text) 60%, transparent);\n text-decoration: line-through;\n margin-right: 4px;\n}\n\n\n`;\n\nexport const BUNDLE_DROPDOWN_CSS = `/**\n * Lime Bundles — Custom variant-picker dropdown styling.\n *\n * Reuses existing CSS variables: no new merchant-configurable surface.\n * --lb-variant-border-{width,color}, --lb-variant-radius, --lb-variant-chevron\n * --lb-bg, --lb-text, --lb-primary-color\n *\n * Mix-match modal context overrides via .lb-mix-match__modal scope to use\n * --lb-picker-variant-* and --lb-picker-bg.\n */\n\n/* Hide the native <select> while keeping it form-serializable and focusable\n programmatically. The .lb-dropdown-state marker is added by JS at bind\n time, so this rule matches every variant-select class (main widget,\n mix-match modal, future bundle types). aria-hidden + tabindex=-1\n (also set in JS) remove it from the accessibility tree. */\n.lb-dropdown-state {\n position: absolute !important;\n width: 1px !important;\n height: 1px !important;\n padding: 0 !important;\n margin: -1px !important;\n overflow: hidden !important;\n clip: rect(0 0 0 0) !important;\n white-space: nowrap !important;\n border: 0 !important;\n pointer-events: none !important;\n}\n\n/* Shell fills its parent column. */\n.lb-dropdown {\n position: relative;\n display: inline-block;\n width: 100%;\n max-width: 100%;\n font-family: inherit;\n}\n\n/* Trigger styled identically to the closed-state native select */\n.lb-dropdown-trigger {\n display: inline-flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n width: 100%;\n border: var(--lb-variant-border-width) solid var(--lb-variant-border-color);\n border-radius: var(--lb-variant-radius);\n padding: 8px 12px;\n font-size: 12px;\n line-height: 16px;\n color: var(--lb-text);\n background: var(--lb-bg);\n font-family: inherit;\n cursor: pointer;\n appearance: none;\n -webkit-appearance: none;\n text-align: start;\n transition: border-color 120ms ease;\n}\n\n.lb-dropdown-trigger:focus-visible {\n outline: 2px solid var(--lb-primary-color);\n outline-offset: 2px;\n}\n\n.lb-dropdown-trigger[aria-expanded=\"true\"] {\n border-color: var(--lb-text);\n}\n\n.lb-dropdown-trigger-value {\n flex: 1 1 auto;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n text-align: start;\n}\n\n.lb-dropdown-chevron {\n flex: 0 0 auto;\n width: 12px;\n height: 12px;\n background: var(--lb-variant-chevron) center / contain no-repeat;\n transition: transform 120ms ease;\n}\n\n.lb-dropdown-trigger[aria-expanded=\"true\"] .lb-dropdown-chevron {\n transform: rotate(180deg);\n}\n\n/* Popover panel — position: absolute against the .lb-dropdown shell\n (already position: relative). Top/left/width come from CSS so we\n never depend on JS having set inline coords by the time the panel\n becomes visible. JS only sets max-height. */\n.lb-dropdown-listbox {\n position: absolute;\n left: 0;\n /* Default to below-trigger placement so the panel doesn't overlap the\n trigger if data-placement is missing for any reason. The explicit\n [data-placement=\"down\"|\"up\"] rules below override this. */\n top: calc(100% + 4px);\n width: 100%;\n z-index: 9999;\n margin: 0;\n padding: 4px 0;\n list-style: none;\n background: var(--lb-bg);\n color: var(--lb-text);\n border: var(--lb-variant-border-width) solid var(--lb-variant-border-color);\n border-radius: var(--lb-variant-radius);\n box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);\n overflow-y: auto;\n overflow-x: hidden;\n /* Custom scrollbar — text colour at 15% opacity (thumb) and 5% (track). */\n scrollbar-width: thin;\n scrollbar-color: color-mix(in srgb, var(--lb-text) 15%, transparent)\n color-mix(in srgb, var(--lb-text) 2%, transparent);\n animation: lb-dropdown-in-down 120ms ease-out;\n transform-origin: top center;\n}\n\n.lb-dropdown-listbox[data-placement=\"down\"] {\n top: calc(100% + 4px);\n}\n\n.lb-dropdown-listbox[data-placement=\"up\"] {\n top: auto;\n bottom: calc(100% + 4px);\n animation-name: lb-dropdown-in-up;\n transform-origin: bottom center;\n}\n\n/* When portaled out of the .lb-dropdown shell (mix-match modal context:\n .lb-mix-match__modal applies translateY which would otherwise trap\n position:fixed), switch to fixed and let JS set viewport coords. */\n.lb-dropdown-listbox[data-lb-dropdown-portal] {\n position: fixed;\n top: auto;\n left: auto;\n bottom: auto;\n width: auto;\n}\n\n/* Custom scrollbar — Webkit/Blink: exact 4px width */\n.lb-dropdown-listbox::-webkit-scrollbar {\n width: 4px;\n}\n.lb-dropdown-listbox::-webkit-scrollbar-track {\n background: color-mix(in srgb, var(--lb-text) 2%, transparent);\n border-radius: 2px;\n}\n.lb-dropdown-listbox::-webkit-scrollbar-thumb {\n background: color-mix(in srgb, var(--lb-text) 15%, transparent);\n border-radius: 2px;\n}\n\n/* Options */\n.lb-dropdown-option {\n padding: 8px 12px;\n font-size: 12px;\n line-height: 16px;\n cursor: pointer;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n color: var(--lb-text);\n}\n\n.lb-dropdown-option[aria-selected=\"true\"] {\n font-weight: 600;\n}\n\n.lb-dropdown-option.is-active,\n.lb-dropdown-option:hover:not([aria-disabled=\"true\"]) {\n background: color-mix(in srgb, var(--lb-text) 2%, transparent);\n}\n\n.lb-dropdown-option[aria-disabled=\"true\"] {\n opacity: 0.4;\n cursor: not-allowed;\n}\n\n/* Animations */\n@keyframes lb-dropdown-in-down {\n from { opacity: 0; transform: translateY(-4px) scale(0.98); }\n to { opacity: 1; transform: translateY(0) scale(1); }\n}\n\n@keyframes lb-dropdown-in-up {\n from { opacity: 0; transform: translateY(4px) scale(0.98); }\n to { opacity: 1; transform: translateY(0) scale(1); }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .lb-dropdown-listbox { animation: none; }\n .lb-dropdown-chevron { transition: none; }\n .lb-dropdown-trigger { transition: none; }\n}\n\n/* Forced-colors mode (Windows high-contrast) */\n@media (forced-colors: active) {\n .lb-dropdown-trigger {\n border-color: ButtonBorder;\n color: ButtonText;\n background: ButtonFace;\n }\n .lb-dropdown-listbox {\n border-color: ButtonBorder;\n background: Canvas;\n color: CanvasText;\n }\n .lb-dropdown-option.is-active {\n background: Highlight;\n color: HighlightText;\n }\n}\n\n/* Mix-match modal context — use picker-scoped variables.\n No CSS fallbacks: --lb-picker-* are always emitted by bundle-widget.liquid\n because WidgetConfig.parse() fully hydrates the merchant config.\n See docs/solutions/ui-bugs/widget-css-single-source-defaults.md. */\n.lb-mix-match__modal .lb-dropdown-trigger {\n border-color: var(--lb-picker-variant-border-color);\n border-width: var(--lb-picker-variant-border-width);\n border-radius: var(--lb-picker-variant-radius);\n background: var(--lb-picker-bg);\n color: var(--lb-picker-text);\n}\n\n.lb-mix-match__modal .lb-dropdown-chevron {\n background-image: var(--lb-picker-variant-chevron);\n}\n\n/* Listbox is portaled out of the transformed .lb-mix-match__modal up to\n its [data-modal-overlay] parent, so picker-scoped rules anchor on the\n overlay attribute, not the modal class. */\n[data-modal-overlay] > .lb-dropdown-listbox {\n border-color: var(--lb-picker-variant-border-color);\n border-width: var(--lb-picker-variant-border-width);\n border-radius: var(--lb-picker-variant-radius);\n background: var(--lb-picker-bg);\n color: var(--lb-picker-text);\n scrollbar-color: color-mix(in srgb, var(--lb-picker-text) 15%, transparent)\n color-mix(in srgb, var(--lb-picker-text) 2%, transparent);\n}\n\n[data-modal-overlay] > .lb-dropdown-listbox::-webkit-scrollbar-track {\n background: color-mix(in srgb, var(--lb-picker-text) 2%, transparent);\n border-radius: 2px;\n}\n[data-modal-overlay] > .lb-dropdown-listbox::-webkit-scrollbar-thumb {\n background: color-mix(in srgb, var(--lb-picker-text) 15%, transparent);\n}\n`;\n\n/**\n * Skeleton styles for the web component's loading state. Rendered\n * synchronously in connectedCallback → renderLoading() so the host\n * element has intrinsic size from render-0 and doesn't shift layout\n * when the real bundle paints. Web-component only (see file header).\n */\nexport const BUNDLE_SKELETON_CSS = `/* Lime Bundles — web-component loading skeleton (not mirrored to theme assets) */\n\n.lb-bundle-widget--loading {\n display: block;\n padding: var(--lb-widget-pad, 20px);\n border: 1px solid var(--lb-border, #E5E5E5);\n border-radius: var(--lb-radius, 12px);\n background: var(--lb-bg, #FFFFFF);\n /* Contain layout/paint so the skeleton doesn't influence ancestor\n layout once the real content swaps in. */\n contain: layout paint;\n}\n\n.lb-bundle-widget--loading .lb-skeleton {\n background: linear-gradient(\n 90deg,\n rgba(0, 0, 0, 0.06) 0%,\n rgba(0, 0, 0, 0.10) 50%,\n rgba(0, 0, 0, 0.06) 100%\n );\n background-size: 200% 100%;\n border-radius: 6px;\n animation: lb-skeleton-pulse 1.4s ease-in-out infinite;\n}\n\n.lb-bundle-widget--loading .lb-skeleton--title {\n height: 28px;\n width: 60%;\n margin-bottom: 16px;\n}\n\n.lb-bundle-widget--loading .lb-skeleton--products {\n display: grid;\n gap: 12px;\n margin-bottom: 16px;\n}\n\n.lb-bundle-widget--loading .lb-skeleton--row {\n display: grid;\n grid-template-columns: 56px 1fr 60px;\n gap: 12px;\n align-items: center;\n}\n\n.lb-bundle-widget--loading .lb-skeleton--thumb {\n height: 56px;\n width: 56px;\n border-radius: 8px;\n}\n\n.lb-bundle-widget--loading .lb-skeleton--line {\n height: 14px;\n border-radius: 4px;\n}\n\n.lb-bundle-widget--loading .lb-skeleton--line + .lb-skeleton--line {\n margin-top: 8px;\n width: 70%;\n}\n\n.lb-bundle-widget--loading .lb-skeleton--price {\n height: 20px;\n width: 60px;\n}\n\n.lb-bundle-widget--loading .lb-skeleton--footer {\n margin-top: 16px;\n padding-top: 16px;\n border-top: 1px solid var(--lb-border, #E5E5E5);\n display: grid;\n grid-template-columns: 1fr auto;\n gap: 12px;\n align-items: center;\n}\n\n.lb-bundle-widget--loading .lb-skeleton--total {\n height: 24px;\n width: 40%;\n}\n\n.lb-bundle-widget--loading .lb-skeleton--cta {\n height: 44px;\n width: 140px;\n border-radius: var(--lb-cta-radius, 8px);\n}\n\n@keyframes lb-skeleton-pulse {\n 0% { background-position: 0% 50%; }\n 100% { background-position: -200% 50%; }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .lb-bundle-widget--loading .lb-skeleton {\n animation: none;\n }\n}\n`;\n\n","/**\n * @lime-bundles/widget — Vanilla JS Web Component for headless storefronts.\n *\n * Usage:\n * <script src=\"https://unpkg.com/@lime-bundles/widget/dist/lime-bundle.js\"></script>\n * <lime-bundle shop-domain=\"...\" storefront-token=\"...\" bundle-gid=\"...\"></lime-bundle>\n */\nimport { LimeBundleElement } from \"./lime-bundle\";\n\n// Register custom element\nif (\n typeof customElements !== \"undefined\" &&\n !customElements.get(\"lime-bundle\")\n) {\n customElements.define(\"lime-bundle\", LimeBundleElement);\n}\n\nexport { LimeBundleElement };\nexport { trackInputMode } from \"./utils/input-mode\";\n"],"mappings":";AAgDA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OASK;;;ACpDP;AAAA,EACE;AAAA,OAKK;;;ACdP,SAAS,gBAAgB;AAEzB,IAAM,EAAE,iBAAiB,qBAAqB,WAAW,kBAAkB,IACzE;AAKF,IAAM,iBAAiB;AACvB,IAAM,aAAa;AACnB,IAAM,oBAAoB;AAuB1B,IAAM,gBAAoC,CAAC;AAI3C,SAAS,kBAAkB,OAAc;AACvC,QAAM,OAAO,MAAM,aAAa;AAChC,WAAS,IAAI,cAAc,SAAS,GAAG,KAAK,GAAG,KAAK;AAClD,UAAM,OAAO,cAAc,CAAC;AAC5B,QAAI,CAAC,KAAK,SAAS,KAAK,KAAK,KAAK,CAAC,KAAK,SAAS,KAAK,OAAO,GAAG;AAC9D,WAAK,MAAM;AAAA,IACb;AAAA,EACF;AACF;AAEA,SAAS,cAAc;AACrB,WAAS,IAAI,cAAc,SAAS,GAAG,KAAK,GAAG,IAAK,eAAc,CAAC,EAAE,MAAM;AAC7E;AAEA,IAAI,uBAAuB;AAC3B,SAAS,0BAA0B;AACjC,MAAI,qBAAsB;AAC1B,WAAS,iBAAiB,eAAe,mBAAmB,IAAI;AAChE,SAAO,iBAAiB,UAAU,mBAAmB,IAAI;AACzD,SAAO,iBAAiB,UAAU,WAAW;AAC7C,yBAAuB;AACzB;AAEA,SAAS,0BAA0B;AACjC,MAAI,CAAC,wBAAwB,cAAc,SAAS,EAAG;AACvD,WAAS,oBAAoB,eAAe,mBAAmB,IAAI;AACnE,SAAO,oBAAoB,UAAU,mBAAmB,IAAI;AAC5D,SAAO,oBAAoB,UAAU,WAAW;AAChD,yBAAuB;AACzB;AAeA,SAAS,YAAY,QAA0C;AAC7D,QAAM,MAAqB,CAAC;AAC5B,WAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,QAAQ,KAAK;AAC9C,UAAM,IAAI,OAAO,QAAQ,CAAC;AAC1B,QAAI,KAAK,EAAE,UAAU,EAAE,UAAU,OAAO,EAAE,eAAe,EAAE,MAAM,CAAC;AAAA,EACpE;AACA,SAAO;AACT;AAEA,SAAS,aAAa,MAA6B;AACjD,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,IAAK,KAAI,CAAC,KAAK,CAAC,EAAE,SAAU,QAAO;AACpE,SAAO;AACT;AAMA,IAAM,yBAAyB;AAAA,EAC7B;AAAA,EACA;AACF;AAEA,IAAM,gBAAgB,uBAAuB;AAAA,EAC3C,CAAC,MAAM,UAAU,CAAC;AACpB,EAAE,KAAK,IAAI;AAEJ,SAAS,aACd,QACyB;AACzB,QAAM,OAAO;AACb,MAAI,OAAO,UAAU,SAAS,mBAAmB,GAAG;AAClD,WAAO,KAAK,wBAAwB;AAAA,EACtC;AAEA,QAAM,MAAM,OAAO;AACnB,QAAM,WAAW,OAAO,YAAY;AACpC,QAAM,YAAY,OAAO,aAAa,YAAY,KAAK;AACvD,QAAM,SAAS,SAAS,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC;AAE9D,SAAO,UAAU,IAAI,mBAAmB;AACxC,SAAO,aAAa,eAAe,MAAM;AACzC,SAAO,aAAa,YAAY,IAAI;AAEpC,QAAM,QAAQ,IAAI,cAAc,KAAK;AACrC,QAAM,YAAY;AAClB,QAAM,aAAa,oBAAoB,EAAE;AAEzC,QAAM,UAAU,IAAI,cAAc,QAAQ;AAC1C,UAAQ,OAAO;AACf,UAAQ,YAAY;AACpB,UAAQ,aAAa,QAAQ,UAAU;AACvC,UAAQ,aAAa,iBAAiB,SAAS;AAC/C,UAAQ,aAAa,iBAAiB,OAAO;AAC7C,QAAM,YAAY,GAAG,MAAM;AAC3B,UAAQ,aAAa,iBAAiB,SAAS;AAC/C,MAAI,UAAW,SAAQ,aAAa,cAAc,SAAS;AAE3D,QAAM,eAAe,IAAI,cAAc,MAAM;AAC7C,eAAa,YAAY;AAEzB,QAAM,UAAU,IAAI,cAAc,MAAM;AACxC,UAAQ,YAAY;AACpB,UAAQ,aAAa,eAAe,MAAM;AAE1C,UAAQ,YAAY,YAAY;AAChC,UAAQ,YAAY,OAAO;AAE3B,QAAM,UAAU,IAAI,cAAc,IAAI;AACtC,UAAQ,KAAK;AACb,UAAQ,YAAY;AACpB,UAAQ,aAAa,QAAQ,SAAS;AACtC,MAAI,UAAW,SAAQ,aAAa,cAAc,SAAS;AAC3D,UAAQ,SAAS;AAEjB,QAAM,YAAY,OAAO;AACzB,SAAO,YAAY,aAAa,OAAO,OAAO,WAAW;AAOzD,QAAM,eAAe,OAAO,QAAQ,sBAAsB;AAC1D,MAAI,cAAc;AAChB,iBAAa,YAAY,OAAO;AAChC,YAAQ,aAAa,2BAA2B,EAAE;AAAA,EACpD,OAAO;AACL,UAAM,YAAY,OAAO;AAAA,EAC3B;AAEA,MAAI,SAAS;AACb,MAAI,cAAc;AAClB,MAAI,YAA4B,oBAAoB;AACpD,MAAI,YAA6B,CAAC;AAClC,MAAI;AAEJ,WAAS,iBAAiB;AACxB,UAAM,OAAO,YAAY,MAAM;AAC/B,UAAM,MAAM,OAAO;AACnB,iBAAa,cAAc,OAAO,KAAK,KAAK,GAAG,IAAI,KAAK,GAAG,EAAE,QAAQ;AAErE,WAAO,QAAQ,WAAY,SAAQ,YAAY,QAAQ,UAAU;AACjE,gBAAY,CAAC;AAEb,aAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,YAAM,KAAK,IAAI,cAAc,IAAI;AACjC,SAAG,KAAK,GAAG,MAAM,QAAQ,CAAC;AAC1B,SAAG,YAAY;AACf,SAAG,aAAa,QAAQ,QAAQ;AAChC,SAAG,aAAa,iBAAiB,MAAM,MAAM,SAAS,OAAO;AAC7D,UAAI,KAAK,CAAC,EAAE,SAAU,IAAG,aAAa,iBAAiB,MAAM;AAC7D,SAAG,aAAa,cAAc,OAAO,QAAQ,CAAC,EAAE,KAAK;AACrD,SAAG,aAAa,cAAc,OAAO,CAAC,CAAC;AACvC,SAAG,cAAc,KAAK,CAAC,EAAE;AACzB,cAAQ,YAAY,EAAE;AACtB,gBAAU,KAAK,EAAE;AAAA,IACnB;AAAA,EACF;AAEA,WAAS,UAAU,UAAkB;AACnC,QAAI,eAAe,KAAK,UAAU,WAAW,GAAG;AAC9C,gBAAU,WAAW,EAAE,UAAU,OAAO,WAAW;AAAA,IACrD;AACA,kBAAc;AACd,QAAI,YAAY,KAAK,UAAU,QAAQ,GAAG;AACxC,YAAM,KAAK,UAAU,QAAQ;AAC7B,SAAG,UAAU,IAAI,WAAW;AAC5B,cAAQ,aAAa,yBAAyB,GAAG,EAAE;AAKnD,YAAM,QAAQ,GAAG;AACjB,YAAM,WAAW,QAAQ,GAAG;AAC5B,YAAM,SAAS,QAAQ;AACvB,YAAM,YAAY,SAAS,QAAQ;AACnC,UAAI,QAAQ,QAAQ;AAClB,gBAAQ,YAAY;AAAA,MACtB,WAAW,WAAW,WAAW;AAC/B,gBAAQ,YAAY,WAAW,QAAQ;AAAA,MACzC;AAAA,IACF,OAAO;AACL,cAAQ,aAAa,yBAAyB,EAAE;AAAA,IAClD;AAAA,EACF;AAEA,WAAS,WAAoB;AAC3B,UAAM,OAAO,QAAQ,sBAAsB;AAC3C,QAAI,KAAK,UAAU,EAAG,QAAO;AAC7B,UAAM,eAAe,KAAK,IAAI,UAAU,UAAU,GAAG,iBAAiB;AACtE,UAAM,gBAAgB,eAAe,iBAAiB;AACtD,UAAM,SAAS,gBAAgB;AAAA,MAC7B,SAAS;AAAA,QACP,KAAK,KAAK;AAAA,QACV,QAAQ,KAAK;AAAA,QACb,MAAM,KAAK;AAAA,QACX,OAAO,KAAK;AAAA,MACd;AAAA,MACA,gBAAgB,OAAO;AAAA,MACvB;AAAA,IACF,CAAC;AACD,YAAQ,aAAa,kBAAkB,OAAO,SAAS;AACvD,YAAQ,MAAM,YAAY,GAAG,OAAO,SAAS;AAI7C,QAAI,QAAQ,aAAa,yBAAyB,GAAG;AACnD,cAAQ,MAAM,MAAM,GAAG,OAAO,SAAS;AACvC,cAAQ,MAAM,OAAO,GAAG,OAAO,UAAU;AACzC,cAAQ,MAAM,QAAQ,GAAG,OAAO,KAAK;AAAA,IACvC;AACA,WAAO;AAAA,EACT;AAEA,WAAS,OAAO;AACd,QAAI,OAAQ;AAEZ,aAAS,IAAI,cAAc,SAAS,GAAG,KAAK,GAAG,KAAK;AAClD,UAAI,cAAc,CAAC,MAAM,SAAU,eAAc,CAAC,EAAE,MAAM;AAAA,IAC5D;AACA,aAAS;AACT,YAAQ,SAAS;AACjB,YAAQ,aAAa,iBAAiB,MAAM;AAC5C,QAAI,CAAC,SAAS,GAAG;AACf,4BAAsB,MAAM,SAAS,CAAC;AAAA,IACxC;AACA,UAAM,OAAO,YAAY,MAAM;AAC/B,UAAM,SAAS,OAAO;AACtB,QAAI,UAAU,KAAK,KAAK,MAAM,KAAK,CAAC,KAAK,MAAM,EAAE,UAAU;AACzD,gBAAU,MAAM;AAAA,IAClB,OAAO;AACL,gBAAU,aAAa,IAAI,CAAC;AAAA,IAC9B;AACA,kBAAc,KAAK,QAAQ;AAC3B,QAAI,cAAc,WAAW,EAAG,yBAAwB;AAAA,EAC1D;AAEA,WAAS,MAAM,cAAuB;AACpC,QAAI,CAAC,OAAQ;AACb,aAAS;AACT,YAAQ,SAAS;AACjB,YAAQ,aAAa,iBAAiB,OAAO;AAC7C,YAAQ,aAAa,yBAAyB,EAAE;AAChD,QAAI,eAAe,KAAK,UAAU,WAAW,GAAG;AAC9C,gBAAU,WAAW,EAAE,UAAU,OAAO,WAAW;AAAA,IACrD;AACA,kBAAc;AACd,UAAM,MAAM,cAAc,QAAQ,QAAQ;AAC1C,QAAI,OAAO,EAAG,eAAc,OAAO,KAAK,CAAC;AACzC,QAAI,cAAc,WAAW,EAAG,yBAAwB;AACxD,QAAI,aAAc,SAAQ,MAAM;AAAA,EAClC;AAEA,WAAS,OAAO,OAAe;AAC7B,UAAM,MAAM,OAAO,QAAQ,KAAK;AAChC,QAAI,CAAC,OAAO,IAAI,SAAU;AAC1B,QAAI,OAAO,UAAU,IAAI,OAAO;AAC9B,aAAO,QAAQ,IAAI;AACnB,YAAM,QAAQ,IAAI,MAAM,UAAU,EAAE,SAAS,KAAK,CAAC;AACnD,aAAO,cAAc,KAAK;AAAA,IAC5B;AACA,mBAAe;AACf,UAAM,IAAI;AAAA,EACZ;AAEA,WAAS,YAAY,QAAwB;AAC3C,YAAQ,OAAO,MAAM;AAAA,MACnB,KAAK;AACH,aAAK;AACL,YAAI,OAAO,eAAe,EAAG,WAAU,OAAO,WAAW;AACzD;AAAA,MACF,KAAK;AACH,cAAM,OAAO,YAAY;AACzB;AAAA,MACF,KAAK;AACH,kBAAU,OAAO,WAAW;AAC5B;AAAA,MACF,KAAK;AACH,eAAO,OAAO,KAAK;AACnB;AAAA,MACF,KAAK,cAAc;AACjB,cAAM,OAAO,YAAY,MAAM;AAC/B,cAAM,SAAS;AAAA,UACb;AAAA,UACA,OAAO;AAAA,UACP,KAAK,IAAI;AAAA,UACT;AAAA,QACF;AACA,oBAAY,OAAO;AACnB,YAAI,OAAO,iBAAiB,MAAM;AAChC,cAAI,CAAC,OAAQ,MAAK;AAClB,oBAAU,OAAO,YAAY;AAAA,QAC/B;AACA;AAAA,MACF;AAAA,MACA,KAAK;AACH;AAAA,MACF,SAAS;AACP,cAAM,cAAqB;AAC3B,aAAK;AAAA,MACP;AAAA,IACF;AAAA,EACF;AAEA,WAAS,UAAU,OAAsB;AACvC,UAAM,OAAO,YAAY,MAAM;AAC/B,UAAM,SAAS;AAAA,MACb;AAAA,QACE,KAAK,MAAM;AAAA,QACX,SAAS,MAAM;AAAA,QACf,SAAS,MAAM;AAAA,QACf,QAAQ,MAAM;AAAA,QACd,UAAU,MAAM;AAAA,MAClB;AAAA,MACA;AAAA,QACE;AAAA,QACA;AAAA,QACA,eAAe,OAAO;AAAA,QACtB,SAAS;AAAA,MACX;AAAA,IACF;AACA,QAAI,OAAO,eAAgB,OAAM,eAAe;AAChD,gBAAY,MAAM;AAAA,EACpB;AAEA,WAAS,eAAe,OAAmB;AACzC,UAAM,eAAe;AACrB,QAAI,OAAQ,OAAM,KAAK;AAAA,QAClB,MAAK;AAAA,EACZ;AAEA,WAAS,eAAe,OAAmB;AACzC,QAAI,SAAS,MAAM;AACnB,WAAO,UAAU,WAAW,SAAS;AACnC,UAAI,OAAO,WAAW,SAAS,oBAAoB,GAAG;AACpD,cAAM,MAAM,SAAS,OAAO,aAAa,YAAY,KAAK,IAAI,EAAE;AAChE,YAAI,CAAC,OAAO,MAAM,GAAG,GAAG;AACtB,iBAAO,GAAG;AACV;AAAA,QACF;AAAA,MACF;AACA,eAAS,OAAO;AAAA,IAClB;AAAA,EACF;AAEA,WAAS,mBAAmB,OAAmB;AAC7C,QAAI,SAAS,MAAM;AACnB,WAAO,UAAU,WAAW,SAAS;AACnC,UAAI,OAAO,WAAW,SAAS,oBAAoB,GAAG;AACpD,YAAI,OAAO,aAAa,eAAe,MAAM,OAAQ;AACrD,cAAM,MAAM,SAAS,OAAO,aAAa,YAAY,KAAK,IAAI,EAAE;AAChE,YAAI,CAAC,OAAO,MAAM,GAAG,KAAK,QAAQ,YAAa,WAAU,GAAG;AAC5D;AAAA,MACF;AACA,eAAS,OAAO;AAAA,IAClB;AAAA,EACF;AAEA,WAAS,kBAAkB;AAKzB,eAAW,MAAM;AACf,UAAI,CAAC,OAAQ;AACb,YAAM,SAAS,SAAS,iBAAiB,IAAI;AAC7C,UAAI,CAAC,MAAM,SAAS,MAAM,EAAG,OAAM,KAAK;AAAA,IAC1C,GAAG,CAAC;AAAA,EACN;AAEA,WAAS,iBAAiB;AACxB,mBAAe;AAAA,EACjB;AAEA,QAAM,WAAW,IAAI,iBAAiB,MAAM;AAC1C,mBAAe;AAAA,EACjB,CAAC;AACD,WAAS,QAAQ,QAAQ;AAAA,IACvB,WAAW;AAAA,IACX,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,iBAAiB,CAAC,YAAY,SAAS,UAAU;AAAA,EACnD,CAAC;AAQD,QAAM,qBAAqB,CAAC,UAAsB,MAAM,eAAe;AAEvE,UAAQ,iBAAiB,SAAS,cAAc;AAChD,UAAQ,iBAAiB,WAAW,SAAS;AAC7C,QAAM,iBAAiB,YAAY,eAAe;AAClD,UAAQ,iBAAiB,aAAa,kBAAkB;AACxD,UAAQ,iBAAiB,SAAS,cAAc;AAChD,UAAQ,iBAAiB,aAAa,kBAAkB;AACxD,SAAO,iBAAiB,UAAU,cAAc;AAEhD,WAAS,UAAU;AACjB,QAAI,OAAQ,OAAM,KAAK;AACvB,aAAS,WAAW;AACpB,YAAQ,oBAAoB,SAAS,cAAc;AACnD,YAAQ,oBAAoB,WAAW,SAAS;AAChD,UAAM,oBAAoB,YAAY,eAAe;AACrD,YAAQ,oBAAoB,aAAa,kBAAkB;AAC3D,YAAQ,oBAAoB,SAAS,cAAc;AACnD,YAAQ,oBAAoB,aAAa,kBAAkB;AAC3D,WAAO,oBAAoB,UAAU,cAAc;AACnD,QAAI,MAAM,WAAY,OAAM,WAAW,YAAY,KAAK;AACxD,QAAI,QAAQ,WAAY,SAAQ,WAAW,YAAY,OAAO;AAC9D,WAAO,UAAU,OAAO,mBAAmB;AAC3C,WAAO,gBAAgB,aAAa;AACpC,WAAO,gBAAgB,UAAU;AACjC,WAAO,KAAK;AAAA,EACd;AAEA,aAAW;AAAA,IACT;AAAA,IACA;AAAA,IACA;AAAA,IACA,OAAO,MAAM,MAAM,KAAK;AAAA,IACxB;AAAA,EACF;AACA,OAAK,uBAAuB;AAE5B,iBAAe;AACf,SAAO;AACT;AAEO,SAAS,iBAAiB,MAAsC;AACrE,QAAM,UAAU,KAAK,iBAAiB,aAAa;AACnD,QAAM,YAAgC,CAAC;AACvC,UAAQ,QAAQ,CAAC,QAAQ;AACvB,UAAM,OAAO,aAAa,GAAwB;AAClD,QAAI,KAAM,WAAU,KAAK,IAAI;AAAA,EAC/B,CAAC;AACD,SAAO;AACT;AAEO,SAAS,mBAAmB,MAAwB;AACzD,QAAM,QAAQ,KAAK,iBAAiB,0BAA0B;AAC9D,QAAM,QAAQ,CAAC,QAAQ;AACrB,UAAM,OAAQ,IAA2B;AACzC,QAAI,KAAM,MAAK,QAAQ;AAAA,EACzB,CAAC;AACH;;;AC7eA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;;;ACdP,SAAS,uBAAuB;AASzB,SAAS,gBAAgB,WAA2C;AACzE,QAAM,SAAS,SAAS,SAAS;AACjC,MAAI,WAAW,KAAM,QAAO;AAE5B,MAAI,UAAU,KAAK,IAAI,EAAG,QAAO;AACjC,QAAM,SAAiB;AAEvB,QAAM,OAAO,SAAS,cAAc,KAAK;AACzC,OAAK,YAAY;AACjB,OAAK,aAAa,kBAAkB,EAAE;AAEtC,QAAM,YAAY,SAAS,cAAc,KAAK;AAC9C,YAAU,YAAY;AACtB,QAAM,YAAY,SAAS,cAAc,MAAM;AAC/C,YAAU,cAAc;AACxB,YAAU,YAAY,SAAS;AAC/B,OAAK,YAAY,SAAS;AAE1B,QAAM,QAAQ,SAAS,cAAc,MAAM;AAC3C,QAAM,YAAY;AAClB,QAAM,aAAa,wBAAwB,EAAE;AAC7C,OAAK,YAAY,KAAK;AAEtB,MAAI,aAAoD;AAExD,WAAS,OAAO;AACd,UAAM,SAAS,SAAS,KAAK,IAAI;AACjC,QAAI,UAAU,GAAG;AACf,WAAK,MAAM,UAAU;AACrB,WAAK;AACL;AAAA,IACF;AACA,UAAM,cAAc,gBAAgB,MAAM;AAAA,EAC5C;AAEA,WAAS,OAAO;AACd,QAAI,eAAe,MAAM;AACvB,oBAAc,UAAU;AACxB,mBAAa;AAAA,IACf;AAAA,EACF;AAEA,OAAK;AACL,eAAa,YAAY,MAAM,GAAI;AAEnC,SAAO,EAAE,IAAI,MAAM,KAAK;AAC1B;AAEA,SAAS,SAAS,KAA4B;AAC5C,QAAM,IAAI,KAAK,MAAM,GAAG;AACxB,SAAO,OAAO,SAAS,CAAC,IAAI,IAAI;AAClC;;;ACvCO,SAAS,eAAe,OAAkC;AAC/D,QAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,SAAO,OAAO;AACd,SAAO,YAAY;AACnB,SAAO,aAAa,mBAAmB,EAAE;AAEzC,QAAM,YAAY,SAAS,cAAc,MAAM;AAC/C,YAAU,YAAY;AACtB,YAAU,aAAa,kBAAkB,EAAE;AAC3C,YAAU,cAAc;AACxB,SAAO,YAAY,SAAS;AAE5B,QAAM,cAAc,SAAS,cAAc,MAAM;AACjD,cAAY,YAAY;AACxB,cAAY,aAAa,oBAAoB,EAAE;AAC/C,cAAY,aAAa,eAAe,MAAM;AAE9C,cAAY,YACV;AAGF,SAAO,YAAY,WAAW;AAE9B,SAAO;AACT;AAkBO,SAAS,YAAY,QAA2B,MAAoB;AACzE,QAAM,QAAQ,OAAO,cAA2B,kBAAkB;AAClE,MAAI,OAAO;AACT,UAAM,cAAc;AAAA,EACtB;AACF;;;ACjEO,SAAS,GACd,KACA,WACA,QAAgC,CAAC,GACpB;AACb,QAAM,OAAO,SAAS,cAAc,GAAG;AACvC,MAAI,UAAW,MAAK,YAAY;AAChC,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,KAAK,GAAG;AAC1C,SAAK,aAAa,GAAG,CAAC;AAAA,EACxB;AACA,SAAO;AACT;;;ACZA;AAAA,EACE;AAAA,EACA;AAAA,OAEK;;;AN8BP,IAAM,wBAAwB;AAAA;AAAA;AAAA;AAAA;AAAA;AAmBvB,SAAS,kBACd,WACA,QACA,aACA,WACA;AACA,QAAM,KAAK,OAAO;AAKlB,QAAM,SAAS,CAAC,WAAmB,cACjC,iBAAiB,QAAQ,WAAW,SAAS;AAG/C,QAAM,OAA0B,CAAC;AACjC,MAAI,WAAW;AACf,SAAO,SAAS,QAAQ,CAAC,SAAS,QAAQ;AACxC,UAAM,MAAM,cAAc,QAAQ,SAAS,GAAG;AAI9C,QAAI,IAAI,QAAQ,EAAG;AACnB,QAAI,IAAI,OAAO;AACb;AACA,UAAI,GAAG,uBAAuB,OAAQ;AAAA,IACxC;AACA,SAAK,KAAK,GAAG;AAAA,EACf,CAAC;AAMD,MAAI,KAAK,WAAW,EAAG;AAEvB,QAAM,WACJ,OAAO,SAAS,CAAC,GAAG,WAAW,gBAAgB,gBAAgB;AAEjE,QAAM,OAAO,GAAG,OAAO,YAAY;AAAA,IACjC,sBAAsB,OAAO,eAAe;AAAA,IAC5C,uBAAuB,OAAO,OAAO,eAAe,aAAa;AAAA,EACnE,CAAC;AAGD,QAAM,eAAe,aAAa,QAAQ,QAAQ;AAClD,OAAK,YAAY,aAAa,EAAE;AAGhC,MAAI,GAAG,UAAU,iBAAiB,OAAO,QAAQ;AAC/C,UAAM,YAAY,gBAAgB,OAAO,MAAM;AAC/C,QAAI,WAAW;AACb,WAAK,YAAY,UAAU,EAAE;AAC7B,kBAAY,UAAU,IAAI;AAAA,IAC5B;AAAA,EACF;AAGA,QAAM,OAAO,GAAG,OAAO,oBAAoB;AAC3C,QAAM,aAAyD,CAAC;AAChE,OAAK,QAAQ,CAAC,aAAa;AACzB,UAAM,SAAS,iBAAiB,UAAU,UAAU,QAAQ,MAAM;AAEhE,oBAAc;AAAA,IAChB,CAAC;AACD,eAAW,KAAK,MAAM;AACtB,SAAK,YAAY,OAAO,EAAE;AAAA,EAC5B,CAAC;AACD,OAAK,YAAY,IAAI;AAErB,OAAK,YAAY,GAAG,OAAO,mBAAmB,CAAC;AAG/C,QAAM,gBAAgB,iBAAiB,MAAM;AAC7C,OAAK,YAAY,cAAc,EAAE;AACjC,QAAM,mBAAmB,GAAG,WAAW,UAAU,iBAAiB,IAAI;AACtE,MAAI,iBAAkB,MAAK,YAAY,iBAAiB,EAAE;AAG1D,QAAM,MAAM,UAAU,QAAQ,UAAU,MAAM;AAC5C,UAAM,QAAyB,KAC5B,OAAO,CAAC,MAAM,EAAE,QAAQ,EACxB,IAAI,CAAC,OAAO;AAAA,MACX,eAAe,EAAE,SAAU;AAAA,MAC3B,UAAU,EAAE;AAAA,MACZ,YAAY;AAAA,QACV,EAAE,KAAK,oBAAoB,OAAO,OAAO,GAAG;AAAA,QAC5C,EAAE,KAAK,qBAAqB,OAAO,OAAO,WAAW;AAAA,MACvD;AAAA,IACF,EAAE;AACJ,QAAI,MAAM,WAAW,EAAG;AACxB,gBAAY,KAAK;AAAA,EACnB,CAAC;AACD,OAAK,YAAY,GAAG;AAEpB,OAAK;AAAA,IACH,GAAG,KAAK,mBAAmB,EAAE,cAAc,IAAI,aAAa,SAAS,CAAC;AAAA,EACxE;AACA,OAAK;AAAA,IACH,GAAG,QAAQ,sBAAsB;AAAA,MAC/B,eAAe;AAAA,MACf,aAAa;AAAA,IACf,CAAC;AAAA,EACH;AAEA,YAAU,YAAY,IAAI;AAK1B,mBAAiB,IAAI;AACrB,cAAY,MAAM,mBAAmB,IAAI,CAAC;AAE1C,gBAAc;AAEd,WAAS,gBAAgB;AACvB,UAAM,aAAa,KAAK,OAAO,CAAC,KAAK,MAAM;AACzC,UAAI,CAAC,EAAE,SAAU,QAAO;AACxB,YAAM,OAAO,WAAW,EAAE,SAAS,MAAM,MAAM;AAC/C,aAAO,MAAM,OAAO,EAAE;AAAA,IACxB,GAAG,CAAC;AACJ,UAAM,YAAY,YAAY,YAAY,OAAO,gBAAgB,IAAI;AACrE,UAAM,eAAe,KAAK,IAAI,GAAG,aAAa,SAAS;AAEvD,kBAAc,OAAO,EAAE,YAAY,WAAW,cAAc,SAAS,CAAC;AACtE,QAAI,kBAAkB;AACpB,uBAAiB,OAAO,EAAE,cAAc,SAAS,CAAC;AAAA,IACpD;AACA,iBAAa;AAAA,MACX,kBAAkB,QAAQ,YAAY,WAAW,QAAQ;AAAA,IAC3D;AAAA,EACF;AACF;AAIA,SAAS,cACP,QACA,SACA,cACiB;AACjB,QAAM,qBACJ,OAAO,qBAAqB,YAAY,KAAK;AAO/C,QAAM,iBACJ,sBAAsB,mBAAmB,SAAS,IAC9C,QAAQ,SAAS,MAAM,OAAO,CAAC,MAAM,mBAAmB,SAAS,EAAE,EAAE,CAAC,IACtE,QAAQ,SAAS;AAEvB,QAAM,eAAe,eAAe,KAAK,CAAC,MAAM,EAAE,gBAAgB,KAAK;AACvE,QAAM,mBAAmB;AACzB,QAAM,QAAQ,CAAC;AACf,QAAM,WAAW,gBAAgB,eAAe,CAAC,KAAK;AACtD,QAAM,MAAM,WAAW,iBAAiB,QAAQ,QAAQ,IAAI,SAAS,EAAE,IAAI;AAE3E,SAAO,EAAE,SAAS,kBAAkB,UAAU,KAAK,MAAM;AAC3D;AAUA,SAAS,aACP,QACA,UACc;AACd,QAAM,KAAK,OAAO;AAClB,QAAM,SAAS,GAAG,OAAO,kBAAkB;AAC3C,QAAM,UAAU,GAAG,OAAO,2BAA2B;AAErD,QAAM,QAAQ,GAAG,MAAM,iBAAiB;AACxC,QAAM,cAAc,OAAO;AAC3B,UAAQ,YAAY,KAAK;AAEzB,MAAI,OAAO,aAAa;AACtB,UAAM,WAAW,GAAG,KAAK,oBAAoB;AAC7C,aAAS,cAAc,OAAO;AAC9B,YAAQ,YAAY,QAAQ;AAAA,EAC9B;AACA,SAAO,YAAY,OAAO;AAE1B,QAAM,UAAU,GAAG,QAAQ,2BAA2B;AAAA,IACpD,qBAAqB;AAAA,EACvB,CAAC;AACD,SAAO,YAAY,OAAO;AAG1B,QAAM,iBAAiB;AAAA,IACrB;AAAA,IACA,OAAO;AAAA,IACP,GAAG,QAAQ;AAAA,EACb;AACA,MAAI,eAAe,aAAa;AAC9B,YAAQ,cAAc,eAAe;AAAA,EACvC,OAAO;AACL,YAAQ,MAAM,UAAU;AAAA,EAC1B;AACA,OAAK;AAEL,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,QAAQ,WAAW;AACjB,UAAI,CAAC,GAAG,QAAQ,eAAe;AAC7B,gBAAQ,MAAM,UAAU;AACxB;AAAA,MACF;AACA,UAAI,WAAW;AACb,gBAAQ,cAAc;AACtB,gBAAQ,MAAM,UAAU;AAAA,MAC1B,OAAO;AACL,gBAAQ,MAAM,UAAU;AAAA,MAC1B;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,kBACP,QACA,YACA,WACA,UACQ;AACR,MAAI,CAAC,OAAO,aAAa,QAAQ,cAAe,QAAO;AACvD,QAAM,UAAU,aAAa;AAC7B,MAAI,WAAW,EAAG,QAAO;AACzB,QAAM,KAAK,OAAO;AAClB,MAAI,GAAG,iBAAiB,gBAAgB,GAAG,gBAAgB,GAAG;AAC5D,WAAO,IAAI,KAAK,MAAM,GAAG,aAAa,CAAC;AAAA,EACzC;AACA,MAAI,GAAG,iBAAiB,kBAAkB,GAAG,gBAAgB,GAAG;AAC9D,WAAO,IAAI,YAAY,KAAK,MAAM,GAAG,gBAAgB,GAAG,GAAG,QAAQ,CAAC;AAAA,EACtE;AACA,SAAO,IAAI,YAAY,SAAS,QAAQ,CAAC;AAC3C;AAOA,SAAS,iBACP,OACA,UACA,QACA,iBACkB;AAClB,QAAM,QAAQ;AAAA,IACZ;AAAA,IACA,MAAM,QACF,qDACA;AAAA,IACJ;AAAA,MACE,mBAAmB,MAAM,QAAQ,GAAG,QAAQ,SAAS,EAAE;AAAA,MACvD,GAAI,MAAM,QAAQ,EAAE,iBAAiB,OAAO,IAAI,CAAC;AAAA,IACnD;AAAA,EACF;AAIA,QAAM,QAAQ,GAAG,OAAO,uBAAuB,EAAE,kBAAkB,GAAG,CAAC;AACvE,QAAM,oBACJ,MAAM,UAAU,SAAS,MAAM,QAAQ,iBAAiB;AAC1D,MAAI,WAAoC;AACxC,MAAI,mBAAmB;AACrB,eAAW,SAAS,cAAc,KAAK;AACvC,aAAS,MAAM,kBAAkB,kBAAkB,KAAK;AAAA,MACtD,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,MAAM;AAAA,IACR,CAAC;AACD,aAAS,MAAM,kBAAkB,WAAW,MAAM,QAAQ;AAC1D,aAAS,QAAQ;AACjB,aAAS,SAAS;AAClB,aAAS,UAAU;AACnB,UAAM,YAAY,QAAQ;AAAA,EAC5B,OAAO;AACL,UAAM,mBAAmB,aAAa,qBAAqB;AAAA,EAC7D;AACA,MAAI,cAAkC;AACtC,MAAI,CAAC,MAAM,OAAO;AAChB,kBAAc,GAAG,QAAQ,uBAAuB;AAAA,MAC9C,kBAAkB;AAAA,IACpB,CAAC;AACD,gBAAY,cAAc,OAAO,MAAM,GAAG;AAC1C,UAAM,YAAY,WAAW;AAAA,EAC/B;AACA,QAAM,YAAY,KAAK;AAGvB,QAAM,OAAO,GAAG,OAAO,wBAAwB;AAC/C,QAAM,OAAO,SAAS,cAAc,GAAG;AACvC,OAAK,YAAY;AACjB,OAAK,OAAO,aAAa,MAAM,QAAQ,MAAM;AAC7C,OAAK,cAAc,MAAM,QAAQ;AACjC,OAAK,YAAY,IAAI;AAErB,MAAI,MAAM,OAAO;AACf,UAAM,WAAW,GAAG,QAAQ,qBAAqB;AACjD,aAAS,cAAc;AACvB,SAAK,YAAY,QAAQ;AAAA,EAC3B,WAAW,MAAM,UAAU;AAEzB,UAAM,SAAS,GAAG,QAAQ,0BAA0B;AACpD,UAAM,UAAU,GAAG,QAAQ,mCAAmC;AAAA,MAC5D,8BAA8B;AAAA,IAChC,CAAC;AACD,UAAM,UAAU,GAAG,QAAQ,2BAA2B;AAAA,MACpD,sBAAsB;AAAA,IACxB,CAAC;AACD,WAAO,YAAY,OAAO;AAC1B,WAAO,YAAY,OAAO;AAC1B,SAAK,YAAY,MAAM;AAKvB,UAAM,cAAc,GAAG,QAAQ,gCAAgC;AAAA,MAC7D,2BAA2B;AAAA,IAC7B,CAAC;AACD,gBAAY,aAAa,UAAU,EAAE;AACrC,SAAK,YAAY,WAAW;AAE5B,UAAM,oBAAoB,CAAC,YAA4B;AACrD,YAAM,OAAO,WAAW,QAAQ,MAAM,MAAM;AAC5C,cAAQ,cAAc,YAAY,MAAM,QAAQ;AAChD,UAAI,QAAQ,gBAAgB;AAC1B,cAAM,MAAM,WAAW,QAAQ,eAAe,MAAM;AACpD,YAAI,MAAM,MAAM;AACd,kBAAQ,cAAc,YAAY,KAAK,QAAQ;AAC/C,kBAAQ,gBAAgB,QAAQ;AAAA,QAClC,OAAO;AACL,kBAAQ,aAAa,UAAU,EAAE;AAAA,QACnC;AAAA,MACF,OAAO;AACL,gBAAQ,aAAa,UAAU,EAAE;AAAA,MACnC;AACA,YAAM,WAAW;AAAA,QACf,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR;AAAA,MACF;AACA,UAAI,UAAU;AACZ,oBAAY,cAAc;AAC1B,oBAAY,gBAAgB,QAAQ;AAAA,MACtC,OAAO;AACL,oBAAY,aAAa,UAAU,EAAE;AAAA,MACvC;AACA,YAAM,YAAY,QAAQ,SAAS,MAAM,QAAQ;AACjD,UAAI,YAAY,WAAW;AACzB,iBAAS,MAAM,kBAAkB,UAAU,KAAK;AAAA,UAC9C,OAAO;AAAA,UACP,QAAQ;AAAA,UACR,MAAM;AAAA,QACR,CAAC;AACD,iBAAS,MAAM,UAAU,WAAW,MAAM,QAAQ;AAAA,MACpD;AAAA,IACF;AAEA,sBAAkB,MAAM,QAAQ;AAQhC,QAAI,MAAM,iBAAiB,SAAS,GAAG;AACrC,YAAM,cAAwB,MAAM,iBAAiB,CAAC,EAAE,gBAAgB;AAAA,QACtE,CAAC,MAAM,EAAE;AAAA,MACX;AACA,YAAM,gBAAgB,MAAM,QAAQ,GAAG,QAAQ,SAAS,EAAE;AAC1D,YAAM,gBAAqC,CAAC;AAE5C,YAAM,iBAAiB,CAAC,WACtB,MAAM,iBAAiB;AAAA,QACrB,CAAC,MACC,EAAE,gBAAgB,MAAM,CAAC,GAAG,MAAM,EAAE,UAAU,OAAO,CAAC,CAAC,KACvD,EAAE,gBAAgB,WAAW,OAAO;AAAA,MACxC,KAAK;AAEP,YAAM,uBAAuB,CAAC,YAA4B;AACxD,gBAAQ,gBAAgB,QAAQ,CAAC,GAAG,MAAM;AACxC,gBAAM,MAAM,cAAc,CAAC;AAC3B,cAAI,OAAO,IAAI,UAAU,EAAE,MAAO,KAAI,QAAQ,EAAE;AAAA,QAClD,CAAC;AAAA,MACH;AAEA,YAAM,mBAAmB,CACvB,aACA,OACA,aAEA,MAAM,iBAAiB,KAAK,CAAC,MAAM;AACjC,YAAI,CAAC,EAAE,iBAAkB,QAAO;AAChC,YAAI,EAAE,gBAAgB,WAAW,GAAG,UAAU,MAAO,QAAO;AAC5D,eAAO,EAAE,gBAAgB;AAAA,UACvB,CAAC,GAAG,MAAM,MAAM,eAAe,EAAE,UAAU,SAAS,CAAC;AAAA,QACvD;AAAA,MACF,CAAC;AAEH,YAAM,oBAAoB,CAAC,aAAuB;AAChD,sBAAc,QAAQ,CAAC,KAAK,MAAM;AAChC,gBAAM,KAAK,IAAI,OAAO,EAAE,QAAQ,CAAC,QAAQ;AACvC,gBAAI,WAAW,CAAC,iBAAiB,GAAG,IAAI,OAAO,QAAQ;AAAA,UACzD,CAAC;AAAA,QACH,CAAC;AAAA,MACH;AAEA,YAAM,eAAe,MAAM;AACzB,cAAM,SAAS,cAAc,IAAI,CAAC,MAAM,EAAE,KAAK;AAC/C,cAAM,UAAU,eAAe,MAAM;AACrC,YAAI,CAAC,SAAS;AAGZ,cAAI,MAAM,UAAU;AAClB,iCAAqB,MAAM,QAAQ;AACnC,8BAAkB,MAAM,SAAS,gBAAgB,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC;AAAA,UACtE;AACA;AAAA,QACF;AACA,cAAM,WAAW;AACjB,cAAM,MAAM,OAAO,MAAM,QAAQ,IAAI,QAAQ,EAAE;AAC/C,YAAI,YAAa,aAAY,cAAc,OAAO,MAAM,GAAG;AAC3D,0BAAkB,OAAO;AACzB,0BAAkB,QAAQ,gBAAgB,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC;AAC7D,wBAAgB;AAAA,MAClB;AAEA,YAAM,kBAAkB,GAAG,OAAO,iCAAiC;AACnE,kBAAY,QAAQ,CAACA,OAAM,aAAa;AACtC,cAAM,QAAQ,GAAG,OAAO,gCAAgC;AAExD,cAAM,QAAQ,GAAG,QAAQ,gCAAgC;AACzD,cAAM,cAAcA;AACpB,cAAM,YAAY,KAAK;AAEvB,cAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,eAAO,YAAY;AACnB,eAAO,aAAa,uBAAuB,EAAE;AAC7C,eAAO,aAAa,wBAAwB,OAAO,WAAW,CAAC,CAAC;AAChE,eAAO,OAAO,cAAc,aAAa,IAAI,WAAW,CAAC;AACzD,eAAO,aAAa,cAAcA,KAAI;AAEtC,cAAM,OAAO,oBAAI,IAAY;AAC7B,cAAM,iBAAiB,QAAQ,CAAC,MAAM;AACpC,gBAAM,QAAQ,EAAE,gBAAgB,QAAQ,GAAG;AAC3C,cAAI,CAAC,SAAS,KAAK,IAAI,KAAK,EAAG;AAC/B,eAAK,IAAI,KAAK;AACd,gBAAM,MAAM,SAAS,cAAc,QAAQ;AAC3C,cAAI,QAAQ;AACZ,cAAI,cAAc;AAClB,cAAI,MAAM,UAAU,gBAAgB,QAAQ,GAAG,UAAU,OAAO;AAC9D,gBAAI,WAAW;AAAA,UACjB;AACA,iBAAO,YAAY,GAAG;AAAA,QACxB,CAAC;AAED,eAAO,iBAAiB,UAAU,YAAY;AAC9C,sBAAc,KAAK,MAAM;AACzB,cAAM,YAAY,MAAM;AACxB,wBAAgB,YAAY,KAAK;AAAA,MACnC,CAAC;AACD,WAAK,YAAY,eAAe;AAEhC,UAAI,MAAM,UAAU;AAClB,0BAAkB,MAAM,SAAS,gBAAgB,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC;AAAA,MACtE;AAAA,IACF,WACE,MAAM,iBAAiB,WAAW,KAClC,MAAM,QAAQ,SAAS,MAAM,SAAS,GACtC;AAGA,YAAM,QAAQ,GAAG,QAAQ,yBAAyB;AAClD,YAAM,cAAc,MAAM,iBAAiB,CAAC,EAAE;AAC9C,WAAK,YAAY,KAAK;AAAA,IACxB;AAAA,EACF;AAEA,QAAM,YAAY,IAAI;AACtB,SAAO,EAAE,IAAI,OAAO,MAAM;AAC5B;AAYA,SAAS,iBAAiB,QAAwC;AAChE,QAAM,MAAM,GAAG,OAAO,mBAAmB;AACzC,QAAM,QAAQ,GAAG,QAAQ,0BAA0B;AACnD,QAAM,cAAc;AACpB,MAAI,YAAY,KAAK;AAErB,QAAM,SAAS,GAAG,QAAQ,2BAA2B;AACrD,QAAM,UAAU,GAAG,QAAQ,2BAA2B;AAAA,IACpD,sBAAsB;AAAA,EACxB,CAAC;AACD,UAAQ,MAAM,UAAU;AACxB,SAAO,YAAY,OAAO;AAC1B,QAAM,OAAO,GAAG,QAAQ,wBAAwB;AAAA,IAC9C,mBAAmB;AAAA,EACrB,CAAC;AACD,SAAO,YAAY,IAAI;AACvB,MAAI,YAAY,MAAM;AAEtB,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,OAAO,EAAE,YAAY,WAAW,cAAc,SAAS,GAAG;AACxD,WAAK,cAAc,YAAY,WAAW,QAAQ;AAClD,UAAI,OAAO,aAAa,QAAQ,sBAAsB,eAAe,GAAG;AACtE,gBAAQ,cAAc,YAAY,YAAY,QAAQ;AACtD,gBAAQ,MAAM,UAAU;AAAA,MAC1B,OAAO;AACL,gBAAQ,MAAM,UAAU;AAAA,MAC1B;AAAA,IACF;AAAA,EACF;AACF;AAOA,SAAS,mBAAqC;AAC5C,QAAM,MAAM,GAAG,OAAO,yBAAyB;AAAA,IAC7C,oBAAoB;AAAA,EACtB,CAAC;AACD,QAAM,QAAQ,SAAS,cAAc,MAAM;AAC3C,QAAM,cAAc;AACpB,MAAI,YAAY,KAAK;AACrB,QAAM,SAAS,GAAG,QAAQ,IAAI,EAAE,uBAAuB,GAAG,CAAC;AAC3D,MAAI,YAAY,MAAM;AACtB,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,OAAO,EAAE,cAAc,SAAS,GAAG;AACjC,UAAI,gBAAgB,GAAG;AACrB,YAAI,MAAM,UAAU;AACpB;AAAA,MACF;AACA,UAAI,MAAM,UAAU;AACpB,aAAO,cAAc,YAAY,cAAc,QAAQ;AAAA,IACzD;AAAA,EACF;AACF;AAEA,SAAS,UACP,QACA,UACA,SACa;AACb,QAAM,QACJ,WAAW,IACP,GAAG,QAAQ,QAAQ,aAAa,IAAI,KAAK,GAAG,kBAC5C,OAAO,aAAa,IAAI,WAAW;AACzC,QAAM,SAAS,eAAe,KAAK;AACnC,MAAI,WAAW,GAAG;AAChB,WAAO,WAAW;AAAA,EACpB,OAAO;AACL,WAAO,iBAAiB,SAAS,MAAM;AACrC,UAAI,OAAO,SAAU;AACrB,cAAQ;AAAA,IACV,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAIA,SAAS,YACP,YACA,UACA,MACQ;AACR,MAAI,SAAS,iBAAiB,cAAc;AAE1C,QAAI,YAAY;AAChB,eAAW,KAAK,MAAM;AACpB,UAAI,CAAC,EAAE,SAAU;AACjB,YAAM,OAAO,WAAW,EAAE,SAAS,MAAM,MAAM;AAC/C,YAAM,MAAM,KAAK,MAAO,OAAO,SAAS,gBAAiB,GAAG;AAC5D,YAAM,UAAU,KAAK,IAAI,GAAG,OAAO,GAAG;AACtC,mBAAa,UAAU,EAAE;AAAA,IAC3B;AACA,WAAO;AAAA,EACT;AAEA,SAAO,KAAK,IAAI,GAAG,aAAa,KAAK,MAAM,SAAS,gBAAgB,GAAG,CAAC;AAC1E;;;AO7nBA,SAAS,oBAAAC,yBAAwB;AAkCjC,IAAMC,yBAAwB;AAAA;AAAA;AAAA;AAAA;AAAA;AAO9B,IAAM,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAMtB,IAAM,iBAAiB;AAAA;AAAA;AAAA;AAAA;AAMvB,IAAM,wBAAwB;AAAA;AAAA;AAAA;AAKvB,SAAS,qBACd,WACA,QACA,aACA,WACA;AACA,QAAM,KAAK,OAAO;AAClB,QAAM,WACJ,OAAO,SAAS,CAAC,GAAG,WAAW,gBAAgB,gBAAgB;AACjE,QAAM,cAAc,OAAO,eAAe;AAC1C,QAAM,SAAS,OAAO,eAAe;AAGrC,QAAM,WAAW,sBAAsB,QAAQ,GAAG,kBAAkB;AACpE,QAAM,eAAe,SAAS,OAAO,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE;AAItD,MAAI,eAAe,YAAa;AAEhC,QAAM,aAA0B,CAAC;AACjC,QAAM,OAAO,GAAG,OAAO,gBAAgB;AAAA,IACrC,0BAA0B,OAAO,WAAW;AAAA,IAC5C,qBAAqB,OAAO,MAAM;AAAA,EACpC,CAAC;AAGD,QAAM,SAASC,cAAa,MAAM;AAClC,OAAK,YAAY,MAAM;AAGvB,MAAI,GAAG,UAAU,iBAAiB,OAAO,QAAQ;AAC/C,UAAM,YAAY,gBAAgB,OAAO,MAAM;AAC/C,QAAI,WAAW;AACb,WAAK,YAAY,UAAU,EAAE;AAC7B,kBAAY,UAAU,IAAI;AAAA,IAC5B;AAAA,EACF;AAGA,QAAM,WAAW,eAAe,WAAW;AAC3C,OAAK,YAAY,SAAS,EAAE;AAG5B,QAAM,iBAAiB,GAAG,OAAO,uBAAuB;AAAA,IACtD,wBAAwB;AAAA,EAC1B,CAAC;AACD,OAAK,YAAY,cAAc;AAE/B,OAAK,YAAY,GAAG,OAAO,mBAAmB,CAAC;AAG/C,QAAM,iBAAiB,qBAAqB,GAAG,QAAQ,kBAAkB;AACzE,OAAK,YAAY,eAAe,EAAE;AAElC,QAAM,aAAa,GAAG,WAAW,UAC7BC,kBAAiB,IACjB;AACJ,MAAI,WAAY,MAAK,YAAY,WAAW,EAAE;AAG9C,QAAM,cAAc,GAAG,OAAO,mCAAmC;AAAA,IAC/D,0BAA0B;AAAA,EAC5B,CAAC;AACD,QAAM,kBAAkB;AAAA,IACtB;AAAA,IACA;AAAA,EACF;AACA,kBAAgB,cAAc,UAAU,WAAW;AACnD,cAAY,YAAY,eAAe;AACvC,OAAK,YAAY,WAAW;AAG5B,QAAM,QAAQ,YAAY,QAAQ,UAAU,UAAU;AAAA,IACpD,YAAY,GAAG;AAAA,IACf,OAAO,CAAC,SAAS,YAAY,aAAa,SAAS,OAAO;AAAA,IAC1D,UAAU,CAAC,WAAW,cAAc,gBAAgB,WAAW,SAAS;AAAA,IACxE,WAAW,MAAM,WAAW,UAAU;AAAA,EACxC,CAAC;AACD,OAAK,YAAY,MAAM,EAAE;AAGzB,QAAM,MAAM,eAAe,UAAU,WAAW,kBAAkB;AAClE,MAAI,WAAW;AACf,MAAI,iBAAiB,SAAS,MAAM;AAClC,QAAI,IAAI,SAAU;AAClB,UAAM,QAAyB,WAAW,IAAI,CAAC,OAAO;AAAA,MACpD,eAAe,EAAE;AAAA,MACjB,UAAU,EAAE;AAAA,MACZ,YAAY;AAAA,QACV,EAAE,KAAK,oBAAoB,OAAO,OAAO,GAAG;AAAA,QAC5C,EAAE,KAAK,qBAAqB,OAAO,OAAO,WAAW;AAAA,MACvD;AAAA,IACF,EAAE;AACF,gBAAY,KAAK;AAAA,EACnB,CAAC;AACD,OAAK,YAAY,GAAG;AAEpB,OAAK;AAAA,IACH,GAAG,KAAK,mBAAmB,EAAE,cAAc,IAAI,aAAa,SAAS,CAAC;AAAA,EACxE;AACA,OAAK;AAAA,IACH,GAAG,QAAQ,sBAAsB;AAAA,MAC/B,eAAe;AAAA,MACf,aAAa;AAAA,IACf,CAAC;AAAA,EACH;AAEA,YAAU,YAAY,IAAI;AAG1B,cAAY,MAAM,mBAAmB,IAAI,CAAC;AAK1C,QAAM,gBAAgB,SAAS,KAAK,CAAC,OAAO,CAAC,GAAG,KAAK;AACrD,QAAM,eACJ,eAAe,yBAAyB,eAAe,SAAS,CAAC;AACnE,MAAI,iBAAiB,cAAc;AACjC,eAAW,KAAK;AAAA,MACd,WAAW,cAAc,QAAQ;AAAA,MACjC,cAAc,cAAc,QAAQ;AAAA,MACpC,WAAW,aAAa;AAAA,MACxB,cAAc,aAAa;AAAA,MAC3B,UACE,aAAa,OAAO,OACpB,cAAc,QAAQ,eAAe,OACrC;AAAA,MACF,YAAY,WAAW,aAAa,MAAM,MAAM;AAAA,MAChD,cAAc,aAAa,iBACvB,WAAW,aAAa,eAAe,MAAM,IAC7C;AAAA,MACJ,gBAAgB;AAAA,QACd,aAAa;AAAA,QACb,aAAa;AAAA,QACb;AAAA,MACF;AAAA,MACA,UAAUC,kBAAiB,QAAQ,cAAc,QAAQ,IAAI,aAAa,EAAE;AAAA,IAC9E,CAAC;AAAA,EACH;AAMA,gBAAc;AAId,WAAS,aAAa,SAAkB,SAAyB;AAC/D,QAAI,WAAW,UAAU,OAAQ;AACjC,eAAW,KAAK;AAAA,MACd,WAAW,QAAQ;AAAA,MACnB,cAAc,QAAQ;AAAA,MACtB,WAAW,QAAQ;AAAA,MACnB,cAAc,QAAQ;AAAA,MACtB,UAAU,QAAQ,OAAO,OAAO,QAAQ,eAAe,OAAO;AAAA,MAC9D,YAAY,WAAW,QAAQ,MAAM,MAAM;AAAA,MAC3C,cAAc,QAAQ,iBAClB,WAAW,QAAQ,eAAe,MAAM,IACxC;AAAA,MACJ,gBAAgB;AAAA,QACd,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR;AAAA,MACF;AAAA,MACA,UAAUA,kBAAiB,QAAQ,QAAQ,IAAI,QAAQ,EAAE;AAAA,IAC3D,CAAC;AACD,kBAAc;AAAA,EAChB;AAEA,WAAS,gBAAgB,WAAmB,WAAmB;AAC7D,UAAM,MAAM,WAAW;AAAA,MACrB,CAAC,MAAM,EAAE,cAAc,aAAa,EAAE,cAAc;AAAA,IACtD;AACA,QAAI,QAAQ,GAAI;AAChB,eAAW,OAAO,KAAK,CAAC;AACxB,kBAAc;AAAA,EAChB;AAEA,WAAS,aAAa,OAAe;AACnC,QAAI,QAAQ,KAAK,SAAS,WAAW,OAAQ;AAC7C,eAAW,OAAO,OAAO,CAAC;AAC1B,kBAAc;AAAA,EAChB;AAEA,WAAS,gBAAgB;AACvB,gBAAY;AACZ,aAAS,OAAO,WAAW,MAAM;AACjC,mBAAe,OAAO,YAAY,QAAQ,QAAQ;AAClD,QAAI,WAAY,YAAW,OAAO,YAAY,QAAQ,QAAQ;AAC9D,gBAAY,MAAM,UAAU,WAAW,WAAW,IAAI,KAAK;AAC3D,UAAM,cAAc;AACpB,cAAU;AAAA,EACZ;AAEA,WAAS,cAAc;AACrB,mBAAe,YAAY;AAC3B,UAAM,aAAa,KAAK,IAAI,aAAa,WAAW,MAAM;AAC1D,aAAS,IAAI,GAAG,IAAI,YAAY,KAAK;AACnC,YAAM,YAAY,WAAW,CAAC;AAC9B,UAAI,WAAW;AACb,uBAAe;AAAA,UACb,iBAAiB,WAAW,GAAG,UAAU,MAAM,aAAa,CAAC,CAAC;AAAA,QAChE;AAAA,MACF,OAAO;AACL,uBAAe;AAAA,UACb,gBAAgB,GAAG,MAAM,MAAM,KAAK,CAAC;AAAA,QACvC;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,WAAS,YAAY;AAInB,UAAM,QAAQ,WAAW;AACzB,QAAI,QAAQ,aAAa;AACvB,UAAI,WAAW;AACf,kBAAY,KAAK,UAAU,cAAc,KAAK,iBAAiB;AAAA,IACjE,OAAO;AACL,UAAI,WAAW;AACf,kBAAY,KAAK,GAAG,IAAI,WAAW,aAAa;AAAA,IAClD;AAAA,EACF;AACF;AAIA,SAASF,cAAa,QAAyC;AAC7D,QAAM,KAAK,OAAO;AAClB,QAAM,SAAS,GAAG,OAAO,kBAAkB;AAC3C,QAAM,UAAU,GAAG,OAAO,2BAA2B;AACrD,QAAM,QAAQ,GAAG,MAAM,iBAAiB;AACxC,QAAM,cAAc,OAAO;AAC3B,UAAQ,YAAY,KAAK;AACzB,SAAO,YAAY,OAAO;AAE1B,MAAI,GAAG,QAAQ,eAAe;AAC5B,UAAM,EAAE,cAAc,cAAc,IAAI,OAAO;AAC/C,QAAI,QAAuB;AAC3B,QAAI,iBAAiB,gBAAgB,gBAAgB,GAAG;AACtD,cAAQ,IAAI,KAAK,MAAM,aAAa,CAAC;AAAA,IACvC,WAAW,iBAAiB,kBAAkB,gBAAgB,GAAG;AAC/D,cAAQ,IAAI;AAAA,QACV,KAAK,MAAM,gBAAgB,GAAG;AAAA,QAC9B,OAAO,SAAS,CAAC,GAAG,WAAW,gBAAgB,gBAAgB;AAAA,MACjE,CAAC;AAAA,IACH;AACA,QAAI,OAAO;AACT,YAAM,QAAQ,GAAG,QAAQ,yBAAyB;AAClD,YAAM,cAAc;AACpB,aAAO,YAAY,KAAK;AAAA,IAC1B;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,eAAe,aAAqB;AAC3C,QAAM,OAAO,GAAG,OAAO,wBAAwB;AAC/C,QAAM,SAAS,GAAG,OAAO,+BAA+B;AACxD,QAAM,QAAQ,GAAG,QAAQ,gCAAgC;AAAA,IACvD,uBAAuB;AAAA,EACzB,CAAC;AACD,QAAM,cAAc,QAAQ,WAAW;AACvC,SAAO,YAAY,KAAK;AACxB,QAAM,YAAY,GAAG,QAAQ,oCAAoC;AAAA,IAC/D,2BAA2B;AAAA,EAC7B,CAAC;AACD,YAAU,cAAc,GAAG,WAAW;AACtC,SAAO,YAAY,SAAS;AAC5B,OAAK,YAAY,MAAM;AAEvB,QAAM,QAAQ,GAAG,OAAO,gCAAgC;AAAA,IACtD,MAAM;AAAA,IACN,iBAAiB;AAAA,IACjB,iBAAiB;AAAA,IACjB,iBAAiB,OAAO,WAAW;AAAA,EACrC,CAAC;AACD,QAAM,OAAO,GAAG,OAAO,+BAA+B;AAAA,IACpD,sBAAsB;AAAA,EACxB,CAAC;AACD,OAAK,MAAM,QAAQ;AACnB,QAAM,YAAY,IAAI;AACtB,OAAK,YAAY,KAAK;AAEtB,WAAS,OAAO,UAAkB;AAChC,UAAM,MAAM,KAAK,IAAI,KAAM,WAAW,cAAe,GAAG;AACxD,UAAM,cAAc,GAAG,QAAQ,OAAO,WAAW;AACjD,QAAI,YAAY,aAAa;AAC3B,gBAAU,cAAc;AAAA,IAC1B,OAAO;AACL,gBAAU,cAAc,GAAG,cAAc,QAAQ;AAAA,IACnD;AACA,SAAK,MAAM,QAAQ,GAAG,GAAG;AACzB,UAAM,aAAa,iBAAiB,OAAO,KAAK,IAAI,UAAU,WAAW,CAAC,CAAC;AAAA,EAC7E;AAEA,SAAO,EAAE,IAAI,MAAM,OAAO;AAC5B;AAEA,SAAS,gBAAgB,OAAe,SAAkC;AACxE,QAAM,OAAO;AAAA,IACX;AAAA,IACA;AAAA,IACA;AAAA,MACE,aAAa,OAAO,QAAQ,CAAC;AAAA,MAC7B,UAAU;AAAA,MACV,MAAM;AAAA,MACN,cAAc;AAAA,IAChB;AAAA,EACF;AACA,QAAM,QAAQ,GAAG,OAAO,2BAA2B;AACnD,QAAM,YAAY;AAClB,OAAK,YAAY,KAAK;AACtB,QAAM,OAAO,GAAG,QAAQ,0BAA0B;AAClD,OAAK,cAAc;AACnB,OAAK,YAAY,IAAI;AACrB,OAAK,iBAAiB,SAAS,OAAO;AACtC,OAAK,iBAAiB,WAAW,CAAC,MAAM;AACtC,QAAI,EAAE,QAAQ,WAAW,EAAE,QAAQ,KAAK;AACtC,QAAE,eAAe;AACjB,cAAQ;AAAA,IACV;AAAA,EACF,CAAC;AACD,SAAO;AACT;AAEA,SAAS,iBACP,WACA,OACA,UACA,UACa;AACb,QAAM,OAAO;AAAA,IACX;AAAA,IACA;AAAA,IACA,EAAE,aAAa,OAAO,QAAQ,CAAC,EAAE;AAAA,EACnC;AACA,QAAM,QAAQ,GAAG,OAAO,uBAAuB,EAAE,kBAAkB,GAAG,CAAC;AACvE,MAAI,UAAU,UAAU;AACtB,UAAM,MAAM,SAAS,cAAc,KAAK;AACxC,QAAI,MAAM,kBAAkB,UAAU,UAAU;AAAA,MAC9C,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,MAAM;AAAA,IACR,CAAC;AACD,QAAI,MAAM,UAAU;AACpB,QAAI,QAAQ;AACZ,QAAI,SAAS;AACb,QAAI,UAAU;AACd,UAAM,YAAY,GAAG;AAAA,EACvB,OAAO;AACL,UAAM,mBAAmB,aAAaD,sBAAqB;AAAA,EAC7D;AACA,QAAM,WAAW,GAAG,QAAQ,qBAAqB;AACjD,WAAS,cAAc,OAAO,UAAU,QAAQ;AAChD,QAAM,YAAY,QAAQ;AAC1B,OAAK,YAAY,KAAK;AAEtB,QAAM,OAAO,GAAG,OAAO,2BAA2B;AAClD,QAAM,QAAQ,GAAG,QAAQ,4BAA4B;AACrD,QAAM,cAAc,UAAU;AAC9B,OAAK,YAAY,KAAK;AACtB,MAAI,UAAU,gBAAgB,UAAU,iBAAiB,iBAAiB;AACxE,UAAM,UAAU,GAAG,QAAQ,8BAA8B;AACzD,YAAQ,cAAc,UAAU;AAChC,SAAK,YAAY,OAAO;AAAA,EAC1B;AACA,QAAM,YAAY,UAAU,aAAa,UAAU;AACnD,QAAM,cACJ,UAAU,iBAAiB,OACvB,UAAU,eAAe,UAAU,WACnC;AACN,QAAM,YAAY,GAAG,QAAQ,4BAA4B;AACzD,MAAI,gBAAgB,QAAQ,cAAc,WAAW;AACnD,UAAM,UAAU,GAAG,QAAQ,8BAA8B;AACzD,YAAQ,cAAc,YAAY,aAAa,QAAQ;AACvD,cAAU,YAAY,OAAO;AAAA,EAC/B;AACA,QAAM,UAAU,SAAS,cAAc,MAAM;AAC7C,UAAQ,cAAc,YAAY,WAAW,QAAQ;AACrD,YAAU,YAAY,OAAO;AAC7B,OAAK,YAAY,SAAS;AAC1B,MAAI,UAAU,gBAAgB;AAC5B,UAAM,YAAY,GAAG,QAAQ,8BAA8B;AAC3D,cAAU,cAAc,UAAU;AAClC,SAAK,YAAY,SAAS;AAAA,EAC5B;AACA,OAAK,YAAY,IAAI;AAErB,QAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,SAAO,OAAO;AACd,SAAO,YAAY;AACnB,SAAO,aAAa,cAAc,UAAU,UAAU,YAAY,EAAE;AACpE,SAAO,YAAY;AACnB,SAAO,iBAAiB,SAAS,CAAC,MAAM;AACtC,MAAE,gBAAgB;AAClB,aAAS;AAAA,EACX,CAAC;AACD,OAAK,YAAY,MAAM;AACvB,SAAO;AACT;AAEA,SAAS,qBAAqB,oBAA6B;AACzD,QAAM,OAAO,GAAG,OAAO,qBAAqB,EAAE,wBAAwB,GAAG,CAAC;AAC1E,OAAK,MAAM,UAAU;AACrB,QAAM,QAAQ,GAAG,QAAQ,0BAA0B;AACnD,QAAM,cAAc;AACpB,OAAK,YAAY,KAAK;AACtB,QAAM,SAAS,GAAG,QAAQ,2BAA2B;AACrD,QAAM,UAAU,GAAG,QAAQ,2BAA2B;AAAA,IACpD,sBAAsB;AAAA,EACxB,CAAC;AACD,MAAI,mBAAoB,QAAO,YAAY,OAAO;AAClD,QAAM,OAAO,GAAG,QAAQ,wBAAwB,EAAE,mBAAmB,GAAG,CAAC;AACzE,SAAO,YAAY,IAAI;AACvB,OAAK,YAAY,MAAM;AAEvB,WAAS,OACP,YACA,QACA,UACA;AACA,QAAI,WAAW,WAAW,GAAG;AAC3B,WAAK,MAAM,UAAU;AACrB;AAAA,IACF;AACA,SAAK,MAAM,UAAU;AACrB,UAAM,aAAa,WAAW;AAAA,MAC5B,CAAC,GAAG,QAAQ,IAAI,IAAI,aAAa,IAAI;AAAA,MACrC;AAAA,IACF;AACA,UAAM,YAAY,uBAAuB,YAAY,OAAO,cAAc;AAC1E,QAAI,sBAAsB,aAAa,WAAW;AAChD,cAAQ,cAAc,YAAY,YAAY,QAAQ;AACtD,cAAQ,MAAM,UAAU;AAAA,IAC1B,OAAO;AACL,cAAQ,MAAM,UAAU;AAAA,IAC1B;AACA,SAAK,cAAc,YAAY,WAAW,QAAQ;AAAA,EACpD;AAEA,SAAO,EAAE,IAAI,MAAM,OAAO;AAC5B;AAEA,SAASE,oBAAmB;AAC1B,QAAM,OAAO,GAAG,OAAO,yBAAyB;AAAA,IAC9C,oBAAoB;AAAA,EACtB,CAAC;AACD,OAAK,MAAM,UAAU;AACrB,QAAM,UAAU,SAAS,cAAc,MAAM;AAC7C,UAAQ,cAAc;AACtB,OAAK,YAAY,OAAO;AACxB,QAAM,SAAS,GAAG,QAAQ,IAAI,EAAE,uBAAuB,GAAG,CAAC;AAC3D,OAAK,YAAY,MAAM;AAEvB,WAAS,OACP,YACA,QACA,UACA;AACA,QAAI,WAAW,WAAW,GAAG;AAC3B,WAAK,MAAM,UAAU;AACrB;AAAA,IACF;AACA,UAAM,aAAa,WAAW;AAAA,MAC5B,CAAC,GAAG,QAAQ,IAAI,IAAI,aAAa,IAAI;AAAA,MACrC;AAAA,IACF;AACA,UAAM,YAAY,uBAAuB,YAAY,OAAO,cAAc;AAC1E,UAAM,UAAU,KAAK,IAAI,GAAG,aAAa,SAAS;AAClD,QAAI,WAAW,GAAG;AAChB,WAAK,MAAM,UAAU;AACrB;AAAA,IACF;AACA,SAAK,MAAM,UAAU;AACrB,WAAO,cAAc,YAAY,SAAS,QAAQ;AAAA,EACpD;AAEA,SAAO,EAAE,IAAI,MAAM,OAAO;AAC5B;AAWA,SAAS,YACP,QACA,UACA,UACA,UACA;AACA,QAAM,UAAU,GAAG,OAAO,+BAA+B;AAAA,IACvD,sBAAsB;AAAA,IACtB,mBAAmB,OAAO;AAAA,EAC5B,CAAC;AACD,UAAQ,MAAM,UAAU;AAExB,QAAM,QAAQ,GAAG,OAAO,uBAAuB;AAAA,IAC7C,MAAM;AAAA,IACN,cAAc;AAAA,IACd,mBAAmB,kBAAkB,WAAW,OAAO,EAAE,CAAC;AAAA,IAC1D,UAAU;AAAA,EACZ,CAAC;AAGD,QAAM,cAAc,GAAG,OAAO,4BAA4B;AAC1D,QAAM,aAAa,GAAG,MAAM,6BAA6B;AAAA,IACvD,IAAI,kBAAkB,WAAW,OAAO,EAAE,CAAC;AAAA,EAC7C,CAAC;AACD,aAAW,cAAc;AACzB,cAAY,YAAY,UAAU;AAClC,QAAM,WAAW,SAAS,cAAc,QAAQ;AAChD,WAAS,OAAO;AAChB,WAAS,YAAY;AACrB,WAAS,aAAa,oBAAoB,EAAE;AAC5C,WAAS,aAAa,cAAc,OAAO;AAC3C,WAAS,YAAY;AACrB,WAAS,iBAAiB,SAAS,KAAK;AACxC,cAAY,YAAY,QAAQ;AAChC,QAAM,YAAY,WAAW;AAG7B,MAAI,cAAuC;AAC3C,MAAI,iBAA2C;AAC/C,MAAI,SAAS,YAAY;AACvB,UAAM,aAAa,GAAG,OAAO,4BAA4B;AACzD,kBAAc,SAAS,cAAc,OAAO;AAC5C,gBAAY,OAAO;AACnB,gBAAY,YAAY;AACxB,gBAAY,aAAa,qBAAqB,EAAE;AAChD,gBAAY,aAAa,QAAQ,WAAW;AAC5C,gBAAY,aAAa,cAAc,iBAAiB;AACxD,gBAAY,aAAa,eAAe,iBAAiB;AACzD,gBAAY,eAAe;AAC3B,gBAAY,iBAAiB,SAAS,MAAM,YAAY,CAAC;AACzD,eAAW,YAAY,WAAW;AAElC,qBAAiB,SAAS,cAAc,QAAQ;AAChD,mBAAe,OAAO;AACtB,mBAAe,YAAY;AAC3B,mBAAe,aAAa,2BAA2B,EAAE;AACzD,mBAAe,aAAa,cAAc,cAAc;AACxD,mBAAe,MAAM,UAAU;AAC/B,mBAAe,YAAY;AAC3B,mBAAe,iBAAiB,SAAS,MAAM;AAC7C,UAAI,CAAC,YAAa;AAClB,kBAAY,QAAQ;AACpB,kBAAY;AACZ,kBAAY,MAAM;AAAA,IACpB,CAAC;AACD,eAAW,YAAY,cAAc;AACrC,UAAM,YAAY,UAAU;AAAA,EAC9B;AAGA,QAAM,OAAO,GAAG,OAAO,4BAA4B;AAAA,IACjD,mBAAmB;AAAA,EACrB,CAAC;AACD,QAAM,YAAY,IAAI;AAEtB,QAAM,QAAQ,GAAG,OAAO,6BAA6B;AAAA,IACnD,oBAAoB;AAAA,EACtB,CAAC;AACD,QAAM,MAAM,UAAU;AACtB,QAAM,YAAY,SAAS,cAAc,GAAG;AAC5C,YAAU,cAAc;AACxB,QAAM,YAAY,SAAS;AAC3B,QAAM,YAAY,KAAK;AAEvB,QAAM,OAAO,GAAG,QAAQ,sBAAsB;AAAA,IAC5C,mBAAmB;AAAA,IACnB,aAAa;AAAA,EACf,CAAC;AACD,QAAM,YAAY,IAAI;AAEtB,UAAQ,YAAY,KAAK;AAGzB,MAAI,YAAY;AAChB,QAAM,cAKD,CAAC;AAEN,WAAS,YAAY;AACnB,QAAI,UAAW;AACf,gBAAY;AACZ,SAAK,YAAY;AAEjB,aAAS,QAAQ,CAAC,OAAO;AAgBvB,YAAM,oBAAoB,GAAG;AAC7B,YAAM,oBACJ,GAAG,SAAS,KAAK,CAAC,MAAM,EAAE,gBAAgB,KAC1C,GAAG,yBACH,GAAG,SAAS,CAAC;AACf,UAAI,CAAC,kBAAmB;AAExB,UAAI,iBAAiB;AAErB,YAAM,YAAY;AAAA,QAChB;AAAA,QACA,GAAG,QACC,sEACA;AAAA,QACJ,EAAE,mBAAmB,GAAG,QAAQ,GAAG,QAAQ,SAAS,EAAE,EAAE;AAAA,MAC1D;AAEA,YAAM,QAAQ,GAAG,OAAO,mCAAmC;AAC3D,YAAM,sBACJ,GAAG,SAAS,KAAK,CAAC,MAAM,EAAE,gBAAgB,KAAK,GAAG,SAAS,CAAC,KAAK;AACnE,YAAM,oBACJ,qBAAqB,SAAS,GAAG,QAAQ,iBAAiB;AAC5D,UAAI,WAAoC;AACxC,UAAI,mBAAmB;AACrB,mBAAW,SAAS,cAAc,KAAK;AACvC,iBAAS,MAAM,kBAAkB,kBAAkB,KAAK;AAAA,UACtD,OAAO;AAAA,UACP,QAAQ;AAAA,UACR,MAAM;AAAA,QACR,CAAC;AACD,iBAAS,MAAM,kBAAkB,WAAW,GAAG,QAAQ;AACvD,iBAAS,QAAQ;AACjB,iBAAS,SAAS;AAClB,iBAAS,UAAU;AACnB,cAAM,YAAY,QAAQ;AAAA,MAC5B,OAAO;AACL,cAAM,mBAAmB,aAAaF,sBAAqB;AAAA,MAC7D;AACA,YAAM,aAAa,GAAG,QAAQ,qBAAqB;AACnD,iBAAW,cAAc;AAAA,QACvBG,kBAAiB,QAAQ,GAAG,QAAQ,IAAI,eAAe,EAAE;AAAA,MAC3D;AACA,YAAM,YAAY,UAAU;AAC5B,gBAAU,YAAY,KAAK;AAE3B,YAAM,OAAO,GAAG,OAAO,kCAAkC;AACzD,YAAM,QAAQ,GAAG,KAAK,mCAAmC;AACzD,YAAM,cAAc,GAAG,QAAQ;AAC/B,WAAK,YAAY,KAAK;AAEtB,YAAM,QAAQ,GAAG,KAAK,mCAAmC;AACzD,YAAM,cAAc;AAAA,QAClB,WAAW,eAAe,MAAM,MAAM,IACpCA,kBAAiB,QAAQ,GAAG,QAAQ,IAAI,eAAe,EAAE;AAAA,QAC3D;AAAA,MACF;AACA,WAAK,YAAY,KAAK;AAEtB,YAAM,YAAY;AAAA,QAChB;AAAA,QACA;AAAA,MACF;AACA,YAAM,kBAAkB;AAAA,QACtB,eAAe;AAAA,QACf,eAAe;AAAA,QACf;AAAA,MACF;AACA,UAAI,iBAAiB;AACnB,kBAAU,cAAc;AAAA,MAC1B,OAAO;AACL,kBAAU,SAAS;AAAA,MACrB;AACA,WAAK,YAAY,SAAS;AAM1B,YAAM,MAAM;AAAA,QACV,IAAI;AAAA,QACJ,SAAS,GAAG;AAAA,QACZ,SAAS;AAAA,QACT,aAAa,MAAM;AAAA,QAAC;AAAA,MACtB;AAMA,UAAI,kBAAkB,SAAS,GAAG;AAChC,cAAM,cAAwB,kBAAkB,CAAC,EAAE,gBAAgB;AAAA,UACjE,CAAC,MAAM,EAAE;AAAA,QACX;AACA,cAAM,gBAAgB,GAAG,QAAQ,GAAG,QAAQ,SAAS,EAAE;AACvD,cAAM,gBAAqC,CAAC;AAE5C,cAAM,iBAAiB,CAAC,WACtB,kBAAkB;AAAA,UAChB,CAAC,MACC,EAAE,gBAAgB,WAAW,OAAO,UACpC,EAAE,gBAAgB,MAAM,CAAC,GAAG,MAAM,EAAE,UAAU,OAAO,CAAC,CAAC;AAAA,QAC3D,KAAK;AAEP,cAAM,uBAAuB,CAAC,MAAsB;AAClD,YAAE,gBAAgB,QAAQ,CAAC,GAAG,MAAM;AAClC,kBAAM,MAAM,cAAc,CAAC;AAC3B,gBAAI,OAAO,IAAI,UAAU,EAAE,MAAO,KAAI,QAAQ,EAAE;AAAA,UAClD,CAAC;AAAA,QACH;AAEA,cAAM,mBAAmB,CACvB,aACA,OACA,aAEA,kBAAkB,KAAK,CAAC,MAAM;AAC5B,cAAI,CAAC,EAAE,iBAAkB,QAAO;AAChC,cAAI,EAAE,gBAAgB,WAAW,GAAG,UAAU,MAAO,QAAO;AAC5D,iBAAO,EAAE,gBAAgB;AAAA,YACvB,CAAC,GAAG,MAAM,MAAM,eAAe,EAAE,UAAU,SAAS,CAAC;AAAA,UACvD;AAAA,QACF,CAAC;AAEH,cAAM,oBAAoB,CAAC,aAAuB;AAChD,wBAAc,QAAQ,CAAC,KAAK,MAAM;AAChC,kBAAM,KAAK,IAAI,OAAO,EAAE,QAAQ,CAAC,QAAQ;AACvC,kBAAI,WAAW,CAAC,iBAAiB,GAAG,IAAI,OAAO,QAAQ;AAAA,YACzD,CAAC;AAAA,UACH,CAAC;AAAA,QACH;AAEA,cAAM,eAAe,MAAM;AACzB,gBAAM,SAAS,cAAc,IAAI,CAAC,MAAM,EAAE,KAAK;AAC/C,gBAAM,OAAO,eAAe,MAAM;AAClC,cAAI,CAAC,MAAM;AAGT,iCAAqB,cAAc;AACnC;AAAA,cACE,eAAe,gBAAgB,IAAI,CAAC,MAAM,EAAE,KAAK;AAAA,YACnD;AACA;AAAA,UACF;AACA,2BAAiB;AACjB,cAAI,UAAU;AACd,gBAAM,UAAUA;AAAA,YACd;AAAA,YACA,GAAG,QAAQ;AAAA,YACX,eAAe;AAAA,UACjB;AACA,gBAAM,cAAc;AAAA,YAClB,WAAW,eAAe,MAAM,MAAM,IAAI;AAAA,YAC1C;AAAA,UACF;AACA,gBAAM,eAAe;AAAA,YACnB,eAAe;AAAA,YACf,eAAe;AAAA,YACf;AAAA,UACF;AACA,cAAI,cAAc;AAChB,sBAAU,cAAc;AACxB,sBAAU,SAAS;AAAA,UACrB,OAAO;AACL,sBAAU,cAAc;AACxB,sBAAU,SAAS;AAAA,UACrB;AAIA,gBAAM,YAAY,eAAe,SAAS,GAAG,QAAQ;AACrD,cAAI,YAAY,WAAW;AACzB,qBAAS,MAAM,kBAAkB,UAAU,KAAK;AAAA,cAC9C,OAAO;AAAA,cACP,QAAQ;AAAA,cACR,MAAM;AAAA,YACR,CAAC;AACD,qBAAS,MAAM,UAAU,WAAW,GAAG,QAAQ;AAAA,UACjD;AACA,4BAAkB,KAAK,gBAAgB,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC;AAC1D,yBAAe;AAAA,QACjB;AAEA,cAAM,kBAAkB,GAAG,OAAO,iCAAiC;AACnE,oBAAY,QAAQ,CAAC,MAAM,aAAa;AACtC,gBAAM,QAAQ,GAAG,OAAO,gCAAgC;AAExD,gBAAM,QAAQ,GAAG,QAAQ,gCAAgC;AACzD,gBAAM,cAAc;AACpB,gBAAM,YAAY,KAAK;AAEvB,gBAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,iBAAO,YAAY;AACnB,iBAAO,aAAa,uBAAuB,EAAE;AAC7C,iBAAO,aAAa,wBAAwB,OAAO,WAAW,CAAC,CAAC;AAChE,iBAAO,OAAO,cAAc,aAAa,IAAI,WAAW,CAAC;AACzD,iBAAO,aAAa,cAAc,IAAI;AAEtC,gBAAM,OAAO,oBAAI,IAAY;AAC7B,4BAAkB,QAAQ,CAAC,MAAM;AAC/B,kBAAM,QAAQ,EAAE,gBAAgB,QAAQ,GAAG;AAC3C,gBAAI,CAAC,SAAS,KAAK,IAAI,KAAK,EAAG;AAC/B,iBAAK,IAAI,KAAK;AACd,kBAAM,MAAM,SAAS,cAAc,QAAQ;AAC3C,gBAAI,QAAQ;AACZ,gBAAI,cAAc;AAClB,gBAAI,kBAAkB,gBAAgB,QAAQ,GAAG,UAAU,OAAO;AAChE,kBAAI,WAAW;AAAA,YACjB;AACA,mBAAO,YAAY,GAAG;AAAA,UACxB,CAAC;AAED,iBAAO,iBAAiB,UAAU,YAAY;AAC9C,wBAAc,KAAK,MAAM;AACzB,gBAAM,YAAY,MAAM;AACxB,0BAAgB,YAAY,KAAK;AAAA,QACnC,CAAC;AACD,aAAK,YAAY,eAAe;AAEhC;AAAA,UACE,kBAAkB,gBAAgB,IAAI,CAAC,MAAM,EAAE,KAAK;AAAA,QACtD;AAAA,MACF,WACE,kBAAkB,WAAW,KAC7B,kBAAkB,UAAU,iBAC5B;AACA,cAAM,eAAe,GAAG,QAAQ,8BAA8B;AAC9D,qBAAa,cAAc,kBAAkB;AAC7C,aAAK,YAAY,YAAY;AAAA,MAC/B;AAEA,UAAI,GAAG,OAAO;AACZ,cAAM,UAAU,GAAG,QAAQ,oCAAoC;AAC/D,gBAAQ,cAAc;AACtB,aAAK,YAAY,OAAO;AAAA,MAC1B;AACA,gBAAU,YAAY,IAAI;AAO1B,YAAM,iBAAiB,MAAM;AAC3B,mBAAW,cAAc;AAAA,UACvBA,kBAAiB,QAAQ,GAAG,QAAQ,IAAI,eAAe,EAAE;AAAA,QAC3D;AAAA,MACF;AAEA,UAAI,CAAC,GAAG,OAAO;AACb,cAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,eAAO,OAAO;AACd,eAAO,YAAY;AACnB,eAAO,cAAc;AACrB,eAAO,iBAAiB,SAAS,MAAM;AACrC,cAAI,SAAS,UAAU,EAAG;AAC1B,mBAAS,MAAM,GAAG,SAAS,cAAc;AACzC,gBAAM;AAAA,QACR,CAAC;AACD,kBAAU,YAAY,MAAM;AAAA,MAC9B;AAKA,UAAI,cAAc,GAAG,QAAQ,MAAM;AAAA,MAAC,IAAI;AACxC,kBAAY,KAAK,GAAG;AAEpB,WAAK,YAAY,SAAS;AAAA,IAC5B,CAAC;AAGD,qBAAiB,IAAI;AAErB,kBAAc;AAAA,EAChB;AAEA,WAAS,cAAc;AACrB,QAAI,CAAC,YAAa;AAClB,UAAM,QAAQ,YAAY,MAAM,KAAK,EAAE,YAAY;AACnD,QAAI,gBAAgB;AAClB,qBAAe,MAAM,UAAU,QAAQ,KAAK;AAAA,IAC9C;AACA,QAAI,eAAe;AACnB,gBAAY,QAAQ,CAAC,QAAQ;AAC3B,YAAM,QAAQ,CAAC,SAAS,IAAI,QAAQ,MAAM,YAAY,EAAE,SAAS,KAAK;AACtE,UAAI,GAAG,MAAM,UAAU,QAAQ,KAAK;AACpC,UAAI,MAAO;AAAA,IACb,CAAC;AACD,UAAM,MAAM,UAAU,iBAAiB,KAAK,QAAQ,KAAK;AAAA,EAC3D;AAGA,MAAI,cAA8B;AAClC,WAAS,UAAU,GAAkB;AACnC,QAAI,EAAE,QAAQ,UAAU;AACtB,QAAE,eAAe;AACjB,YAAM;AACN;AAAA,IACF;AACA,QAAI,EAAE,QAAQ,OAAO;AACnB,gBAAU,GAAG,KAAK;AAAA,IACpB;AAAA,EACF;AAEA,MAAI,SAAS;AAEb,WAAS,OAAO;AACd,QAAI,OAAQ;AACZ,QAAI,SAAS,UAAU,EAAG;AAC1B,aAAS;AACT,cAAU;AACV,kBAAe,QAAQ,YAAY,EAChC;AACH,YAAQ,MAAM,UAAU;AACxB,YAAQ,UAAU,IAAI,mCAAmC;AACzD,UAAM,MAAM;AACZ,aAAS,iBAAiB,WAAW,SAAS;AAC9C,YAAQ,iBAAiB,SAAS,cAAc;AAAA,EAClD;AAEA,WAAS,QAAQ;AACf,QAAI,CAAC,OAAQ;AACb,aAAS;AACT,YAAQ,UAAU,OAAO,mCAAmC;AAC5D,YAAQ,MAAM,UAAU;AACxB,aAAS,oBAAoB,WAAW,SAAS;AACjD,YAAQ,oBAAoB,SAAS,cAAc;AACnD,QAAI,uBAAuB,aAAa;AACtC,kBAAY,MAAM;AAAA,IACpB;AAAA,EACF;AAEA,WAAS,eAAe,GAAe;AACrC,QAAI,EAAE,WAAW,QAAS,OAAM;AAAA,EAClC;AAEA,WAAS,gBAAgB;AACvB,gBAAY,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;AAAA,EAC5C;AAEA,SAAO,EAAE,IAAI,SAAS,MAAM,OAAO,cAAc;AACnD;AAIA,SAAS,sBACP,QACA,aACmB;AACnB,QAAM,SAA4B,CAAC;AACnC,QAAM,OAAO,oBAAI,IAAY;AAC7B,aAAW,WAAW,OAAO,UAAU;AACrC,QAAI,KAAK,IAAI,QAAQ,EAAE,EAAG;AAC1B,SAAK,IAAI,QAAQ,EAAE;AACnB,UAAM,YAAY,QAAQ,SAAS,MAAM,OAAO,CAAC,MAAM,EAAE,gBAAgB;AACzE,UAAM,QAAQ,UAAU,WAAW;AACnC,QAAI,SAAS,gBAAgB,OAAQ;AACrC,WAAO,KAAK;AAAA,MACV;AAAA,MACA,UAAU,QAAQ,SAAS;AAAA,MAC3B,uBAAuB,UAAU,CAAC,KAAK;AAAA,MACvC;AAAA,IACF,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAEA,SAAS,UAAU,GAAkB,WAAwB;AAC3D,QAAM,aAAa,UAAU;AAAA,IAC3B;AAAA,EACF;AACA,MAAI,WAAW,WAAW,EAAG;AAC7B,QAAM,QAAQ,WAAW,CAAC;AAC1B,QAAM,OAAO,WAAW,WAAW,SAAS,CAAC;AAC7C,QAAM,SAAU,UAAU,YAAY,EACnC;AACH,MAAI,EAAE,YAAY,WAAW,OAAO;AAClC,MAAE,eAAe;AACjB,SAAK,MAAM;AAAA,EACb,WAAW,CAAC,EAAE,YAAY,WAAW,MAAM;AACzC,MAAE,eAAe;AACjB,UAAM,MAAM;AAAA,EACd;AACF;AAEA,SAAS,WAAW,KAAqB;AACvC,SAAO,IAAI,QAAQ,mBAAmB,GAAG;AAC3C;;;AC/hCO,SAAS,mBACd,WACA,QACA,aACA,WACA;AACA,QAAM,KAAK,OAAO;AAClB,QAAM,UAAU,OAAO,SAAS,CAAC;AACjC,QAAM,UAAU,SAAS,SAAS,MAAM,KAAK,CAAC,MAAM,EAAE,gBAAgB;AAItE,MAAI,CAAC,WAAW,GAAG,uBAAuB,OAAQ;AAElD,QAAM,iBAAiB,UAAU,WAAW,QAAQ,MAAM,MAAM,IAAI;AACpE,QAAM,WAAW,SAAS,MAAM,gBAAgB;AAOhD,QAAM,eAAe,OAAO,eAAe;AAE3C,QAAM,WAAW,OAAO,YAAY,IAAkB,CAAC,MAAM,UAAU;AACrE,QAAI;AACJ,QAAI,iBAAiB,gBAAgB;AACnC,YAAM,MAAM,KAAK,OAAO,KAAK,UAAU,KAAK,GAAG;AAC/C,gBAAU,KAAK,IAAI,GAAG,iBAAiB,GAAG;AAAA,IAC5C,OAAO;AACL,YAAM,MAAM,KAAK,cAAc;AAC/B,YAAM,WAAW,KAAK,MAAO,iBAAiB,MAAO,GAAG;AACxD,gBAAU,KAAK,IAAI,GAAG,iBAAiB,QAAQ;AAAA,IACjD;AACA,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,KAAK,KAAK;AAAA,MACV,mBAAmB;AAAA,MACnB,uBAAuB;AAAA,IACzB;AAAA,EACF,CAAC;AAED,QAAM,gBAAgB,kBAAkB,QAAQ;AAChD,MAAI,gBAAgB,GAAG,gBAAgB,eAAe,gBAAgB;AACtE,MAAI,OAAO,GAAG,gBAAgB,UAAU;AACtC,oBAAgB,MAAM,GAAG,aAAa,GAAG,SAAS,SAAS,CAAC;AAAA,EAC9D;AAEA,QAAM,eACJ,GAAG,aAAa,cAAc,SAC1B,MAAM,GAAG,aAAa,WAAW,GAAG,SAAS,SAAS,CAAC,IACvD;AAEN,QAAM,OAAO,GAAG,OAAO,WAAW;AAClC,OAAK;AAAA,IACHC,cAAa,QAAQ,UAAU,eAAe,UAAU,YAAY;AAAA,EACtE;AAEA,MAAI,GAAG,UAAU,iBAAiB,OAAO,QAAQ;AAC/C,UAAM,YAAY,gBAAgB,OAAO,MAAM;AAC/C,QAAI,WAAW;AACb,WAAK,YAAY,UAAU,EAAE;AAC7B,kBAAY,UAAU,IAAI;AAAA,IAC5B;AAAA,EACF;AAEA,QAAM,YAAY,GAAG,OAAO,oBAAoB;AAAA,IAC9C,MAAM;AAAA,IACN,cAAc;AAAA,IACd,mBAAmB;AAAA,EACrB,CAAC;AAED,WAAS,QAAQ,CAAC,MAAM;AACtB,UAAM,SAAS;AAAA,MACb;AAAA,MACA,EAAE,UAAU;AAAA,MACZ;AAAA,MACA,GAAG,aAAa,WAAW,EAAE,UAAU,eACnC,GAAG,aAAa,OAChB;AAAA,MACJ,GAAG,QAAQ;AAAA,MACX,GAAG,QAAQ;AAAA,IACb;AACA,WAAO,iBAAiB,SAAS,MAAM,WAAW,EAAE,KAAK,CAAC;AAG1D,WAAO,iBAAiB,WAAW,CAAC,MAAM;AACxC,UAAI,EAAE,QAAQ,WAAW,EAAE,QAAQ,KAAK;AACtC,UAAE,eAAe;AACjB,mBAAW,EAAE,KAAK;AAClB;AAAA,MACF;AACA,UACE,EAAE,QAAQ,eACV,EAAE,QAAQ,gBACV,EAAE,QAAQ,aACV,EAAE,QAAQ,aACV;AACA,UAAE,eAAe;AACjB,cAAM,QACJ,EAAE,QAAQ,eAAe,EAAE,QAAQ,eAAe,IAAI;AACxD,cAAM,QAAQ,EAAE,QAAQ,QAAQ,SAAS,UAAU,SAAS;AAC5D,mBAAW,IAAI;AACf,cAAM,SAAS,UAAU,SAAS,IAAI;AACtC,gBAAQ,MAAM;AAAA,MAChB;AAAA,IACF,CAAC;AACD,cAAU,YAAY,MAAM;AAAA,EAC9B,CAAC;AAED,OAAK,YAAY,SAAS;AAC1B,OAAK,YAAY,GAAG,OAAO,mBAAmB,CAAC;AAE/C,MAAI,YAAYC;AAAA,IACd;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAG,QAAQ;AAAA,IACX,GAAG,QAAQ;AAAA,EACb;AACA,OAAK,YAAY,SAAS;AAE1B,MAAI,eAAmC,GAAG,WAAW,UACjDC,kBAAiB,UAAU,eAAe,QAAQ,IAClD;AACJ,MAAI,aAAc,MAAK,YAAY,YAAY;AAE/C,QAAM,MAAMC,WAAU,QAAQ,MAAM;AAClC,QAAI,CAAC,QAAS;AACd,UAAM,IAAI,SAAS,aAAa;AAChC,QAAI,CAAC,EAAG;AACR,gBAAY;AAAA,MACV;AAAA,QACE,eAAe,QAAQ;AAAA,QACvB,UAAU,EAAE;AAAA,QACZ,YAAY;AAAA,UACV,EAAE,KAAK,oBAAoB,OAAO,OAAO,GAAG;AAAA,UAC5C,EAAE,KAAK,qBAAqB,OAAO,OAAO,WAAW;AAAA,QACvD;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AACD,OAAK,YAAY,GAAG;AAEpB,OAAK;AAAA,IACH,GAAG,KAAK,mBAAmB,EAAE,cAAc,IAAI,aAAa,SAAS,CAAC;AAAA,EACxE;AACA,OAAK;AAAA,IACH,GAAG,QAAQ,sBAAsB;AAAA,MAC/B,eAAe;AAAA,MACf,aAAa;AAAA,IACf,CAAC;AAAA,EACH;AAEA,YAAU,YAAY,IAAI;AAE1B,WAAS,WAAW,KAAa;AAC/B,QAAI,QAAQ,iBAAiB,MAAM,KAAK,OAAO,SAAS,OAAQ;AAChE,oBAAgB;AAChB,UAAM,KAAK,UAAU,QAAQ,EAAE,QAAQ,CAAC,MAAM,MAAM;AAClD,WAAK,aAAa,gBAAgB,OAAO,MAAM,GAAG,CAAC;AACnD,MAAC,KAAqB,WAAW,MAAM,MAAM,IAAI;AAAA,IACnD,CAAC;AACD,UAAM,aAAaF;AAAA,MACjB;AAAA,MACA;AAAA,MACA;AAAA,MACA,GAAG,QAAQ;AAAA,MACX,GAAG,QAAQ;AAAA,IACb;AACA,cAAU,YAAY,UAAU;AAChC,gBAAY;AACZ,QAAI,cAAc;AAChB,YAAM,SAASC,kBAAiB,UAAU,KAAK,QAAQ;AACvD,mBAAa,YAAY,MAAM;AAC/B,qBAAe;AAAA,IACjB;AAIA,UAAM,UAAU,KAAK,cAA2B,qBAAqB;AACrE,QAAI,SAAS;AACX,YAAM,QAAQ,SAAS,SAAS,GAAG,GAAG,UAAU,YAAY;AAC5D,UAAI,OAAO;AACT,gBAAQ,cAAc;AACtB,gBAAQ,MAAM,UAAU;AAAA,MAC1B,OAAO;AACL,gBAAQ,MAAM,UAAU;AAAA,MAC1B;AAAA,IACF;AAAA,EACF;AACF;AAIA,SAASF,cACP,QACA,UACA,eACA,UACA,cACa;AACb,QAAM,KAAK,OAAO;AAClB,QAAM,SAAS,GAAG,OAAO,kBAAkB;AAC3C,QAAM,UAAU,GAAG,OAAO,2BAA2B;AACrD,QAAM,QAAQ,GAAG,MAAM,iBAAiB;AACxC,QAAM,cAAc,OAAO;AAC3B,UAAQ,YAAY,KAAK;AACzB,SAAO,YAAY,OAAO;AAE1B,MAAI,GAAG,QAAQ,eAAe;AAC5B,UAAM,QAAQ,SAAS,SAAS,aAAa,GAAG,UAAU,YAAY;AACtE,QAAI,OAAO;AACT,YAAM,UAAU,GAAG,QAAQ,2BAA2B;AAAA,QACpD,qBAAqB;AAAA,MACvB,CAAC;AACD,cAAQ,cAAc;AACtB,aAAO,YAAY,OAAO;AAAA,IAC5B;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,eACP,GACA,YACA,UACA,cACA,kBACA,kBACa;AACb,QAAM,OAAO,GAAG,OAAO,mBAAmB;AAAA,IACxC,MAAM;AAAA,IACN,gBAAgB,OAAO,UAAU;AAAA,IACjC,UAAU,aAAa,MAAM;AAAA,IAC7B,mBAAmB,OAAO,EAAE,KAAK;AAAA,IACjC,iBAAiB,OAAO,EAAE,GAAG;AAAA,EAC/B,CAAC;AAED,QAAM,QAAQ,GAAG,QAAQ,kBAAkB;AAC3C,QAAM,YAAY,GAAG,QAAQ,sBAAsB,CAAC;AACpD,OAAK,YAAY,KAAK;AAEtB,QAAM,OAAO,GAAG,QAAQ,sBAAsB;AAC9C,QAAM,QAAQ,GAAG,QAAQ,uBAAuB;AAChD,QAAM,cAAc,OAAO,EAAE,GAAG;AAChC,OAAK,YAAY,KAAK;AAEtB,QAAM,QAAQ,GAAG,QAAQ,uBAAuB;AAChD,MAAI,oBAAoB,EAAE,oBAAoB,EAAE,uBAAuB;AACrE,UAAM,UAAU,GAAG,QAAQ,yBAAyB;AACpD,YAAQ,cAAc,YAAY,EAAE,uBAAuB,QAAQ;AACnE,UAAM,YAAY,OAAO;AAAA,EAC3B;AACA,MAAI,kBAAkB;AACpB,UAAM,OAAO,SAAS,cAAc,MAAM;AAC1C,SAAK,aAAa,wBAAwB,EAAE;AAC5C,SAAK,cAAc,YAAY,EAAE,mBAAmB,QAAQ;AAC5D,UAAM,YAAY,IAAI;AACtB,UAAM,OAAO,GAAG,QAAQ,sBAAsB;AAC9C,SAAK,cAAc;AACnB,UAAM,YAAY,IAAI;AAAA,EACxB;AACA,OAAK,YAAY,KAAK;AACtB,OAAK,YAAY,IAAI;AAErB,QAAM,QAAQ,GAAG,QAAQ,uBAAuB;AAChD,MAAI,cAAc;AAChB,UAAM,cAAc;AAAA,EACtB,OAAO;AACL,UAAM,MAAM,UAAU;AAAA,EACxB;AACA,OAAK,YAAY,KAAK;AAEtB,SAAO;AACT;AAEA,SAASC,kBACP,UACA,eACA,UACA,eACA,oBACa;AACb,QAAM,IAAI,SAAS,aAAa;AAChC,QAAM,aAAa,IAAI,EAAE,oBAAoB,EAAE,MAAM;AACrD,QAAM,oBAAoB,IAAI,EAAE,wBAAwB,EAAE,MAAM;AAChE,QAAM,UAAU,KAAK,IAAI,GAAG,oBAAoB,UAAU;AAE1D,QAAM,MAAM,GAAG,OAAO,mBAAmB;AACzC,QAAM,QAAQ,GAAG,QAAQ,4BAA4B;AAAA,IACnD,oBAAoB;AAAA,EACtB,CAAC;AACD,QAAM,cAAc;AACpB,MAAI,iBAAiB,GAAG;AACtB,UAAM,QAAQ,SAAS,cAAc,MAAM;AAC3C,UAAM,aAAa,mBAAmB,EAAE;AACxC,UAAM,cAAc,KAAK,EAAE,GAAG,QAAQ,EAAE,QAAQ,IAAI,KAAK,GAAG;AAC5D,UAAM,YAAY,KAAK;AAAA,EACzB;AACA,MAAI,YAAY,KAAK;AAErB,QAAM,SAAS,GAAG,QAAQ,2BAA2B;AACrD,MAAI,sBAAsB,UAAU,GAAG;AACrC,UAAM,UAAU,GAAG,QAAQ,2BAA2B;AAAA,MACpD,sBAAsB;AAAA,IACxB,CAAC;AACD,YAAQ,cAAc,YAAY,mBAAmB,QAAQ;AAC7D,WAAO,YAAY,OAAO;AAAA,EAC5B;AACA,QAAM,OAAO,GAAG,QAAQ,wBAAwB,EAAE,oBAAoB,GAAG,CAAC;AAC1E,OAAK,cAAc,YAAY,YAAY,QAAQ;AACnD,SAAO,YAAY,IAAI;AACvB,MAAI,YAAY,MAAM;AACtB,SAAO;AACT;AAEA,SAASC,kBACP,UACA,eACA,UACa;AACb,QAAM,IAAI,SAAS,aAAa;AAChC,QAAM,aAAa,IAAI,EAAE,oBAAoB,EAAE,MAAM;AACrD,QAAM,oBAAoB,IAAI,EAAE,wBAAwB,EAAE,MAAM;AAChE,QAAM,UAAU,KAAK,IAAI,GAAG,oBAAoB,UAAU;AAE1D,QAAM,MAAM,GAAG,OAAO,yBAAyB,EAAE,oBAAoB,GAAG,CAAC;AACzE,MAAI,WAAW,EAAG,KAAI,MAAM,UAAU;AACtC,QAAM,UAAU,SAAS,cAAc,MAAM;AAC7C,UAAQ,cAAc;AACtB,MAAI,YAAY,OAAO;AACvB,QAAM,SAAS,GAAG,QAAQ,IAAI,EAAE,uBAAuB,GAAG,CAAC;AAC3D,SAAO,cAAc,YAAY,SAAS,QAAQ;AAClD,MAAI,YAAY,MAAM;AACtB,SAAO;AACT;AAEA,SAASC,WACP,QACA,SACa;AACb,QAAM,UAAU,OAAO,SAAS,CAAC;AACjC,QAAM,cAAc,SAAS,SAAS,MAAM,KAAK,CAAC,MAAM,EAAE,gBAAgB;AAC1E,QAAM,QAAQ,cACV,OAAO,aAAa,IAAI,WAAW,gBACnC;AACJ,QAAM,SAAS,eAAe,KAAK;AACnC,MAAI,CAAC,YAAa,QAAO,WAAW;AACpC,SAAO,iBAAiB,SAAS,MAAM;AACrC,QAAI,OAAO,SAAU;AACrB,YAAQ;AAAA,EACV,CAAC;AACD,SAAO;AACT;AAIA,SAAS,SACP,UACA,UACA,cACe;AACf,MAAI,CAAC,SAAU,QAAO;AACtB,QAAM,EAAE,KAAK,IAAI;AACjB,MAAI,iBAAiB,gBAAgB;AACnC,UAAM,SAAS,KAAK,UAAU;AAC9B,QAAI,SAAS,EAAG,QAAO,IAAI,YAAY,KAAK,MAAM,SAAS,GAAG,GAAG,QAAQ,CAAC;AAC1E,WAAO;AAAA,EACT;AACA,MAAI,iBAAiB,cAAc;AACjC,UAAM,MAAM,KAAK,cAAc;AAC/B,QAAI,MAAM,EAAG,QAAO,IAAI,KAAK,MAAM,GAAG,CAAC;AACvC,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEA,SAAS,kBAAkB,UAAkC;AAC3D,MAAI,cAAc;AAClB,MAAI,YAAY;AAChB,WAAS,QAAQ,CAAC,GAAG,MAAM;AACzB,UAAM,UAAU,EAAE,wBAAwB,EAAE;AAC5C,QAAI,UAAU,aAAa;AACzB,oBAAc;AACd,kBAAY;AAAA,IACd;AAAA,EACF,CAAC;AACD,SAAO;AACT;AAEA,SAAS,MAAM,GAAW,KAAa,KAAqB;AAC1D,SAAO,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,CAAC,CAAC;AACvC;;;AT5VA,SAAS,6BAA6B;;;AUvDtC,IAAM,WAAW,oBAAI,IAAI;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,IAAM,UAAU,oBAAI,IAAiB;AACrC,IAAI,oBAAoB;AAExB,SAAS,OAAO,IAA4C;AAC1D,QAAM,MAAM,OAAO,gBAAgB,mBAAmB;AACtD,aAAWC,OAAM,SAAS;AACxB,IAAAA,IAAG,UAAU,IAAI,EAAE;AACnB,IAAAA,IAAG,UAAU,OAAO,GAAG;AAAA,EACzB;AACF;AAEA,SAAS,UAAU,GAAwB;AACzC,MAAI,SAAS,IAAI,EAAE,GAAG,EAAG,QAAO,gBAAgB;AAClD;AAEA,SAAS,gBAAsB;AAC7B,SAAO,aAAa;AACtB;AAEA,SAAS,kBAAwB;AAC/B,MAAI,kBAAmB;AACvB,sBAAoB;AACpB,WAAS,iBAAiB,WAAW,WAAW,IAAI;AACpD,WAAS,iBAAiB,eAAe,eAAe,IAAI;AAC9D;AAEA,SAAS,kBAAwB;AAC/B,MAAI,CAAC,kBAAmB;AACxB,sBAAoB;AACpB,WAAS,oBAAoB,WAAW,WAAW,IAAI;AACvD,WAAS,oBAAoB,eAAe,eAAe,IAAI;AACjE;AAEO,SAAS,eAAe,QAAiC;AAC9D,SAAO,UAAU,IAAI,aAAa;AAClC,SAAO,UAAU,OAAO,gBAAgB;AACxC,UAAQ,IAAI,MAAM;AAClB,kBAAgB;AAEhB,SAAO,MAAM;AACX,YAAQ,OAAO,MAAM;AACrB,WAAO,UAAU,OAAO,aAAa;AACrC,WAAO,UAAU,OAAO,gBAAgB;AACxC,QAAI,QAAQ,SAAS,EAAG,iBAAgB;AAAA,EAC1C;AACF;;;ACpEO,IAAM,kBAAkB;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;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;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;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;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;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;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;AAojBxB,IAAM,mBAAmB;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;AA4EzB,IAAM,uBAAuB;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;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;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;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;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;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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAshB7B,IAAM,oBAAoB;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;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;AAgI1B,IAAM,sBAAsB;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;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;AA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","names":["name","resolveBundleQty","PLACEHOLDER_THUMB_SVG","renderHeader","renderSavingsBar","resolveBundleQty","renderHeader","renderPricingRow","renderSavingsBar","renderCta","el"]}