@lime-bundles/widget 3.2.0 → 3.2.1
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.cjs +72 -45
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +72 -45
- package/dist/index.js.map +1 -1
- package/dist/lime-bundle.global.js +78 -49
- package/dist/lime-bundle.global.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../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"],"sourcesContent":["/**\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","/**\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 persists the bucket via a\n * first-party cookie; variant attribution is recorded server-side from\n * the analytics events the widget already emits.\n */\n private async applyABVariants(): Promise<void> {\n if (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 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 isVariantFulfillable,\n shouldShowLowStockBadge,\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(\n rowState,\n currency,\n qtyFor,\n {\n enabled: wc.showLowStockBadge,\n threshold: wc.lowStockThreshold,\n },\n () => {\n // Variant change → recompute pricing.\n updatePricing();\n },\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 // \"In stock\" here means fulfillable for THIS variant's required quantity —\n // a variant with 3 units and required qty 5 is unfulfillable, even though\n // `availableForSale` is true. See packages/core/src/inventory/predicate.ts.\n const firstInStock =\n merchantScoped.find((v) =>\n isVariantFulfillable(v, resolveBundleQty(bundle, product.id, v.id)),\n ) ?? 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 lowStock: { enabled: boolean; threshold: 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 // Read-only variant text right under the title — same treatment as\n // the mix-match widget's filled-slot variant. Only renders when\n // the merchant pinned a single variant of a multi-variant product\n // (no picker would render in that case). Suppressed for products\n // with the default single variant.\n const isSinglePinnedVariant =\n state.eligibleVariants.length === 1 &&\n state.product.variants.nodes.length > 1;\n if (isSinglePinnedVariant) {\n const badge = el(\"span\", \"lb-bundle-variant-badge\");\n badge.textContent = state.eligibleVariants[0].title;\n info.appendChild(badge);\n }\n\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 // Low-stock badge — updated alongside price on variant change. Hidden\n // when the threshold isn't met or quantityAvailable is unknown.\n const lowStockEl = el(\"span\", \"lb-bundle-low-stock-badge\", {\n \"data-low-stock-badge\": \"\",\n });\n lowStockEl.setAttribute(\"hidden\", \"\");\n info.appendChild(lowStockEl);\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 const required = qtyFor(state.product.id, variant.id);\n if (\n shouldShowLowStockBadge(\n variant,\n required,\n lowStock.threshold,\n lowStock.enabled,\n )\n ) {\n lowStockEl.textContent = `Only ${variant.quantityAvailable} left`;\n lowStockEl.removeAttribute(\"hidden\");\n } else {\n lowStockEl.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 (!isVariantFulfillable(v, qtyFor(state.product.id, v.id))) 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 }\n // Single-variant case is handled above the price row, immediately\n // under the product title (same pattern as the mix-match widget).\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\n// Walk up to find the nearest ancestor that clips overflow on the Y axis.\n// Used so placement flips upward before the listbox would be hidden by a\n// scrollable container (`.lb-fixed__products` / `.lb-bundle__products`).\n// Stops at body/html — past that, the viewport bound is correct.\nfunction findScrollableAncestor(el: Element): HTMLElement | null {\n const win = el.ownerDocument?.defaultView;\n if (!win) return null;\n let cur: Element | null = el.parentElement;\n while (cur && cur !== el.ownerDocument.body) {\n const style = win.getComputedStyle(cur);\n const overflowY = style.overflowY;\n if (\n overflowY === \"auto\" ||\n overflowY === \"scroll\" ||\n overflowY === \"hidden\"\n ) {\n return cur as HTMLElement;\n }\n cur = cur.parentElement;\n }\n return null;\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 // When portaled (modal context) the listbox is position:fixed and not\n // clipped by any ancestor — viewport bounds are correct. In-shell, the\n // listbox is position:absolute inside the bundle's product list, which\n // is overflow:auto with a capped max-height. Without clip bounds, the\n // placement decision uses viewport room and we'd open downward into\n // the clipped zone. Pass the scrollable ancestor's rect so placement\n // flips upward before the panel would hide behind the widget footer.\n const scrollable = listbox.hasAttribute(\"data-lb-dropdown-portal\")\n ? null\n : findScrollableAncestor(trigger);\n const clip = scrollable\n ? (() => {\n const r = scrollable.getBoundingClientRect();\n return { top: r.top, bottom: r.bottom };\n })()\n : undefined;\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 clip,\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 `bundle.minQuantity` empty slots (one per distinct\n * product the shopper must pick).\n * 2. Clicking a slot opens the picker modal with the eligible products.\n * 3. Inside the modal, each product card shows a variant select, a qty\n * stepper (when `widgetConfig.mixMatchShowQuantitySelector !== false`),\n * and an Add button. The stepper is bounded by the merchant's\n * `productRules[productId]` (`{ min, max }`) and the variant's stock\n * via `maxAddableQuantity`.\n * 4. Adding a pick fills a slot with the chosen variant and quantity.\n * Remove-x on a filled slot empties it again.\n * 5. CTA unlocks when distinct picks ≥ `bundle.minQuantity`. On click,\n * selections are aggregated by `(productId, variantId)` into one cart\n * line per variant with summed quantity.\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 ProductRule,\n ProductVariant,\n} from \"@lime-bundles/core\";\nimport {\n DEFAULT_PRODUCT_RULE,\n isVariantFulfillable,\n maxAddableQuantity,\n shouldShowLowStockBadge,\n} 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\n/**\n * One pick by the shopper. A pick is a unique (productId, variantId) row\n * in the slot list with its own merchant-bounded quantity. Two picks of\n * the same variant are not allowed — the shopper bumps the stepper instead\n * (the slot's row is replaced when re-added). The CartLineInput aggregator\n * still defends against duplicate (productId, variantId) entries by summing.\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 /** Shopper-chosen qty, clamped to `[rule.min, maxAddableQuantity(...)]`. */\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\nconst STEPPER_MINUS_SVG = `\n<svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <line x1=\"3\" y1=\"7\" x2=\"11\" y2=\"7\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n</svg>`;\n\nconst STEPPER_PLUS_SVG = `\n<svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <line x1=\"7\" y1=\"3\" x2=\"7\" y2=\"11\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n <line x1=\"3\" y1=\"7\" x2=\"11\" y2=\"7\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n</svg>`;\n\n/** Resolve the merchant rule for a product, applying the runtime default. */\nfunction ruleFor(bundle: MixMatchBundleData, productId: string): ProductRule {\n return bundle.productRules[productId] ?? DEFAULT_PRODUCT_RULE;\n}\n\n/** Sum of quantities the shopper has already allocated to this variant. */\nfunction alreadyInBundleFor(\n selections: Selection[],\n productId: string,\n variantId: string,\n): number {\n let sum = 0;\n for (const s of selections) {\n if (s.productId === productId && s.variantId === variantId) sum += s.quantity;\n }\n return sum;\n}\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 showQtySelector = wc.mixMatchShowQuantitySelector !== false;\n\n // Build eligible-products list, honoring outOfStockBehavior. A product is\n // eligible when at least one of its variants can satisfy `rule.min` units.\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 requiredQty from\n // distinct in-stock products, don't render the widget at all. Matches\n // 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 });\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 ? renderSavingsBar() : 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 showQtySelector,\n selections,\n onAdd: (product, variant, quantity) =>\n addSelection(product, variant, quantity),\n isComplete: () => selections.length >= requiredQty,\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 onAddToCart(buildCartLines(selections, bundle));\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 // 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(\n product: Product,\n variant: ProductVariant,\n quantity: number,\n ) {\n if (selections.length >= requiredQty) return;\n const rule = ruleFor(bundle, product.id);\n const cap = maxAddableQuantity(\n variant,\n rule.max,\n alreadyInBundleFor(selections, product.id, variant.id),\n );\n // Refuse the pick when stock can't satisfy the per-product minimum —\n // otherwise the slot would carry more units than the variant has, and\n // Shopify's checkout-time inventory check would fail with an opaque\n // \"out of stock\" error after the customer hit Add to cart.\n if (cap < rule.min) return;\n // Clamp defensively. The picker stepper enforces these bounds, but\n // a malformed Add (e.g. keyboard event before stepper init) shouldn't\n // bypass them.\n const clamped = Math.max(rule.min, Math.min(quantity, cap));\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: clamped,\n });\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/**\n * Aggregate selections into one CartLineInput per (productId, variantId).\n * Matches the discount-function attribution contract: every line carries\n * `_lime_bundle_gid` and `_lime_bundle_type` so the orders/create webhook\n * can map purchases back to this bundle.\n */\nfunction buildCartLines(\n selections: Selection[],\n bundle: MixMatchBundleData,\n): CartLineInput[] {\n const grouped = new Map<string, { variantId: string; quantity: number }>();\n for (const s of selections) {\n const key = `${s.productId}::${s.variantId}`;\n const existing = grouped.get(key);\n if (existing) {\n existing.quantity += s.quantity;\n } else {\n grouped.set(key, { variantId: s.variantId, quantity: s.quantity });\n }\n }\n return Array.from(grouped.values()).map((line) => ({\n merchandiseId: line.variantId,\n quantity: line.quantity,\n attributes: [\n { key: \"_lime_bundle_gid\", value: bundle.id },\n { key: \"_lime_bundle_type\", value: bundle.bundleType },\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 // Filled-slot badge shows the shopper's chosen qty as \"× N\", which makes\n // it visually distinct from the picker's per-product qty stepper. Bare\n // \"1\" on a square thumbnail looks like a placeholder digit.\n qtyBadge.textContent = `×${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 showQtySelector: boolean;\n /** Live reference to the parent's selections. Read inside the modal to\n * compute remaining stock per variant (`maxAddableQuantity`). */\n selections: Selection[];\n onAdd: (product: Product, variant: ProductVariant, quantity: number) => void;\n isComplete: () => 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 /** Recompute stepper bounds + count badge from current selections. */\n refreshFromSelections: () => void;\n }> = [];\n\n function buildRows() {\n if (rowsBuilt) return;\n rowsBuilt = true;\n list.innerHTML = \"\";\n\n eligible.forEach((ep) => {\n const rule = ruleFor(bundle, ep.product.id);\n\n // Mirrors the Liquid picker-modal pattern in lb-mix-match.liquid:\n // - title/price/unit-price are <p> elements so they stack as blocks.\n // - <select> appears for products with >1 variant; the add button\n // dispatches the currently-selected variant.\n // - Variants that can't satisfy `rule.min` units are sold-out for\n // this bundle's purposes.\n const availableVariants = ep.variants;\n const firstAvailVariant =\n ep.variants.find((v) => isVariantFulfillable(v, rule.min)) ??\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) => isVariantFulfillable(v, rule.min)) ??\n ep.variants[0] ??\n 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 // Count badge shows how many units of the *current* variant the\n // shopper has already pinned to this bundle. Hidden at zero.\n const countBadge = el(\"span\", \"lb-bundle-qty-badge\");\n countBadge.hidden = true;\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 // Picker price reflects single-unit price — the stepper shows\n // multiplier separately. Keeps the price label stable as the\n // shopper bumps the stepper.\n price.textContent = formatCents(parseCents(currentVariant.price.amount), currency);\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 // Low-stock badge — re-rendered on variant change.\n const lowStockBadge = el(\"span\", \"lb-bundle-low-stock-badge\", {\n \"data-low-stock-badge\": \"\",\n });\n lowStockBadge.hidden = true;\n info.appendChild(lowStockBadge);\n const refreshLowStockBadge = (variant: ProductVariant) => {\n if (\n shouldShowLowStockBadge(\n variant,\n rule.min,\n bundle.widgetConfig.lowStockThreshold,\n bundle.widgetConfig.showLowStockBadge,\n )\n ) {\n lowStockBadge.textContent = `Only ${variant.quantityAvailable} left`;\n lowStockBadge.hidden = false;\n } else {\n lowStockBadge.hidden = true;\n }\n };\n refreshLowStockBadge(currentVariant);\n\n // Per-pick quantity stepper sits inside info between the low-stock\n // badge and the variant picker so the modal reads price → unit\n // price → qty → variant. Built before the variant logic so its\n // bounds closure can pick up later currentVariant updates from\n // the variant select handlers via the shared `currentVariant`\n // binding. Mirrors the Liquid template structure.\n const computeStepperBounds = () => {\n const already = alreadyInBundleFor(\n handlers.selections,\n ep.product.id,\n currentVariant.id,\n );\n const cap = maxAddableQuantity(currentVariant, rule.max, already);\n // Stepper max can never drop below `rule.min` while the variant is\n // fulfillable — but if `cap` is below `rule.min` (e.g. only 1 unit\n // left and rule.min=2) we surface that by disabling Add and\n // pinning the stepper at rule.min.\n const max = Math.max(rule.min, cap);\n return { min: rule.min, max, cap };\n };\n\n let stepper: ReturnType<typeof renderQtyStepper> | null = null;\n if (!ep.isOos && handlers.showQtySelector) {\n // Wrap label + stepper in a .lb-bundle-variant-option-group so\n // the label-to-stepper gap inherits the same 2px variant option\n // groups use. The compound .lb-mix-match__qty-stepper-group\n // class carries the margin-top that spaces the qty group from\n // whatever sits above. Mirrors the variant picker structure\n // further down so the modal reads \"Quantity / [stepper]\"\n // parallel to \"Size / [select]\".\n const qtyGroup = el(\n \"div\",\n \"lb-bundle-variant-option-group lb-mix-match__qty-stepper-group\",\n );\n const qtyLabel = el(\"span\", \"lb-bundle-variant-option-label\");\n qtyLabel.textContent = \"Quantity\";\n qtyGroup.appendChild(qtyLabel);\n stepper = renderQtyStepper({\n initial: rule.min,\n getBounds: () => {\n const { min, max } = computeStepperBounds();\n return { min, max };\n },\n });\n qtyGroup.appendChild(stepper.el);\n info.appendChild(qtyGroup);\n }\n\n const row = {\n el: productEl,\n product: ep.product,\n variant: firstAvailVariant,\n refreshFromSelections: () => {},\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 are disabled, so the customer gets clear feedback.\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 (!isVariantFulfillable(v, rule.min)) 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.\n syncSelectsToVariant(currentVariant);\n recomputeDisabled(\n currentVariant.selectedOptions.map((o) => o.value),\n );\n return;\n }\n currentVariant = next;\n row.variant = next;\n price.textContent = formatCents(parseCents(currentVariant.price.amount), currency);\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.\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 refreshLowStockBadge(currentVariant);\n row.refreshFromSelections();\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 // --- Quantity stepper + Add button ---\n // Stepper rendering happened earlier (right after the low-stock\n // badge, before the variant picker) so the modal reads price →\n // unit price → qty → variant. The Add button stays in this\n // actions block to the right of the row. Mirrors the Liquid\n // template ordering in extensions/bundle-theme/snippets/\n // lb-mix-match.liquid + bundle-mix-match.js.\n let addBtn: HTMLButtonElement | null = null;\n\n const refreshAddState = () => {\n if (!addBtn) return;\n const { cap } = computeStepperBounds();\n const already = alreadyInBundleFor(\n handlers.selections,\n ep.product.id,\n currentVariant.id,\n );\n // Update the in-thumb count badge to show the live aggregate qty.\n if (already > 0) {\n countBadge.textContent = String(already);\n countBadge.hidden = false;\n } else {\n countBadge.hidden = true;\n }\n // Add disabled when: bundle is already full, or variant can't\n // accept another rule.min units.\n addBtn.disabled = handlers.isComplete() || cap < rule.min;\n };\n\n if (!ep.isOos) {\n const actions = el(\"div\", \"lb-mix-match__modal-product-actions\");\n\n 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 (!addBtn || addBtn.disabled) return;\n if (handlers.isComplete()) return;\n // When the stepper is hidden, Add adds qty = rule.min (per spec).\n const qty = stepper ? stepper.value() : rule.min;\n handlers.onAdd(ep.product, currentVariant, qty);\n // Reset stepper to rule.min so the next pick of this product\n // doesn't carry over the previous shopper-chosen qty.\n if (stepper) stepper.reset(rule.min);\n close();\n });\n actions.appendChild(addBtn);\n productEl.appendChild(actions);\n }\n\n row.refreshFromSelections = () => {\n if (stepper) stepper.refresh();\n refreshAddState();\n };\n // Initialise the badge + Add disabled state.\n row.refreshFromSelections();\n\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.isComplete()) 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.refreshFromSelections());\n }\n\n return { el: overlay, open, close, refreshCounts };\n}\n\n// --- Quantity stepper ---\n\ninterface QtyStepper {\n el: HTMLElement;\n /** Current value (clamped to bounds). */\n value: () => number;\n /** Reset to a known qty (clamped to current bounds). */\n reset: (qty: number) => void;\n /** Re-evaluate bounds — disable / clamp when the cap drops. */\n refresh: () => void;\n}\n\nfunction renderQtyStepper(opts: {\n initial: number;\n getBounds: () => { min: number; max: number };\n}): QtyStepper {\n const wrap = el(\"div\", \"lb-mix-match__qty-stepper\", {\n role: \"group\",\n \"aria-label\": \"Quantity\",\n });\n const minus = document.createElement(\"button\");\n minus.type = \"button\";\n minus.className =\n \"lb-mix-match__qty-stepper-button lb-mix-match__qty-stepper-button--minus\";\n minus.setAttribute(\"aria-label\", \"Decrease quantity\");\n minus.innerHTML = STEPPER_MINUS_SVG;\n wrap.appendChild(minus);\n\n const valueEl = el(\"span\", \"lb-mix-match__qty-stepper-value\", {\n \"aria-live\": \"polite\",\n });\n wrap.appendChild(valueEl);\n\n const plus = document.createElement(\"button\");\n plus.type = \"button\";\n plus.className =\n \"lb-mix-match__qty-stepper-button lb-mix-match__qty-stepper-button--plus\";\n plus.setAttribute(\"aria-label\", \"Increase quantity\");\n plus.innerHTML = STEPPER_PLUS_SVG;\n wrap.appendChild(plus);\n\n let current = clamp(opts.initial, opts.getBounds());\n\n function clamp(n: number, b: { min: number; max: number }): number {\n return Math.max(b.min, Math.min(b.max, n));\n }\n\n function paint() {\n const b = opts.getBounds();\n current = clamp(current, b);\n valueEl.textContent = String(current);\n minus.disabled = current <= b.min;\n plus.disabled = current >= b.max;\n }\n\n minus.addEventListener(\"click\", () => {\n const b = opts.getBounds();\n current = clamp(current - 1, b);\n paint();\n });\n plus.addEventListener(\"click\", () => {\n const b = opts.getBounds();\n current = clamp(current + 1, b);\n paint();\n });\n\n paint();\n\n return {\n el: wrap,\n value: () => current,\n reset(qty) {\n current = clamp(qty, opts.getBounds());\n paint();\n },\n refresh: paint,\n };\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 rule = ruleFor(bundle, product.id);\n // A variant is \"available for this bundle\" when it can fulfil at least\n // `rule.min` units. Stricter rules raise the bar (e.g. min=3 + only\n // 2 in stock = sold out for this bundle's purposes).\n const available = product.variants.nodes.filter((v) =>\n isVariantFulfillable(v, rule.min),\n );\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 * 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 { isVariantFulfillable, shouldShowLowStockBadge } 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 // For volume bundles, the natural per-bundle floor is the smallest\n // tier's minQuantity — anything below that can't even buy the cheapest\n // tier. Variants that satisfy that floor are \"fulfillable\" for visibility\n // and CTA-enable purposes.\n const minTierQty = bundle.volumeTiers[0]?.minQuantity ?? 1;\n const variant = product?.variants.nodes.find((v) =>\n isVariantFulfillable(v, minTierQty),\n );\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 // Low-stock badge — shown when the variant has stock at or below the\n // merchant-configured threshold. Hidden when stock is unknown\n // (Storefront token without read_product_inventory) or above threshold.\n if (\n variant &&\n shouldShowLowStockBadge(\n variant,\n minTierQty,\n wc.lowStockThreshold,\n wc.showLowStockBadge,\n )\n ) {\n const lowStock = el(\"span\", \"lb-bundle-low-stock-badge\", {\n \"data-low-stock-badge\": \"\",\n });\n lowStock.textContent = `Only ${variant.quantityAvailable} left`;\n root.appendChild(lowStock);\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 minTierQty = bundle.volumeTiers[0]?.minQuantity ?? 1;\n const isAvailable = product?.variants.nodes.some((v) =>\n isVariantFulfillable(v, minTierQty),\n );\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/* Read-only variant text shown below the product title when only one\n variant is in scope (single-variant product, or merchant pinned a\n single variant). Same visual treatment as the mix-match filled\n slot's variant text — see .lb-mix-match__filled-variant in\n bundle-mix-match.css. Both share this rule via the comma selector\n so the storefront UX stays consistent across bundle types. */\n.lb-bundle-variant-badge,\n.lb-mix-match__slot--filled .lb-mix-match__filled-variant {\n display: block;\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/* 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:not([aria-checked=\"true\"]),\n.using-mouse .lb-bundle-widget :focus-visible:not([aria-checked=\"true\"]),\n.using-mouse .lb-mix-match__modal-overlay :focus:not([aria-checked=\"true\"]),\n.using-mouse .lb-mix-match__modal-overlay :focus-visible:not([aria-checked=\"true\"]) {\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/* \"Only X left\" low-stock badge — appears alongside product/variant\n info when ProductVariant.quantityAvailable falls at or below\n wc.lowStockThreshold. Hidden when quantityAvailable is unknown\n (Storefront token without read_product_inventory). */\n.lb-bundle-low-stock-badge {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 2px 8px;\n font-size: 11px;\n font-weight: 600;\n line-height: 1.4;\n color: var(--lb-low-stock-text);\n background-color: var(--lb-low-stock-bg);\n border-radius: 4px;\n white-space: nowrap;\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__filled-variant — styled jointly with\n .lb-bundle-variant-badge above to keep the variant text consistent\n across mix-match and fixed bundle widgets. */\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: 60px;\n min-width: 60px;\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: 2px 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/* Per-pick quantity stepper inside the picker modal product row.\n Three flex cells (− / number / +) sharing a single outer border —\n driven by its own --lb-picker-qty-stepper-* variables so merchants\n can style the stepper independently of the variant dropdown. The\n cell dividers come from a 1px border on the centre value rather\n than per-button borders, so the rounded outer corners stay clean.\n Mirrors extensions/bundle-theme/assets/bundle-mix-match.css so\n the headless web component matches the Liquid theme widget. */\n/* Quantity label + stepper sit together in a .lb-bundle-variant-option-group\n wrapper so the label-to-stepper gap inherits the same 2px the variant\n pickers use. The wrapper carries the margin-top that spaces the qty\n group from the unit-price / low-stock-badge above; the stepper itself\n has no top margin so the group can be repositioned without coupling. */\n.lb-mix-match__qty-stepper-group {\n margin-top: 8px;\n /* Group is a flex column; without an explicit width it stretches to\n fill the info column so the −/value/+ cells get pulled apart. Pin\n to fit-content so the wrapper hugs the stepper's natural width. */\n width: fit-content;\n}\n\n.lb-mix-match__qty-stepper {\n display: inline-flex;\n align-items: stretch;\n flex-shrink: 0;\n height: 32px;\n border: var(--lb-picker-qty-stepper-border-width) solid var(--lb-picker-qty-stepper-border-color);\n border-radius: var(--lb-picker-qty-stepper-radius);\n background-color: var(--lb-picker-bg);\n overflow: hidden;\n box-sizing: border-box;\n}\n\n.lb-mix-match__qty-stepper-button {\n appearance: none;\n -webkit-appearance: none;\n background: transparent;\n border: none;\n margin: 0;\n padding: 0;\n width: 28px;\n font-family: inherit;\n font-size: 16px;\n font-weight: 500;\n line-height: 1;\n color: var(--lb-picker-text);\n cursor: pointer;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n}\n\n.lb-mix-match__qty-stepper-button:hover:not(:disabled) {\n background: color-mix(in srgb, var(--lb-picker-text) 6%, transparent);\n}\n\n.lb-mix-match__qty-stepper-button:focus-visible {\n outline: 2px solid var(--lb-primary-color);\n outline-offset: -2px;\n}\n\n.lb-mix-match__qty-stepper-button:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n\n.lb-mix-match__qty-stepper-value {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 36px;\n padding: 0 8px;\n font-family: inherit;\n font-size: 13px;\n font-weight: 500;\n color: var(--lb-picker-text);\n box-sizing: border-box;\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: var(--lb-tier-selected-border-color);\n}\n\n/* Suppress the UA default focus outline so a freshly-clicked selected\n tier doesn't briefly show the 1px focus ring on top of (or in place of)\n the custom selected-state outline below. Keyboard focus is still\n indicated by the :focus-visible rule. */\n.lb-volume__tier:focus {\n outline: none;\n}\n\n/* Keyboard focus indicator — explicitly excludes the selected tier so\n the selected-state outline rule below has full ownership of the\n outline property when both states apply at once. */\n.lb-volume__tier:focus-visible:not([aria-checked=\"true\"]) {\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"],"mappings":"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A;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA0kBxB,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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAkmB7B,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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA2I1B,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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAoQ5B,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;;;AXxjDnC,IAAM,iBAAiB,CAAC,eAAuB,cAAc,UAAU;AAMvE,SAAS,qBAAqB,UAAwC;AACpE,MAAI,SAAU,QAAO,SAAS,KAAK,KAAK;AAExC,MAAI,OAAO,aAAa,aAAa;AACnC,UAAM,OAAO,SAAS;AAAA,MACpB;AAAA,IACF;AACA,QAAI,MAAM,QAAS,QAAO,KAAK,QAAQ,KAAK,KAAK;AAAA,EACnD;AAEA,MAAI,OAAO,WAAW,aAAa;AACjC,UAAM,QAAQ,OAAO,SAAS,SAAS,MAAM,uBAAuB;AACpE,QAAI,QAAQ,CAAC,EAAG,QAAO,mBAAmB,MAAM,CAAC,CAAC;AAAA,EACpD;AAEA,SAAO;AACT;AAEO,IAAM,oBAAN,cAAgC,YAAY;AAAA,EACjD,OAAO,qBAAqB;AAAA,IAC1B;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EAEQ;AAAA,EACA,UAA0B,CAAC;AAAA,EAC3B,kBAA0C;AAAA,EAC1C,qBAAwC,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMzC,iBAAoC,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMrC,gBAA+B;AAAA,EAEvC,cAAc;AACZ,UAAM;AACN,SAAK,SAAS,KAAK,aAAa,EAAE,MAAM,OAAO,CAAC;AAAA,EAClD;AAAA,EAEA,oBAAoB;AAIlB,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,uBAAuB;AACrB,SAAK,iBAAiB,MAAM;AAC5B,SAAK,oBAAoB;AACzB,SAAK,kBAAkB;AAAA,EACzB;AAAA,EAEQ,sBAAsB;AAC5B,eAAW,WAAW,KAAK,mBAAoB,SAAQ;AACvD,SAAK,qBAAqB,CAAC;AAAA,EAC7B;AAAA,EAEQ,oBAAoB;AAC1B,eAAW,WAAW,KAAK,eAAgB,SAAQ;AACnD,SAAK,iBAAiB,CAAC;AAAA,EACzB;AAAA,EAEA,yBACE,MACA,UACA,UACA;AACA,QAAI,aAAa,YAAY,CAAC,KAAK,YAAa;AAChD,QACE,SAAS,gBACT,SAAS,oBACT,SAAS,iBACT,SAAS,oBACT;AACA,UAAI,KAAK,cAAc,KAAK,iBAAiB;AAC3C,aAAK,YAAY;AAAA,MACnB;AAAA,IACF;AAAA,EACF;AAAA,EAEA,IAAY,aAAqB;AAC/B,WAAO,KAAK,aAAa,aAAa,KAAK;AAAA,EAC7C;AAAA,EAEA,IAAY,kBAA0B;AACpC,WAAO,KAAK,aAAa,kBAAkB,KAAK;AAAA,EAClD;AAAA,EAEA,IAAY,YAAoB;AAC9B,WAAO,KAAK,aAAa,YAAY,KAAK;AAAA,EAC5C;AAAA,EAEA,IAAY,oBAA4B;AACtC,WAAO,KAAK,aAAa,gBAAgB,KAAK;AAAA,EAChD;AAAA,EAEA,IAAY,SAAiB;AAC3B,WAAO,KAAK,aAAa,SAAS,KAAK;AAAA,EACzC;AAAA,EAEA,IAAY,mBAA4B;AACtC,WAAO,KAAK,aAAa,WAAW,MAAM;AAAA,EAC5C;AAAA,EAEA,MAAc,cAAc;AAC1B,QAAI,CAAC,KAAK,cAAc,CAAC,KAAK,iBAAiB;AAC7C,WAAK;AAAA,QACH;AAAA,MACF;AACA;AAAA,IACF;AAEA,SAAK,iBAAiB,MAAM;AAC5B,UAAM,aAAa,IAAI,gBAAgB;AACvC,SAAK,kBAAkB;AAEvB,SAAK,cAAc;AAEnB,UAAM,aAAS,qCAAuB;AAAA,MACpC,YAAY,KAAK;AAAA,MACjB,aAAa,KAAK;AAAA,IACpB,CAAC;AAED,QAAI;AAKF,UAAI;AACJ,UAAI,mBAAmB;AACvB,UAAI,KAAK,WAAW;AAClB,2BAAmB;AACnB,wBAAgB,KAAK,kBAAkB,QAAQ,WAAW,MAAM;AAAA,MAClE,OAAO;AACL,cAAM,SAAS,qBAAqB,KAAK,iBAAiB;AAC1D,YAAI,CAAC,QAAQ;AACX,eAAK,oBAAoB;AACzB,eAAK;AAAA,YACH;AAAA,UACF;AACA;AAAA,QACF;AACA,wBAAgB,KAAK;AAAA,UACnB;AAAA,UACA,WAAW;AAAA,UACX;AAAA,QACF;AAAA,MACF;AAIA,YAAM,aAAa,OAChB,MAA6B,oCAAuB,QAAW;AAAA,QAC9D,QAAQ,WAAW;AAAA,MACrB,CAAC,EACA,MAAM,MAAM,IAAI;AAEnB,YAAM;AACN,UAAI,WAAW,OAAO,QAAS;AAO/B,UAAI,oBAAoB,KAAK,QAAQ,WAAW,GAAG;AACjD,aAAK,oBAAoB;AACzB,aAAK,YAAY,kBAAkB;AACnC;AAAA,MACF;AAEA,YAAM,MAAM,MAAM;AAClB,UAAI,KAAK,MAAM,WAAW,OAAO;AAI/B,0CAAgB,KAAK,YAAY,IAAI,KAAK,UAAU,KAAK;AACzD,cAAM,gBAAY,gCAAkB,IAAI,KAAK,UAAU,KAAK;AAC5D,YAAI,UAAU,GAAI,MAAK,gBAAgB,UAAU;AAAA,MACnD;AAMA,YAAM,KAAK,gBAAgB;AAE3B,WAAK,cAAc;AAAA,IACrB,SAAS,KAAK;AACZ,UAAI,WAAW,OAAO,QAAS;AAC/B,WAAK,UAAU,CAAC;AAChB,WAAK,oBAAoB;AACzB,WAAK;AAAA,QACH,eAAe,QAAQ,IAAI,UAAU;AAAA,MACvC;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAc,kBAAiC;AAC7C,QAAI,KAAK,QAAQ,WAAW,EAAG;AAC/B,UAAM,UAAU,MAAM,QAAQ;AAAA,MAC5B,KAAK,QAAQ,IAAI,OAAO,WAAW;AACjC,YAAI,CAAC,OAAO,YAAY,CAAC,OAAO,WAAY,QAAO;AACnD,YAAI;AACF,gBAAM,aAAa,UAAM;AAAA,YACvB,OAAO;AAAA,YACP,OAAO;AAAA,UACT;AACA,cAAI,YAAY,YAAY,KAAK;AAC/B,uBAAO,8BAAgB,MAAM;AAAA,UAC/B;AAAA,QACF,SAAS,KAAK;AAIZ,kBAAQ;AAAA,YACN,kDAAkD,OAAO,EAAE;AAAA,YAC3D;AAAA,UACF;AAAA,QACF;AACA,eAAO;AAAA,MACT,CAAC;AAAA,IACH;AACA,SAAK,UAAU;AAAA,EACjB;AAAA,EAEA,MAAc,kBACZ,QACA,QACe;AACf,UAAM,OAAO,MAAM,OAAO;AAAA,MACxB;AAAA,MACA,EAAE,IAAI,KAAK,UAAU;AAAA,MACrB,EAAE,OAAO;AAAA,IACX;AACA,QAAI,CAAC,KAAK,YAAY;AACpB,WAAK,UAAU,CAAC;AAChB;AAAA,IACF;AACA,UAAM,aAAS;AAAA,MACb,KAAK,WAAW;AAAA,MAChB,KAAK,WAAW;AAAA,IAClB;AACA,SAAK,UAAU,SAAS,CAAC,MAAM,IAAI,CAAC;AAAA,EACtC;AAAA,EAEA,MAAc,oBACZ,QACA,QACA,eACe;AAIf,UAAM,OAAO,MAAM,OAAO;AAAA,MACxB;AAAA,MACA,EAAE,QAAQ,cAAc;AAAA,MACxB,EAAE,OAAO;AAAA,IACX;AACA,QAAI,CAAC,KAAK,SAAS;AACjB,WAAK,UAAU,CAAC;AAChB;AAAA,IACF;AACA,UAAM,OAAO,KAAK,QAAQ,WAAW,YAAY,SAAS,CAAC;AAC3D,UAAM,UAA0B,CAAC;AACjC,eAAW,OAAO,MAAM;AACtB,YAAM,aAAS,oCAAsB,IAAI,IAAI,IAAI,MAAM;AACvD,UAAI,OAAQ,SAAQ,KAAK,MAAM;AAAA,IACjC;AACA,SAAK,UAAU;AAAA,EACjB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,kBAAkB,OACxB,QACA,UACkB;AAClB,UAAM,KAAK,IAAI,YAAY,2BAA2B;AAAA,MACpD,QAAQ,EAAE,MAAM;AAAA,MAChB,SAAS;AAAA,MACT,UAAU;AAAA,MACV,YAAY;AAAA,IACd,CAAC;AAGD,UAAM,eAAe,KAAK,cAAc,EAAE;AAG1C,SAAK,qBAAqB,QAAQ,KAAK;AAEvC,QAAI,cAAc;AAChB,YAAM,KAAK,iBAAiB,KAAK;AAAA,IACnC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAc,iBAAiB,OAAuC;AACpE,QAAI,OAAO,WAAW,YAAa;AAEnC,UAAM,aAAS,qCAAuB;AAAA,MACpC,YAAY,KAAK;AAAA,MACjB,aAAa,KAAK;AAAA,IACpB,CAAC;AACD,UAAM,UAAU,OAAO;AACvB,UAAM,MAAM,eAAe,KAAK,UAAU;AAC1C,UAAM,iBAAiB,SAAS,QAAQ,GAAG,KAAK;AAEhD,QAAI;AACF,UAAI,cAA6B;AAEjC,UAAI,gBAAgB;AAClB,cAAM,MAAM,MAAM,OAAO;AAAA,UACvB;AAAA,UACA,EAAE,QAAQ,gBAAgB,MAAM;AAAA,QAClC;AACA,cAAM,UAAU,IAAI;AACpB,YAAI,SAAS,YAAY,QAAQ;AAG/B,mBAAS,WAAW,GAAG;AAAA,QACzB,WAAW,SAAS,MAAM;AACxB,wBAAc,QAAQ,KAAK;AAAA,QAC7B;AAAA,MACF;AAEA,UAAI,CAAC,aAAa;AAChB,cAAM,MAAM,MAAM,OAAO;AAAA,UACvB;AAAA,UACA,EAAE,OAAO,EAAE,MAAM,EAAE;AAAA,QACrB;AACA,cAAM,UAAU,IAAI;AACpB,YAAI,SAAS,MAAM;AACjB,mBAAS,QAAQ,KAAK,QAAQ,KAAK,EAAE;AACrC,wBAAc,QAAQ,KAAK;AAAA,QAC7B;AAAA,MACF;AAEA,UAAI,aAAa;AACf,eAAO,SAAS,OAAO,WAAW;AAAA,MACpC,OAAO;AACL,aAAK;AAAA,UACH,IAAI,YAAY,qBAAqB;AAAA,YACnC,QAAQ,EAAE,SAAS,wBAAwB,MAAM,aAAa;AAAA,YAC9D,SAAS;AAAA,YACT,UAAU;AAAA,UACZ,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF,SAAS,KAAK;AACZ,WAAK;AAAA,QACH,IAAI,YAAY,qBAAqB;AAAA,UACnC,QAAQ;AAAA,YACN,SACE,eAAe,QAAQ,IAAI,UAAU;AAAA,YACvC,MAAM;AAAA,UACR;AAAA,UACA,SAAS;AAAA,UACT,UAAU;AAAA,QACZ,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,qBACN,QACA,OACM;AACN,QAAI,CAAC,KAAK,oBAAoB,CAAC,KAAK,OAAQ;AAE5C,UAAM,WAAW,MAAM,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,UAAU,CAAC;AAC7D,UAAM,aAAa,MAAM,OAAO,CAAC,KAAK,SAAS;AAC7C,YAAM,UAAU,OAAO,SAAS;AAAA,QAAK,CAAC,MACpC,EAAE,SAAS,MAAM,KAAK,CAAC,MAAM,EAAE,OAAO,KAAK,aAAa;AAAA,MAC1D;AACA,YAAM,UAAU,SAAS,SAAS,MAAM;AAAA,QACtC,CAAC,MAAM,EAAE,OAAO,KAAK;AAAA,MACvB;AACA,YAAM,QAAQ,UAAU,WAAW,QAAQ,MAAM,MAAM,IAAI;AAC3D,aAAO,MAAM,QAAQ,KAAK;AAAA,IAC5B,GAAG,CAAC;AAEJ;AAAA,MACE,EAAE,YAAY,KAAK,YAAY,QAAQ,KAAK,OAAO;AAAA,MACnD;AAAA,QACE,WAAW,OAAO;AAAA,QAClB,YAAY,OAAO;AAAA,QACnB,WAAW,OAAO,SAAS,CAAC,GAAG,MAAM;AAAA,QACrC;AAAA,QACA,YAAY,KAAK,MAAM,aAAa,GAAG,IAAI;AAAA,MAC7C;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,gBAAgB;AACtB,SAAK,oBAAoB;AACzB,SAAK,kBAAkB;AACvB,SAAK,OAAO,YAAY;AAQxB,UAAM,QAAQ,SAAS,cAAc,OAAO;AAC5C,UAAM,cAAc;AAAA,MAClB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,EAAE,KAAK,IAAI;AACX,SAAK,OAAO,YAAY,KAAK;AAK7B,QAAI,KAAK,eAAe;AACtB,YAAM,cAAc,SAAS,cAAc,OAAO;AAClD,kBAAY,aAAa,qBAAqB,iBAAiB;AAC/D,kBAAY,cAAc,KAAK;AAC/B,WAAK,OAAO,YAAY,WAAW;AAAA,IACrC;AASA,eAAW,UAAU,KAAK,SAAS;AACjC,YAAM,YAAY,SAAS,cAAc,KAAK;AAC9C,gBAAU,YAAY;AACtB,gBAAU,aAAa,QAAQ,QAAQ;AACvC,gBAAU,aAAa,cAAc,OAAO,KAAK;AACjD,gBAAU,aAAa,oBAAoB,OAAO,UAAU;AAC5D,gBAAU,aAAa,mBAAmB,OAAO,EAAE;AAKnD,8CAAsB,WAAW,OAAO,YAAY;AAIpD,WAAK,eAAe,KAAK,eAAe,SAAS,CAAC;AAElD,YAAM,WAAW,CAAC,UAChB,KAAK,gBAAgB,QAAQ,KAAK;AACpC,YAAM,kBAAkB,CAAC,OACvB,KAAK,eAAe,KAAK,EAAE;AAE7B,cAAQ,OAAO,YAAY;AAAA,QACzB,KAAK;AACH,4BAAkB,WAAW,QAAQ,UAAU,eAAe;AAC9D;AAAA,QACF,KAAK;AACH,+BAAqB,WAAW,QAAQ,UAAU,eAAe;AACjE;AAAA,QACF,KAAK;AACH,6BAAmB,WAAW,QAAQ,UAAU,eAAe;AAC/D;AAAA,MACJ;AAEA,WAAK,OAAO,YAAY,SAAS;AACjC,WAAK,mBAAmB,QAAQ,SAAS;AAAA,IAC3C;AAEA,UAAM,QAAQ,KAAK,QAAQ,CAAC;AAC5B,SAAK;AAAA,MACH,IAAI,YAAY,sBAAsB;AAAA,QACpC,QAAQ;AAAA,UACN,aAAa,KAAK,QAAQ;AAAA,UAC1B,aAAa,KAAK,QAAQ,IAAI,CAAC,MAAM,EAAE,UAAU;AAAA;AAAA;AAAA,UAGjD,YAAY,OAAO;AAAA,UACnB,OAAO,OAAO;AAAA,QAChB;AAAA,QACA,SAAS;AAAA,QACT,UAAU;AAAA,MACZ,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEQ,mBAAmB,QAAsB,SAAkB;AACjE,QAAI,CAAC,KAAK,oBAAoB,CAAC,KAAK,OAAQ;AAC5C,UAAM,cAAU,gCAAkB,SAAS,MAAM;AAC/C;AAAA,QACE,EAAE,YAAY,KAAK,YAAY,QAAQ,KAAK,OAAO;AAAA,QACnD;AAAA,UACE,WAAW,OAAO;AAAA,UAClB,YAAY,OAAO;AAAA,QACrB;AAAA,MACF;AAAA,IACF,CAAC;AACD,SAAK,mBAAmB,KAAK,OAAO;AAAA,EACtC;AAAA,EAEQ,gBAAgB;AAItB,SAAK,OAAO,YAAY;AAAA,eACb,eAAe;AAAA,eACf,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,EAmChC;AAAA,EAEQ,YAAY,SAAiB;AACnC,SAAK,OAAO,YAAY;AACxB,SAAK;AAAA,MACH,IAAI,YAAY,qBAAqB;AAAA,QACnC,QAAQ,EAAE,SAAS,MAAM,aAAa;AAAA,QACtC,SAAS;AAAA,QACT,UAAU;AAAA,MACZ,CAAC;AAAA,IACH;AAAA,EACF;AAEF;;;ADhqBA,IACE,OAAO,mBAAmB,eAC1B,CAAC,eAAe,IAAI,aAAa,GACjC;AACA,iBAAe,OAAO,eAAe,iBAAiB;AACxD;","names":["import_core","import_core","el","import_core","import_core","import_core","name","import_core","PLACEHOLDER_THUMB_SVG","renderHeader","renderSavingsBar","clamp","import_core","renderHeader","renderPricingRow","renderSavingsBar","renderCta","import_core","el"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../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"],"sourcesContent":["/**\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","/**\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 persists the bucket via a\n * first-party cookie; variant attribution is recorded server-side from\n * the analytics events the widget already emits.\n */\n private async applyABVariants(): Promise<void> {\n if (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 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 isVariantFulfillable,\n shouldShowLowStockBadge,\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(\n rowState,\n currency,\n qtyFor,\n {\n enabled: wc.showLowStockBadge,\n threshold: wc.lowStockThreshold,\n },\n () => {\n // Variant change → recompute pricing.\n updatePricing();\n },\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 // \"In stock\" here means fulfillable for THIS variant's required quantity —\n // a variant with 3 units and required qty 5 is unfulfillable, even though\n // `availableForSale` is true. See packages/core/src/inventory/predicate.ts.\n const firstInStock =\n merchantScoped.find((v) =>\n isVariantFulfillable(v, resolveBundleQty(bundle, product.id, v.id)),\n ) ?? 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 lowStock: { enabled: boolean; threshold: 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 // Read-only variant text right under the title — same treatment as\n // the mix-match widget's filled-slot variant. Only renders when\n // the merchant pinned a single variant of a multi-variant product\n // (no picker would render in that case). Suppressed for products\n // with the default single variant.\n const isSinglePinnedVariant =\n state.eligibleVariants.length === 1 &&\n state.product.variants.nodes.length > 1;\n if (isSinglePinnedVariant) {\n const badge = el(\"span\", \"lb-bundle-variant-badge\");\n badge.textContent = state.eligibleVariants[0].title;\n info.appendChild(badge);\n }\n\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 // Low-stock badge — updated alongside price on variant change. Hidden\n // when the threshold isn't met or quantityAvailable is unknown.\n const lowStockEl = el(\"span\", \"lb-bundle-low-stock-badge\", {\n \"data-low-stock-badge\": \"\",\n });\n lowStockEl.setAttribute(\"hidden\", \"\");\n info.appendChild(lowStockEl);\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 const required = qtyFor(state.product.id, variant.id);\n if (\n shouldShowLowStockBadge(\n variant,\n required,\n lowStock.threshold,\n lowStock.enabled,\n )\n ) {\n lowStockEl.textContent = `Only ${variant.quantityAvailable} left`;\n lowStockEl.removeAttribute(\"hidden\");\n } else {\n lowStockEl.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 (!isVariantFulfillable(v, qtyFor(state.product.id, v.id))) 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 }\n // Single-variant case is handled above the price row, immediately\n // under the product title (same pattern as the mix-match widget).\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\n// Walk up to find the nearest ancestor that clips overflow on the Y axis.\n// Used so placement flips upward before the listbox would be hidden by a\n// scrollable container (`.lb-fixed__products` / `.lb-bundle__products`).\n// Stops at body/html — past that, the viewport bound is correct.\nfunction findScrollableAncestor(el: Element): HTMLElement | null {\n const win = el.ownerDocument?.defaultView;\n if (!win) return null;\n let cur: Element | null = el.parentElement;\n while (cur && cur !== el.ownerDocument.body) {\n const style = win.getComputedStyle(cur);\n const overflowY = style.overflowY;\n if (\n overflowY === \"auto\" ||\n overflowY === \"scroll\" ||\n overflowY === \"hidden\"\n ) {\n return cur as HTMLElement;\n }\n cur = cur.parentElement;\n }\n return null;\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 // Always clip placement to the trigger's nearest scrollable ancestor.\n // In-shell (main widget): clips to the bundle's product list so the\n // panel flips upward before it would hide behind the widget footer.\n // Portaled (modal context): clips to the modal's scrollable list\n // (e.g. `.lb-mix-match__modal-list`) so the panel flips upward when\n // the trigger sits near the modal's bottom edge — even if there's\n // viewport room below. Without this clip, position:fixed listboxes\n // attached to the overlay would open downward and overflow the modal.\n const scrollable = findScrollableAncestor(trigger);\n const clip = scrollable\n ? (() => {\n const r = scrollable.getBoundingClientRect();\n return { top: r.top, bottom: r.bottom };\n })()\n : undefined;\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 clip,\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 `bundle.minQuantity` empty slots (one per distinct\n * product the shopper must pick).\n * 2. Clicking a slot opens the picker modal with the eligible products.\n * 3. Inside the modal, each product card shows a variant select, a qty\n * stepper (when `widgetConfig.mixMatchShowQuantitySelector !== false`),\n * and an Add button. The stepper is bounded by the merchant's\n * `productRules[productId]` (`{ min, max }`) and the variant's stock\n * via `maxAddableQuantity`.\n * 4. Adding a pick fills a slot with the chosen variant and quantity.\n * Remove-x on a filled slot empties it again.\n * 5. CTA unlocks when distinct picks ≥ `bundle.minQuantity`. On click,\n * selections are aggregated by `(productId, variantId)` into one cart\n * line per variant with summed quantity.\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 ProductRule,\n ProductVariant,\n} from \"@lime-bundles/core\";\nimport {\n DEFAULT_PRODUCT_RULE,\n isVariantFulfillable,\n maxAddableQuantity,\n shouldShowLowStockBadge,\n} 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\n/**\n * One pick by the shopper. A pick is a unique (productId, variantId) row\n * in the slot list with its own merchant-bounded quantity. Two picks of\n * the same variant are not allowed — the shopper bumps the stepper instead\n * (the slot's row is replaced when re-added). The CartLineInput aggregator\n * still defends against duplicate (productId, variantId) entries by summing.\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 /** Shopper-chosen qty, clamped to `[rule.min, maxAddableQuantity(...)]`. */\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\nconst STEPPER_MINUS_SVG = `\n<svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <line x1=\"3\" y1=\"7\" x2=\"11\" y2=\"7\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n</svg>`;\n\nconst STEPPER_PLUS_SVG = `\n<svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <line x1=\"7\" y1=\"3\" x2=\"7\" y2=\"11\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n <line x1=\"3\" y1=\"7\" x2=\"11\" y2=\"7\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"/>\n</svg>`;\n\n/** Resolve the merchant rule for a product, applying the runtime default. */\nfunction ruleFor(bundle: MixMatchBundleData, productId: string): ProductRule {\n return bundle.productRules[productId] ?? DEFAULT_PRODUCT_RULE;\n}\n\n/** Sum of quantities the shopper has already allocated to this variant. */\nfunction alreadyInBundleFor(\n selections: Selection[],\n productId: string,\n variantId: string,\n): number {\n let sum = 0;\n for (const s of selections) {\n if (s.productId === productId && s.variantId === variantId) sum += s.quantity;\n }\n return sum;\n}\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 showQtySelector = wc.mixMatchShowQuantitySelector !== false;\n\n // Build eligible-products list, honoring outOfStockBehavior. A product is\n // eligible when at least one of its variants can satisfy `rule.min` units.\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 requiredQty from\n // distinct in-stock products, don't render the widget at all. Matches\n // 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 });\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 ? renderSavingsBar() : null;\n if (savingsBar) root.appendChild(savingsBar.el);\n\n // --- Modal overlay ---\n const modal = renderModal(bundle, eligible, currency, {\n showSearch: wc.showSearch,\n showQtySelector,\n selections,\n onAdd: (product, variant, quantity) =>\n addSelection(product, variant, quantity),\n isComplete: () => selections.length >= requiredQty,\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 onAddToCart(buildCartLines(selections, bundle));\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 // 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(\n product: Product,\n variant: ProductVariant,\n quantity: number,\n ) {\n if (selections.length >= requiredQty) return;\n const rule = ruleFor(bundle, product.id);\n const cap = maxAddableQuantity(\n variant,\n rule.max,\n alreadyInBundleFor(selections, product.id, variant.id),\n );\n // Refuse the pick when stock can't satisfy the per-product minimum —\n // otherwise the slot would carry more units than the variant has, and\n // Shopify's checkout-time inventory check would fail with an opaque\n // \"out of stock\" error after the customer hit Add to cart.\n if (cap < rule.min) return;\n // Clamp defensively. The picker stepper enforces these bounds, but\n // a malformed Add (e.g. keyboard event before stepper init) shouldn't\n // bypass them.\n const clamped = Math.max(rule.min, Math.min(quantity, cap));\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: clamped,\n });\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 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/**\n * Aggregate selections into one CartLineInput per (productId, variantId).\n * Matches the discount-function attribution contract: every line carries\n * `_lime_bundle_gid` and `_lime_bundle_type` so the orders/create webhook\n * can map purchases back to this bundle.\n */\nfunction buildCartLines(\n selections: Selection[],\n bundle: MixMatchBundleData,\n): CartLineInput[] {\n const grouped = new Map<string, { variantId: string; quantity: number }>();\n for (const s of selections) {\n const key = `${s.productId}::${s.variantId}`;\n const existing = grouped.get(key);\n if (existing) {\n existing.quantity += s.quantity;\n } else {\n grouped.set(key, { variantId: s.variantId, quantity: s.quantity });\n }\n }\n return Array.from(grouped.values()).map((line) => ({\n merchandiseId: line.variantId,\n quantity: line.quantity,\n attributes: [\n { key: \"_lime_bundle_gid\", value: bundle.id },\n { key: \"_lime_bundle_type\", value: bundle.bundleType },\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 // Filled-slot badge shows the shopper's chosen qty as \"× N\", which makes\n // it visually distinct from the picker's per-product qty stepper. Bare\n // \"1\" on a square thumbnail looks like a placeholder digit.\n qtyBadge.textContent = `×${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 showQtySelector: boolean;\n /** Live reference to the parent's selections. Read inside the modal to\n * compute remaining stock per variant (`maxAddableQuantity`). */\n selections: Selection[];\n onAdd: (product: Product, variant: ProductVariant, quantity: number) => void;\n isComplete: () => 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 /** Recompute stepper bounds + count badge from current selections. */\n refreshFromSelections: () => void;\n }> = [];\n\n function buildRows() {\n if (rowsBuilt) return;\n rowsBuilt = true;\n list.innerHTML = \"\";\n\n eligible.forEach((ep) => {\n const rule = ruleFor(bundle, ep.product.id);\n\n // Mirrors the Liquid picker-modal pattern in lb-mix-match.liquid:\n // - title/price/unit-price are <p> elements so they stack as blocks.\n // - <select> appears for products with >1 variant; the add button\n // dispatches the currently-selected variant.\n // - Variants that can't satisfy `rule.min` units are sold-out for\n // this bundle's purposes.\n const availableVariants = ep.variants;\n const firstAvailVariant =\n ep.variants.find((v) => isVariantFulfillable(v, rule.min)) ??\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) => isVariantFulfillable(v, rule.min)) ??\n ep.variants[0] ??\n 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 // Count badge shows how many units of the *current* variant the\n // shopper has already pinned to this bundle. Hidden at zero.\n const countBadge = el(\"span\", \"lb-bundle-qty-badge\");\n countBadge.hidden = true;\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 // Picker price reflects single-unit price — the stepper shows\n // multiplier separately. Keeps the price label stable as the\n // shopper bumps the stepper.\n price.textContent = formatCents(parseCents(currentVariant.price.amount), currency);\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 // Low-stock badge — re-rendered on variant change.\n const lowStockBadge = el(\"span\", \"lb-bundle-low-stock-badge\", {\n \"data-low-stock-badge\": \"\",\n });\n lowStockBadge.hidden = true;\n info.appendChild(lowStockBadge);\n const refreshLowStockBadge = (variant: ProductVariant) => {\n if (\n shouldShowLowStockBadge(\n variant,\n rule.min,\n bundle.widgetConfig.lowStockThreshold,\n bundle.widgetConfig.showLowStockBadge,\n )\n ) {\n lowStockBadge.textContent = `Only ${variant.quantityAvailable} left`;\n lowStockBadge.hidden = false;\n } else {\n lowStockBadge.hidden = true;\n }\n };\n refreshLowStockBadge(currentVariant);\n\n // Per-pick quantity stepper sits inside info between the low-stock\n // badge and the variant picker so the modal reads price → unit\n // price → qty → variant. Built before the variant logic so its\n // bounds closure can pick up later currentVariant updates from\n // the variant select handlers via the shared `currentVariant`\n // binding. Mirrors the Liquid template structure.\n const computeStepperBounds = () => {\n const already = alreadyInBundleFor(\n handlers.selections,\n ep.product.id,\n currentVariant.id,\n );\n const cap = maxAddableQuantity(currentVariant, rule.max, already);\n // Stepper max can never drop below `rule.min` while the variant is\n // fulfillable — but if `cap` is below `rule.min` (e.g. only 1 unit\n // left and rule.min=2) we surface that by disabling Add and\n // pinning the stepper at rule.min.\n const max = Math.max(rule.min, cap);\n return { min: rule.min, max, cap };\n };\n\n let stepper: ReturnType<typeof renderQtyStepper> | null = null;\n let qtyGroup: HTMLElement | null = null;\n if (!ep.isOos && handlers.showQtySelector) {\n // Wrap stepper in a .lb-bundle-variant-option-group so it\n // inherits the same flex-column layout the variant pickers use.\n // The compound .lb-mix-match__qty-stepper-group class is the\n // qty-stepper hook; the group is appended to the action-row\n // wrapper below (next to the Add button) — not to info — so the\n // stepper sits left of the Add button.\n qtyGroup = el(\n \"div\",\n \"lb-bundle-variant-option-group lb-mix-match__qty-stepper-group\",\n );\n stepper = renderQtyStepper({\n initial: rule.min,\n getBounds: () => {\n const { min, max } = computeStepperBounds();\n return { min, max };\n },\n });\n qtyGroup.appendChild(stepper.el);\n }\n\n const row = {\n el: productEl,\n product: ep.product,\n variant: firstAvailVariant,\n refreshFromSelections: () => {},\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 are disabled, so the customer gets clear feedback.\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 (!isVariantFulfillable(v, rule.min)) 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.\n syncSelectsToVariant(currentVariant);\n recomputeDisabled(\n currentVariant.selectedOptions.map((o) => o.value),\n );\n return;\n }\n currentVariant = next;\n row.variant = next;\n price.textContent = formatCents(parseCents(currentVariant.price.amount), currency);\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.\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 refreshLowStockBadge(currentVariant);\n row.refreshFromSelections();\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 // --- Quantity stepper + Add button ---\n // Stepper rendering happened earlier (right after the low-stock\n // badge, before the variant picker). The action row sits at the\n // bottom of the info column with a CSS margin-top so the modal\n // reads thumb | (title → price → variants → [stepper] [Add]).\n // Mirrors the Liquid template ordering in extensions/bundle-theme/\n // snippets/lb-mix-match.liquid + bundle-mix-match.js.\n let addBtn: HTMLButtonElement | null = null;\n\n const refreshAddState = () => {\n if (!addBtn) return;\n const { cap } = computeStepperBounds();\n const already = alreadyInBundleFor(\n handlers.selections,\n ep.product.id,\n currentVariant.id,\n );\n // Update the in-thumb count badge to show the live aggregate qty.\n if (already > 0) {\n countBadge.textContent = String(already);\n countBadge.hidden = false;\n } else {\n countBadge.hidden = true;\n }\n // The discount counts distinct products, so re-adding the same\n // product across slots wastes them without advancing the bundle's\n // pick count. When this product (any variant) is already in a\n // slot, mark the row \"Added\" and disable Add — product-in-bundle\n // takes priority over the stock-based disable below.\n const productInBundle = handlers.selections.some(\n (s) => s.productId === ep.product.id,\n );\n if (productInBundle) {\n addBtn.disabled = true;\n addBtn.textContent = \"Added\";\n productEl.classList.add(\"lb-mix-match__modal-product--in-bundle\");\n } else {\n addBtn.textContent = \"Add\";\n productEl.classList.remove(\"lb-mix-match__modal-product--in-bundle\");\n // Add disabled when: bundle is already full, or variant can't\n // accept another rule.min units.\n addBtn.disabled = handlers.isComplete() || cap < rule.min;\n }\n };\n\n if (!ep.isOos) {\n const actions = el(\"div\", \"lb-mix-match__modal-product-actions\");\n\n // Stepper sits to the left of the Add button (when the merchant\n // enabled the qty selector). When disabled, the Add button takes\n // the full action-row width via `flex: 1` in CSS.\n if (qtyGroup) actions.appendChild(qtyGroup);\n\n 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 (!addBtn || addBtn.disabled) return;\n if (handlers.isComplete()) return;\n // When the stepper is hidden, Add adds qty = rule.min (per spec).\n const qty = stepper ? stepper.value() : rule.min;\n handlers.onAdd(ep.product, currentVariant, qty);\n // Reset stepper to rule.min so the next pick of this product\n // doesn't carry over the previous shopper-chosen qty.\n if (stepper) stepper.reset(rule.min);\n close();\n });\n actions.appendChild(addBtn);\n info.appendChild(actions);\n }\n\n row.refreshFromSelections = () => {\n if (stepper) stepper.refresh();\n refreshAddState();\n };\n // Initialise the badge + Add disabled state.\n row.refreshFromSelections();\n\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.isComplete()) 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.refreshFromSelections());\n }\n\n return { el: overlay, open, close, refreshCounts };\n}\n\n// --- Quantity stepper ---\n\ninterface QtyStepper {\n el: HTMLElement;\n /** Current value (clamped to bounds). */\n value: () => number;\n /** Reset to a known qty (clamped to current bounds). */\n reset: (qty: number) => void;\n /** Re-evaluate bounds — disable / clamp when the cap drops. */\n refresh: () => void;\n}\n\nfunction renderQtyStepper(opts: {\n initial: number;\n getBounds: () => { min: number; max: number };\n}): QtyStepper {\n const wrap = el(\"div\", \"lb-mix-match__qty-stepper\", {\n role: \"group\",\n \"aria-label\": \"Quantity\",\n });\n const minus = document.createElement(\"button\");\n minus.type = \"button\";\n minus.className =\n \"lb-mix-match__qty-stepper-button lb-mix-match__qty-stepper-button--minus\";\n minus.setAttribute(\"aria-label\", \"Decrease quantity\");\n minus.innerHTML = STEPPER_MINUS_SVG;\n wrap.appendChild(minus);\n\n const valueEl = el(\"span\", \"lb-mix-match__qty-stepper-value\", {\n \"aria-live\": \"polite\",\n });\n wrap.appendChild(valueEl);\n\n const plus = document.createElement(\"button\");\n plus.type = \"button\";\n plus.className =\n \"lb-mix-match__qty-stepper-button lb-mix-match__qty-stepper-button--plus\";\n plus.setAttribute(\"aria-label\", \"Increase quantity\");\n plus.innerHTML = STEPPER_PLUS_SVG;\n wrap.appendChild(plus);\n\n let current = clamp(opts.initial, opts.getBounds());\n\n function clamp(n: number, b: { min: number; max: number }): number {\n return Math.max(b.min, Math.min(b.max, n));\n }\n\n function paint() {\n const b = opts.getBounds();\n current = clamp(current, b);\n valueEl.textContent = String(current);\n minus.disabled = current <= b.min;\n plus.disabled = current >= b.max;\n }\n\n minus.addEventListener(\"click\", () => {\n const b = opts.getBounds();\n current = clamp(current - 1, b);\n paint();\n });\n plus.addEventListener(\"click\", () => {\n const b = opts.getBounds();\n current = clamp(current + 1, b);\n paint();\n });\n\n paint();\n\n return {\n el: wrap,\n value: () => current,\n reset(qty) {\n current = clamp(qty, opts.getBounds());\n paint();\n },\n refresh: paint,\n };\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 rule = ruleFor(bundle, product.id);\n // A variant is \"available for this bundle\" when it can fulfil at least\n // `rule.min` units. Stricter rules raise the bar (e.g. min=3 + only\n // 2 in stock = sold out for this bundle's purposes).\n const available = product.variants.nodes.filter((v) =>\n isVariantFulfillable(v, rule.min),\n );\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 * 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 { isVariantFulfillable, shouldShowLowStockBadge } 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 // For volume bundles, the natural per-bundle floor is the smallest\n // tier's minQuantity — anything below that can't even buy the cheapest\n // tier. Variants that satisfy that floor are \"fulfillable\" for visibility\n // and CTA-enable purposes.\n const minTierQty = bundle.volumeTiers[0]?.minQuantity ?? 1;\n const variant = product?.variants.nodes.find((v) =>\n isVariantFulfillable(v, minTierQty),\n );\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 // Low-stock badge — shown when the variant has stock at or below the\n // merchant-configured threshold. Hidden when stock is unknown\n // (Storefront token without read_product_inventory) or above threshold.\n if (\n variant &&\n shouldShowLowStockBadge(\n variant,\n minTierQty,\n wc.lowStockThreshold,\n wc.showLowStockBadge,\n )\n ) {\n const lowStock = el(\"span\", \"lb-bundle-low-stock-badge\", {\n \"data-low-stock-badge\": \"\",\n });\n lowStock.textContent = `Only ${variant.quantityAvailable} left`;\n root.appendChild(lowStock);\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 minTierQty = bundle.volumeTiers[0]?.minQuantity ?? 1;\n const isAvailable = product?.variants.nodes.some((v) =>\n isVariantFulfillable(v, minTierQty),\n );\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/* Read-only variant text shown below the product title when only one\n variant is in scope (single-variant product, or merchant pinned a\n single variant). Same visual treatment as the mix-match filled\n slot's variant text — see .lb-mix-match__filled-variant in\n bundle-mix-match.css. Both share this rule via the comma selector\n so the storefront UX stays consistent across bundle types. */\n.lb-bundle-variant-badge,\n.lb-mix-match__slot--filled .lb-mix-match__filled-variant {\n display: block;\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/* 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: 8px;\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: 8px;\n}\n\n/* Desktop: lay variant option groups in a wrapping row inside the modal\n (Size + Color side-by-side). Mobile keeps the column stack from the\n base rule above. The 768px breakpoint mirrors bundle-mix-match.css. */\n@media (min-width: 768px) {\n .lb-mix-match__modal-product-info .lb-bundle-variant-option-groups {\n flex-direction: row;\n flex-wrap: wrap;\n }\n /* Direct children only — the qty-stepper-group is also a\n .lb-bundle-variant-option-group but lives in the actions wrapper\n and must keep its natural width. */\n .lb-mix-match__modal-product-info .lb-bundle-variant-option-groups > .lb-bundle-variant-option-group {\n flex: 1;\n }\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:not([aria-checked=\"true\"]),\n.using-mouse .lb-bundle-widget :focus-visible:not([aria-checked=\"true\"]),\n.using-mouse .lb-mix-match__modal-overlay :focus:not([aria-checked=\"true\"]),\n.using-mouse .lb-mix-match__modal-overlay :focus-visible:not([aria-checked=\"true\"]) {\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/* \"Only X left\" low-stock badge — appears alongside product/variant\n info when ProductVariant.quantityAvailable falls at or below\n wc.lowStockThreshold. Hidden when quantityAvailable is unknown\n (Storefront token without read_product_inventory). */\n.lb-bundle-low-stock-badge {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 2px 8px;\n font-size: 11px;\n font-weight: 600;\n line-height: 1.4;\n color: var(--lb-low-stock-text);\n background-color: var(--lb-low-stock-bg);\n border-radius: 4px;\n white-space: nowrap;\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__filled-variant — styled jointly with\n .lb-bundle-variant-badge above to keep the variant text consistent\n across mix-match and fixed bundle widgets. */\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/* === 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: 520px;\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: grid;\n grid-template-columns: auto 1fr;\n align-items: start;\n column-gap: 20px;\n padding-top: 20px;\n padding-bottom: 16px;\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: 60px;\n min-width: 60px;\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: 2px 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/* Per-pick quantity stepper inside the picker modal product row.\n Three flex cells (− / number / +) sharing a single outer border —\n driven by its own --lb-picker-qty-stepper-* variables so merchants\n can style the stepper independently of the variant dropdown. The\n cell dividers come from a 1px border on the centre value rather\n than per-button borders, so the rounded outer corners stay clean.\n Mirrors extensions/bundle-theme/assets/bundle-mix-match.css so\n the headless web component matches the Liquid theme widget. */\n/* Quantity label + stepper sit together in a .lb-bundle-variant-option-group\n wrapper so the label-to-stepper gap inherits the same 2px the variant\n pickers use. The wrapper carries the margin-top that spaces the qty\n group from the unit-price / low-stock-badge above; the stepper itself\n has no top margin so the group can be repositioned without coupling. */\n.lb-mix-match__qty-stepper-group {\n /* Override the base .lb-bundle-variant-option-group column flex so the\n stepper child stretches on the cross-axis (vertical) — this is what\n lets the group track the Add button's height in the action row\n without pinning a fixed pixel value. */\n flex-direction: row;\n width: fit-content;\n}\n\n.lb-mix-match__qty-stepper {\n display: inline-flex;\n align-items: stretch;\n flex-shrink: 0;\n border: var(--lb-picker-qty-stepper-border-width) solid var(--lb-picker-qty-stepper-border-color);\n border-radius: var(--lb-picker-qty-stepper-radius);\n background-color: var(--lb-picker-bg);\n overflow: hidden;\n box-sizing: border-box;\n}\n\n.lb-mix-match__qty-stepper-button {\n appearance: none;\n -webkit-appearance: none;\n background: transparent;\n border: none;\n margin: 0;\n padding: 0;\n width: 28px;\n font-family: inherit;\n font-size: 16px;\n font-weight: 500;\n line-height: 1;\n color: var(--lb-picker-text);\n cursor: pointer;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n}\n\n.lb-mix-match__qty-stepper-button:hover:not(:disabled) {\n background: color-mix(in srgb, var(--lb-picker-text) 6%, transparent);\n}\n\n.lb-mix-match__qty-stepper-button:focus-visible {\n outline: 2px solid var(--lb-primary-color);\n outline-offset: -2px;\n}\n\n.lb-mix-match__qty-stepper-button:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n\n.lb-mix-match__qty-stepper-value {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 36px;\n padding: 0 8px;\n font-family: inherit;\n font-size: 13px;\n font-weight: 500;\n color: var(--lb-picker-text);\n box-sizing: border-box;\n}\n\n/* Action row — qty stepper (left) + Add button (right). Sits at the\n bottom of the info column with a 12px top margin so it visually\n separates from the variant pickers / price block above. When the\n stepper is hidden, Add fills the row via flex: 1 below.\n align-items: stretch so the stepper-group tracks the Add button's\n height (driven by font size + button padding) without a pinned px. */\n.lb-mix-match__modal-product-actions {\n display: flex;\n flex-wrap: wrap;\n align-items: stretch;\n row-gap: 12px;\n column-gap: 8px;\n margin-top: 12px;\n}\n\n.lb-mix-match__modal-add {\n flex: 1;\n padding: 10px 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}\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/* Already in a slot — prevents shoppers from filling multiple slots\n with the same product (which wouldn't qualify the discount: it\n counts distinct products, not slot occupancy). The thumb keeps\n its qty badge so the shopper sees what's already in the bundle. */\n.lb-mix-match__modal-product--in-bundle {\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: var(--lb-tier-selected-border-color);\n}\n\n/* Suppress the UA default focus outline so a freshly-clicked selected\n tier doesn't briefly show the 1px focus ring on top of (or in place of)\n the custom selected-state outline below. Keyboard focus is still\n indicated by the :focus-visible rule. */\n.lb-volume__tier:focus {\n outline: none;\n}\n\n/* Keyboard focus indicator — explicitly excludes the selected tier so\n the selected-state outline rule below has full ownership of the\n outline property when both states apply at once. */\n.lb-volume__tier:focus-visible:not([aria-checked=\"true\"]) {\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"],"mappings":"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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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA0lBxB,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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;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+mB7B,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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA2I1B,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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;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":["import_core","import_core","el","import_core","import_core","import_core","name","import_core","PLACEHOLDER_THUMB_SVG","renderHeader","renderSavingsBar","clamp","import_core","renderHeader","renderPricingRow","renderSavingsBar","renderCta","import_core","el"]}
|