{
  "version": 3,
  "sources": ["/cdn/shop/t/345/assets/sf-core.js?cb=1792986592361"],
  "sourcesContent": ["/* sf-core.js v3.20.2-rc.33 \u2014 storefront-core opt-in behaviors (scoped to selected components).\n * Critical add-to-cart ships separately in sf-core-cart.js. Binds data-ds-* hooks only.\n * Generated \u2014 do not edit in the theme. */\n/* 0header-nav-drawer.js */\n(function () {\ntry {\n/* 0header-nav-drawer \u2014 v2 mobile nav drawer (\"D \u00B7 Card grid\"): open/close,\n * sheet offset, scroll lock, Back closes without navigating (Baymard\n * #1057), card \u2192 level drill-down, single-open groups, \"More\" fold >10.\n *\n * Filename starts with `0`: build.mjs concatenates src/behaviors/*.js\n * alphabetically into one bundle, so this forces our popstate listener to\n * register (and run) before collection-listing.js / script.collection-\n * filters.js. Must stay [a-z0-9-] \u2014 a leading `_` passes build.mjs fine but\n * build.mjs's sf-asset regex doesn't match it, so the component silently\n * ships with no drawer JS.\n *\n * Close-by-hand uses replaceState(), never history.back() \u2014 back() fires a\n * page-wide popstate that collection AJAX can't tell apart from real\n * navigation (that was the reload bug). See __dsNavDrawerPoppedOverlay.\n *\n * Coexists with header-nav.js (legacy drawer, no-ops on this markup). No\n * import/export: the build wraps this file in an IIFE.\n */\n\n(function () {\n  if (document.__dsNavDrawerBound) return;\n  document.__dsNavDrawerBound = true;\n\n  var DESKTOP_MIN = 1024;\n  var MAX_CARDS = 10;\n  var DIR_CLEAR_DELAY = 300;\n  var HISTORY_KEY = 'dsNavDrawer';\n\n  // The one drawer currently open: { nav, trigger }. A page has one header.\n  var active = null;\n\n  function navForTrigger(trigger) {\n    var header = trigger && trigger.closest('[data-ds-header]');\n    return header && header.querySelector('[data-ds-nav-drawer]');\n  }\n\n  function triggerForNav(nav) {\n    var header = nav && nav.closest('[data-ds-header]');\n    return header && header.querySelector('[data-ds-header-menu-button]');\n  }\n\n  function isOpen(nav) {\n    return !!nav && nav.hasAttribute('data-ds-nav-drawer-open');\n  }\n\n  /* ---- Sheet position: starts at the bottom edge of the search row ------ */\n\n  function sizeDrawer(nav) {\n    if (!nav) return;\n    var header = nav.closest('[data-ds-header]');\n    var bar = header && header.querySelector('[data-ds-header-main-row]');\n    var edge = bar || header;\n    var top = edge ? edge.getBoundingClientRect().bottom : 0;\n    /* Floor so a fractional edge never leaves a hairline of the page between\n       the search row and the sheet. */\n    top = Math.max(0, Math.min(window.innerHeight, Math.floor(top)));\n    nav.style.setProperty('--ds-nav-drawer-top', top + 'px');\n    nav.style.setProperty('--ds-nav-drawer-max-height', Math.max(0, Math.floor(window.innerHeight - top)) + 'px');\n  }\n\n  function sizeOpenDrawer() {\n    if (active && isOpen(active.nav)) sizeDrawer(active.nav);\n  }\n\n  /* ---- Page scroll lock --------------------------------------------------- */\n\n  function syncScrollLock() {\n    var open = document.querySelector('[data-ds-nav-drawer][data-ds-nav-drawer-open]');\n    document.documentElement.toggleAttribute('data-ds-nav-drawer-scroll-lock', Boolean(open));\n  }\n\n  /* ---- Levels and groups --------------------------------------------------- */\n\n  function closeGroups(scope) {\n    scope.querySelectorAll('[data-ds-nav-drawer-group-toggle][aria-expanded=\"true\"]').forEach(function (toggle) {\n      setGroup(toggle, false);\n    });\n  }\n\n  function setGroup(toggle, open) {\n    toggle.setAttribute('aria-expanded', open ? 'true' : 'false');\n    var id = toggle.getAttribute('aria-controls');\n    var panel = id && document.getElementById(id);\n    if (panel) panel.hidden = !open;\n  }\n\n  function resetToRoot(nav) {\n    nav.querySelectorAll('[data-ds-nav-drawer-level]').forEach(function (level) {\n      level.hidden = true;\n      level.removeAttribute('data-ds-nav-drawer-dir');\n    });\n    closeGroups(nav);\n    nav.querySelectorAll('[data-ds-nav-drawer-scroll]').forEach(function (scroller) {\n      scroller.removeAttribute('data-ds-nav-drawer-dir');\n      scroller.scrollTop = 0;\n    });\n    var returned = nav.querySelector('[data-ds-nav-drawer-returned-to]');\n    if (returned) returned.removeAttribute('data-ds-nav-drawer-returned-to');\n    var open = nav.querySelector('[data-ds-nav-drawer-open-card]');\n    if (open) open.removeAttribute('data-ds-nav-drawer-open-card');\n  }\n\n  // Marks the card whose level is open \u2014 \"which branch am I in\" while inside\n  // the drawer's own UI state, separate from data-ds-nav-drawer-current (the\n  // real page URL, set by Liquid). Skipped if the card is already the true\n  // current page: one highlight source, not two competing ones.\n  function markOpenCard(card) {\n    if (card.hasAttribute('data-ds-nav-drawer-current')) return;\n    card.setAttribute('data-ds-nav-drawer-open-card', '');\n  }\n\n  function openLevel(card) {\n    var id = card.getAttribute('aria-controls');\n    var level = id && document.getElementById(id);\n    if (!level) return false;\n    var nav = card.closest('[data-ds-nav-drawer]');\n    // Entering a level starts with every group collapsed.\n    closeGroups(nav || level);\n    var scroller = level.closest('[data-ds-nav-drawer-scroll]');\n    if (scroller) {\n      scroller.removeAttribute('data-ds-nav-drawer-dir');\n      // The level is positioned absolute, inset: 0 against this scroller \u2014 if\n      // the root grid was scrolled down first, that scrollTop still offsets\n      // the level's containing block, pushing it up out of view.\n      scroller.scrollTop = 0;\n    }\n    level.setAttribute('data-ds-nav-drawer-dir', 'fwd');\n    level.hidden = false;\n    level.scrollTop = 0;\n    markOpenCard(card);\n    var back = level.querySelector('[data-ds-nav-drawer-back]');\n    if (back) back.focus({ preventScroll: true });\n    return true;\n  }\n\n  // Highlights the card just returned to via in-drawer Back \u2014 focus() alone\n  // often isn't painted as :focus-visible after a mouse click. Skipped if\n  // another card is already the true current page (convention 03: one\n  // \"where am I\" source, not two competing highlights).\n  function markReturnedTo(card) {\n    var nav = card.closest('[data-ds-nav-drawer]');\n    if (nav) {\n      var current = nav.querySelector('[data-ds-nav-drawer-card][data-ds-nav-drawer-current]');\n      if (current && current !== card) return;\n      var stale = nav.querySelector('[data-ds-nav-drawer-returned-to]');\n      if (stale && stale !== card) stale.removeAttribute('data-ds-nav-drawer-returned-to');\n    }\n    card.setAttribute('data-ds-nav-drawer-returned-to', '');\n    card.addEventListener('blur', function onBlur() {\n      card.removeEventListener('blur', onBlur);\n      card.removeAttribute('data-ds-nav-drawer-returned-to');\n    });\n  }\n\n  function closeLevel(back) {\n    var level = back.closest('[data-ds-nav-drawer-level]');\n    if (!level) return;\n    level.hidden = true;\n    level.removeAttribute('data-ds-nav-drawer-dir');\n    closeGroups(level);\n    // The root grid plays the \"back\" slide; clear it once it has run so the\n    // next return can replay it.\n    var scroller = level.closest('[data-ds-nav-drawer-scroll]');\n    if (scroller) {\n      scroller.setAttribute('data-ds-nav-drawer-dir', 'back');\n      window.clearTimeout(scroller.__dsNavDrawerDirTimer);\n      scroller.__dsNavDrawerDirTimer = window.setTimeout(function () {\n        scroller.removeAttribute('data-ds-nav-drawer-dir');\n      }, DIR_CLEAR_DELAY);\n    }\n    var card = level.id && document.querySelector('[data-ds-nav-drawer-card][aria-controls=\"' + cssEscape(level.id) + '\"]');\n    if (card) {\n      card.removeAttribute('data-ds-nav-drawer-open-card');\n      card.focus({ preventScroll: true });\n      markReturnedTo(card);\n    }\n  }\n\n  function cssEscape(value) {\n    return window.CSS && CSS.escape ? CSS.escape(value) : String(value).replace(/[\"\\\\]/g, '\\\\$&');\n  }\n\n  /* ---- Open and close ------------------------------------------------------ */\n\n  function syncTrigger(trigger, nav, open) {\n    if (!trigger) return;\n    if (nav.id) trigger.setAttribute('aria-controls', nav.id);\n    trigger.setAttribute('aria-expanded', open ? 'true' : 'false');\n    var label = trigger.getAttribute(open ? 'data-label-close' : 'data-label-open');\n    if (label) trigger.setAttribute('aria-label', label);\n    var iconOpen = trigger.querySelector('[data-ds-header-menu-icon-open]');\n    var iconClose = trigger.querySelector('[data-ds-header-menu-icon-close]');\n    if (iconOpen) iconOpen.classList.toggle('sf-hidden', open);\n    if (iconClose) iconClose.classList.toggle('sf-hidden', !open);\n  }\n\n  function openDrawer(nav, trigger) {\n    if (!nav) return;\n    if (active && active.nav !== nav) closeDrawer({ skipHistory: true });\n    trigger = trigger || triggerForNav(nav);\n    resetToRoot(nav);\n    sizeDrawer(nav);\n    nav.setAttribute('data-ds-nav-drawer-open', '');\n    syncTrigger(trigger, nav, true);\n    active = { nav: nav, trigger: trigger };\n    syncScrollLock();\n    // A real Back press pops this; no URL change, so no navigation happens.\n    try {\n      var state = {};\n      var current = history.state;\n      if (current && typeof current === 'object') {\n        for (var key in current) state[key] = current[key];\n      }\n      state[HISTORY_KEY] = true;\n      history.pushState(state, '');\n    } catch (error) {\n      /* sandboxed frames can refuse pushState \u2014 the drawer still works */\n    }\n  }\n\n  // options.fromPop: history entry already gone (real Back pressed).\n  // options.skipHistory: leave history alone (bfcache restore, editor reload).\n  // options.focusTrigger: return focus to the hamburger (Escape).\n  function closeDrawer(options) {\n    options = options || {};\n    var nav = active && active.nav;\n    var trigger = active && active.trigger;\n    active = null;\n    if (nav) {\n      nav.removeAttribute('data-ds-nav-drawer-open');\n      syncTrigger(trigger || triggerForNav(nav), nav, false);\n    }\n    syncScrollLock();\n    // Drop the flag via replaceState, not history.back() \u2014 back() fires a\n    // page-wide popstate other listeners (collection AJAX) can't tell\n    // apart from real navigation.\n    if (!options.fromPop && !options.skipHistory && history.state && history.state[HISTORY_KEY]) {\n      try {\n        var cleaned = {};\n        for (var k in history.state) if (k !== HISTORY_KEY) cleaned[k] = history.state[k];\n        history.replaceState(cleaned, '');\n      } catch (error) {\n        /* ignore */\n      }\n    }\n    if (options.focusTrigger && trigger && trigger.isConnected) trigger.focus();\n  }\n\n  window.addEventListener('popstate', function () {\n    if (!active) return;\n    // Flags this pop as overlay-close, not navigation, for collection AJAX\n    // listeners (theme repo) \u2014 see file header for the ordering guarantee.\n    window.__dsNavDrawerPoppedOverlay = true;\n    closeDrawer({ fromPop: true });\n  });\n\n  // A bfcache restore can bring the page back with the drawer still open.\n  window.addEventListener('pageshow', function (event) {\n    if (!event.persisted) return;\n    var stale = document.querySelector('[data-ds-nav-drawer][data-ds-nav-drawer-open]');\n    if (stale) {\n      active = { nav: stale, trigger: triggerForNav(stale) };\n      closeDrawer({ skipHistory: true });\n    }\n  });\n\n  /* ---- Fold grids longer than MAX_CARDS behind \"More\" --------------------- */\n\n  function gridItems(grid) {\n    return Array.prototype.filter.call(grid.querySelectorAll('[data-ds-nav-drawer-item]'), function (item) {\n      return item.closest('[data-ds-nav-drawer-grid]') === grid;\n    });\n  }\n\n  function moreItemFor(grid) {\n    var items = grid.querySelectorAll('[data-ds-nav-drawer-more-item]');\n    for (var i = 0; i < items.length; i += 1) {\n      if (items[i].closest('[data-ds-nav-drawer-grid]') === grid) return items[i];\n    }\n    return null;\n  }\n\n  function foldGrid(grid) {\n    // Once the shopper expanded this grid, keep it expanded until the section\n    // re-renders (which brings fresh markup without the marker).\n    if (grid.hasAttribute('data-ds-nav-drawer-expanded')) return;\n    var items = gridItems(grid);\n    var moreItem = moreItemFor(grid);\n    var fold = items.length > MAX_CARDS && !!moreItem;\n    items.forEach(function (item, index) {\n      var overflow = fold && index >= MAX_CARDS;\n      if (overflow) {\n        item.hidden = true;\n        item.setAttribute('data-ds-nav-drawer-overflow', '');\n      } else if (item.hasAttribute('data-ds-nav-drawer-overflow')) {\n        // Only undo what this file hid; [hidden] set by Liquid stays.\n        item.hidden = false;\n        item.removeAttribute('data-ds-nav-drawer-overflow');\n      }\n    });\n    if (moreItem) {\n      moreItem.hidden = !fold;\n      var more = moreItem.querySelector('[data-ds-nav-drawer-more]');\n      if (more) more.setAttribute('aria-expanded', 'false');\n    }\n  }\n\n  function expandGrid(grid, focusFirst) {\n    var revealed = [];\n    gridItems(grid).forEach(function (item) {\n      if (!item.hasAttribute('data-ds-nav-drawer-overflow')) return;\n      item.hidden = false;\n      item.removeAttribute('data-ds-nav-drawer-overflow');\n      revealed.push(item);\n    });\n    grid.setAttribute('data-ds-nav-drawer-expanded', '');\n    var moreItem = moreItemFor(grid);\n    if (moreItem) {\n      var more = moreItem.querySelector('[data-ds-nav-drawer-more]');\n      if (more) more.setAttribute('aria-expanded', 'true');\n      moreItem.hidden = true;\n    }\n    if (focusFirst && revealed.length) {\n      var card = revealed[0].querySelector('[data-ds-nav-drawer-card]');\n      if (card) card.focus({ preventScroll: false });\n    }\n  }\n\n  function foldGrids(root) {\n    var scope = root && root.querySelectorAll ? root : document;\n    scope.querySelectorAll('[data-ds-nav-drawer-grid]').forEach(foldGrid);\n  }\n\n  /* ---- Observers: remeasure while open -------------------------------------- */\n\n  var headerBox = typeof ResizeObserver === 'function' ? new ResizeObserver(sizeOpenDrawer) : null;\n\n  /* Dismissing the announcement bar shortens the header while the drawer stays\n     open; a size observer misses that when the bar's wrapper collapses without\n     resizing the header box, so also remeasure when header children change. */\n  function watchHeaders(root) {\n    var scope = root && root.querySelectorAll ? root : document;\n    var headers = Array.prototype.slice.call(scope.querySelectorAll('[data-ds-header]'));\n    if (scope.matches && scope.matches('[data-ds-header]')) headers.push(scope);\n    headers.forEach(function (header) {\n      if (header.__dsNavDrawerWatch || !header.querySelector('[data-ds-nav-drawer]')) return;\n      header.__dsNavDrawerWatch = true;\n      if (headerBox) headerBox.observe(header);\n      if (typeof MutationObserver === 'function') {\n        new MutationObserver(sizeOpenDrawer).observe(header, { childList: true, subtree: true });\n      }\n    });\n  }\n\n  // Safety net: if more than one card ends up marked current (menu data\n  // mistake, future Liquid regression), show none rather than several \u2014 a\n  // wrong single highlight is a data bug; several at once reads as broken.\n  function guardSingleCurrent(root) {\n    var scope = root && root.querySelectorAll ? root : document;\n    scope.querySelectorAll('[data-ds-nav-drawer]').forEach(function (nav) {\n      var current = nav.querySelectorAll('[data-ds-nav-drawer-card][data-ds-nav-drawer-current]');\n      if (current.length > 1) {\n        current.forEach(function (card) {\n          card.removeAttribute('data-ds-nav-drawer-current');\n          card.removeAttribute('aria-current');\n        });\n      }\n    });\n  }\n\n  function init(root) {\n    foldGrids(root);\n    watchHeaders(root);\n    guardSingleCurrent(root);\n    // A section re-render drops the open nav from the DOM; release the lock\n    // and the state that pointed at it.\n    if (active && !active.nav.isConnected) {\n      active = null;\n    }\n    syncScrollLock();\n  }\n\n  init(document);\n\n  // A reload keeps history.state; clear a leftover flag so this page's\n  // drawer (which starts closed) doesn't act on it.\n  if (history.state && history.state[HISTORY_KEY]) {\n    try {\n      var cleaned = {};\n      for (var k in history.state) if (k !== HISTORY_KEY) cleaned[k] = history.state[k];\n      history.replaceState(cleaned, '');\n    } catch (error) {\n      /* ignore */\n    }\n  }\n\n  /* ---- Delegated events --------------------------------------------------- */\n\n  document.addEventListener('click', function (event) {\n    if (!event.target.closest) return;\n\n    // header-nav.js also sees this click (and preventDefaults it) but no-ops\n    // on v2 markup; neither listener depends on running first.\n    var trigger = event.target.closest('[data-ds-header-menu-button]');\n    if (trigger) {\n      var nav = navForTrigger(trigger);\n      if (!nav) return;\n      event.preventDefault();\n      if (isOpen(nav)) closeDrawer();\n      else openDrawer(nav, trigger);\n      return;\n    }\n\n    var drawer = event.target.closest('[data-ds-nav-drawer]');\n    if (!drawer) return;\n\n    // Tap on the scrim.\n    if (event.target.closest('[data-ds-nav-drawer-backdrop]')) {\n      event.preventDefault();\n      if (active && active.nav === drawer) closeDrawer();\n      return;\n    }\n\n    // Cards with a level drill down; cards without aria-controls are links.\n    var card = event.target.closest('[data-ds-nav-drawer-card][aria-controls]');\n    if (card) {\n      if (openLevel(card)) event.preventDefault();\n      return;\n    }\n\n    var back = event.target.closest('[data-ds-nav-drawer-back]');\n    if (back) {\n      event.preventDefault();\n      closeLevel(back);\n      return;\n    }\n\n    // One open group per level.\n    var toggle = event.target.closest('[data-ds-nav-drawer-group-toggle]');\n    if (toggle) {\n      event.preventDefault();\n      var willOpen = toggle.getAttribute('aria-expanded') !== 'true';\n      if (willOpen) {\n        var scope = toggle.closest('[data-ds-nav-drawer-level]') || drawer;\n        scope.querySelectorAll('[data-ds-nav-drawer-group-toggle][aria-expanded=\"true\"]').forEach(function (other) {\n          if (other !== toggle) setGroup(other, false);\n        });\n      }\n      setGroup(toggle, willOpen);\n      return;\n    }\n\n    // Reveal the folded cards in place.\n    var more = event.target.closest('[data-ds-nav-drawer-more]');\n    if (more) {\n      event.preventDefault();\n      var grid = more.closest('[data-ds-nav-drawer-grid]');\n      if (grid) expandGrid(grid, true);\n    }\n  });\n\n  // Escape, capture phase: so the region modal's own Escape handler (which may\n  // close it first in bubble order) can't make this think no modal was open \u2014\n  // Escape over an open region modal closes only that modal.\n  document.addEventListener(\n    'keydown',\n    function (event) {\n      if (event.key !== 'Escape' || !active) return;\n      if (document.querySelector('[data-ds-region-modal][aria-hidden=\"false\"]')) return;\n      closeDrawer({ focusTrigger: true });\n    },\n    true,\n  );\n\n  // iOS ignores overflow:hidden on the page for touch scrolling; block drags\n  // outside the sheet (and an open region modal above it).\n  document.addEventListener(\n    'touchmove',\n    function (event) {\n      if (!document.documentElement.hasAttribute('data-ds-nav-drawer-scroll-lock')) return;\n      if (event.target.closest && event.target.closest('[data-ds-nav-drawer-panel]')) return;\n      if (event.target.closest && event.target.closest('[data-ds-region-modal][aria-hidden=\"false\"]')) return;\n      if (event.cancelable) event.preventDefault();\n    },\n    { passive: false },\n  );\n\n  window.addEventListener('resize', function () {\n    if (!active) return;\n    if (window.innerWidth >= DESKTOP_MIN) {\n      closeDrawer();\n      return;\n    }\n    sizeDrawer(active.nav);\n  });\n\n  window.addEventListener('scroll', sizeOpenDrawer, { passive: true });\n\n  document.addEventListener('ds:header-announcement-dismiss', function () {\n    sizeOpenDrawer();\n    requestAnimationFrame(sizeOpenDrawer);\n  });\n\n  /* ---- Theme Editor --------------------------------------------------------- */\n\n  document.addEventListener('shopify:section:load', function (event) {\n    init(event.target);\n  });\n\n  document.addEventListener('shopify:block:select', function (event) {\n    var target = event.target;\n    var nav = target && target.closest && target.closest('[data-ds-nav-drawer]');\n    if (!nav) return;\n    init(nav);\n    if (window.innerWidth >= DESKTOP_MIN) return;\n    // A folded card can't be edited while hidden \u2014 unfold its grid.\n    var folded = target.closest('[data-ds-nav-drawer-overflow]') ||\n      (target.querySelector && target.querySelector('[data-ds-nav-drawer-overflow]'));\n    var grid = folded && folded.closest('[data-ds-nav-drawer-grid]');\n    if (grid) expandGrid(grid, false);\n    if (!isOpen(nav)) openDrawer(nav);\n  });\n\n  document.addEventListener('shopify:section:unload', function () {\n    window.setTimeout(function () {\n      if (active && !active.nav.isConnected) active = null;\n      syncScrollLock();\n    }, 0);\n  });\n})();\n\n} catch (sfError) {\nif (typeof console !== 'undefined') console.error('[sf-core] behavior \"0header-nav-drawer\" failed to initialise:', sfError);\n}\n})();\n\n/* accordion.js */\n(function () {\ntry {\n/* accordion \u2014 expand/collapse with height animation + chevron rotation.\n * Binds [data-ds-accordion] and data-ds-accordion-{trigger,content,chevron}\n * (see snippets/ds-accordion.liquid). aria-expanded on the trigger is the state.\n * Progressive enhancement: without JS the content stays visible (open).\n *\n * Authoring rules: src/behaviors/README.md. No import/export.\n */\n\nif (!document.__dsAccordionBound) {\n  document.__dsAccordionBound = true;\n  document.addEventListener('click', function (e) {\n    var trigger = e.target.closest && e.target.closest('[data-ds-accordion-trigger]');\n    if (!trigger) return;\n    var root = trigger.closest('[data-ds-accordion]');\n    if (!root) return;\n    var content = root.querySelector('[data-ds-accordion-content]');\n    if (!content) return;\n\n    var isOpen = trigger.getAttribute('aria-expanded') === 'true';\n    trigger.setAttribute('aria-expanded', isOpen ? 'false' : 'true');\n\n    if (isOpen) {\n      // Closing: pin the current height, then animate to 0 next frame.\n      content.style.maxHeight = content.scrollHeight + 'px';\n      requestAnimationFrame(function () { content.style.maxHeight = '0'; });\n    } else {\n      // Opening: measure real height, then animate 0 \u2192 height.\n      content.style.maxHeight = 'none';\n      var h = content.scrollHeight;\n      content.style.maxHeight = '0';\n      requestAnimationFrame(function () { content.style.maxHeight = h + 'px'; });\n      content.addEventListener('transitionend', function onEnd() {\n        content.style.maxHeight = 'none';\n        content.removeEventListener('transitionend', onEnd);\n      });\n    }\n\n    var chevron = root.querySelector('[data-ds-accordion-chevron]');\n    if (chevron) chevron.classList.toggle('sf-rotate-180', !isOpen);\n  });\n}\n\n} catch (sfError) {\nif (typeof console !== 'undefined') console.error('[sf-core] behavior \"accordion\" failed to initialise:', sfError);\n}\n})();\n\n/* announcement.js */\n(function () {\ntry {\n/* announcement \u2014 rotate announcement item blocks vertically.\n *\n * Pauses while shoppers hover/focus the bar and restarts after Theme Editor\n * section renders. Progressive enhancement: the first message is visible and\n * the remaining faces are inert without JS.\n *\n * No external CSS required. Animation/layout styles are applied inline.\n *\n * Authoring rules: src/behaviors/README.md. No import/export.\n */\n\nvar ANNOUNCEMENT = '[data-ds-header-announcement]';\nvar ANNOUNCEMENT_SLIDE = '[data-ds-announcement-slide]';\nvar ANNOUNCEMENT_VIEWPORT = '[data-ds-announcement-viewport]';\nvar ANNOUNCEMENT_TRACK = '[data-ds-announcement-track]';\n\nfunction bindAnnouncement(root) {\n  if (root.dataset.dsAnnouncementBound) return;\n  root.dataset.dsAnnouncementBound = '1';\n\n  var viewport = root.querySelector(ANNOUNCEMENT_VIEWPORT);\n  var track = root.querySelector(ANNOUNCEMENT_TRACK);\n  var slides = Array.from(root.querySelectorAll(ANNOUNCEMENT_SLIDE));\n\n  if (!viewport || !track || !slides.length) return;\n\n  var active = 0;\n  var timer = null;\n  var animating = false;\n\n  var duration = 500;\n  var interval = Number(root.dataset.dsAnnouncementInterval || 4000);\n\n  interval = Math.max(2000, interval);\n\n  var reduceMotion = window.matchMedia &&\n    window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n\n  /*\n   * Viewport\n   *\n   * Critical one-line layout is already present in the server-rendered markup\n   * so the first paint has the same height as the hydrated state (no CLS). The\n   * band height is owned by the snippet class (sf-h-8 below lg, sf-h-11 at lg \u2014\n   * 32/44px), so DON'T pin an inline height here: an inline value beats the\n   * class and would force one height at every breakpoint, overflowing the band\n   * and knocking the copy off-centre.\n   */\n  viewport.style.position = 'relative';\n  viewport.style.display = 'block';\n  viewport.style.overflow = 'hidden';\n\n  /*\n   * Keep the track a block box (width = viewport width). As a flex item it would\n   * take min-width:auto and grow to the message's min-content width, so a long\n   * nowrap message would never be clipped by the viewport \u2014 and the marquee,\n   * which only scrolls when the copy overflows, would never start. Block layout\n   * clamps the copy to the band width so the overflow (and the scroll) is real.\n   */\n  track.style.position = 'relative';\n  track.style.display = 'block';\n  track.style.height = '100%';\n\n  /*\n   * Initial slide state.\n   */\n  slides.forEach(function (slide, index) {\n    slide.dataset.dsAnnouncementIndex = String(index);\n    slide.dataset.state = index === 0 ? 'active' : 'idle';\n\n    slide.style.width = '100%';\n    slide.style.minWidth = '0';\n    slide.style.boxSizing = 'border-box';\n    slide.style.whiteSpace = 'nowrap';\n    slide.style.overflow = 'hidden';\n\n    var message = slide.querySelector('[data-ds-announcement-message]');\n\n    if (message) {\n      message.style.minWidth = '0';\n      message.style.overflow = 'hidden';\n      message.style.textOverflow = 'ellipsis';\n      message.style.whiteSpace = 'nowrap';\n    }\n\n    /*\n     * Announcement items can contain message + CTA, so keep their contents\n     * centered as one horizontal row.\n     */\n    slide.style.alignItems = 'center';\n    slide.style.justifyContent = 'center';\n\n    /*\n     * Don't overwrite display if the nested block already explicitly defines\n     * one. Inline-flex is needed for the usual span-based announcement item.\n     */\n    if (!slide.style.display) {\n      slide.style.display = 'flex';\n    }\n\n    if (!reduceMotion) {\n      slide.style.transition =\n        'transform ' +\n        duration +\n        'ms cubic-bezier(.22, 1, .36, 1), opacity ' +\n        duration +\n        'ms cubic-bezier(.22, 1, .36, 1)';\n\n      slide.style.willChange = 'transform, opacity';\n    }\n\n    if (index === 0) {\n      slide.style.position = 'relative';\n      slide.style.inset = '';\n      slide.style.height = '100%';\n      slide.style.transform = 'translate3d(0, 0, 0)';\n      slide.style.opacity = '1';\n      slide.style.pointerEvents = '';\n\n      slide.removeAttribute('aria-hidden');\n    } else {\n      slide.style.position = 'absolute';\n      slide.style.inset = '0';\n      slide.style.transform = 'translate3d(0, 100%, 0)';\n      slide.style.opacity = '0';\n      slide.style.pointerEvents = 'none';\n\n      slide.setAttribute('aria-hidden', 'true');\n    }\n  });\n\n  /*\n   * Marquee for messages that don't fit on one line (typically mobile).\n   *\n   * Instead of clipping the copy with an ellipsis (\"\u2026\"), the active slide's\n   * message scrolls horizontally in a continuous one-direction loop so the\n   * shopper can read all of it. Skipped entirely under reduced-motion (the\n   * server-rendered ellipsis stays) and when the copy already fits.\n   */\n  var marquee = null;\n\n  function pauseMarquee() {\n    if (marquee && marquee.anim) marquee.anim.pause();\n  }\n\n  function resumeMarquee() {\n    if (marquee && marquee.anim && !reduceMotion) marquee.anim.play();\n  }\n\n  function teardownMarquee() {\n    if (!marquee) return;\n    try { marquee.anim.cancel(); } catch (error) { /* already gone */ }\n    if (marquee.inner) marquee.inner.style.transform = '';\n    marquee = null;\n  }\n\n  function setupMarquee(slide) {\n    teardownMarquee();\n\n    if (reduceMotion || !slide) return;\n\n    var message = slide.querySelector('[data-ds-announcement-message]');\n    if (!message) return;\n\n    /*\n     * Build (once) an inner \"track\" we can translate while the message box\n     * clips. The real copy plus an aria-hidden clone ride the track with a gap\n     * between them; translating by exactly one copy + gap lands the clone where\n     * the original started, so looping back to 0 is seamless \u2014 a continuous\n     * one-direction scroll with no visible jump-back. Once the text lives in a\n     * child element the parent's text-overflow ellipsis no longer applies.\n     */\n    var GAP = 48;\n\n    var track = message.querySelector('[data-ds-announcement-marquee]');\n    var copy;\n\n    if (!track) {\n      track = document.createElement('span');\n      track.setAttribute('data-ds-announcement-marquee', '');\n      track.style.display = 'inline-flex';\n      track.style.flexWrap = 'nowrap';\n      track.style.alignItems = 'center';\n      track.style.willChange = 'transform';\n\n      copy = document.createElement('span');\n      copy.setAttribute('data-ds-announcement-marquee-copy', '');\n      copy.style.whiteSpace = 'nowrap';\n\n      while (message.firstChild) {\n        copy.appendChild(message.firstChild);\n      }\n\n      track.appendChild(copy);\n      message.appendChild(track);\n    } else {\n      track.style.transform = '';\n      copy = track.querySelector('[data-ds-announcement-marquee-copy]');\n    }\n\n    message.style.overflow = 'hidden';\n    message.style.textOverflow = 'clip';\n    message.style.whiteSpace = 'nowrap';\n\n    var single = Math.ceil(copy.getBoundingClientRect().width);\n    var clone = track.querySelector('[data-ds-announcement-marquee-clone]');\n\n    /* Fits on one line \u2014 leave it centered, drop any clone, nothing to loop. */\n    if (single <= message.clientWidth + 4) {\n      if (clone) track.removeChild(clone);\n      track.style.gap = '';\n      return;\n    }\n\n    if (typeof track.animate !== 'function') return;\n\n    /* Trailing clone makes the wrap-around seamless. */\n    if (!clone) {\n      clone = copy.cloneNode(true);\n      clone.removeAttribute('data-ds-announcement-marquee-copy');\n      clone.setAttribute('data-ds-announcement-marquee-clone', '');\n      clone.setAttribute('aria-hidden', 'true');\n      track.appendChild(clone);\n    }\n\n    track.style.gap = GAP + 'px';\n\n    var distance = single + GAP;\n\n    /* ~55px/s constant scroll. */\n    var duration = Math.max(4000, Math.round((distance / 55) * 1000));\n\n    marquee = {\n      inner: track,\n      slide: slide,\n      anim: track.animate(\n        [\n          { transform: 'translateX(0)' },\n          { transform: 'translateX(-' + distance + 'px)' }\n        ],\n        {\n          duration: duration,\n          iterations: Infinity,\n          easing: 'linear'\n        }\n      )\n    };\n  }\n\n  setupMarquee(slides[active]);\n\n  /* Web fonts can reflow the copy after first paint \u2014 re-measure once ready. */\n  if (document.fonts && document.fonts.ready &&\n    typeof document.fonts.ready.then === 'function') {\n    document.fonts.ready.then(function () {\n      setupMarquee(slides[active]);\n    });\n  }\n\n  /* Re-measure when the viewport (and therefore the overflow) changes. */\n  var resizeTimer = null;\n  window.addEventListener('resize', function () {\n    window.clearTimeout(resizeTimer);\n    resizeTimer = window.setTimeout(function () {\n      setupMarquee(slides[active]);\n    }, 150);\n  });\n\n  /* Pause the scroll while the shopper is reading (hover/focus). */\n  root.addEventListener('mouseenter', pauseMarquee);\n  root.addEventListener('mouseleave', resumeMarquee);\n  root.addEventListener('focusin', pauseMarquee);\n  root.addEventListener('focusout', function (event) {\n    if (!root.contains(event.relatedTarget)) resumeMarquee();\n  });\n\n  if (slides.length < 2) return;\n\n  function clearTimer() {\n    window.clearTimeout(timer);\n    timer = null;\n  }\n\n  function schedule() {\n    clearTimer();\n\n    if (!root.isConnected) return;\n    if (animating) return;\n\n    timer = window.setTimeout(flip, interval);\n  }\n\n  function completeFlip(previous, next, nextIndex) {\n    /*\n     * Previous slide returns to the waiting position below the viewport.\n     */\n    previous.dataset.state = 'idle';\n    previous.style.position = 'absolute';\n    previous.style.inset = '0';\n    previous.style.transform = 'translate3d(0, 100%, 0)';\n    previous.style.opacity = '0';\n    previous.style.pointerEvents = 'none';\n    previous.setAttribute('aria-hidden', 'true');\n\n    /*\n     * New active slide becomes the element defining the track's natural\n     * height.\n     */\n    next.dataset.state = 'active';\n    next.style.position = 'relative';\n    next.style.inset = '';\n    next.style.height = '100%';\n    next.style.transform = 'translate3d(0, 0, 0)';\n    next.style.opacity = '1';\n    next.style.pointerEvents = '';\n    next.removeAttribute('aria-hidden');\n\n    active = nextIndex;\n    animating = false;\n\n    setupMarquee(next);\n\n    schedule();\n  }\n\n  function flip() {\n    if (!root.isConnected) return;\n    if (animating) return;\n\n    animating = true;\n    clearTimer();\n\n    var previous = slides[active];\n    var nextIndex = (active + 1) % slides.length;\n    var next = slides[nextIndex];\n\n    /*\n     * Reduced motion: swap immediately.\n     */\n    if (reduceMotion) {\n      completeFlip(previous, next, nextIndex);\n      return;\n    }\n\n    /*\n     * Prepare the incoming slide below the viewport.\n     */\n    next.dataset.state = 'entering';\n    next.style.position = 'absolute';\n    next.style.inset = '0';\n    next.style.transform = 'translate3d(0, 100%, 0)';\n    next.style.opacity = '0';\n    next.style.pointerEvents = 'none';\n    next.removeAttribute('aria-hidden');\n\n    /*\n     * Previous remains visually active at its current position.\n     */\n    previous.style.position = 'relative';\n    previous.style.inset = '';\n    previous.style.transform = 'translate3d(0, 0, 0)';\n    previous.style.opacity = '1';\n\n    /*\n     * Commit the incoming slide's starting pose before transitioning it.\n     */\n    next.getBoundingClientRect();\n\n    requestAnimationFrame(function () {\n      if (!root.isConnected) return;\n\n      previous.dataset.state = 'leaving';\n      next.dataset.state = 'active';\n\n      previous.style.transform = 'translate3d(0, -100%, 0)';\n      previous.style.opacity = '0';\n      previous.style.pointerEvents = 'none';\n\n      next.style.transform = 'translate3d(0, 0, 0)';\n      next.style.opacity = '1';\n    });\n\n    var finished = false;\n\n    function finish(event) {\n      if (finished) return;\n\n      /*\n       * Ignore transitionend events bubbling from children and ignore opacity;\n       * transform is the transition that defines completion.\n       */\n      if (event) {\n        if (event.target !== next) return;\n        if (event.propertyName !== 'transform') return;\n      }\n\n      finished = true;\n\n      next.removeEventListener('transitionend', finish);\n\n      completeFlip(previous, next, nextIndex);\n    }\n\n    next.addEventListener('transitionend', finish);\n\n    /*\n     * Safety fallback for browsers/layout changes that prevent transitionend\n     * from firing.\n     */\n    window.setTimeout(function () {\n      finish();\n    }, duration + 100);\n  }\n\n  /*\n   * Pause rotation while the shopper is interacting with the bar.\n   */\n  root.addEventListener('mouseenter', function () {\n    clearTimer();\n  });\n\n  root.addEventListener('mouseleave', function () {\n    if (!animating) schedule();\n  });\n\n  root.addEventListener('focusin', function () {\n    clearTimer();\n  });\n\n  root.addEventListener('focusout', function (event) {\n    if (!root.contains(event.relatedTarget) && !animating) {\n      schedule();\n    }\n  });\n\n  schedule();\n}\n\nfunction initAnnouncements(scope) {\n  if (!scope) return;\n\n  if (scope.matches && scope.matches(ANNOUNCEMENT)) {\n    bindAnnouncement(scope);\n  }\n\n  if (scope.querySelectorAll) {\n    scope.querySelectorAll(ANNOUNCEMENT).forEach(bindAnnouncement);\n  }\n}\n\nif (!document.__dsAnnouncementBound) {\n  document.__dsAnnouncementBound = true;\n\n  var startAnnouncements = function () {\n    initAnnouncements(document);\n  };\n\n  if (document.readyState === 'loading') {\n    document.addEventListener('DOMContentLoaded', startAnnouncements);\n  } else {\n    startAnnouncements();\n  }\n\n  /*\n   * Shopify Theme Editor: bind newly rendered sections.\n   */\n  document.addEventListener('shopify:section:load', function (event) {\n    initAnnouncements(event.target);\n  });\n\n  /*\n   * Dismiss announcement bar.\n   */\n  document.addEventListener('click', function (event) {\n    var dismiss =\n      event.target.closest &&\n      event.target.closest('[data-ds-header-announcement-dismiss]');\n\n    var root = dismiss && dismiss.closest(ANNOUNCEMENT);\n\n    if (root) {\n      root.remove();\n      document.dispatchEvent(new CustomEvent('ds:header-announcement-dismiss'));\n    }\n  });\n}\n} catch (sfError) {\nif (typeof console !== 'undefined') console.error('[sf-core] behavior \"announcement\" failed to initialise:', sfError);\n}\n})();\n\n/* cart-added.js */\n(function () {\ntry {\n/* cart-added \u2014 opens the \"Added to cart\" dialog (ds-cart-added with modal: true)\n * after an AJAX add, filled from the line that was just added.\n *\n * Reads the ds:cart:added detail buy-buttons.js emits \u2014 the Cart AJAX API\n * response for /cart/add.js, whose first non-child line carries what to show:\n * image, product_title, options_with_values, final_line_price. No component DOM\n * is touched but this dialog's own hooks.\n *\n * The BuildYou path adds its add-ons in a second request (product-addon.js) and\n * then re-emits this event with the parent product line plus its add-on children,\n * so the dialog re-fills with the product, the add-on rows and a combined total.\n * That event lands after the BuildYou SDK bridge's own product-only event\n * (blocks/ds-product-customization-form.liquid), which forwards the SDK's format\n * and falls through to \"no line\" \u2014 the dialog then keeps the line its Liquid\n * rendered, the product and variant on the page, until the add-on event refines it.\n *\n * Progressive enhancement: without this script the dialog stays hidden and the\n * add still happens \u2014 buy-buttons.js already falls back to a full-page POST.\n *\n * Authoring rules: src/behaviors/README.md. No import/export.\n */\n\n(function () {\n  if (document.__dsCartAddedBound) return;\n  document.__dsCartAddedBound = true;\n\n  var lastFocus = null;\n\n  function modal() {\n    return document.querySelector('[data-ds-cart-added-modal]');\n  }\n\n  // One attribute is the whole state: src/cart-added.css animates off it.\n  function isOpen(el) {\n    return Boolean(el) && el.getAttribute('aria-hidden') === 'false';\n  }\n\n  // The Cart AJAX API returns the added line itself, or { items: [...] } when\n  // several land at once. Take the first line that is not an add-on: a bundle add\n  // sends the add-ons FIRST and the main product last (Cross-sell Bundle spec\n  // \u00A73.2), and the add-on path (product-addon.js) emits ds:cart:added for a\n  // request that contains ONLY add-ons \u2014 the dialog must show the product either\n  // way, never its gift box.\n  function isChild(line) {\n    if (!line) return false;\n    var properties = line.properties;\n    return Boolean(\n      line.parent_relationship ||\n        line.parent_line_key ||\n        (properties && properties._bundle_role === 'addon')\n    );\n  }\n\n  // The add-on lines of the same response. buy-buttons.js sends main + add-ons in\n  // ONE /cart/add.js call, so a bundle arrives here whole and the dialog can show\n  // what the shopper actually paid \u2014 the product price alone would understate it.\n  function addedAddons(detail) {\n    if (!detail || !Array.isArray(detail.items)) return [];\n    return detail.items.filter(isChild);\n  }\n\n  function addedLine(detail) {\n    if (!detail) return null;\n    if (Array.isArray(detail.items)) {\n      for (var i = 0; i < detail.items.length; i += 1) {\n        if (!isChild(detail.items[i])) return detail.items[i];\n      }\n      return null;\n    }\n    if (isChild(detail)) return null;\n    return detail.product_title || detail.title ? detail : null;\n  }\n\n  function optionLabels(line) {\n    var pairs = line.options_with_values;\n    if (!Array.isArray(pairs)) return [];\n    return pairs\n      .filter(function (pair) {\n        // Shopify sends \"Title / Default Title\" for products with no real options.\n        return pair && pair.value && pair.value !== 'Default Title';\n      })\n      .map(function (pair) {\n        // Colour values may be \"Name|swatch-file\"; only the name is for shoppers.\n        return { name: pair.name, value: String(pair.value).split('|')[0].trim() };\n      });\n  }\n\n  /* A \"Buy More Save More\" value is one pack: \"1 PC\" or \"2 PCS\" (the s is\n     optional). The count is how many pieces that variant sells. */\n  function packCount(line) {\n    var pairs = line && line.options_with_values;\n    if (!Array.isArray(pairs)) return 0;\n    for (var i = 0; i < pairs.length; i++) {\n      var pair = pairs[i];\n      if (!pair || !pair.name || pair.name.indexOf('Buy More Save More') === -1) continue;\n      var parts = String(pair.value || '').trim().split(/\\s+/);\n      if (!parts.length || !/^\\d+$/.test(parts[0])) continue;\n      if (parts.length > 1 && !/^pcs?$/i.test(parts[1])) continue;\n      var count = Number(parts[0]);\n      if (count >= 1) return count;\n    }\n    return 0;\n  }\n\n  /* The /pc figure painted inside the selected pack pill (\"$30.95/pc\"), not\n     the pack total in the price block. */\n  function selectedPillPiece(count) {\n    var pills = document.querySelectorAll('[data-ds-bmsm-pill][aria-current=\"true\"]');\n    for (var i = 0; i < pills.length; i++) {\n      var top = pills[i].querySelector('[data-ds-bmsm-pill-top]');\n      var bot = pills[i].querySelector('[data-ds-bmsm-pill-bot]');\n      if (!top || !bot) continue;\n      var parts = top.textContent.trim().split(/\\s+/);\n      if (!/^\\d+$/.test(parts[0]) || Number(parts[0]) !== count) continue;\n      if (parts.length > 1 && !/^pcs?$/i.test(parts[1])) continue;\n      var text = bot.textContent.replace(/\\s+/g, ' ').trim();\n      if (text) return text;\n    }\n    return '';\n  }\n\n  // Shopify money fields are integer cents; the shop's format lives in Liquid, not\n  // here, so render the store default and let the theme override via data-money.\n  function money(cents, root) {\n    var format = root.getAttribute('data-ds-cart-added-money') || '${{amount}}';\n    var amount = (Number(cents) || 0) / 100;\n    return format.replace(/\\{\\{\\s*amount[a-z_]*\\s*\\}\\}/, amount.toFixed(2));\n  }\n\n  // A live personalization canvas snapshot, or '' if it can't be read (a\n  // cross-origin-tainted canvas throws on toDataURL \u2014 never let that break fill).\n  function canvasPreview(canvas, type, quality) {\n    if (!canvas || typeof canvas.toDataURL !== 'function') return '';\n    try {\n      return canvas.toDataURL({ format: type, quality: quality }) || '';\n    } catch (e) {\n      return '';\n    }\n  }\n\n  function eventPreview(detail) {\n    // The exact frame the shopper just personalized, in priority order: a live\n    // personalization canvas (buildyouCanvas, then engraver), then the SDK's\n    // in-memory preview (window.BuildYou.publicPreviewUrl, usually a data: URI),\n    // then whatever preview URL the event detail carried. ds:cart:added fires only\n    // after the bridge's /cart.js round trip, so these are set by modal-open time.\n    var win = typeof window !== 'undefined' ? window : null;\n    var candidate =\n      (win && win.buildyouCanvas && canvasPreview(win.buildyouCanvas, 'png', 0.7)) ||\n      (win && win.engraver && canvasPreview(win.engraver.canvas, 'jpeg', 0.7)) ||\n      (win && win.BuildYou && win.BuildYou.publicPreviewUrl) ||\n      (detail &&\n        typeof detail === 'object' &&\n        (detail.previewUrl ||\n          detail.previewImageUrl ||\n          detail.publicPreviewUrl ||\n          detail.preview_image)) ||\n      '';\n    if (candidate && typeof candidate === 'object') candidate = candidate.url || candidate.src || '';\n    return typeof candidate === 'string' ? candidate : '';\n  }\n\n  function fill(root, line, previewUrl) {\n    var titleText = line.product_title || line.title || '';\n    var image = root.querySelector('[data-ds-cart-added-image]');\n    if (image) {\n      // The thumbnail IS the personalization preview now \u2014 no separate Preview\n      // button \u2014 so it carries the zoom-lightbox hooks the button used to carry.\n      // BuildYou can send the already-rendered browser preview with its success\n      // event. Prefer it over the persisted line-property URL: the latter points\n      // at S3 and may not be available yet when the modal opens.\n      var src =\n        previewUrl ||\n        (line.properties && line.properties['_customily-preview']) ||\n        lineImage(line);\n      if (src) {\n        // Swapping src on an already-loaded <img> won't re-show the spinner on\n        // its own \u2014 reset it so the frame goes gray/spinning again, same as a\n        // fresh ds-image on the cart page (image-fallback.js hides it once more\n        // on the new load/error).\n        var wrap = image.closest('[data-ds-image]');\n        var spinner = wrap && wrap.querySelector('[data-ds-image-loading]');\n        if (spinner) spinner.hidden = false;\n        delete image.dataset.dsImageRetries;\n        image.src = src;\n        image.alt = titleText;\n        image.setAttribute('data-ds-image-zoom-src', src);\n        image.setAttribute('data-ds-image-zoom-alt', titleText);\n        image.setAttribute('data-ds-image-zoom-title', titleText);\n        image.removeAttribute('hidden');\n      } else {\n        image.setAttribute('hidden', '');\n      }\n    }\n\n    var title = root.querySelector('[data-ds-cart-added-title]');\n    if (title && titleText) title.textContent = titleText;\n\n    var options = root.querySelector('[data-ds-cart-added-options]');\n    if (options) {\n      var labels = optionLabels(line);\n      options.textContent = '';\n      labels.forEach(function (label) {\n        /* Same two spans the Liquid draws: the label in the row's own weight,\n         * the chosen value semibold navy. textContent on each, so a value that\n         * came back from Shopify can never inject markup. */\n        var span = document.createElement('span');\n        span.appendChild(document.createTextNode(label.name + ': '));\n        var value = document.createElement('span');\n        value.className = 'sf-font-semibold sf-text-action-secondary';\n        value.textContent = label.value;\n        span.appendChild(value);\n        options.appendChild(span);\n      });\n      if (labels.length) {\n        options.removeAttribute('hidden');\n      } else {\n        options.setAttribute('hidden', '');\n      }\n    }\n\n    var amount = root.querySelector('[data-ds-price-amount]');\n    var cents = lineCents(line);\n    if (amount && cents !== null) amount.textContent = money(cents, root);\n\n\n    var qtyValue = Math.max(1, Number(line.quantity) || 1);\n    var quantity = root.querySelector('[data-ds-cart-added-quantity]');\n    if (quantity) quantity.textContent = String(qtyValue);\n\n    /* Under the amount: \"$30.95/pc\" from the selected pack pill whenever the\n       line is a pack, including quantity 1. \"$46.90/each\" only when the line\n       is not a pack and more than one was added. */\n    var unit = root.querySelector('[data-ds-cart-added-unit]');\n    if (unit) {\n      var count = packCount(line);\n      var fromPill = count >= 1 ? selectedPillPiece(count) : '';\n      var unitCents = Number(line.final_price);\n      var showUnit = false;\n      if (fromPill) {\n        unit.textContent = fromPill;\n        showUnit = true;\n      } else if (count >= 1 && isFinite(unitCents) && unitCents > 0) {\n        var piece = money(Math.round(unitCents / count), root).replace(/\\s*[A-Z]{3}\\s*$/, '').trim();\n        unit.textContent = piece + '/pc';\n        showUnit = true;\n      } else if (count === 0 && qtyValue > 1 && isFinite(unitCents) && unitCents > 0) {\n        unit.textContent = money(unitCents, root).replace(/\\s*[A-Z]{3}\\s*$/, '').trim() + '/each';\n        showUnit = true;\n      }\n      if (showUnit) unit.removeAttribute('hidden');\n      else unit.setAttribute('hidden', '');\n    }\n\n    // ponytail: Edit item hidden in this modal on request (2026-09-08) \u2014 swap\n    // this block back for `fillEdit(root, line);` to restore it.\n    var editLinkWrap = root.querySelector('[data-ds-cart-added-edit-link]');\n    var editBuildYouWrap = root.querySelector('[data-ds-cart-added-edit-buildyou]');\n    if (editLinkWrap) editLinkWrap.setAttribute('hidden', '');\n    if (editBuildYouWrap) editBuildYouWrap.setAttribute('hidden', '');\n\n    fillProperties(root, line);\n  }\n\n  function fillEdit(root, line) {\n    var properties = (line && line.properties) || {};\n    var isBuildYou = Boolean(\n      line &&\n        line.key &&\n        properties['_buildyou-customization-id'] &&\n        properties['_source_app'] === 'buildyou_nova'\n    );\n    var linkWrap = root.querySelector('[data-ds-cart-added-edit-link]');\n    var link = linkWrap && linkWrap.querySelector('[data-ds-cart-added-edit]');\n    var buildYouWrap = root.querySelector('[data-ds-cart-added-edit-buildyou]');\n\n    if (isBuildYou && buildYouWrap) {\n      buildYouWrap.setAttribute('data-line-key', line.key);\n      buildYouWrap.setAttribute('hidden', '');\n      if (linkWrap) linkWrap.setAttribute('hidden', '');\n      loadBuildYouCartBundle(buildYouWrap, linkWrap);\n      return;\n    }\n\n    if (buildYouWrap) {\n      buildYouWrap.setAttribute('hidden', '');\n      buildYouWrap.removeAttribute('data-line-key');\n    }\n    if (linkWrap) linkWrap.removeAttribute('hidden');\n    if (link && line.url) link.setAttribute('href', line.url);\n  }\n\n  // The cart bundle scans `.by-edit-design-slot[data-line-key]` once at startup.\n  // Its MutationObserver only watches #cart_line_form, which does not exist on a\n  // PDP, so loading it before this modal has the real line key leaves a visible\n  // but unbound button. Stamp the key above, then load the exact cart-page bundle.\n  function loadBuildYouCartBundle(editControl, fallbackControl) {\n    window.BuildYou = window.BuildYou || {};\n    if (window.BuildYou.cartScriptState === 'ready') {\n      if (editControl.dataset.byEditBound === '1') editControl.removeAttribute('hidden');\n      return;\n    }\n    if (window.BuildYou.cartScriptState === 'loading') {\n      return;\n    }\n    window.BuildYou.cartScriptState = 'loading';\n\n    // Injected at build time from .env.<env> (build.mjs applyEnvUrls), the same\n    // way the Liquid snippets get their BuildYou URLs: prod build \u2192 prod CDN,\n    // dev build \u2192 dev CDN. No hardcoded environment here.\n    var cartCssUrl = 'https://assets.buildyou.io/scripts/shopify/cart.bundle.css';\n    var cartCssFallbackUrl = 'https://cdn.bestbuildyou.com/scripts/shopify/cart.bundle.css';\n    var cartJsUrl = 'https://assets.buildyou.io/scripts/shopify/cart.bundle.js';\n    var cartJsFallbackUrl = 'https://cdn.bestbuildyou.com/scripts/shopify/cart.bundle.js';\n    var css = document.createElement('link');\n    css.rel = 'stylesheet';\n    css.crossOrigin = 'anonymous';\n    css.onerror = function () {\n      css.onerror = null;\n      css.href = cartCssFallbackUrl;\n    };\n    css.href = cartCssUrl;\n    document.head.appendChild(css);\n\n    function appendScript(src, canRetry) {\n      var script = document.createElement('script');\n      script.async = true;\n      script.crossOrigin = 'anonymous';\n      script.onload = function () {\n        window.BuildYou.cartScriptState = 'ready';\n        if (editControl.dataset.byEditBound === '1') editControl.removeAttribute('hidden');\n      };\n      script.onerror = function () {\n        if (canRetry) {\n          appendScript(cartJsFallbackUrl, false);\n        } else {\n          window.BuildYou.cartScriptState = 'failed';\n          if (fallbackControl) fallbackControl.removeAttribute('hidden');\n        }\n      };\n      script.src = src;\n      document.head.appendChild(script);\n    }\n\n    appendScript(cartJsUrl, true);\n  }\n\n  function visibleProperties(line) {\n    var properties = (line && line.properties) || {};\n    var optionNames = {};\n    if (Array.isArray(line && line.options_with_values)) {\n      line.options_with_values.forEach(function (option) {\n        if (option && option.name) optionNames[option.name] = true;\n      });\n    }\n    return Object.keys(properties).reduce(function (rows, key) {\n      var value = properties[key];\n      if (!value) return rows;\n      if (key.charAt(0) !== '_') {\n        rows.push({ name: key, value: value });\n        return rows;\n      }\n      if (key.indexOf('_custom_') !== 0 || /\\(originals?\\)|\\(filtered\\)/.test(String(value))) {\n        return rows;\n      }\n      var label = key.replace(/^_custom_\\S+\\s*/, '').trim();\n      if (label && !optionNames[label]) rows.push({ name: label, value: value });\n      return rows;\n    }, []);\n  }\n\n  // Rebuild the plain \"Label: Value\" lines the static card draws \u2014 one per line,\n  // the same flat style as the chosen options above (Figma 5543:37874), not the\n  // boxed list the full cart line uses.\n  function fillProperties(root, line) {\n    var el = root.querySelector('[data-ds-cart-added-properties]');\n    if (!el) return;\n    // ponytail: personalization lines hidden on request (2026-09-05) \u2014 matches the\n    // snippet's static override; restore `visibleProperties(line)` to bring them back.\n    var rows = [];\n    el.textContent = '';\n    rows.forEach(function (row) {\n      var span = document.createElement('span');\n      span.textContent = row.name + ': ' + row.value;\n      el.appendChild(span);\n    });\n    if (rows.length) el.removeAttribute('hidden');\n    else el.setAttribute('hidden', '');\n  }\n\n  // The line total in cents, or null when the payload carries no price at all \u2014\n  // the caller then leaves whatever Liquid already rendered rather than writing 0.\n  function lineCents(line) {\n    var cents = line && line.final_line_price;\n    if (cents === undefined || cents === null) cents = line && line.line_price;\n    if (cents === undefined || cents === null) cents = line && line.final_price;\n    if (cents === undefined || cents === null) return null;\n    return Number(cents) || 0;\n  }\n\n  function lineImage(line) {\n    var src = (line && (line.image || line.featured_image)) || '';\n    if (src && src.url) src = src.url;\n    return src || '';\n  }\n\n  function fillAddonRow(node, addon, root) {\n    var image = node.querySelector('[data-ds-cart-addon-image]');\n    var src = lineImage(addon);\n    if (image) {\n      if (src) {\n        image.src = src;\n        image.alt = '';\n        image.removeAttribute('hidden');\n      } else {\n        image.setAttribute('hidden', '');\n      }\n    }\n\n    var title = node.querySelector('[data-ds-cart-addon-title]');\n    if (title) title.textContent = addon.product_title || addon.title || '';\n\n    // Storybook pm add-on row has Quantity only \u2014 no variant line.\n    var variant = node.querySelector('[data-ds-cart-addon-variant]');\n    if (variant) {\n      variant.textContent = '';\n      variant.setAttribute('hidden', '');\n    }\n\n    var quantity = node.querySelector('[data-ds-cart-addon-quantity]');\n    if (quantity) {\n      var qty = Number(addon.quantity);\n      if (qty > 0) {\n        // Same quantity as before (was \"\u00D7 N\"); Storybook pm label format only.\n        var labelMatch = (quantity.textContent || '').match(/^(.*?:\\s*)/);\n        quantity.textContent = (labelMatch ? labelMatch[1] : 'Quantity: ') + qty;\n        quantity.removeAttribute('hidden');\n      } else {\n        quantity.setAttribute('hidden', '');\n      }\n    }\n\n    var price = node.querySelector('[data-ds-cart-addon-price]');\n    if (price) price.textContent = money(lineCents(addon) || 0, root);\n\n    /* Same short note as the main line: \"$5.95/each\" under the line total\n     * once more than one was added. addon.final_price is Shopify's per-unit\n     * cents, so nothing is divided here either. */\n    var addonUnit = node.querySelector('[data-ds-cart-addon-unit]');\n    if (addonUnit) {\n      var addonQty = Number(addon.quantity) || 0;\n      /* `final_price` is what Shopify's own lines carry; a line assembled by a\n       * bridge (the BuildYou path builds its own) may only have the line total,\n       * so divide that back out rather than dropping the note. */\n      var addonUnitCents = Number(addon.final_price);\n      if (!isFinite(addonUnitCents) || addonUnitCents <= 0) {\n        var addonLineCents = Number(lineCents(addon));\n        if (isFinite(addonLineCents) && addonQty > 0) addonUnitCents = addonLineCents / addonQty;\n      }\n      if (addonQty > 1 && isFinite(addonUnitCents) && addonUnitCents > 0) {\n        addonUnit.textContent = money(addonUnitCents, root).replace(/\\s*[A-Z]{3}\\s*$/, '').trim() + '/each';\n        addonUnit.removeAttribute('hidden');\n      } else {\n        addonUnit.setAttribute('hidden', '');\n      }\n    }\n    return node;\n  }\n\n  // No add-ons: the whole block goes, item total included \u2014 on its own it would\n  // just restate the price already shown above.\n  function fillAddons(root, line, addons) {\n    var block = root.querySelector('[data-ds-cart-added-addons]');\n    if (!block) return;\n    var list = root.querySelector('[data-ds-cart-added-addon-list]');\n    var template = root.querySelector('[data-ds-cart-added-addon-template]');\n    if (!addons.length || !list || !template || !template.content.firstElementChild) {\n      block.setAttribute('hidden', '');\n      return;\n    }\n\n    list.textContent = '';\n    var total = lineCents(line) || 0;\n    addons.forEach(function (addon) {\n      total += lineCents(addon) || 0;\n      list.appendChild(fillAddonRow(template.content.firstElementChild.cloneNode(true), addon, root));\n    });\n\n    var totalEl = root.querySelector('[data-ds-cart-added-total]');\n    if (totalEl) totalEl.textContent = money(total, root);\n    block.removeAttribute('hidden');\n  }\n\n  function open(detail) {\n    var el = modal();\n    if (!el) return;\n    var line = addedLine(detail);\n    if (line) fill(el, line, eventPreview(detail));\n    fillAddons(el, line, addedAddons(detail));\n\n    lastFocus = document.activeElement;\n    el.setAttribute('aria-hidden', 'false');\n    document.body.style.overflow = 'hidden';\n\n    var closeBtn = el.querySelector('[data-ds-cart-added-close]');\n    // Focus after paint so the dialog is on screen first.\n    window.requestAnimationFrame(function () {\n      if (closeBtn) closeBtn.focus({ focusVisible: false });\n    });\n  }\n\n  function close() {\n    var el = modal();\n    if (!isOpen(el)) return;\n    // Move focus OUT before hiding. aria-hidden on an ancestor of the focused\n    // element is invalid \u2014 the browser warns and refuses to hide it from the\n    // a11y tree \u2014 and the Edit control that triggered a close still holds focus\n    // here. Blur first, then restore focus to whatever opened the dialog.\n    var active = document.activeElement;\n    if (active && el.contains(active) && typeof active.blur === 'function') active.blur();\n    // The sheet keeps sliding out on its own \u2014 the CSS delays visibility until\n    // the transition ends, so nothing here has to wait for it.\n    el.setAttribute('aria-hidden', 'true');\n    document.body.style.overflow = '';\n    if (lastFocus && typeof lastFocus.focus === 'function') lastFocus.focus();\n    lastFocus = null;\n  }\n\n  // Buy buttons with the dialog switched off render no dialog and carry this\n  // attribute instead: the add then goes straight to the cart page.\n  function redirectTarget() {\n    var el = document.querySelector('[data-ds-cart-added-redirect]');\n    return (el && el.getAttribute('data-ds-cart-added-redirect')) || '';\n  }\n\n  document.addEventListener('ds:cart:added', function (e) {\n    if (modal()) {\n      open(e.detail);\n      return;\n    }\n    var to = redirectTarget();\n    if (to) window.location.assign(to);\n  });\n\n  // Edit design: the BuildYou slot opens its editor in-page, and the cart bundle\n  // binds its handler on the button itself \u2014 that target-phase handler runs\n  // before any bubble listener here. Close in the CAPTURE phase so our body\n  // scroll-lock is lifted BEFORE the editor opens and snapshots body overflow;\n  // otherwise it saves our 'hidden' and restores it on close, leaving the page\n  // unscrollable. No preventDefault \u2014 the bundle / plain link still runs.\n  document.addEventListener(\n    'click',\n    function (e) {\n      if (e.target.closest && e.target.closest('[data-ds-cart-added-edit]')) close();\n    },\n    true\n  );\n\n  document.addEventListener('click', function (e) {\n    if (!e.target.closest) return;\n    if (e.target.closest('[data-ds-cart-added-continue]')) {\n      e.preventDefault();\n      close();\n      // The trigger (e.g. a quick-add card) can be far down the page \u2014 send the\n      // shopper back to the top, where continued browsing normally starts.\n      window.scrollTo({ top: 0, behavior: 'smooth' });\n      return;\n    }\n    if (e.target.closest('[data-ds-cart-added-close]')) {\n      e.preventDefault();\n      close();\n      return;\n    }\n    // Backdrop: the overlay itself, never the dialog inside it.\n    var el = modal();\n    if (isOpen(el) && e.target === el) close();\n  });\n\n  document.addEventListener('keydown', function (e) {\n    if (e.key !== 'Escape') return;\n    if (isOpen(modal())) close();\n  });\n})();\n\n} catch (sfError) {\nif (typeof console !== 'undefined') console.error('[sf-core] behavior \"cart-added\" failed to initialise:', sfError);\n}\n})();\n\n/* cart-alerts.js */\n(function () {\ntry {\n/* cart-alerts \u2014 drives the state-driven notices under the cart title\n * (snippets/ds-cart-alerts.liquid): the price-update notice's dismiss button and\n * the cart-expiry countdown ticker.\n *\n * Progressive enhancement: Liquid renders both notices fully (the countdown at\n * its starting mm:ss); this only makes them interactive \u2014 the countdown ticks\n * down and swaps to the expired copy at zero, and the price-update notice can be\n * dismissed for the session. Without JS the copy still reads correctly.\n *\n * Authoring rules: src/behaviors/README.md. No import/export.\n */\n\nvar CART_ALERTS = '[data-ds-cart-alerts]';\nvar PRICE_UPDATE = '[data-ds-cart-price-update]';\nvar PRICE_UPDATE_CLOSE = '[data-ds-cart-price-update-close]';\nvar EXPIRY = '[data-ds-cart-expiry]';\nvar EXPIRY_TIME = '[data-ds-cart-expiry-time]';\nvar DISMISS_KEY = 'ds-cart-price-update-dismissed';\n\nfunction cartAlertsPad(n) {\n  return n < 10 ? '0' + n : '' + n;\n}\n\nfunction cartAlertsFormat(seconds) {\n  var h = Math.floor(seconds / 3600);\n  var m = Math.floor((seconds % 3600) / 60);\n  var s = seconds % 60;\n  return h > 0\n    ? h + ':' + cartAlertsPad(m) + ':' + cartAlertsPad(s)\n    : cartAlertsPad(m) + ':' + cartAlertsPad(s);\n}\n\nfunction cartAlertsInitPriceUpdate(region) {\n  var pu = region.querySelector(PRICE_UPDATE);\n  if (!pu) return;\n  var message = pu.textContent.trim();\n  try {\n    if (sessionStorage.getItem(DISMISS_KEY) === message) pu.hidden = true;\n  } catch (e) {}\n  var close = pu.querySelector(PRICE_UPDATE_CLOSE);\n  if (close) {\n    close.addEventListener('click', function () {\n      pu.hidden = true;\n      try {\n        sessionStorage.setItem(DISMISS_KEY, message);\n      } catch (e) {}\n    });\n  }\n}\n\nfunction cartAlertsInitExpiry(region) {\n  var exp = region.querySelector(EXPIRY);\n  if (!exp) return;\n  var seconds = parseInt(exp.getAttribute('data-ds-cart-expiry-seconds'), 10) || 0;\n  if (seconds <= 0) return;\n  var timeEl = exp.querySelector(EXPIRY_TIME);\n  var expiredText = exp.getAttribute('data-ds-cart-expiry-expired') || '';\n  var remaining = seconds;\n  var timer = setInterval(function () {\n    remaining -= 1;\n    if (remaining <= 0) {\n      clearInterval(timer);\n      // Swap the whole row content for the expired message, keeping the tone.\n      exp.textContent = '';\n      var span = document.createElement('span');\n      span.className = 'sf-font-medium';\n      span.textContent = expiredText;\n      exp.appendChild(span);\n      return;\n    }\n    if (timeEl) timeEl.textContent = cartAlertsFormat(remaining);\n  }, 1000);\n}\n\nfunction cartAlertsInit(region) {\n  if (!region || region.__dsCartAlertsBound) return;\n  region.__dsCartAlertsBound = true;\n  cartAlertsInitPriceUpdate(region);\n  cartAlertsInitExpiry(region);\n}\n\nfunction cartAlertsInitAll() {\n  var regions = document.querySelectorAll(CART_ALERTS);\n  for (var i = 0; i < regions.length; i++) cartAlertsInit(regions[i]);\n}\n\nif (!document.__dsCartAlertsBound) {\n  document.__dsCartAlertsBound = true;\n\n  if (document.readyState === 'loading') {\n    document.addEventListener('DOMContentLoaded', cartAlertsInitAll, { once: true });\n  } else {\n    cartAlertsInitAll();\n  }\n\n  // The region can also arrive after load \u2014 the theme editor re-renders a section\n  // by swapping its HTML in, with no DOMContentLoaded. Watch for it so the\n  // countdown starts and the dismiss button binds in that case too. (The\n  // per-region guard keeps re-scans idempotent.)\n  if (typeof MutationObserver === 'function') {\n    new MutationObserver(function (mutations) {\n      for (var i = 0; i < mutations.length; i++) {\n        if (mutations[i].addedNodes && mutations[i].addedNodes.length) {\n          cartAlertsInitAll();\n          return;\n        }\n      }\n    }).observe(document.documentElement, { childList: true, subtree: true });\n  }\n}\n\n} catch (sfError) {\nif (typeof console !== 'undefined') console.error('[sf-core] behavior \"cart-alerts\" failed to initialise:', sfError);\n}\n})();\n\n/* cart-checkout-sticky.js */\n(function () {\ntry {\n/* cart-checkout-sticky \u2014 reveal/hide the mobile sticky checkout bar.\n *\n * Binds [data-ds-cart-checkout-sticky] (snippets/ds-cart-checkout-sticky.liquid).\n * The bar starts hidden (sf-hidden). While the in-card Checkout button\n * ([data-ds-cart-checkout-anchor], in ds-cart-summary) is OFF screen, this shows\n * the bar; once that button scrolls back into view the bar hides again, so the\n * shopper never sees two checkout buttons at once. With no JS the bar never\n * shows and the in-card button still works (progressive enhancement).\n *\n * The bar is CSS-hidden from tablet-air (820) up (tablet-air:sf-hidden, never toggled here) where the\n * Order Summary is a sticky sidebar; the observer still runs there but the CSS\n * keeps the bar gone.\n *\n * It also opens/closes the Order Summary bottom sheet the bar's total expands\n * into \u2014 see bindSheet below.\n *\n * Idempotent + Theme-Editor-safe: the section can re-render, so re-scans must\n * not double-observe. Authoring rules: src/behaviors/README.md. No import/export.\n */\nfunction cartCheckoutStickyInit(bar) {\n  if (!bar || bar.dataset.dsBound) return;\n\n  // Watch the real in-card checkout button. Scope to this bar's cart so a page\n  // with more than one never crosses the wires; fall back to document.\n  var scope = bar.closest('[data-ds-cart]') || document;\n  var anchor = scope.querySelector('[data-ds-cart-checkout-anchor]');\n  if (!anchor) return;\n\n  bar.dataset.dsBound = '1';\n\n  // Without IntersectionObserver, leave the bar hidden: the in-card button is\n  // enough and revealing a permanent bar would be worse than none.\n  if (!('IntersectionObserver' in window)) return;\n\n  var observer = new IntersectionObserver(\n    function (entries) {\n      var entry = entries[0];\n      if (!entry) return;\n      // In view \u2192 the shopper has reached the real button; hide the bar.\n      // Out of view (above or below) \u2192 show it.\n      if (entry.isIntersecting) {\n        bar.classList.add('sf-hidden');\n      } else {\n        bar.classList.remove('sf-hidden');\n      }\n    },\n    { threshold: 0 }\n  );\n  observer.observe(anchor);\n\n  bindSheet(bar, scope);\n}\n\n/* The Order Summary sheet the bar's total opens (SF0031). Plain markup behind\n * `hidden`: this flips the scrim + sheet, keeps aria-expanded honest, turns the\n * chevron over, and closes on Escape, on the scrim and on the X. The page\n * behind it is locked while it is open so the sheet is what scrolls. */\nfunction bindSheet(bar, scope) {\n  var toggle = bar.querySelector('[data-ds-cart-sticky-toggle]');\n  if (!toggle) return;\n\n  var root = scope === document ? document : scope;\n  var sheet = root.querySelector('[data-ds-cart-sticky-sheet]');\n  var scrim = root.querySelector('[data-ds-cart-sticky-scrim]');\n  if (!sheet || !scrim) return;\n\n  var chevron = bar.querySelector('[data-ds-cart-sticky-chevron]');\n\n  function open() {\n    sheet.hidden = false;\n    scrim.hidden = false;\n    toggle.setAttribute('aria-expanded', 'true');\n    if (chevron) chevron.classList.add('sf-rotate-180');\n    document.documentElement.style.overflow = 'hidden';\n    var close = sheet.querySelector('[data-ds-cart-sticky-close]');\n    if (close && typeof close.focus === 'function') close.focus({ focusVisible: false });\n  }\n\n  function close() {\n    sheet.hidden = true;\n    scrim.hidden = true;\n    toggle.setAttribute('aria-expanded', 'false');\n    if (chevron) chevron.classList.remove('sf-rotate-180');\n    document.documentElement.style.overflow = '';\n    if (typeof toggle.focus === 'function') toggle.focus();\n  }\n\n  toggle.addEventListener('click', function () {\n    if (sheet.hidden) open();\n    else close();\n  });\n\n  scrim.addEventListener('click', close);\n\n  sheet.addEventListener('click', function (event) {\n    if (event.target.closest('[data-ds-cart-sticky-close]')) close();\n  });\n\n  document.addEventListener('keydown', function (event) {\n    if (event.key === 'Escape' && !sheet.hidden) close();\n  });\n\n  // The bar, the sheet and the scrim are CSS-hidden from tablet-air up. That\n  // hides them, but the scroll lock set in open() lives on the document and\n  // would stay after a resize, so the page could not scroll. Close for real\n  // when the layout leaves the bar behind.\n  var airValue = getComputedStyle(document.documentElement).getPropertyValue('--breakpoint-tablet-air').trim();\n  var airPx = parseInt(airValue, 10) || 820;\n  var air = window.matchMedia('(min-width: ' + airPx + 'px)');\n  var onAir = function (event) {\n    if (event.matches && !sheet.hidden) close();\n  };\n  if (air.addEventListener) air.addEventListener('change', onAir);\n  else air.addListener(onAir);\n}\n\nfunction cartCheckoutStickyInitAll() {\n  var bars = document.querySelectorAll('[data-ds-cart-checkout-sticky]');\n  for (var i = 0; i < bars.length; i++) cartCheckoutStickyInit(bars[i]);\n}\n\nif (!document.__dsCartCheckoutStickyBound) {\n  document.__dsCartCheckoutStickyBound = true;\n\n  if (document.readyState === 'loading') {\n    document.addEventListener('DOMContentLoaded', cartCheckoutStickyInitAll, { once: true });\n  } else {\n    cartCheckoutStickyInitAll();\n  }\n\n  // The theme editor re-renders a section by swapping its HTML in, with no\n  // DOMContentLoaded \u2014 re-scan so the bar re-binds. The per-bar guard keeps it\n  // idempotent.\n  if (typeof MutationObserver === 'function') {\n    new MutationObserver(function (mutations) {\n      for (var i = 0; i < mutations.length; i++) {\n        if (mutations[i].addedNodes && mutations[i].addedNodes.length) {\n          cartCheckoutStickyInitAll();\n          return;\n        }\n      }\n    }).observe(document.documentElement, { childList: true, subtree: true });\n  }\n}\n\n} catch (sfError) {\nif (typeof console !== 'undefined') console.error('[sf-core] behavior \"cart-checkout-sticky\" failed to initialise:', sfError);\n}\n})();\n\n/* cart-item-total.js */\n(function () {\ntry {\n/* cart-item-total \u2014 keeps a cart line's \"Item total (incl. add-ons)\" honest while\n * the buyer is still stepping. Pure arithmetic, no network: cart-lines.js owns\n * /cart/change.js.\n *\n * It moves the total by the DIFFERENCE against the rendered quantities, never by\n * re-adding the line, so a total the server discounted keeps its discount. A line\n * without data-ds-cart-item-total-cents is left alone.\n *\n * Authoring rules: src/behaviors/README.md. No import/export.\n */\n\nvar CART_ITEM = '[data-ds-cart-item]';\nvar CART_ITEM_TOTAL = '[data-ds-cart-item-total-value] [data-ds-price-amount]';\nvar CART_ITEM_LINE = '[data-ds-cart-item-line-price] [data-ds-price-amount]';\nvar CART_ITEM_EACH = '[data-ds-cart-item-each]';\n\nfunction cartItemCents(el, attr) {\n  var raw = el && el.getAttribute(attr);\n  if (raw === null || raw === undefined || raw === '') return null;\n  var value = Number(raw);\n  return Number.isFinite(value) ? value : null;\n}\n\n// Blank means mid-edit, not \"zero of these\".\nfunction cartItemQuantity(scope, rendered) {\n  var input = scope && scope.querySelector('input[type=\"number\"]');\n  if (!input) return rendered;\n  var raw = String(input.value).trim();\n  if (raw === '') return rendered;\n  var value = Number(raw);\n  if (!Number.isFinite(value) || value < 0) return rendered;\n  return value;\n}\n\nfunction cartItemMoney(root, cents) {\n  // money_format may carry markup; this writes text, so drop the tags.\n  var format = (root.getAttribute('data-ds-cart-item-money') || '${{amount}}').replace(/<[^>]*>/g, '');\n  var amount = (Number(cents) || 0) / 100;\n  return format.replace(/\\{\\{\\s*amount[a-z_]*\\s*\\}\\}/, amount.toFixed(2));\n}\n\n// The line's own quantity: the first stepper outside any add-on row.\nfunction cartItemMainQuantity(root, renderedQty) {\n  var steppers = root.querySelectorAll('[data-ds-number-input]');\n  for (var i = 0; i < steppers.length; i += 1) {\n    if (!steppers[i].closest('[data-ds-cart-addon-line]')) {\n      return cartItemQuantity(steppers[i], renderedQty);\n    }\n  }\n  return renderedQty;\n}\n\nfunction cartItemSync(root) {\n  if (!root) return;\n  var unit = cartItemCents(root, 'data-ds-cart-item-price');\n  var renderedQty = cartItemCents(root, 'data-ds-cart-item-quantity');\n  if (renderedQty === null) renderedQty = 1;\n  var mainQty = cartItemMainQuantity(root, renderedQty);\n\n  // Purchase-row line total (unit \u00D7 quantity). Moved by the stepper DIFFERENCE\n  // off the rendered line total, not re-multiplied, so a per-line discount the\n  // server applied is kept \u2014 the same reasoning as the add-ons total below.\n  var line = root.querySelector(CART_ITEM_LINE);\n  if (line && unit !== null) {\n    var lineCents = cartItemCents(root, 'data-ds-cart-item-line-cents');\n    if (lineCents === null) lineCents = unit * renderedQty;\n    line.textContent = cartItemMoney(root, lineCents + unit * (mainQty - renderedQty));\n  }\n  // \"$X each\" is only meaningful once there is more than one; at 1 the line\n  // total already IS the unit price.\n  var each = root.querySelector(CART_ITEM_EACH);\n  if (each) each.hidden = !(mainQty > 1);\n\n  // Each add-on row shows its own line total (unit x quantity) with \"(\u2026 each)\"\n  // under it; move that total by the stepper difference too, and show the each\n  // note only past 1 \u2014 the same rules as the purchase row above.\n  var addonRows = root.querySelectorAll('[data-ds-cart-addon-line]');\n  for (var k = 0; k < addonRows.length; k += 1) {\n    var rowUnit = cartItemCents(addonRows[k], 'data-ds-cart-addon-line-price');\n    var rowRendered = cartItemCents(addonRows[k], 'data-ds-cart-addon-line-quantity');\n    if (rowRendered === null) rowRendered = 1;\n    var rowQty = cartItemQuantity(addonRows[k], rowRendered);\n    var rowAmount = addonRows[k].querySelector('[data-ds-cart-addon-line-amount]');\n    var rowCents = cartItemCents(addonRows[k], 'data-ds-cart-addon-line-cents');\n    if (rowAmount && rowUnit !== null && rowCents !== null) {\n      rowAmount.textContent = '+ ' + cartItemMoney(root, rowCents + rowUnit * (rowQty - rowRendered));\n    }\n    var rowEach = addonRows[k].querySelector('[data-ds-cart-addon-line-each]');\n    if (rowEach) rowEach.hidden = !(rowQty > 1);\n  }\n  var amount = root.querySelector(CART_ITEM_TOTAL);\n  var rendered = cartItemCents(root, 'data-ds-cart-item-total-cents');\n  // Nothing to reason from: leave the add-ons total the caller rendered. A line\n  // with no add-ons has no such row and is done after the line total above.\n  if (!amount || unit === null || rendered === null) return;\n\n  var delta = unit * (mainQty - renderedQty);\n  var rows = root.querySelectorAll('[data-ds-cart-addon-line]');\n  for (var j = 0; j < rows.length; j += 1) {\n    var addonUnit = cartItemCents(rows[j], 'data-ds-cart-addon-line-price');\n    if (addonUnit === null) continue;\n    var addonRendered = cartItemCents(rows[j], 'data-ds-cart-addon-line-quantity');\n    if (addonRendered === null) addonRendered = 1;\n    delta += addonUnit * (cartItemQuantity(rows[j], addonRendered) - addonRendered);\n  }\n\n  amount.textContent = cartItemMoney(root, rendered + delta);\n}\n\nfunction cartItemTotalBusy(root, busy) {\n  if (!root) return;\n  var pending = Number(root.getAttribute('data-ds-cart-item-total-pending') || 0);\n  pending = Math.max(0, pending + (busy ? 1 : -1));\n  root.setAttribute('data-ds-cart-item-total-pending', String(pending));\n  busy = pending > 0;\n  var value = root.querySelector('[data-ds-cart-item-total-value]');\n  var loading = root.querySelector('[data-ds-cart-item-total-loading]');\n  if (value) value.hidden = busy;\n  if (loading) loading.hidden = !busy;\n}\n\nif (!document.__dsCartItemTotalBound) {\n  document.__dsCartItemTotalBound = true;\n\n  // The buttons dispatch input+change on the field, so one listener covers both.\n  document.addEventListener('input', function (event) {\n    var field = event.target;\n    if (!field || !field.closest || field.type !== 'number') return;\n    cartItemSync(field.closest(CART_ITEM));\n  });\n  document.addEventListener('change', function (event) {\n    var field = event.target;\n    if (!field || !field.closest || field.type !== 'number') return;\n    cartItemSync(field.closest(CART_ITEM));\n  });\n  document.addEventListener('ds:cart:updating', function (event) {\n    var detail = event.detail || {};\n    if (detail.root) cartItemTotalBusy(detail.root, !!detail.busy);\n  });\n}\n\n} catch (sfError) {\nif (typeof console !== 'undefined') console.error('[sf-core] behavior \"cart-item-total\" failed to initialise:', sfError);\n}\n})();\n\n/* cart-lines.js */\n(function () {\ntry {\n/* cart-lines \u2014 the cart page's quantity and remove requests.\n *\n * A cross-sell add-on rides in the cart as its own independent Shopify line,\n * grouped with its main only by the hidden _bundle_group properties \u2014 Shopify has\n * no parent/child link between them and cascades nothing. So a quantity change\n * touches only the line it was made on (an add-on's quantity is independent of its\n * main's), but a REMOVAL of a main takes its add-ons with it: they are nested\n * under it on the page and must not be left behind as orphan lines. That cascade\n * is one atomic /cart/update.js; a single line (a main with no add-ons, or an\n * add-on removed on its own) still goes through /cart/change.js. The response is\n * the new truth, so the line's rendered quantities and total are restamped from it.\n *\n * A removal is IMMEDIATE: the line leaves the page optimistically and the\n * request goes to Shopify straight away \u2014 there is no undo window. If the\n * request fails the line is put back with the reason, so the page never lies\n * about the cart. A confirmation UI (ds-confirm-modal) can still ask first.\n *\n * Emits, alongside buy-buttons.js:\n *   ds:cart:updated  detail = the full cart\n *   ds:cart:error    detail = { description } | { error }\n *   ds:toast         detail = the error copy, when a removal fails \u2014 which here\n *                    means the request itself failed (offline, or the cart\n *                    rejected it), never \\\"sold out\\\": removing is a removal, and\n *                    inventory cannot refuse one\n *   ds:cart:removed  detail = { key, title, image } \u2014 a plain \"removed\" notice\n * and asks first, so a confirmation UI can take over:\n *   ds:cart:remove-request  cancelable, detail = { key, root, addon }\n * and listens for:\n *   ds:cart:remove-confirm  detail = { key }, from the confirmation UI\n *\n * Optimistic: the field keeps what the buyer set while the request is in flight\n * and rolls back to the rendered quantity if it fails.\n *\n * Authoring rules: src/behaviors/README.md. No import/export.\n */\nvar CART_LINE = '[data-ds-cart-item]';\nvar ADDON_LINE = '[data-ds-cart-addon-line]';\n\nfunction cartLinesUrl(path) {\n  var root = window.Shopify && window.Shopify.routes && window.Shopify.routes.root;\n  return (root || '/') + path;\n}\n\nfunction cartLinesScope(el) {\n  var addon = el.closest(ADDON_LINE);\n  var root = el.closest(CART_LINE);\n  return {\n    root: root,\n    addon: addon,\n    el: addon || root,\n    key: (addon || root) && (addon || root).getAttribute(addon ? 'data-ds-cart-addon-line-key' : 'data-ds-cart-item-key'),\n    quantityAttr: addon ? 'data-ds-cart-addon-line-quantity' : 'data-ds-cart-item-quantity',\n  };\n}\n\nfunction cartLinesRendered(scope) {\n  var raw = scope.el && scope.el.getAttribute(scope.quantityAttr);\n  var value = Number(raw);\n  return Number.isFinite(value) ? value : 1;\n}\n\n// Requests can overlap \u2014 typing a second quantity before the first answers \u2014 so\n// the line counts them. Stamping each button with its previous state instead\n// loses that state on the second call and leaves the steppers disabled for good.\nfunction cartLinesBusy(scope, busy) {\n  if (!scope.root) return;\n  var inFlight = Number(scope.root.getAttribute('data-ds-cart-lines-pending') || 0);\n  inFlight = Math.max(0, inFlight + (busy ? 1 : -1));\n  scope.root.setAttribute('data-ds-cart-lines-pending', String(inFlight));\n  scope.root.setAttribute('aria-busy', inFlight ? 'true' : 'false');\n  // number-input.js owns which stepper is disabled at min/max, so let it decide\n  // again by re-running its own sync instead of restoring a remembered flag.\n  scope.root.querySelectorAll('[data-ds-number-input] input[type=\"number\"]').forEach(function (input) {\n    var field = input.closest('[data-ds-number-input]');\n    if (!field) return;\n    field.querySelectorAll('button[data-ds-number-input-decrease], button[data-ds-number-input-increase]')\n      .forEach(function (button) { button.disabled = !!inFlight; });\n    if (!inFlight) input.dispatchEvent(new Event('input', { bubbles: true }));\n  });\n}\n\n// The buyer's own value stands until the server answers; a failure puts the\n// rendered one back and lets cart-item-total.js re-read it.\nfunction cartLinesRestore(scope) {\n  var input = scope.el && scope.el.querySelector('input[type=\"number\"]');\n  if (!input) return;\n  input.value = String(cartLinesRendered(scope));\n  input.dispatchEvent(new Event('input', { bubbles: true }));\n}\n\n// Restamp EVERY rendered line from the response, not just the one that made the\n// request: `key` hashes variant + properties, not an id, so any line's key can\n// come back different from what is still stamped in the DOM (\"no valid id or\n// line parameter\" on every request after, until reload) \u2014 the response is the\n// only place a current key can be read from. Order is what ties a DOM row back\n// to its item: cart.items keeps its relative order for lines that still exist\n// (a removal only closes the gap it leaves), and the page renders main rows and\n// each one's add-on rows in that same order, so zipping DOM order against\n// cart.items order re-identifies every row without needing a stored id at all.\nfunction cartLinesApply(cart) {\n  if (!cart || !Array.isArray(cart.items)) return;\n  var addonsByGroup = {};\n  cart.items.forEach(function (item) {\n    var group = item.properties && item.properties._bundle_group;\n    if (group && item.properties._bundle_role === 'addon') {\n      (addonsByGroup[group] = addonsByGroup[group] || []).push(item);\n    }\n  });\n  var mains = cart.items.filter(function (item) {\n    return !item.properties || item.properties._bundle_role !== 'addon';\n  });\n  var mainIndex = 0;\n  document.querySelectorAll(CART_LINE).forEach(function (root) {\n    var item = mains[mainIndex];\n    mainIndex += 1;\n    if (!item) return;\n    root.setAttribute('data-ds-cart-item-key', item.key);\n    root.setAttribute('data-ds-cart-item-quantity', String(item.quantity));\n    var lineOnly = Number(item.final_line_price || item.line_price || 0);\n    root.setAttribute('data-ds-cart-item-line-cents', String(lineOnly));\n    var total = lineOnly;\n    var group = item.properties && item.properties._bundle_group;\n    var siblings = (group && addonsByGroup[group]) || [];\n    var addonIndex = 0;\n    root.querySelectorAll('[data-ds-cart-addon-line-key]').forEach(function (row) {\n      var child = siblings[addonIndex];\n      addonIndex += 1;\n      if (!child) return;\n      row.setAttribute('data-ds-cart-addon-line-key', child.key);\n      row.setAttribute('data-ds-cart-addon-line-quantity', String(child.quantity));\n      var childLine = Number(child.final_line_price || child.line_price || 0);\n      // The row's own total moves off this; left stale, the next sync snaps\n      // the price back to the first-paint amount.\n      row.setAttribute('data-ds-cart-addon-line-cents', String(childLine));\n      total += childLine;\n    });\n    root.setAttribute('data-ds-cart-item-total-cents', String(total));\n    var field = root.querySelector('input[type=\"number\"]');\n    if (field) field.dispatchEvent(new Event('input', { bubbles: true }));\n  });\n}\n\nfunction cartLinesShowEmpty() {\n  var form = document.querySelector('[data-ds-cart-form]');\n  var live = document.querySelector('[data-ds-cart-empty-live]');\n  var template = document.querySelector('[data-ds-cart-empty-template]');\n  if (!live || !template || !template.content) return false;\n  if (form) form.hidden = true;\n  // The alert band (price-update / expiry countdown) sits in the title group\n  // ABOVE the form, so hiding the form alone leaves it on an empty cart. It is a\n  // filled-cart notice, so it goes with the lines. Inline display beats the\n  // region's own lg:sf-flex class, which the [hidden] attribute would not.\n  var alerts = document.querySelector('[data-ds-cart-alerts-region]');\n  if (alerts) alerts.style.display = 'none';\n  // The cross-sell band is a filled-cart surface: the empty state keeps the page\n  // on its one action (Continue shopping), and a reload would not render it.\n  document.querySelectorAll('[data-ds-cart-recommendations]').forEach(function (band) {\n    band.hidden = true;\n  });\n  if (!live.firstElementChild) live.appendChild(template.content.cloneNode(true));\n  live.hidden = false;\n  return true;\n}\n\nfunction cartLinesHideEmpty() {\n  var form = document.querySelector('[data-ds-cart-form]');\n  var live = document.querySelector('[data-ds-cart-empty-live]');\n  if (form) form.hidden = false;\n  var alerts = document.querySelector('[data-ds-cart-alerts-region]');\n  if (alerts) alerts.style.display = '';\n  document.querySelectorAll('[data-ds-cart-recommendations]').forEach(function (band) {\n    band.hidden = false;\n  });\n  if (!live) return;\n  live.hidden = true;\n  live.textContent = '';\n}\n\n// The empty Component is already templated by the section, so the last line can\n// switch views in the same frame. The response confirms the cart but never needs\n// a page reload just to ask Liquid for markup we already have.\nfunction cartLinesReloadIfEmpty(cart) {\n  if (!cart || Number(cart.item_count) !== 0) return false;\n  cartLinesShowEmpty();\n  return true;\n}\n\n// Main products only: an add-on is not a piece of its own, so it must not swell\n// the heading count either (Cross-sell Bundle spec \u00A73.3). Mirrors the section's\n// server-rendered count and dsCartMainItemCount in cart-count.js.\nfunction cartLinesPieceCount(cart) {\n  if (cart && Array.isArray(cart.items)) {\n    var n = 0;\n    for (var i = 0; i < cart.items.length; i += 1) {\n      var properties = cart.items[i].properties;\n      if (!properties || properties._bundle_role !== 'addon') {\n        n += Number(cart.items[i].quantity) || 0;\n      }\n    }\n    return n;\n  }\n  return Number(cart && cart.item_count) || 0;\n}\n\n// The heading is \"Shopping Cart (4 items)\". Liquid stamps the bare heading and\n// the two unit words; the count is the only part that moves. A title that still\n// says \"(14 items)\" must not be treated as the base, or the next update appends\n// a second \"(5)\" and the old total never leaves.\nfunction cartLinesTitleBase(title) {\n  var base = title.getAttribute('data-ds-cart-title-base');\n  if (base) return base;\n  base = title.textContent.replace(/\\s*\\(\\d+(?:\\s+[^\\d)]+)?\\)\\s*$/, '').trim();\n  title.setAttribute('data-ds-cart-title-base', base);\n  return base;\n}\n\nfunction cartLinesTitleWrite(title, count) {\n  var base = cartLinesTitleBase(title);\n  var n = Number(count) || 0;\n  var countEl = title.querySelector('[data-ds-cart-title-count]');\n  title.textContent = base;\n  if (n <= 0) return;\n  if (!countEl) {\n    countEl = document.createElement('span');\n    countEl.setAttribute('data-ds-cart-title-count', '');\n    countEl.className = 'sf-font-display sf-text-h5 sf-font-regular sf-text-tertiary';\n  }\n  var one = title.getAttribute('data-ds-cart-title-one');\n  var other = title.getAttribute('data-ds-cart-title-other');\n  var label = one || other ? ' (' + n + ' ' + (n === 1 ? one || other : other || one) + ')' : ' (' + n + ')';\n  countEl.textContent = label;\n  title.appendChild(countEl);\n}\n\nfunction cartLinesTitle(cart) {\n  var title = document.querySelector('[data-ds-cart-title]');\n  if (!title || !cart || !Array.isArray(cart.items)) return;\n  cartLinesTitleWrite(title, cartLinesPieceCount(cart));\n}\n\n// Removing a main changes the visible piece count with certainty; it does not\n// need Shopify to recalculate prices first. Keep the heading in step with the\n// detached row while totals wait for the authoritative response.\nfunction cartLinesTitleDelta(delta) {\n  var title = document.querySelector('[data-ds-cart-title]');\n  if (!title || !delta) return;\n  var match = title.textContent.match(/\\((\\d+)(?:\\s+[^\\d)]+)?\\)\\s*$/);\n  var count = Math.max(0, (match ? Number(match[1]) : 0) + delta);\n  cartLinesTitleWrite(title, count);\n}\n\nfunction cartLinesUpdating(entry, busy) {\n  document.dispatchEvent(new CustomEvent('ds:cart:updating', {\n    detail: { busy: busy, root: entry && entry.parentRoot },\n  }));\n}\n\nfunction cartLinesFinishUpdating(entry) {\n  if (!entry || !entry.loading) return;\n  entry.loading = false;\n  cartLinesUpdating(entry, false);\n}\n\nfunction cartLinesPublish(cart) {\n  cartLinesApply(cart);\n  window.tfxCart = cart;\n  document.dispatchEvent(new CustomEvent('ds:cart:updated', { detail: cart }));\n  cartLinesTitle(cart);\n  cartLinesReloadIfEmpty(cart);\n}\n\n// Concurrent mutations may invalidate rendered line keys. A bundle 404 only\n// proves that one old key disappeared, not that its independent add-ons did.\nfunction cartLinesCleanupBundle(entry) {\n  if (!entry.group) return Promise.resolve(false);\n  return fetch(cartLinesUrl('cart.js'), {\n    headers: { Accept: 'application/json', 'X-Requested-With': 'XMLHttpRequest' },\n  }).then(function (res) {\n    if (!res.ok) throw new Error('cart read failed');\n    return res.json();\n  }).then(function (cart) {\n    var updates = {};\n    (cart.items || []).forEach(function (item) {\n      if (item.properties && item.properties._bundle_group === entry.group) updates[item.key] = 0;\n    });\n    if (!Object.keys(updates).length) {\n      cartLinesPublish(cart);\n      return true;\n    }\n    return fetch(cartLinesUrl('cart/update.js'), {\n      method: 'POST',\n      headers: {\n        Accept: 'application/json',\n        'Content-Type': 'application/json',\n        'X-Requested-With': 'XMLHttpRequest',\n      },\n      body: JSON.stringify({ updates: updates }),\n    }).then(function (res) {\n      return res.json().then(function (data) {\n        if (!res.ok) return false;\n        cartLinesPublish(data);\n        return true;\n      });\n    });\n  }).catch(function () { return false; });\n}\n\nfunction cartLinesChange(scope, quantity, onDone) {\n  if (!scope.key) return;\n  cartLinesBusy(scope, true);\n  fetch(cartLinesUrl('cart/change.js'), {\n    method: 'POST',\n    headers: {\n      Accept: 'application/json',\n      'Content-Type': 'application/json',\n      'X-Requested-With': 'XMLHttpRequest',\n    },\n    body: JSON.stringify({ id: scope.key, quantity: quantity }),\n  })\n    .then(function (res) {\n      return res.json().then(function (data) {\n        return { ok: res.ok, data: data };\n      });\n    })\n    .then(function (result) {\n      cartLinesBusy(scope, false);\n      if (!result.ok) {\n        cartLinesRestore(scope);\n        document.dispatchEvent(new CustomEvent('ds:cart:error', { detail: result.data }));\n        return;\n      }\n      cartLinesApply(result.data);\n      window.tfxCart = result.data;\n      document.dispatchEvent(new CustomEvent('ds:cart:updated', { detail: result.data }));\n      cartLinesTitle(result.data);\n      cartLinesReloadIfEmpty(result.data);\n      if (onDone) onDone(result.data);\n    })\n    .catch(function (error) {\n      cartLinesBusy(scope, false);\n      cartLinesRestore(scope);\n      document.dispatchEvent(new CustomEvent('ds:cart:error', { detail: { error: String(error) } }));\n    });\n}\n\nvar cartLinesInFlight = [];\nvar cartLinesScreenPending = 0;\n\nfunction cartLinesScreenLoading(busy) {\n  cartLinesScreenPending = Math.max(0, cartLinesScreenPending + (busy ? 1 : -1));\n  var root = document.querySelector('[data-ds-cart]');\n  var overlay = document.querySelector('[data-ds-cart-screen-loading]');\n  if (root) root.setAttribute('aria-busy', cartLinesScreenPending ? 'true' : 'false');\n  if (overlay) overlay.hidden = !cartLinesScreenPending;\n}\n\nfunction cartLinesAnnounceRemoved(entry) {\n  if (!entry) return;\n  entry.announced = true;\n  document.dispatchEvent(new CustomEvent('ds:cart:removed', {\n    detail: { key: entry.key, title: entry.title, image: entry.image },\n  }));\n}\n\nfunction cartLinesDetach(entry) {\n  entry.parent = entry.node.parentNode;\n  entry.before = entry.node.nextElementSibling;\n  if (entry.parent) entry.parent.removeChild(entry.node);\n  if (entry.parentRoot) cartLinesSyncAddons(entry.parentRoot);\n}\n\n// SF0190 \u2014 a main line's \"Add-ons Items\" block and its \"Item total (incl.\n// add-ons)\" row exist only while it has an add-on: ds-cart renders neither for\n// a line without one. Removing the last add-on row by hand must drop both, or\n// the card shows an empty heading a reload would not; restoring a failed\n// removal brings them back.\nfunction cartLinesSyncAddons(root) {\n  var block = root.querySelector('[data-ds-cart-item-addons]');\n  if (!block) return;\n  var empty = !block.querySelector(ADDON_LINE);\n  block.hidden = empty;\n  var total = root.querySelector('[data-ds-cart-item-total]');\n  if (total) total.hidden = empty;\n}\n\n// A removal that failed to commit: the line the buyer watched leave comes back,\n// with its optimistic count/title decrement undone.\nfunction cartLinesRestoreNode(entry) {\n  if (!entry.parent) return;\n  if (entry.before && entry.before.parentNode === entry.parent) {\n    entry.parent.insertBefore(entry.node, entry.before);\n  } else {\n    entry.parent.appendChild(entry.node);\n  }\n  if (entry.parentRoot) cartLinesSyncAddons(entry.parentRoot);\n  var field = entry.node.querySelector('input[type=\"number\"]');\n  if (field) field.dispatchEvent(new Event('input', { bubbles: true }));\n  if (entry.mainQuantity) {\n    cartLinesTitleDelta(entry.mainQuantity);\n    document.dispatchEvent(new CustomEvent('ds:cart:optimistic-count', {\n      detail: { delta: entry.mainQuantity },\n    }));\n  }\n  cartLinesFinishUpdating(entry);\n  if (document.querySelector(CART_LINE)) cartLinesHideEmpty();\n}\n\n// Send the removal the buyer already saw happen. A failure here is the one case\n// where the line comes back \u2014 with the reason.\nfunction cartLinesCommit(entry) {\n  // One key removes through /cart/change.js; a main dragging its add-ons out with\n  // it removes them all in one atomic /cart/update.js, so the cart never shows a\n  // half-gone bundle.\n  var keys = entry.keys && entry.keys.length ? entry.keys : [entry.key];\n  var multi = keys.length > 1;\n  var payload;\n  if (multi) {\n    var updates = {};\n    keys.forEach(function (k) { updates[k] = 0; });\n    payload = { updates: updates };\n  } else {\n    payload = { id: keys[0], quantity: 0 };\n  }\n  var request = {\n    method: 'POST',\n    headers: {\n      Accept: 'application/json',\n      'Content-Type': 'application/json',\n      'X-Requested-With': 'XMLHttpRequest',\n    },\n    body: JSON.stringify(payload),\n  };\n\n  // The add-on row is already gone; its parent card is what's still on\n  // screen while this request is in flight, so it carries the busy state.\n  if (entry.parentRoot) cartLinesBusy({ root: entry.parentRoot }, true);\n  cartLinesScreenLoading(true);\n  if (!entry.loading) {\n    entry.loading = true;\n    cartLinesUpdating(entry, true);\n  }\n\n  var operation = fetch(cartLinesUrl(multi ? 'cart/update.js' : 'cart/change.js'), request)\n    .then(function (res) {\n      return res.json().then(\n        function (data) { return { ok: res.ok, status: res.status, data: data }; },\n        function () { return { ok: res.ok, status: res.status, data: null }; }\n      );\n    })\n    .then(function (result) {\n      if (entry.parentRoot) cartLinesBusy({ root: entry.parentRoot }, false);\n      cartLinesScreenLoading(false);\n      if (result.ok) {\n        cartLinesPublish(result.data);\n        cartLinesFinishUpdating(entry);\n        if (!entry.announced) cartLinesAnnounceRemoved(entry);\n        return true;\n      }\n      // 404 means the cart no longer has that line \u2014 another tab removed it, or\n      // the cart was rebuilt. The buyer wanted it gone and it is gone, so this\n      // is the outcome, not a failure: leave it removed and say nothing.\n      if (result.status === 404 && multi) {\n        return cartLinesCleanupBundle(entry).then(function (clean) {\n          if (clean) {\n            cartLinesFinishUpdating(entry);\n            return true;\n          }\n          cartLinesUncommit(entry, '');\n          return false;\n        });\n      }\n      if (result.status === 404) {\n        cartLinesFinishUpdating(entry);\n        return true;\n      }\n      cartLinesUncommit(entry, (result.data && result.data.description) || '');\n      document.dispatchEvent(new CustomEvent('ds:cart:error', { detail: result.data }));\n      return false;\n    })\n    .catch(function (error) {\n      if (entry.parentRoot) cartLinesBusy({ root: entry.parentRoot }, false);\n      cartLinesScreenLoading(false);\n      // Offline, blocked, or a non-JSON answer: the line is still in the cart,\n      // so put it back rather than leave the page lying about it.\n      cartLinesUncommit(entry, '');\n      document.dispatchEvent(new CustomEvent('ds:cart:error', { detail: { error: String(error) } }));\n      return false;\n    });\n  cartLinesInFlight.push(operation);\n  operation.then(function () {\n    var index = cartLinesInFlight.indexOf(operation);\n    if (index > -1) cartLinesInFlight.splice(index, 1);\n  });\n  return operation;\n}\n\n// The commit failed, so the line the buyer watched leave comes back \u2014 say why,\n// or it reads as the page breaking.\nfunction cartLinesUncommit(entry, description) {\n  cartLinesRestoreNode(entry);\n  var region = document.querySelector('[data-ds-toast-region]');\n  if (!region) return;\n  document.dispatchEvent(new CustomEvent('ds:toast', {\n    detail: {\n      highlight: entry.title || '',\n      message: description || region.getAttribute('data-ds-toast-remove-failed') || 'could not be removed',\n    },\n  }));\n}\n\nfunction cartLinesLabel(node) {\n  var title = node.querySelector('[data-ds-cart-item-title], [data-ds-cart-addon-line-title]');\n  var image = node.querySelector('img[src]');\n  return {\n    title: title ? title.textContent.trim() : '',\n    image: image ? image.getAttribute('src') : '',\n  };\n}\n\nfunction cartLinesDrop(scope) {\n  if (!scope.el || !scope.key) return;\n  var label = cartLinesLabel(scope.el);\n  // Removing a whole line takes its add-ons with it \u2014 they hang off it in the DOM\n  // and, being property-grouped rather than Shopify parent/child, Shopify will not\n  // drop them on its own. Removing an add-on on its own touches only that add-on.\n  var keys = [scope.key];\n  if (scope.root && !scope.addon) {\n    scope.root.querySelectorAll('[data-ds-cart-addon-line-key]').forEach(function (row) {\n      var childKey = row.getAttribute('data-ds-cart-addon-line-key');\n      if (childKey && keys.indexOf(childKey) === -1) keys.push(childKey);\n    });\n  }\n  // An add-on removed alone leaves its main's \"Item total\" row on the page with\n  // a now-stale cents value \u2014 nothing else restamps it once the commit lands.\n  var parentRoot = scope.addon ? scope.root : null;\n  var entry = {\n    key: scope.key, keys: keys, node: scope.el, parent: null, before: null,\n    title: label.title, image: label.image, parentRoot: parentRoot,\n    mainQuantity: scope.addon ? 0 : cartLinesRendered(scope), loading: false,\n    group: scope.root && scope.root.getAttribute('data-ds-cart-item-bundle-group'),\n    announced: false,\n  };\n  cartLinesDetach(entry);\n  if (!document.querySelector(CART_LINE)) cartLinesShowEmpty();\n  entry.loading = true;\n  cartLinesUpdating(entry, true);\n  if (entry.mainQuantity) {\n    cartLinesTitleDelta(-entry.mainQuantity);\n    document.dispatchEvent(new CustomEvent('ds:cart:optimistic-count', {\n      detail: { delta: -entry.mainQuantity },\n    }));\n  }\n  // Immediate: the line is already gone from the page, so send it now.\n  cartLinesCommit(entry);\n}\n\nfunction cartLinesRemove(scope) {\n  var request = new CustomEvent('ds:cart:remove-request', {\n    cancelable: true,\n    detail: { key: scope.key, root: scope.root, addon: scope.addon },\n  });\n  // A confirmation UI (ds-confirm-modal) cancels this and calls back once the\n  // buyer has said yes; with nothing listening the removal goes straight through.\n  if (!document.dispatchEvent(request)) return;\n  cartLinesDrop(scope);\n}\n\nif (!document.__dsCartLinesBound) {\n  document.__dsCartLinesBound = true;\n\n  // change, not input: one request when the buyer settles, not per keystroke.\n  document.addEventListener('change', function (event) {\n    var field = event.target;\n    if (!field || !field.closest || field.type !== 'number') return;\n    if (!field.closest(CART_LINE)) return;\n    var scope = cartLinesScope(field);\n    if (cartLinesInFlight.length) return cartLinesRestore(scope);\n    var quantity = Number(field.value);\n    if (!Number.isFinite(quantity) || quantity < 0) return cartLinesRestore(scope);\n    if (quantity === cartLinesRendered(scope)) return;\n    cartLinesChange(scope, quantity);\n  });\n\n  document.addEventListener('click', function (event) {\n    var trigger = event.target && event.target.closest\n      ? event.target.closest('[data-ds-cart-item-remove], [data-ds-cart-addon-line-remove]')\n      : null;\n    if (!trigger || !trigger.closest(CART_LINE)) return;\n    event.preventDefault();\n    if (cartLinesInFlight.length) return;\n    cartLinesRemove(cartLinesScope(trigger));\n  });\n\n  // The confirmation UI re-requests the removal it cancelled.\n  document.addEventListener('ds:cart:remove-confirm', function (event) {\n    var key = event.detail && event.detail.key;\n    if (!key) return;\n    if (cartLinesInFlight.length) return;\n    var el = document.querySelector(\n      '[data-ds-cart-addon-line-key=\"' + key + '\"], [data-ds-cart-item-key=\"' + key + '\"]',\n    );\n    if (!el) return;\n    var scope = cartLinesScope(el.querySelector('input[type=\"number\"]') || el);\n    cartLinesDrop(scope);\n  });\n}\n\n} catch (sfError) {\nif (typeof console !== 'undefined') console.error('[sf-core] behavior \"cart-lines\" failed to initialise:', sfError);\n}\n})();\n\n/* cart-photo-lightbox.js */\n(function () {\ntry {\n/* cart-photo-lightbox \u2014 opens a cart line's personalization photos in the\n * gallery viewer (ds-cart-photo-lightbox). Any element with [data-ds-cart-photo]\n * carries data-ds-cart-photo-target=\"<overlay id>\" and data-ds-cart-photo-index;\n * clicking it opens that line's overlay ON the clicked photo.\n *\n * The viewer itself \u2014 track, chevrons, counter, rail \u2014 is a plain ds-gallery, so\n * behaviors/gallery.js drives it; this behavior only opens/closes the dialog,\n * jumps it to the clicked photo (`ds:gallery:set`, instant), and retitles the\n * header to the active photo's file name as the shopper steps through. Titles\n * come off the overlay's data-ds-cart-photo-lightbox-titles JSON, so retitling\n * never has to read the gallery's slide markup.\n *\n * It owns only the dialog: open/close, portal to <body> (so a transformed/sticky\n * ancestor never traps its z-index \u2014 same reason as media-lightbox.js), scroll\n * lock, focus trap + restore. Progressive enhancement: without this script the\n * card thumbnails are plain images and nothing opens.\n *\n * Authoring rules: src/behaviors/README.md. No import/export.\n */\n\n(function () {\n  if (document.__dsCartPhotoLightboxBound) return;\n  document.__dsCartPhotoLightboxBound = true;\n\n  var lastTrigger = null;\n  var titleObserver = null;\n\n  // Portal the overlay to <body>: a cart line can sit in a stacking context\n  // (transforms during row updates), which would trap a fixed overlay's\n  // z-index. Drops any stale twin a section re-render left with the same id.\n  function hoist(overlay) {\n    if (!overlay) return overlay;\n    if (overlay.id) {\n      document.querySelectorAll('[data-ds-cart-photo-lightbox]').forEach(function (el) {\n        if (el !== overlay && el.id === overlay.id && el.parentNode) {\n          el.parentNode.removeChild(el);\n        }\n      });\n    }\n    if (overlay.parentNode !== document.body) document.body.appendChild(overlay);\n    return overlay;\n  }\n\n  function hoistAll() {\n    document.querySelectorAll('[data-ds-cart-photo-lightbox]').forEach(hoist);\n  }\n\n  function isOpen(overlay) {\n    return !!overlay && overlay.getAttribute('aria-hidden') === 'false';\n  }\n\n  function openOverlayEl() {\n    var list = document.querySelectorAll('[data-ds-cart-photo-lightbox]');\n    for (var i = 0; i < list.length; i++) {\n      if (isOpen(list[i])) return list[i];\n    }\n    return null;\n  }\n\n  function titlesFor(overlay) {\n    var raw = overlay.getAttribute('data-ds-cart-photo-lightbox-titles');\n    if (!raw) return [];\n    try {\n      var parsed = JSON.parse(raw);\n      return Array.isArray(parsed) ? parsed : [];\n    } catch (e) {\n      return [];\n    }\n  }\n\n  function setTitle(overlay, index) {\n    var titleEl = overlay.querySelector('[data-ds-cart-photo-lightbox-title]');\n    if (!titleEl) return;\n    var titles = titlesFor(overlay);\n    var name = titles[index];\n    if (name) titleEl.textContent = name;\n  }\n\n  // Retitle the header whenever the gallery moves to another slide, by watching\n  // its active-index attribute \u2014 no dependency on gallery.js emitting an event.\n  function watchTitle(overlay, gallery) {\n    if (titleObserver) titleObserver.disconnect();\n    if (!gallery || typeof MutationObserver === 'undefined') return;\n    titleObserver = new MutationObserver(function () {\n      setTitle(overlay, Number(gallery.dataset.dsGalleryActive || 0));\n    });\n    titleObserver.observe(gallery, {\n      attributes: true,\n      attributeFilter: ['data-ds-gallery-active'],\n    });\n  }\n\n  function open(trigger) {\n    var id = trigger.getAttribute('data-ds-cart-photo-target');\n    var overlay = id ? hoist(document.getElementById(id)) : null;\n    if (!overlay) return;\n\n    var index = Number(trigger.getAttribute('data-ds-cart-photo-index') || 0);\n    lastTrigger = trigger;\n\n    overlay.setAttribute('aria-hidden', 'false');\n    document.documentElement.style.overflow = 'hidden';\n\n    // Jump the gallery straight to the clicked photo \u2014 instant, so the viewer\n    // opens already on that slide instead of sliding to it.\n    var gallery = overlay.querySelector('[data-ds-gallery]');\n    if (gallery) {\n      gallery.dispatchEvent(\n        new CustomEvent('ds:gallery:set', {\n          bubbles: true,\n          detail: { index: index, instant: true },\n        }),\n      );\n      watchTitle(overlay, gallery);\n    }\n    setTitle(overlay, index);\n\n    var close = overlay.querySelector('[data-ds-cart-photo-lightbox-close]');\n    if (close) window.requestAnimationFrame(function () { close.focus({ focusVisible: false }); });\n  }\n\n  function close(overlay) {\n    if (!overlay) return;\n    if (titleObserver) { titleObserver.disconnect(); titleObserver = null; }\n    overlay.setAttribute('aria-hidden', 'true');\n    document.documentElement.style.overflow = '';\n    if (lastTrigger && typeof lastTrigger.focus === 'function') lastTrigger.focus();\n    lastTrigger = null;\n  }\n\n  function trapTab(e, overlay) {\n    var dialog = overlay.querySelector('[data-ds-cart-photo-lightbox-dialog]') || overlay;\n    var focusable = Array.prototype.filter.call(\n      dialog.querySelectorAll(\n        'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])',\n      ),\n      function (el) {\n        return el.offsetParent !== null || el === document.activeElement;\n      },\n    );\n    if (!focusable.length) return;\n    var first = focusable[0];\n    var last = focusable[focusable.length - 1];\n    if (e.shiftKey && document.activeElement === first) {\n      e.preventDefault();\n      last.focus();\n    } else if (!e.shiftKey && document.activeElement === last) {\n      e.preventDefault();\n      first.focus();\n    } else if (!dialog.contains(document.activeElement)) {\n      e.preventDefault();\n      first.focus();\n    }\n  }\n\n  document.addEventListener('click', function (e) {\n    if (!e.target.closest) return;\n\n    var closeBtn = e.target.closest('[data-ds-cart-photo-lightbox-close]');\n    if (closeBtn) {\n      e.preventDefault();\n      close(closeBtn.closest('[data-ds-cart-photo-lightbox]'));\n      return;\n    }\n\n    var trigger = e.target.closest('[data-ds-cart-photo]');\n    if (trigger) {\n      e.preventDefault();\n      open(trigger);\n      return;\n    }\n\n    // Click the dimmed backdrop (the overlay itself, not its card) to close.\n    var overlay = e.target.closest('[data-ds-cart-photo-lightbox]');\n    if (overlay && isOpen(overlay) && e.target === overlay) close(overlay);\n  });\n\n  document.addEventListener('keydown', function (e) {\n    var overlay = openOverlayEl();\n    if (overlay) {\n      if (e.key === 'Escape') { close(overlay); return; }\n      if (e.key === 'Tab') { trapTab(e, overlay); return; }\n      return;\n    }\n    if (e.key === 'Enter' || e.key === ' ' || e.key === 'Spacebar') {\n      var trigger = e.target.closest && e.target.closest('[data-ds-cart-photo]');\n      if (trigger) {\n        e.preventDefault();\n        open(trigger);\n      }\n    }\n  });\n\n  // Portal up front so the first open never flashes trapped under a sibling row.\n  if (document.readyState === 'loading') {\n    document.addEventListener('DOMContentLoaded', hoistAll, { once: true });\n  } else {\n    hoistAll();\n  }\n})();\n\n} catch (sfError) {\nif (typeof console !== 'undefined') console.error('[sf-core] behavior \"cart-photo-lightbox\" failed to initialise:', sfError);\n}\n})();\n\n/* cart-popover.js */\n(function () {\ntry {\n/* cart-popover \u2014 keeps the header mini-cart honest after an AJAX cart change.\n * Pure restamp from the ds:cart:updated payload (the full cart, already fetched\n * by buy-buttons.js / cart-lines.js): no network of its own, the same contract\n * cart-summary.js has for the Order Summary.\n *\n * Open/close is NOT here. Hovering the anchor is what opens the panel and CSS\n * does that natively \u2014 src/cart-popover.css. This file only rewrites contents,\n * so without it the popover still shows what Liquid rendered on page load.\n *\n * Lines are rebuilt from the two <template>s the snippet ships, so the restamped\n * markup is the same markup Liquid renders. An add-on is nested under the product\n * it rides with, never listed on its own (Cross-sell Bundle spec \u00A73.3).\n *\n * Authoring rules: src/behaviors/README.md. No import/export.\n */\n\nvar CART_POPOVER = '[data-ds-cart-popover]';\n\nfunction cartPopoverMoney(root, cents) {\n  var format = (root.getAttribute('data-ds-cart-popover-money') || '${{amount}}').replace(/<[^>]*>/g, '');\n  var amount = (Number(cents) || 0) / 100;\n  return format.replace(/\\{\\{\\s*amount[a-z_]*\\s*\\}\\}/, amount.toFixed(2));\n}\n\n// Same test as cart-count.js: an add-on is a child line, not a piece of its own.\nfunction cartPopoverIsAddon(item) {\n  if (!item) return false;\n  var properties = item.properties;\n  return Boolean(item.parent_relationship || (properties && properties._bundle_role === 'addon'));\n}\n\nfunction cartPopoverImage(item) {\n  var src = (item && item.properties && item.properties['_customily-preview']) || '';\n  if (!src) src = (item && (item.image || item.featured_image)) || '';\n  if (src && src.url) src = src.url;\n  return src || '';\n}\n\nfunction cartPopoverSet(root, selector, text) {\n  var el = root.querySelector(selector);\n  if (el) el.textContent = text;\n}\n\nfunction cartPopoverToggle(el, on) {\n  if (!el) return;\n  el.hidden = !on;\n}\n\nfunction cartPopoverFillImage(node, selector, src, alt) {\n  var image = node.querySelector(selector);\n  if (!image) return;\n  if (src) {\n    image.src = src;\n    image.alt = alt || '';\n    image.removeAttribute('hidden');\n  } else {\n    image.setAttribute('hidden', '');\n  }\n}\n\nfunction cartPopoverFillText(node, selector, text, hideWhenBlank) {\n  var el = node.querySelector(selector);\n  if (!el) return;\n  el.textContent = text || '';\n  if (!hideWhenBlank) return;\n  if (text) el.removeAttribute('hidden');\n  else el.setAttribute('hidden', '');\n}\n\n// \"Default Title\" is what Shopify sends for a product with no real options.\n// Colour values may be \"Name|swatch-file\"; only the name is for shoppers.\nfunction cartPopoverVariant(item) {\n  var title = item.variant_title;\n  if (!title || title === 'Default Title') return '';\n  return String(title).split(' / ').map(function (part) {\n    return part.split('|')[0].trim();\n  }).join(' / ');\n}\n\nfunction cartPopoverAddonNode(root, addon) {\n  var template = root.querySelector('[data-ds-cart-popover-addon-template]');\n  if (!template || !template.content.firstElementChild) return null;\n  var node = template.content.firstElementChild.cloneNode(true);\n  var title = addon.product_title || addon.title || '';\n  cartPopoverFillImage(node, '[data-ds-cart-addon-image]', cartPopoverImage(addon), '');\n  cartPopoverFillText(node, '[data-ds-cart-addon-title]', title, false);\n  var variant = cartPopoverVariant(addon);\n  cartPopoverFillText(node, '[data-ds-cart-addon-variant]', variant, true);\n  cartPopoverFillText(node, '[data-ds-cart-addon-price]', cartPopoverMoney(root, addon.final_line_price), false);\n  return node;\n}\n\nfunction cartPopoverLineNode(root, item, addons) {\n  var template = root.querySelector('[data-ds-cart-popover-line-template]');\n  if (!template || !template.content.firstElementChild) return null;\n  var node = template.content.firstElementChild.cloneNode(true);\n  var title = item.product_title || item.title || '';\n\n  cartPopoverFillImage(node, '[data-ds-cart-popover-line-image]', cartPopoverImage(item), title);\n  var titleEl = node.querySelector('[data-ds-cart-popover-line-title]');\n  if (titleEl) {\n    titleEl.textContent = title;\n    if (item.url && titleEl.tagName === 'A') titleEl.setAttribute('href', item.url);\n  }\n  var variant = cartPopoverVariant(item);\n  cartPopoverFillText(node, '[data-ds-cart-popover-line-variant]', variant, true);\n  cartPopoverFillText(node, '[data-ds-cart-popover-line-quantity]', String(Number(item.quantity) || 1), false);\n  cartPopoverFillText(node, '[data-ds-cart-popover-line-price]', cartPopoverMoney(root, item.final_price), false);\n  cartPopoverFillText(node, '[data-ds-cart-popover-line-total]', cartPopoverMoney(root, item.final_line_price), false);\n\n  var list = node.querySelector('[data-ds-cart-popover-line-addons]');\n  if (list) {\n    list.textContent = '';\n    addons.forEach(function (addon) {\n      var row = cartPopoverAddonNode(root, addon);\n      if (row) list.appendChild(row);\n    });\n    cartPopoverToggle(list, addons.length > 0);\n  }\n  return node;\n}\n\n// The add-ons of one main line: they share its _bundle_group, which is the only\n// grouping this cart uses (the Cart AJAX API cannot set a parent_id).\nfunction cartPopoverAddonsOf(items, item) {\n  var group = item.properties && item.properties._bundle_group;\n  if (!group) return [];\n  return items.filter(function (candidate) {\n    return (\n      cartPopoverIsAddon(candidate) &&\n      candidate.properties &&\n      candidate.properties._bundle_group === group\n    );\n  });\n}\n\nfunction cartPopoverApply(root, cart) {\n  var items = Array.isArray(cart.items) ? cart.items : [];\n  var mains = items.filter(function (item) {\n    return !cartPopoverIsAddon(item);\n  });\n\n  var list = root.querySelector('[data-ds-cart-popover-lines]');\n  if (list) {\n    list.textContent = '';\n    mains.forEach(function (item) {\n      var node = cartPopoverLineNode(root, item, cartPopoverAddonsOf(items, item));\n      if (node) list.appendChild(node);\n    });\n    cartPopoverToggle(list, mains.length > 0);\n  }\n\n  var count = mains.reduce(function (total, item) {\n    return total + (Number(item.quantity) || 0);\n  }, 0);\n  cartPopoverSet(root, '[data-ds-cart-popover-count]', '(' + count + ')');\n\n  cartPopoverToggle(root.querySelector('[data-ds-cart-popover-empty]'), mains.length === 0);\n  cartPopoverToggle(root.querySelector('[data-ds-cart-popover-footer]'), mains.length > 0);\n\n  cartPopoverSet(root, '[data-ds-cart-popover-subtotal]', cartPopoverMoney(root, cart.items_subtotal_price));\n  cartPopoverSet(root, '[data-ds-cart-popover-total]', cartPopoverMoney(root, cart.total_price));\n\n  var discountCents = Number(cart.total_discount || 0);\n  cartPopoverToggle(root.querySelector('[data-ds-cart-popover-discount-row]'), discountCents > 0);\n  if (discountCents > 0) {\n    cartPopoverSet(root, '[data-ds-cart-popover-discount]', '-' + cartPopoverMoney(root, discountCents));\n  }\n}\n\nfunction cartPopoverBusy(root, busy) {\n  if (!root) return;\n  var pending = Number(root.getAttribute('data-ds-cart-popover-pending') || 0);\n  pending = Math.max(0, pending + (busy ? 1 : -1));\n  root.setAttribute('data-ds-cart-popover-pending', String(pending));\n  busy = pending > 0;\n  root.setAttribute('aria-busy', busy ? 'true' : 'false');\n  root.querySelectorAll('[data-ds-cart-popover-subtotal], [data-ds-cart-popover-total]').forEach(function (el) {\n    el.hidden = busy;\n  });\n  root.querySelectorAll('[data-ds-cart-popover-loading]').forEach(function (el) {\n    el.hidden = !busy;\n  });\n  var pendingEl = root.querySelector('[data-ds-cart-popover-pending]');\n  if (pendingEl) pendingEl.hidden = !busy;\n  if (busy) {\n    ['[data-ds-cart-popover-empty]', '[data-ds-cart-popover-lines]', '[data-ds-cart-popover-footer]']\n      .forEach(function (selector) {\n        var el = root.querySelector(selector);\n        if (el) el.hidden = true;\n      });\n  }\n}\n\nif (!document.__dsCartPopoverBound) {\n  document.__dsCartPopoverBound = true;\n\n  document.addEventListener('ds:cart:updated', function (event) {\n    var cart = event.detail;\n    if (!cart || !Array.isArray(cart.items)) return;\n    document.querySelectorAll(CART_POPOVER).forEach(function (root) {\n      cartPopoverApply(root, cart);\n    });\n  });\n\n  document.addEventListener('ds:cart:updating', function (event) {\n    document.querySelectorAll(CART_POPOVER).forEach(function (root) {\n      cartPopoverBusy(root, !!(event.detail && event.detail.busy));\n    });\n  });\n}\n\n} catch (sfError) {\nif (typeof console !== 'undefined') console.error('[sf-core] behavior \"cart-popover\" failed to initialise:', sfError);\n}\n})();\n\n/* cart-summary.js */\n(function () {\ntry {\n/* cart-summary \u2014 keeps the Order Summary honest after a quantity change. Pure\n * restamp from the /cart/change.js response cart-lines.js already fetched: no\n * network of its own.\n *\n * It moves Subtotal, Discount and Estimated Total (plus the mobile sticky bar\n * total/saving) to the numbers the server\n * returned, toggling the Discount and \"You're saving\" rows when a saving appears\n * or clears. Shipping and Tax stay estimate copy \u2014 Shopify only knows them at\n * checkout \u2014 so they are never rewritten. A summary without the *-cents anchors\n * (a bare render, Storybook) is left exactly as drawn.\n *\n * Listens for ds:cart:updated (detail = the full cart). Binds data-ds-* only.\n * Authoring rules: src/behaviors/README.md. No import/export.\n */\nvar CART_SUMMARY = '[data-ds-cart-summary]';\n\nfunction cartSummaryMoney(root, cents) {\n  var format = (root.getAttribute('data-ds-cart-summary-money') || '${{amount}}').replace(/<[^>]*>/g, '');\n  var amount = (Number(cents) || 0) / 100;\n  return format.replace(/\\{\\{\\s*amount[a-z_]*\\s*\\}\\}/, amount.toFixed(2));\n}\n\nfunction cartSummarySet(root, selector, text) {\n  var el = root.querySelector(selector);\n  if (el) el.textContent = text;\n}\n\nfunction cartSummaryCount(root, count) {\n  cartSummarySet(root, '[data-ds-cart-summary-count-value]', String(count));\n  var word = root.querySelector('[data-ds-cart-summary-items-word]');\n  if (word) {\n    word.textContent = count === 1 ? word.getAttribute('data-one') : word.getAttribute('data-other');\n  }\n}\n\nfunction cartSummaryToggle(el, on) {\n  if (!el) return;\n  el.hidden = !on;\n  el.style.display = on ? '' : 'none';\n}\n\nfunction cartSummaryApply(root, cart) {\n  if (!root || !cart) return;\n\n  if (Array.isArray(cart.items)) {\n    var count = cart.items.reduce(function (total, item) {\n      var properties = item && item.properties;\n      var addon = item && (item.parent_relationship || (properties && properties._bundle_role === 'addon'));\n      return addon ? total : total + (Number(item && item.quantity) || 0);\n    }, 0);\n    root.setAttribute('data-ds-cart-summary-count', String(count));\n    cartSummaryCount(root, count);\n  }\n\n  if (typeof cart.items_subtotal_price === 'number') {\n    root.setAttribute('data-ds-cart-summary-subtotal-cents', String(cart.items_subtotal_price));\n    cartSummarySet(root, '[data-ds-cart-summary-subtotal]', cartSummaryMoney(root, cart.items_subtotal_price));\n  }\n  if (typeof cart.total_price === 'number') {\n    root.setAttribute('data-ds-cart-summary-total-cents', String(cart.total_price));\n    cartSummarySet(root, '[data-ds-cart-summary-total]', cartSummaryMoney(root, cart.total_price));\n  }\n\n  var discountCents = Number(cart.total_discount || 0);\n  var discountRow = root.querySelector('[data-ds-cart-summary-discount-row]');\n  if (discountRow) {\n    cartSummaryToggle(discountRow, discountCents > 0);\n    if (discountCents > 0) {\n      cartSummarySet(root, '[data-ds-cart-summary-discount]', '-' + cartSummaryMoney(root, discountCents));\n    }\n  }\n\n  // Original minus charged: compare-at plus discounts, the whole saving.\n  var savingCents = Number(cart.original_total_price || 0) - Number(cart.total_price || 0);\n  var savingRow = root.querySelector('[data-ds-cart-summary-saving]');\n  if (savingRow) {\n    cartSummaryToggle(savingRow, savingCents > 0);\n    if (savingCents > 0) {\n      cartSummarySet(root, '[data-ds-cart-summary-saving-amount]', cartSummaryMoney(root, savingCents));\n    }\n  }\n}\n\n\n/* Sticky checkout bar total/saving sit OUTSIDE [data-ds-cart-summary] (on\n * [data-ds-cart-checkout-sticky]), so cartSummaryApply never reaches them.\n * Mirror total + saving here from the same cart payload. */\nfunction cartSummaryApplyStickyBar(cart) {\n  if (!cart) return;\n  var bars = document.querySelectorAll('[data-ds-cart-checkout-sticky]');\n  if (!bars.length) return;\n  var formatRoot =\n    document.querySelector(CART_SUMMARY + '[data-ds-cart-summary-money]') ||\n    document.querySelector(CART_SUMMARY);\n  if (!formatRoot) return;\n\n  var totalText =\n    typeof cart.total_price === 'number' ? cartSummaryMoney(formatRoot, cart.total_price) : '';\n  var savingCents = Number(cart.original_total_price || 0) - Number(cart.total_price || 0);\n\n  bars.forEach(function (bar) {\n    if (totalText) {\n      var totalEl = bar.querySelector('[data-ds-cart-summary-total]');\n      if (totalEl) totalEl.textContent = totalText;\n    }\n    var savingRow = bar.querySelector('[data-ds-cart-summary-saving]');\n    if (!savingRow) return;\n    cartSummaryToggle(savingRow, savingCents > 0);\n    if (savingCents > 0) {\n      var amt = bar.querySelector('[data-ds-cart-summary-saving-amount]');\n      if (amt) amt.textContent = cartSummaryMoney(formatRoot, savingCents);\n    }\n  });\n}\n\nfunction cartSummaryBusy(root, busy) {\n  if (!root) return;\n  var pending = Number(root.getAttribute('data-ds-cart-summary-pending') || 0);\n  pending = Math.max(0, pending + (busy ? 1 : -1));\n  root.setAttribute('data-ds-cart-summary-pending', String(pending));\n  busy = pending > 0;\n  root.setAttribute('aria-busy', busy ? 'true' : 'false');\n  root.querySelectorAll('[data-ds-cart-summary-subtotal], [data-ds-cart-summary-total]').forEach(function (el) {\n    el.hidden = busy;\n  });\n  root.querySelectorAll('[data-ds-cart-summary-loading]').forEach(function (el) {\n    el.hidden = !busy;\n  });\n  if (busy) {\n    root.querySelectorAll('[data-ds-cart-summary-discount-row], [data-ds-cart-summary-saving]').forEach(function (el) {\n      el.hidden = true;\n      el.style.display = 'none';\n    });\n  }\n}\n\nif (!document.__dsCartSummaryBound) {\n  document.__dsCartSummaryBound = true;\n\n  document.addEventListener('ds:cart:updated', function (event) {\n    var cart = event.detail;\n    if (!cart || !Array.isArray(cart.items)) return;\n    document.querySelectorAll(CART_SUMMARY).forEach(function (root) {\n      cartSummaryApply(root, cart);\n    });\n    cartSummaryApplyStickyBar(cart);\n  });\n\n  document.addEventListener('ds:cart:updating', function (event) {\n    document.querySelectorAll(CART_SUMMARY).forEach(function (root) {\n      cartSummaryBusy(root, !!(event.detail && event.detail.busy));\n    });\n  });\n\n  document.addEventListener('ds:cart:optimistic-count', function (event) {\n    var delta = Number(event.detail && event.detail.delta) || 0;\n    if (!delta) return;\n    document.querySelectorAll(CART_SUMMARY).forEach(function (root) {\n      var count = Math.max(0, Number(root.getAttribute('data-ds-cart-summary-count') || 0) + delta);\n      root.setAttribute('data-ds-cart-summary-count', String(count));\n      cartSummaryCount(root, count);\n    });\n  });\n}\n\n} catch (sfError) {\nif (typeof console !== 'undefined') console.error('[sf-core] behavior \"cart-summary\" failed to initialise:', sfError);\n}\n})();\n\n/* checkbox.js */\n(function () {\ntry {\n/* checkbox \u2014 apply the mixed (indeterminate) state, which is a DOM property\n * with no HTML attribute. Binds [data-ds-checkbox-indeterminate] (see\n * snippets/ds-checkbox.liquid). A MutationObserver re-applies it to checkboxes\n * added after load (Theme Editor section re-render, async mounts).\n *\n * Authoring rules: src/behaviors/README.md. No import/export.\n */\n\nfunction applyIndeterminate(scope) {\n  scope.querySelectorAll('[data-ds-checkbox-indeterminate]').forEach(function (input) {\n    input.indeterminate = true;\n  });\n}\n\nif (!document.__dsCheckboxBound) {\n  document.__dsCheckboxBound = true;\n  var start = function () {\n    applyIndeterminate(document);\n    new MutationObserver(function (records) {\n      records.forEach(function (record) {\n        record.addedNodes.forEach(function (node) {\n          if (node.nodeType === 1) applyIndeterminate(node);\n        });\n      });\n    }).observe(document.documentElement, { childList: true, subtree: true });\n  };\n  if (document.readyState === 'loading') {\n    document.addEventListener('DOMContentLoaded', start);\n  } else {\n    start();\n  }\n}\n\n} catch (sfError) {\nif (typeof console !== 'undefined') console.error('[sf-core] behavior \"checkbox\" failed to initialise:', sfError);\n}\n})();\n\n/* checkout-loading.js */\n(function () {\ntry {\n/* checkout-loading \u2014 the Checkout button's busy state, from click to navigation.\n *\n * The handover to Shopify is silent on a slow connection: nothing on screen\n * changes until the checkout document arrives, so the buyer clicks again. This\n * appends a spinner absolute to the right edge so the label stays centred,\n * sets aria-busy and swallows further clicks \u2014 whether the control is the cart\n * form's submit (name=\"checkout\"), the ATC modal's link, or the cart popover's.\n *\n * `disabled` is deliberately NOT set: on the cart page the control is the form's\n * submitter and carries name=\"checkout\", which a disabled button drops \u2014 and\n * cart-lines.js re-submits through it (requestSubmit(target)) after flushing a\n * deferred removal. Pointer-events + a click guard block the double-submit\n * without touching what gets posted.\n *\n * The state normally ends with the page going away, and is restored on the\n * paths where it cannot: a cart error, bfcache restore, and a safety timeout\n * for the handovers that quietly never happen.\n *\n * Authoring rules: src/behaviors/README.md. No import/export.\n */\nvar CHECKOUT_LOADING = '[data-ds-cart-checkout], [data-ds-cart-popover-checkout], [name=\"checkout\"]';\n\n// Match the button's own icon size. The start icon, when there is one, states\n// it outright through its `sf-h-icon-*` class; without one there is nothing on\n// the button that names its icon step, so fall back to md (20px), what\n// ds-button draws everywhere except its s step.\nfunction checkoutLoadingIconSize(el) {\n  var icon = el.querySelector(':scope > svg[data-ds-icon]');\n  var match = icon && /(?:^|\\s)sf-h-icon-([a-z]+)(?:\\s|$)/.exec(icon.getAttribute('class') || '');\n  return match ? match[1] : 'md';\n}\n\nfunction checkoutLoadingIcon(el) {\n  var size = checkoutLoadingIconSize(el);\n  var span = document.createElement('span');\n  // Absolute to the right edge so the Checkout label stays centred.\n  span.className = 'sf-absolute sf-right-3 sf-animate-spin sf-inline-flex';\n  span.setAttribute('data-ds-checkout-loading-spinner', '');\n  span.innerHTML =\n    '<svg class=\"sf-h-icon-' + size + ' sf-w-icon-' + size + ' sf-shrink-0\" viewBox=\"0 0 24 24\"' +\n    ' fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"' +\n    ' stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\" data-ds-icon=\"spinner\">' +\n    '<path d=\"M12 3a9 9 0 1 0 9 9\" /></svg>';\n  return span;\n}\n\n// Nothing here is certain to navigate: a click may be cancelled downstream, or\n// the cart may answer without a toast. The timer is the last resort that hands\n// the button back rather than leaving it busy forever.\nvar CHECKOUT_LOADING_TIMEOUT = 15000;\n\nfunction checkoutLoadingArmTimeout(el) {\n  clearTimeout(el.__dsCheckoutLoadingTimer);\n  el.__dsCheckoutLoadingTimer = setTimeout(function () {\n    checkoutLoadingStop(el);\n  }, CHECKOUT_LOADING_TIMEOUT);\n}\n\nfunction checkoutLoadingStart(el) {\n  if (!el || el.hasAttribute('data-ds-checkout-loading')) return;\n  el.setAttribute('data-ds-checkout-loading', '');\n  el.setAttribute('aria-busy', 'true');\n  el.classList.add('sf-pointer-events-none', 'sf-relative');\n  // Absolute spinner at the right edge. Dim the label; hide any existing end\n  // icon so only the spinner shows.\n  var label = el.querySelector(':scope > span');\n  if (label) label.classList.add('sf-opacity-70');\n  var icon = el.querySelector(':scope > svg[data-ds-icon]');\n  if (icon) icon.setAttribute('hidden', '');\n  el.appendChild(checkoutLoadingIcon(el));\n  checkoutLoadingArmTimeout(el);\n}\n\nfunction checkoutLoadingStop(el) {\n  if (!el || !el.hasAttribute('data-ds-checkout-loading')) return;\n  clearTimeout(el.__dsCheckoutLoadingTimer);\n  el.__dsCheckoutLoadingTimer = null;\n  el.removeAttribute('data-ds-checkout-loading');\n  el.setAttribute('aria-busy', 'false');\n  el.classList.remove('sf-pointer-events-none', 'sf-relative');\n  var spinner = el.querySelector('[data-ds-checkout-loading-spinner]');\n  if (spinner && spinner.parentNode) spinner.parentNode.removeChild(spinner);\n  var label = el.querySelector(':scope > span.sf-opacity-70');\n  if (label) label.classList.remove('sf-opacity-70');\n  var icon = el.querySelector(':scope > svg[data-ds-icon]');\n  if (icon) icon.removeAttribute('hidden');\n}\n\nfunction checkoutLoadingStopAll() {\n  document.querySelectorAll('[data-ds-checkout-loading]').forEach(checkoutLoadingStop);\n}\n\nif (!document.__dsCheckoutLoadingBound) {\n  document.__dsCheckoutLoadingBound = true;\n\n  // Some storefronts run a search/analytics app (Searchanise was the one caught)\n  // that binds a delegated click on the cart form's Checkout button to record a\n  // conversion: it preventDefaults the buyer's first click, fires its beacon, then\n  // re-fires the click synthetically \u2014 and a synthetic click never submits the\n  // form, so the handover silently never happens and the buyer has to click a\n  // second time (the app also stamps the button, so the second click gets through).\n  // Beat it in the CAPTURE phase, which always runs before a delegated bubble\n  // handler: on the first real click, start the busy state and submit the cart\n  // form ourselves, then stop the event so the app can't swallow it. Scoped to the\n  // form's name=\"checkout\" submit; the popover/modal checkout links are plain\n  // navigations and fall through to the handler below.\n  document.addEventListener('click', function (event) {\n    var el = event.target && event.target.closest ? event.target.closest('[name=\"checkout\"]') : null;\n    if (!el) return;\n    var form = el.form || (el.closest && el.closest('form'));\n    if (!form) return;\n    if (el.hasAttribute('data-ds-checkout-loading')) {\n      event.preventDefault();\n      return;\n    }\n    if (event.defaultPrevented) return;\n    if (event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;\n    // Ours is the only submission \u2014 cancel the native one so the form is not\n    // posted twice, and stop the app's bubble handler from ever seeing the click.\n    event.preventDefault();\n    event.stopPropagation();\n    checkoutLoadingStart(el);\n    if (form.requestSubmit) form.requestSubmit(el);\n    else form.submit();\n  }, true);\n\n  document.addEventListener('click', function (event) {\n    var el = event.target && event.target.closest ? event.target.closest(CHECKOUT_LOADING) : null;\n    if (!el) return;\n    // Already handed over \u2014 this click must not post the cart form again.\n    if (el.hasAttribute('data-ds-checkout-loading')) {\n      event.preventDefault();\n      return;\n    }\n    // Neither of these hands over to checkout: a cancelled click goes nowhere,\n    // and a middle/modified click opens a tab while this page stays put.\n    if (event.defaultPrevented) return;\n    if (event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;\n    checkoutLoadingStart(el);\n  });\n\n  document.addEventListener('ds:cart:error', checkoutLoadingStopAll);\n  // A failed commit reports itself only through the toast on one path, and\n  // nothing else raises one while a checkout is in flight.\n  document.addEventListener('ds:toast', checkoutLoadingStopAll);\n\n  // Back from checkout: bfcache restores the DOM it was frozen with.\n  window.addEventListener('pageshow', function (event) {\n    if (event.persisted) checkoutLoadingStopAll();\n  });\n}\n\n} catch (sfError) {\nif (typeof console !== 'undefined') console.error('[sf-core] behavior \"checkout-loading\" failed to initialise:', sfError);\n}\n})();\n\n/* collapsible.js */\n(function () {\ntry {\n/* collapsible \u2014 clamp long content with a Show more / Show less toggle.\n * Binds [data-ds-collapsible] and data-ds-collapsible-{viewport,toggle}\n * (see snippets/ds-collapsible.liquid). The snippet ships fully expanded with\n * the toggle hidden; on init we measure the viewport against its clamp height\n * and, only when it overflows, collapse it and reveal the toggle. Content that\n * already fits loses the toggle entirely. Without this JS the content stays\n * visible in full \u2014 nothing hides behind a control that cannot open.\n *\n * Authoring rules: src/behaviors/README.md. No import/export.\n */\n\nfunction bindCollapsible(el) {\n  if (el.dataset.dsCollapsibleBound) return;\n  var viewport = el.querySelector('[data-ds-collapsible-viewport]');\n  var toggle = el.querySelector('[data-ds-collapsible-toggle]');\n  if (!viewport || !toggle) return;\n  el.dataset.dsCollapsibleBound = '1';\n\n  // Measure against the clamp: collapse, read whether content is taller than\n  // the capped viewport, then keep it collapsed (with the toggle) only if so.\n  el.setAttribute('data-collapsed', 'true');\n  var overflows = viewport.scrollHeight - viewport.clientHeight > 1;\n  if (!overflows) {\n    el.removeAttribute('data-collapsed');\n    return;\n  }\n  toggle.hidden = false;\n  toggle.setAttribute('aria-expanded', 'false');\n}\n\nfunction initCollapsibles(scope) {\n  (scope || document).querySelectorAll('[data-ds-collapsible]').forEach(bindCollapsible);\n}\n\nfunction animateCollapsible(el, viewport, collapsed) {\n  var version = (el.__dsCollapsibleMotion || 0) + 1;\n  el.__dsCollapsibleMotion = version;\n\n  if (viewport.__dsCollapsibleEnd) {\n    viewport.removeEventListener('transitionend', viewport.__dsCollapsibleEnd);\n  }\n\n  // Start from the height that is actually painted. This also makes a second\n  // click during a running transition reverse smoothly from its current point.\n  var from = viewport.getBoundingClientRect().height;\n  var clamp = parseFloat(getComputedStyle(viewport).getPropertyValue('--collapse-max-height'));\n  var to = collapsed ? Math.min(viewport.scrollHeight, clamp) : viewport.scrollHeight;\n\n  viewport.style.maxHeight = from + 'px';\n  el.setAttribute('data-collapsed', collapsed ? 'true' : 'false');\n  // Commit the start length before scheduling the destination length. A\n  // transition cannot interpolate to/from `none`, which was the original snap.\n  void viewport.offsetHeight;\n\n  var finish = function (event) {\n    if (event.target !== viewport || event.propertyName !== 'max-height') return;\n    if (el.__dsCollapsibleMotion !== version) return;\n    viewport.style.maxHeight = '';\n    viewport.removeEventListener('transitionend', finish);\n    viewport.__dsCollapsibleEnd = null;\n  };\n  viewport.__dsCollapsibleEnd = finish;\n  viewport.addEventListener('transitionend', finish);\n\n  requestAnimationFrame(function () {\n    if (el.__dsCollapsibleMotion !== version) return;\n    viewport.style.maxHeight = to + 'px';\n  });\n}\n\nfunction restoreCollapsibleItemPosition(el) {\n  var target = el.closest('[data-ds-accordion]') || el;\n  var rect = target.getBoundingClientRect();\n  if (rect.top >= 0) return;\n\n  var top = rect.top + window.scrollY;\n  // The viewport height is already animating. A simultaneous smooth scroll\n  // creates a second, competing motion and can chase a moving layout target.\n  // Reposition immediately so the height transition remains the only motion.\n  window.scrollTo({\n    top: Math.max(0, Math.round(top)),\n    behavior: 'auto',\n  });\n}\n\nif (!document.__dsCollapsibleBound) {\n  document.__dsCollapsibleBound = true;\n\n  var run = function () { initCollapsibles(document); };\n  if (document.readyState === 'loading') {\n    document.addEventListener('DOMContentLoaded', run, { once: true });\n  } else {\n    run();\n  }\n  // Theme Editor section re-render and any async-swapped section (e.g. the PDP).\n  document.addEventListener('shopify:section:load', run);\n\n  // Content injected after load (Storybook mount, lazy sections): re-scan on the\n  // next frame. bindCollapsible is guarded per element, so re-runs are cheap.\n  if (typeof MutationObserver !== 'undefined') {\n    var queued = false;\n    new MutationObserver(function () {\n      if (queued) return;\n      queued = true;\n      requestAnimationFrame(function () { queued = false; run(); });\n    }).observe(document.documentElement, { childList: true, subtree: true });\n  }\n\n  document.addEventListener('click', function (e) {\n    var toggle = e.target.closest && e.target.closest('[data-ds-collapsible-toggle]');\n    if (!toggle) return;\n    var el = toggle.closest('[data-ds-collapsible]');\n    if (!el) return;\n    var collapsed = el.getAttribute('data-collapsed') === 'true';\n    animateCollapsible(el, el.querySelector('[data-ds-collapsible-viewport]'), !collapsed);\n    if (!collapsed) restoreCollapsibleItemPosition(el);\n    toggle.setAttribute('aria-expanded', collapsed ? 'true' : 'false');\n    var next = collapsed ? toggle.getAttribute('data-label-less') : toggle.getAttribute('data-label-more');\n    if (next) toggle.textContent = next;\n  });\n}\n\n} catch (sfError) {\nif (typeof console !== 'undefined') console.error('[sf-core] behavior \"collapsible\" failed to initialise:', sfError);\n}\n})();\n\n/* confirm-modal.js */\n(function () {\ntry {\n/* confirm-modal \u2014 holds a destructive action until the buyer says yes.\n *\n * cart-lines.js fires a cancelable ds:cart:remove-request before it touches the\n * cart; this cancels it, fills the dialog from the line that asked, and answers\n * with ds:cart:remove-confirm when the buyer confirms. Nothing else in core\n * knows about the dialog, so a page without one removes straight away.\n *\n * Open/closed is [aria-hidden]; src/confirm-modal.css carries the motion. Focus\n * is trapped while open and returned to whatever opened it.\n *\n * Authoring rules: src/behaviors/README.md. No import/export.\n */\n\nvar CONFIRM_MODAL = '[data-ds-confirm-modal]';\nvar FOCUSABLE = 'a[href], button:not([disabled]), input:not([disabled]), select, textarea, [tabindex]:not([tabindex=\"-1\"])';\n\nvar confirmModalReturn = null;\nvar confirmModalKey = '';\nvar confirmModalScroll = '';\n\nfunction confirmModalEl() {\n  return document.querySelector(CONFIRM_MODAL);\n}\n\nfunction confirmModalFill(root, line, addon) {\n  var source = addon || line;\n  var set = function (hook, value) {\n    var el = root.querySelector(hook);\n    if (el && value !== null && value !== undefined) el.textContent = value;\n  };\n  var title = source.querySelector('[data-ds-cart-item-title], [data-ds-cart-addon-line-title]');\n  set('[data-ds-confirm-modal-product]', title ? title.textContent.trim() : '');\n\n  // The variant line is the options list, joined between its items \u2014 joining the\n  // wrapper's text instead puts the separator inside each option.\n  var options = addon\n    ? [addon.querySelector('[data-ds-cart-addon-line-title] + p')]\n    : [].slice.call(line.querySelectorAll('[data-ds-cart-item-title] + div > *'));\n  set('[data-ds-confirm-modal-variant]', options\n    .filter(Boolean)\n    .map(function (el) { return el.textContent.trim(); })\n    .filter(Boolean)\n    .join(' \u00B7 '));\n\n  var price = source.querySelector('[data-ds-price-amount], [data-ds-cart-addon-line-amount]');\n  var amount = root.querySelector('[data-ds-confirm-modal-price] [data-ds-price-amount]');\n  if (amount && price) amount.textContent = price.textContent.trim();\n\n  var from = source.querySelector('img');\n  var into = root.querySelector('[data-ds-confirm-modal-image]');\n  if (into && from && from.getAttribute('src')) {\n    into.src = from.getAttribute('src');\n    into.alt = from.getAttribute('alt') || '';\n    into.removeAttribute('hidden');\n  } else if (into) {\n    into.setAttribute('hidden', '');\n  }\n}\n\nfunction confirmModalClose(root) {\n  if (!root || root.getAttribute('aria-hidden') === 'true') return;\n  root.setAttribute('aria-hidden', 'true');\n  root.removeAttribute('data-ds-confirm-modal-key');\n  // Put back whatever was there: another dialog (ds-cart-added) may still be\n  // open and holding its own lock.\n  document.body.style.overflow = confirmModalScroll;\n  confirmModalKey = '';\n  if (confirmModalReturn && confirmModalReturn.focus) confirmModalReturn.focus();\n  confirmModalReturn = null;\n}\n\nfunction confirmModalOpen(root, detail) {\n  confirmModalKey = detail.key || '';\n  root.setAttribute('data-ds-confirm-modal-key', confirmModalKey);\n  if (detail.root) confirmModalFill(root, detail.root, detail.addon);\n\n  confirmModalReturn = document.activeElement;\n  confirmModalScroll = document.body.style.overflow;\n  root.setAttribute('aria-hidden', 'false');\n  document.body.style.overflow = 'hidden';\n\n  // The card often opens under the pointer, so a footer button would paint\n  // :hover immediately. Hold hover until the pointer actually moves.\n  root.setAttribute('data-ds-confirm-modal-settle', '');\n  var settle = function () {\n    root.removeAttribute('data-ds-confirm-modal-settle');\n    window.removeEventListener('pointermove', settle);\n  };\n  window.addEventListener('pointermove', settle);\n\n  // Focus the dialog, not a button. Focusing Keep/Close on open matches :focus\n  // and paints the hover fill before the buyer has touched anything. Tab still\n  // reaches the actions; the trap below starts from whichever control is first.\n  var dialog = root.querySelector('[data-ds-confirm-modal-dialog]');\n  window.requestAnimationFrame(function () {\n    if (dialog && dialog.focus) dialog.focus();\n  });\n}\n\nfunction confirmModalTrap(root, event) {\n  var items = [].slice.call(root.querySelectorAll(FOCUSABLE)).filter(function (el) {\n    return el.offsetParent !== null;\n  });\n  if (!items.length) return;\n  var first = items[0];\n  var last = items[items.length - 1];\n  var dialog = root.querySelector('[data-ds-confirm-modal-dialog]');\n  // The dialog itself is focused on open (tabindex -1, not in this list).\n  // Shift+Tab from there would otherwise leave the sheet.\n  if (event.shiftKey && (document.activeElement === first || document.activeElement === dialog)) {\n    event.preventDefault();\n    last.focus();\n  } else if (!event.shiftKey && document.activeElement === last) {\n    event.preventDefault();\n    first.focus();\n  }\n}\n\nif (!document.__dsConfirmModalBound) {\n  document.__dsConfirmModalBound = true;\n\n  document.addEventListener('ds:cart:remove-request', function (event) {\n    var root = confirmModalEl();\n    if (!root) return;\n    event.preventDefault();\n    confirmModalOpen(root, event.detail || {});\n  });\n\n  document.addEventListener('click', function (event) {\n    var root = confirmModalEl();\n    if (!root || root.getAttribute('aria-hidden') !== 'false') return;\n    var target = event.target;\n    if (!target || !target.closest) return;\n\n    if (target.closest('[data-ds-confirm-modal-confirm]')) {\n      var key = confirmModalKey;\n      confirmModalClose(root);\n      document.dispatchEvent(new CustomEvent('ds:cart:remove-confirm', { detail: { key: key } }));\n      return;\n    }\n    if (target.closest('[data-ds-confirm-modal-cancel]') || target.closest('[data-ds-confirm-modal-close]')) {\n      confirmModalClose(root);\n      return;\n    }\n    // The scrim is the root itself; a click inside the card must not close it.\n    if (target === root) confirmModalClose(root);\n  });\n\n  document.addEventListener('keydown', function (event) {\n    var root = confirmModalEl();\n    if (!root || root.getAttribute('aria-hidden') !== 'false') return;\n    if (event.key === 'Escape') {\n      confirmModalClose(root);\n    } else if (event.key === 'Tab') {\n      confirmModalTrap(root, event);\n    }\n  });\n}\n\n} catch (sfError) {\nif (typeof console !== 'undefined') console.error('[sf-core] behavior \"confirm-modal\" failed to initialise:', sfError);\n}\n})();\n\n/* customer-reviews.js */\n(function () {\ntry {\n/* customer-reviews \u2014 fills the homepage band from Judge.me's featured carousel.\n *\n * Why this exists at all: the band's aggregate has always been real (Judge.me\n * writes the shop average and count into shop metafields), but the CARDS were\n * hand-typed section blocks. There is no JSON to read and no tokenless endpoint\n * for a whole store \u2014 `reviews_for_widget` 404s the moment you drop\n * `product_id` \u2014 so their markup is the feed.\n *\n * TWO feeds, read in this order:\n *\n *   `.jdgm-rev`          \u2014 the all-reviews page (all_reviews_0 + all_reviews_1).\n *                          The SAME markup the PDP widget uses, so the same\n *                          attributes: data-verified-buyer, data-score,\n *                          data-product-title, data-product-url. Preferred,\n *                          because an attribute survives a restyle where a\n *                          class does not.\n *   `.jdgm-carousel-item` \u2014 the featured carousel. Every field is a class with\n *                          no attribute twin and the rating has no data-score\n *                          at all, so this is only the fallback.\n *\n * `store_summary_widget_data` has no `reviews` array, which is what the first\n * pass looked for and why it was written off. It does have `photo_gallery`: ten\n * entries carrying reviewer_name, body_html, verified_buyer, location and\n * cf_answers. Those are reviews, in JSON, at shop scope \u2014 the thing this file\n * exists because it could not find. Whether they also carry the photo and the\n * product is the open question; if they do, this parser becomes Liquid and the\n * markup feeds drop to fallbacks.\n *\n * Nothing is force-filled: a card is built only from the parts that parsed, and\n * a feed that yields nothing leaves the merchant's curated cards alone.\n *\n * Authoring rules: src/behaviors/README.md. No import/export.\n */\n\nvar ROOT = '[data-ds-customer-reviews]';\n\nfunction text(node, selector) {\n  var found = node.querySelector(selector);\n  return found ? found.textContent.trim() : '';\n}\n\n/* One `.jdgm-rev` to a plain object. Everything but the body and the author\n * comes off an attribute, which is why this feed is preferred. */\nfunction parseReview(el) {\n  var body = text(el, '.jdgm-rev__body');\n  if (!body) return null;\n\n  var rating = el.querySelector('.jdgm-rev__rating');\n  var pic = el.querySelector('.jdgm-rev__pic-link img');\n\n  return {\n    body: body,\n    author: text(el, '.jdgm-rev__author'),\n    product: el.getAttribute('data-product-title') || '',\n    href: el.getAttribute('data-product-url') || '',\n    image: pic ? pic.getAttribute('data-src') || pic.getAttribute('src') || '' : '',\n    rating: rating ? parseInt(rating.getAttribute('data-score'), 10) || 5 : 5,\n    verified: el.getAttribute('data-verified-buyer') === 'true',\n  };\n}\n\n/* One `.jdgm-carousel-item` to a plain object, or null when it carries no\n * review body \u2014 the carousel pads itself with product-only tiles. */\nfunction parseItem(el) {\n  var body = text(el, '.jdgm-carousel-item__review-body');\n  if (!body) return null;\n\n  var link = el.querySelector('a.jdgm-carousel-item__product, .jdgm-carousel-item__product a, a.jdgm-carousel-item__full-review-link');\n\n  /* The carousel's rating carries NO `data-score` \u2014 unlike the review widget's,\n   * which does. It states the score in `aria-label` (\"5 stars\") over a run of\n   * `.jdgm-star.jdgm--on` spans. Counting the lit stars is the reading that\n   * cannot be broken by a locale change; the label is the fallback. Reading\n   * data-score here (which is what shipped first) silently made every card five\n   * stars, and a wall of fives is exactly the thing a shopper stops believing. */\n  var ratingEl = el.querySelector('.jdgm-carousel-item__review-rating');\n  var rating = 5;\n  if (ratingEl) {\n    var lit = ratingEl.querySelectorAll('.jdgm-star.jdgm--on').length;\n    if (lit > 0) {\n      rating = lit;\n    } else {\n      var label = parseInt(ratingEl.getAttribute('aria-label'), 10);\n      if (label > 0) rating = label;\n    }\n  }\n\n  /* `jdgm-carousel-item__product-image` is the class on the <img> ITSELF, not on\n   * a wrapper around one. Its `alt` is the product title, which is the only\n   * place the title appears when the carousel's theme drops the product line. */\n  var image = el.querySelector('img.jdgm-carousel-item__product-image, .jdgm-carousel-item__image-wrapper img');\n\n  return {\n    body: body,\n    author: text(el, '.jdgm-carousel-item__reviewer-name'),\n    product: text(el, '.jdgm-carousel-item__product-title') || (image ? image.getAttribute('alt') || '' : ''),\n    href: link ? link.getAttribute('href') || '' : '',\n    // Judge.me lazy-loads, so the real URL is usually on data-src.\n    image: image ? image.getAttribute('data-src') || image.getAttribute('src') || '' : '',\n    rating: rating,\n    verified: true,\n  };\n}\n\n/* ds-rating stacks a filled star over a track star per slot and reveals the\n * overlay with a width utility, so re-pointing it is a class swap \u2014 no second\n * star implementation here. Same routine the PDP band uses. */\nfunction setStars(container, score) {\n  var halves = Math.max(0, Math.min(10, Math.round(score * 2)));\n  container.setAttribute('data-ds-rating-value', String(score));\n  container.querySelectorAll('[role=\"img\"] > span').forEach(function (slot, index) {\n    var overlay = slot.querySelector('span');\n    if (!overlay) return;\n    var filled = (index + 1) * 2;\n    overlay.classList.remove('sf-w-full', 'sf-w-1/2', 'sf-hidden');\n    if (halves >= filled) overlay.classList.add('sf-w-full');\n    else if (halves === filled - 1) overlay.classList.add('sf-w-1/2');\n    else overlay.classList.add('sf-hidden');\n  });\n}\n\nfunction fillCard(node, review) {\n  var quote = node.querySelector('blockquote');\n  if (quote) quote.textContent = '\u201C' + review.body + '\u201D';\n\n  var author = node.querySelector('cite');\n  if (author) {\n    if (!review.author) author.remove();\n    else author.textContent = review.author;\n  }\n\n  var image = node.querySelector('img');\n  if (image && review.image) {\n    image.setAttribute('src', review.image);\n    image.removeAttribute('srcset');\n    image.setAttribute('alt', '');\n  }\n\n  var stars = node.querySelector('[data-ds-rating]');\n  if (stars) setStars(stars, review.rating);\n\n  /* ds-review-card ships the verified mark on by default; an unverified review must not\n   * claim otherwise. */\n  var badge = node.querySelector('[data-ds-review-card-verified]');\n  if (badge && review.verified === false) badge.remove();\n\n  return node;\n}\n\nfunction setup(root) {\n  var source = root.querySelector('[data-ds-customer-reviews-source]');\n  var template = root.querySelector('[data-ds-customer-reviews-template]');\n  var track = root.querySelector('[data-ds-customer-reviews-track]');\n  if (!source || !template || !track) return;\n\n  var reviews = [];\n  source.querySelectorAll('.jdgm-rev').forEach(function (el) {\n    var review = parseReview(el);\n    if (review) reviews.push(review);\n  });\n  if (!reviews.length) {\n    source.querySelectorAll('.jdgm-carousel-item').forEach(function (el) {\n      var review = parseItem(el);\n      if (review) reviews.push(review);\n    });\n  }\n\n  /* Nothing parsable: leave the merchant's curated cards alone. The homepage\n   * having four hand-picked quotes is a better failure than an empty band. */\n  if (!reviews.length) return;\n\n  /* Figma's card leads with a photo, and the all-reviews feed carries one only\n   * where the shopper uploaded it. Photo-bearing reviews go first so the band\n   * looks like the design whenever the store has enough of them; the rest fill\n   * in behind, drawing ds-image's empty frame rather than being dropped. */\n  reviews.sort(function (a, b) {\n    return (b.image ? 1 : 0) - (a.image ? 1 : 0);\n  });\n\n  var limit = parseInt(root.getAttribute('data-ds-customer-reviews-limit'), 10) || 8;\n\n  // Four cards fit the desktop viewport; extras stay available in the scroller.\n  track.innerHTML = '';\n  reviews.slice(0, limit).forEach(function (review) {\n    var node = template.content.firstElementChild.cloneNode(true);\n    track.appendChild(fillCard(node, review));\n  });\n}\n\nfunction init(scope) {\n  (scope || document).querySelectorAll(ROOT).forEach(function (root) {\n    if (root.dataset.dsBound) return;\n    root.dataset.dsBound = '1';\n    setup(root);\n  });\n}\n\nif (document.readyState === 'loading') {\n  document.addEventListener('DOMContentLoaded', function () { init(); });\n} else {\n  init();\n}\n\ndocument.addEventListener('shopify:section:load', function (event) {\n  init(event.target);\n});\n\n} catch (sfError) {\nif (typeof console !== 'undefined') console.error('[sf-core] behavior \"customer-reviews\" failed to initialise:', sfError);\n}\n})();\n\n/* customily-preview.js */\n(function () {\ntry {\n/* customily-preview \u2014 the PDP preview dialog Customily renders has a title\n * band and an image, and no footer. The storefront preview (PreviewModalPattern)\n * closes with Close + Add to Cart. This mounts that row into Customily's card\n * and points the two buttons at the controls Customily already handles: the\n * header X, and #customily-cart-btn.\n *\n * It also repaints a selected swatch. Customily writes\n * `border: solid #e95d2c !important` on the chosen <label>, and an inline\n * !important beats every stylesheet. BuildYou's selected tile is navy\n * (--action-secondary-default), so once that orange border is on the label\n * this sets border-color back to the token.\n *\n * Customily's markup is not a ds component. This file is the same exception as\n * src/customily.css: a skin for one third-party dialog, declared by\n * ds-product-customization-form.\n *\n * Authoring rules: src/behaviors/README.md. No import/export.\n */\n\n(function () {\n  if (document.__dsCustomilyPreviewBound) return;\n  document.__dsCustomilyPreviewBound = true;\n\n  /* Customily clones #customily-cart-btn into the preview (same id, no\n     listeners) and may portal that clone ahead of the real button.\n     getElementById then returns the clone, and clicking it adds nothing.\n     Keep the button that still sits in the product form. */\n  var pageCart = null;\n\n  function rememberPageCart() {\n    var buttons = document.querySelectorAll('[id=\"customily-cart-btn\"]');\n    for (var i = 0; i < buttons.length; i++) {\n      if (!buttons[i].closest('.customily-modal-preview-only')) {\n        pageCart = buttons[i];\n        return;\n      }\n    }\n  }\n\n  function cartButton() {\n    if (pageCart && pageCart.isConnected && !pageCart.closest('.customily-modal-preview-only')) {\n      return pageCart;\n    }\n    rememberPageCart();\n    if (pageCart && pageCart.isConnected) return pageCart;\n    return document.getElementById('customily-cart-btn');\n  }\n\n  function labelOf(btn) {\n    var text = btn ? (btn.innerText || '').replace(/\\s+/g, ' ').trim() : '';\n    return text || 'Add to Cart';\n  }\n\n  /* Same loading treatment as BY's preview Add to Cart: the label stays, dims,\n     and a spinner sits on the right edge. The label does not change to\n     \"Loading...\". */\n  function setAdding(add, adding) {\n    var label = add.querySelector('[data-ds-customily-preview-add-label]');\n    var spinner = add.querySelector('[data-ds-customily-preview-add-spinner]');\n    add.disabled = !!adding;\n    if (adding) add.setAttribute('aria-busy', 'true');\n    else add.removeAttribute('aria-busy');\n    if (label) label.classList.toggle('sf-opacity-70', !!adding);\n    if (spinner) {\n      if (adding) spinner.removeAttribute('hidden');\n      else spinner.setAttribute('hidden', '');\n    }\n  }\n\n  /* The footer is mounted once and Customily reuses the same dialog. A\n     successful add closes the modal while the button is still busy, so the\n     next Preview opens with Add to Cart disabled. Clear that before show. */\n  function resetAdding() {\n    var buttons = document.querySelectorAll('[data-ds-customily-preview-add]');\n    for (var i = 0; i < buttons.length; i++) setAdding(buttons[i], false);\n  }\n\n  var previewToken = 0;\n\n  function closePreview() {\n    var modal = document.querySelector('.customily-modal-preview-only');\n    if (!modal) return;\n    var x = modal.querySelector('.customily-close-button');\n    if (x) x.click();\n  }\n\n  function cartCount() {\n    return fetch('/cart.js', { headers: { Accept: 'application/json' } })\n      .then(function (res) { return res.json(); })\n      .then(function (cart) { return cart && cart.item_count ? cart.item_count : 0; })\n      .catch(function () { return 0; });\n  }\n\n  /* #customily-bottom-wrapper is display:none. Customily's add runs after the\n     export, and a visibility check then bails if we hide the button again\n     in the same turn as the click. Keep it in the document, off screen,\n     until the add settles. */\n  function revealCart(cart) {\n    var hidden = cart && cart.closest('#customily-bottom-wrapper');\n    if (!hidden) return function () {};\n    var prevStyle = hidden.getAttribute('style');\n    hidden.style.setProperty('display', 'block', 'important');\n    hidden.style.setProperty('position', 'fixed', 'important');\n    hidden.style.setProperty('left', '-10000px', 'important');\n    hidden.style.setProperty('top', '0', 'important');\n    hidden.style.setProperty('width', 'auto', 'important');\n    hidden.style.setProperty('height', 'auto', 'important');\n    hidden.style.setProperty('margin', '0', 'important');\n    hidden.style.setProperty('opacity', '0', 'important');\n    hidden.style.setProperty('pointer-events', 'none', 'important');\n    return function () {\n      if (prevStyle == null) hidden.removeAttribute('style');\n      else hidden.setAttribute('style', prevStyle);\n    };\n  }\n\n  function watchCart(before, token, onAdded, onGiveUp) {\n    var tries = 0;\n    function tick() {\n      if (token !== previewToken) return;\n      fetch('/cart.js', { headers: { Accept: 'application/json' } })\n        .then(function (res) { return res.json(); })\n        .then(function (cart) {\n          if (token !== previewToken) return;\n          if (cart && cart.item_count > before) {\n            onAdded(cart);\n            return;\n          }\n          tries += 1;\n          if (tries < 40) window.setTimeout(tick, 500);\n          else onGiveUp();\n        })\n        .catch(function () {\n          tries += 1;\n          if (tries < 40) window.setTimeout(tick, 500);\n          else onGiveUp();\n        });\n    }\n    window.setTimeout(tick, 500);\n  }\n\n  function mount(modal) {\n    var main = modal.querySelector('.main');\n    if (!main || main.querySelector('[data-ds-customily-preview-actions]')) return;\n\n    var actions = document.createElement('div');\n    actions.setAttribute('data-ds-customily-preview-actions', '');\n\n    var close = document.createElement('button');\n    close.type = 'button';\n    close.setAttribute('data-ds-customily-preview-close', '');\n    close.textContent = 'Close';\n\n    var add = document.createElement('button');\n    add.type = 'button';\n    add.setAttribute('data-ds-customily-preview-add', '');\n    var addLabel = document.createElement('span');\n    addLabel.setAttribute('data-ds-customily-preview-add-label', '');\n    addLabel.textContent = labelOf(cartButton());\n    var addSpinner = document.createElement('span');\n    addSpinner.setAttribute('data-ds-customily-preview-add-spinner', '');\n    addSpinner.setAttribute('hidden', '');\n    addSpinner.innerHTML =\n      '<svg class=\"sf-h-icon-sm sf-w-icon-sm sf-shrink-0\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\" data-ds-icon=\"spinner\"><path d=\"M12 3a9 9 0 1 0 9 9\" /></svg>';\n    add.appendChild(addLabel);\n    add.appendChild(addSpinner);\n\n    close.addEventListener('click', function () {\n      var x = main.querySelector('.customily-close-button');\n      if (x) x.click();\n    });\n    add.addEventListener('click', function (event) {\n      event.preventDefault();\n      event.stopPropagation();\n      var cart = cartButton();\n      if (!cart || cart.disabled || add.disabled) return;\n      setAdding(add, true);\n      /* Customily posts /cart/add.js itself after the export. That request\n         never goes through buy-buttons, so the cart sheet and the header\n         count stay put and this modal keeps covering the page. Watch the\n         cart until the line lands, then open our sheet and close the modal.\n         If buy-buttons does emit ds:cart:added, that wins and we only close. */\n      var token = ++previewToken;\n      var restore = revealCart(cart);\n      var finished = false;\n      function done(ok) {\n        if (finished || token !== previewToken) return;\n        finished = true;\n        restore();\n        if (!ok) setAdding(add, false);\n      }\n      function onAdded() {\n        document.removeEventListener('ds:cart:added', onAdded);\n        document.removeEventListener('ds:cart:error', onError);\n        if (token !== previewToken) return;\n        done(true);\n        closePreview();\n      }\n      function onError() {\n        document.removeEventListener('ds:cart:added', onAdded);\n        document.removeEventListener('ds:cart:error', onError);\n        if (token !== previewToken) return;\n        done(false);\n      }\n      document.addEventListener('ds:cart:added', onAdded);\n      document.addEventListener('ds:cart:error', onError);\n      cartCount().then(function (before) {\n        if (token !== previewToken) return;\n        cart.click();\n        watchCart(before, token, function (cartState) {\n          if (finished || token !== previewToken) return;\n          var items = cartState.items || [];\n          var line = items[items.length - 1];\n          done(true);\n          if (line) document.dispatchEvent(new CustomEvent('ds:cart:added', { detail: line }));\n          document.dispatchEvent(new CustomEvent('ds:cart:updated', { detail: cartState }));\n          closePreview();\n        }, function () {\n          document.removeEventListener('ds:cart:added', onAdded);\n          document.removeEventListener('ds:cart:error', onError);\n          done(false);\n        });\n      });\n    });\n\n    actions.appendChild(close);\n    actions.appendChild(add);\n    main.appendChild(actions);\n  }\n\n  function scan() {\n    var modals = document.querySelectorAll('.customily-modal-preview-only');\n    for (var i = 0; i < modals.length; i++) mount(modals[i]);\n  }\n\n  /* Customily inserts the dialog after this script, and sometimes after the\n     show event. Watch for the card and mount once it exists. */\n  var queued = false;\n  function schedule() {\n    if (queued) return;\n    queued = true;\n    window.requestAnimationFrame(function () {\n      queued = false;\n      scan();\n    });\n  }\n\n  /* Customily's selected orange. Compared without spaces so `rgb(233, 93, 44)`\n     and `rgb(233,93,44)` are the same border. */\n  var SELECTED_ORANGE = 'rgb(233,93,44)';\n\n  function paintSwatches() {\n    var root = document.getElementById('customily-options');\n    if (!root) return;\n    var navy = getComputedStyle(document.documentElement)\n      .getPropertyValue('--action-secondary-default')\n      .trim();\n    if (!navy) return;\n    var labels = root.querySelectorAll(\n      '.customily-swatch label, .swatch-container label, .cl-color-picker label'\n    );\n    for (var i = 0; i < labels.length; i++) {\n      var border = getComputedStyle(labels[i]).borderTopColor.replace(/\\s+/g, '');\n      if (border !== SELECTED_ORANGE) continue;\n      labels[i].style.setProperty('border-color', navy, 'important');\n    }\n  }\n\n  /* The orange border is already on the label when Customily inserts it, so an\n     attribute-only observer never sees that first write. childList catches the\n     insert. A later selection only mutates style, which the same observer\n     already handled. */\n  function setImportant(el, prop, value) {\n    if (!el || !value) return;\n    if (el.style.getPropertyPriority(prop) !== 'important') {\n      el.style.setProperty(prop, value, 'important');\n      return;\n    }\n    var current = el.style.getPropertyValue(prop);\n    if (current === value || current === value + 'px') return;\n    el.style.setProperty(prop, value, 'important');\n  }\n\n  var BUTTON_PROPS = ['display', 'flex', 'flex-direction', 'justify-content', 'align-items', 'width', 'max-width', 'margin-left', 'margin-right', 'text-align'];\n\n  function clearImportant(el, props) {\n    if (!el) return;\n    for (var i = 0; i < props.length; i++) {\n      if (el.style.getPropertyPriority(props[i]) === 'important') el.style.removeProperty(props[i]);\n    }\n  }\n\n  /* Only while Customily is reading the photo (both classes). Before upload\n     and after it finishes, Replace and its icon stay as Customily drew them.\n     Inline overrides from the processing pass are removed, or they would keep\n     hiding Replace once the classes are gone. */\n  function isUploading(btn) {\n    return btn.classList.contains('disabled') && btn.classList.contains('disable-pointer-events');\n  }\n\n  function restoreUploadButton(btn) {\n    if (btn.hasAttribute('data-ds-processing-on')) {\n      btn.removeAttribute('data-ds-processing-on');\n      clearImportant(btn, BUTTON_PROPS);\n      if (btn.parentElement) clearImportant(btn.parentElement, ['justify-content']);\n      var file = btn.closest ? btn.closest('.customily-file-input') : null;\n      if (file) clearImportant(file, ['justify-content', 'text-align']);\n    }\n    var label = btn.querySelector('[data-ds-customily-processing]');\n    if (label) label.remove();\n    /* One label only. Select Image has no icon; Replace wraps the rotate icon.\n       A photo in the field means the remove control is in the DOM. Clear only\n       an important display we wrote earlier, then hide the label that is not\n       the current one \u2014 without that, both words stay on screen. */\n    var field = btn.closest ? btn.closest('.customily-file-input') : null;\n    if (!field) return;\n    var hasValue = !!field.querySelector('.cl_remove-image');\n    var kids = btn.children;\n    for (var k = 0; k < kids.length; k++) {\n      var child = kids[k];\n      if (child.hasAttribute('data-ds-customily-processing')) continue;\n      var cls = typeof child.className === 'string' ? child.className : '';\n      if (cls.indexOf('spinner') !== -1) {\n        child.removeAttribute('data-ds-processing-hidden');\n        clearImportant(child, ['display', 'flex', 'margin-left', 'margin-right']);\n        continue;\n      }\n      if (child.tagName !== 'SPAN') continue;\n      if (child.style.getPropertyPriority('display') === 'important') child.style.removeProperty('display');\n      var isReplace = !!child.querySelector('.cl-icon, svg');\n      var shouldHide = hasValue ? !isReplace : isReplace;\n      if (shouldHide) {\n        if (child.style.display !== 'none') child.style.display = 'none';\n      }\n    }\n  }\n\n  function paintProcessing() {\n    var root = document.getElementById('customily-options');\n    if (!root) return;\n    var css = getComputedStyle(document.documentElement);\n    var fontSize = css.getPropertyValue('--font-body-sm-size').trim();\n    var lineHeight = css.getPropertyValue('--font-body-sm-lh').trim();\n    var gap = css.getPropertyValue('--space-2').trim();\n    var buttons = root.querySelectorAll('.customily-download-button');\n    for (var i = 0; i < buttons.length; i++) {\n      var btn = buttons[i];\n      if (!isUploading(btn)) {\n        restoreUploadButton(btn);\n        continue;\n      }\n      btn.setAttribute('data-ds-processing-on', '');\n      setImportant(btn, 'display', 'inline-flex');\n      setImportant(btn, 'flex', '0 0 auto');\n      setImportant(btn, 'flex-direction', 'row');\n      setImportant(btn, 'justify-content', 'flex-start');\n      setImportant(btn, 'align-items', 'center');\n      setImportant(btn, 'width', 'fit-content');\n      setImportant(btn, 'max-width', '100%');\n      setImportant(btn, 'margin-left', '0');\n      setImportant(btn, 'margin-right', 'auto');\n      setImportant(btn, 'text-align', 'left');\n      var row = btn.parentElement;\n      if (row) setImportant(row, 'justify-content', 'flex-start');\n      var file = btn.closest ? btn.closest('.customily-file-input') : null;\n      if (file) {\n        setImportant(file, 'justify-content', 'flex-start');\n        setImportant(file, 'text-align', 'left');\n      }\n      /* Do not set display on Customily's own spans. v-show keeps Select\n         Image and Replace as inline style, and overwriting it leaves both\n         words on screen after the upload. CSS pulls those spans out of the\n         line only while the uploading classes are present. */\n      var spinner = null;\n      var kids = btn.children;\n      for (var k = 0; k < kids.length; k++) {\n        var child = kids[k];\n        if (child.hasAttribute('data-ds-customily-processing')) continue;\n        var cls = typeof child.className === 'string' ? child.className : '';\n        if (cls.indexOf('spinner') === -1) continue;\n        spinner = child;\n        setImportant(child, 'flex', '0 0 auto');\n        setImportant(child, 'margin-left', '0');\n        if (gap) setImportant(child, 'margin-right', gap);\n        child.setAttribute('data-ds-processing-hidden', '');\n      }\n      var label = btn.querySelector('[data-ds-customily-processing]');\n      if (!label) {\n        label = document.createElement('span');\n        label.setAttribute('data-ds-customily-processing', '');\n        label.textContent = 'Processing by AI ...';\n        btn.appendChild(label);\n      }\n      if (spinner && spinner.nextSibling !== label) btn.insertBefore(label, spinner.nextSibling);\n      setImportant(label, 'display', 'inline');\n      setImportant(label, 'flex', '0 0 auto');\n      setImportant(label, 'margin-left', '0px');\n      setImportant(label, 'margin-right', '0px');\n      setImportant(label, 'color', 'inherit');\n      setImportant(label, 'font-weight', 'inherit');\n      setImportant(label, 'white-space', 'nowrap');\n      setImportant(label, 'text-align', 'left');\n      if (fontSize) setImportant(label, 'font-size', fontSize);\n      if (lineHeight) setImportant(label, 'line-height', lineHeight);\n    }\n  }\n\n  var paintQueued = false;\n  function schedulePaint() {\n    if (paintQueued) return;\n    paintQueued = true;\n    window.requestAnimationFrame(function () {\n      paintQueued = false;\n      paintSwatches();\n      paintProcessing();\n    });\n  }\n\n  var swatchWatch = null;\n  var swatchRoot = null;\n  function watchSwatches() {\n    var root = document.getElementById('customily-options');\n    if (!root || !window.MutationObserver) return;\n    if (swatchRoot !== root) {\n      if (swatchWatch) swatchWatch.disconnect();\n      swatchRoot = root;\n      swatchWatch = new MutationObserver(function () {\n        paintSwatches();\n        schedulePaint();\n      });\n      swatchWatch.observe(root, {\n        subtree: true,\n        childList: true,\n        attributes: true,\n        attributeFilter: ['style', 'class'],\n      });\n    }\n    schedulePaint();\n  }\n\n  window.addEventListener('customily-preview-modal-show', function () {\n    resetAdding();\n    schedule();\n  });\n  document.addEventListener('click', function (event) {\n    var target = event.target;\n    if (!target || !target.closest) return;\n    if (!target.closest('#customily-preview-button, .customily-preview-button')) return;\n    resetAdding();\n    rememberPageCart();\n    schedule();\n    window.setTimeout(schedule, 400);\n  }, true);\n  if (window.MutationObserver) {\n    new MutationObserver(function () {\n      schedule();\n      watchSwatches();\n    }).observe(document.documentElement, {\n      childList: true,\n      subtree: true,\n    });\n  }\n  schedule();\n  watchSwatches();\n})();\n\n} catch (sfError) {\nif (typeof console !== 'undefined') console.error('[sf-core] behavior \"customily-preview\" failed to initialise:', sfError);\n}\n})();\n\n/* delivery-breakdown.js */\n(function () {\ntry {\n/* delivery-breakdown \u2014 opens the \"Delivery breakdown\" dialog from the PDP's\n * \"Order today and get it by\" line ([data-ds-delivery-breakdown-open]).\n *\n * Every number in the dialog is server-rendered by ds-delivery-info, so this\n * only flips [aria-hidden] (src/delivery-breakdown.css carries the motion),\n * traps Tab and returns focus.\n *\n * NOT a singleton: each card renders its own dialog, because each holds its own\n * numbers. ds-product-delivery has no block limit, so a trigger resolves the\n * dialog inside ITS card and only falls back to the first in the document \u2014\n * `document.querySelector` alone would open the first card's ETA from the\n * second card's line.\n *\n * Progressive enhancement: without this the trigger is inert and the dialog\n * stays hidden \u2014 the ETA is already printed on the card.\n *\n * Authoring rules: src/behaviors/README.md. No import/export.\n */\n\nvar DB_ROOT = '[data-ds-delivery-breakdown]';\nvar DB_FOCUSABLE = 'a[href], button:not([disabled]), input:not([disabled]), select, textarea, [tabindex]:not([tabindex=\"-1\"])';\n\nvar dbReturn = null;\nvar dbScroll = '';\n\n// The dialog a trigger belongs to: its own card first, the page's first one as\n// the fallback for a caller that mounts the dialog outside the card.\nfunction dbFor(trigger) {\n  var card = trigger && trigger.closest && trigger.closest('[data-ds-delivery-info]');\n  return (card && card.querySelector(DB_ROOT)) || document.querySelector(DB_ROOT);\n}\n\n// The one currently open \u2014 there can only be one, since opening locks the page.\nfunction dbOpenEl() {\n  var list = document.querySelectorAll(DB_ROOT);\n  for (var i = 0; i < list.length; i++) {\n    if (list[i].getAttribute('aria-hidden') === 'false') return list[i];\n  }\n  return null;\n}\n\nfunction dbOpen(root, trigger) {\n  dbReturn = trigger || document.activeElement;\n  dbScroll = document.body.style.overflow;\n  root.setAttribute('aria-hidden', 'false');\n  document.body.style.overflow = 'hidden';\n  window.requestAnimationFrame(function () {\n    var close = root.querySelector('[data-ds-delivery-breakdown-close]');\n    if (close) close.focus({ focusVisible: false });\n  });\n}\n\nfunction dbClose(root) {\n  if (!root || root.getAttribute('aria-hidden') === 'true') return;\n  root.setAttribute('aria-hidden', 'true');\n  // Put back whatever was there: another dialog may still hold its own lock.\n  document.body.style.overflow = dbScroll;\n  if (dbReturn && dbReturn.focus) dbReturn.focus();\n  dbReturn = null;\n}\n\nfunction dbTrap(root, event) {\n  var items = [].slice.call(root.querySelectorAll(DB_FOCUSABLE)).filter(function (el) {\n    return el.offsetParent !== null;\n  });\n  if (!items.length) return;\n  var first = items[0];\n  var last = items[items.length - 1];\n  if (event.shiftKey && document.activeElement === first) {\n    event.preventDefault();\n    last.focus();\n  } else if (!event.shiftKey && document.activeElement === last) {\n    event.preventDefault();\n    first.focus();\n  } else if (!root.contains(document.activeElement)) {\n    event.preventDefault();\n    first.focus();\n  }\n}\n\nif (!document.__dsDeliveryBreakdownBound) {\n  document.__dsDeliveryBreakdownBound = true;\n\n  document.addEventListener('click', function (event) {\n    if (!event.target.closest) return;\n\n    var trigger = event.target.closest('[data-ds-delivery-breakdown-open]');\n    if (trigger) {\n      var target = dbFor(trigger);\n      if (!target) return;\n      event.preventDefault();\n      dbOpen(target, trigger);\n      return;\n    }\n\n    var root = dbOpenEl();\n    if (!root) return;\n    // The scrim is the root itself; a click inside the card must not close it.\n    if (event.target.closest('[data-ds-delivery-breakdown-close]') || event.target === root) {\n      dbClose(root);\n    }\n  });\n\n  document.addEventListener('keydown', function (event) {\n    var root = dbOpenEl();\n    if (!root) return;\n    if (event.key === 'Escape') dbClose(root);\n    else if (event.key === 'Tab') dbTrap(root, event);\n  });\n}\n\n} catch (sfError) {\nif (typeof console !== 'undefined') console.error('[sf-core] behavior \"delivery-breakdown\" failed to initialise:', sfError);\n}\n})();\n\n/* gallery.js */\n(function () {\ntry {\n/* gallery \u2014 product gallery carousel: a NATIVE scroll-snap viewer plus prev/next\n * chevrons, thumbnail select, counter, and inline-video play/pause. Binds\n * [data-ds-gallery] and its data-ds-gallery-* children (see\n * snippets/ds-gallery.liquid).\n *\n * The viewer is a horizontal scroll-snap container \u2014 one slide per viewport,\n * exactly like ds-hero-slider. The finger-swipe on a phone is therefore the\n * BROWSER'S OWN native scroll (momentum, snap and all), which stays smooth even\n * inside a throttled in-app webview (Facebook/Instagram) where a hand-wired\n * JS transform-drag janks. This script only ADDS the enhancements on top of\n * that zero-JS baseline: it reads the active slide back from the scroll\n * position, moves the selection when a chevron or thumbnail is pressed (via\n * scrollTo, so swipe and click share ONE scroll position instead of fighting a\n * transform), restages the thumbnail ring + counter, and plays/pauses the\n * inline video. Progressive enhancement: without this script the first image +\n * all thumbs still render and the viewer still swipes; only the counter,\n * thumbnail ring and video autoplay are off.\n *\n * The thumbnail strip is a ds-thumbnail-rail (snippets/ds-thumbnail-rail.liquid),\n * whose tiles are ds-thumbnail atoms. This script is the shipped driver for that\n * rail when it sits inside a gallery: it moves the selection, restages the tile\n * ring, centres the selected tile, and swaps the counter. In Storybook\n * runtime/media-behaviour.ts drives the rail; the native swipe needs no JS.\n *\n * Authoring rules: src/behaviors/README.md. No import/export \u2014 the build wraps\n * this file in an IIFE and concatenates it into dist/sf-core.js.\n */\n\n// The two class sets ds-thumbnail toggles for `selected`. MUST match\n// selected_ring / live in snippets/ds-thumbnail.liquid \u2014 the snippet is the\n// source of truth and server-renders the active tile; this only restages it when\n// the selection changes. runtime/media-behaviour.ts mirrors the same sets;\n// update all three together.\nvar TILE_SELECTED = ['sf-border-secondary'];\nvar TILE_IDLE = ['hover:sf-border-strong', 'hover:sf-shadow-2', 'focus-visible:sf-outline', 'focus-visible:sf-outline-focus', 'focus-visible:sf-outline-offset-focus'];\n\n// The rail's SCROLL CONTAINER, which is not always the element carrying the\n// [data-ds-thumbnail-rail] hook. In ds-gallery that hook sits on a positioning\n// wrapper (lg:relative lg:w-gallery-rail, overflow visible) and the element that\n// actually scrolls is the ds-thumbnail-rail inside it \u2014 scrolling the wrapper is\n// a no-op, and its flex-direction is `row` even where the real strip is a column,\n// so the axis comes out wrong too. The rail snippet always stamps its own\n// orientation, so that attribute is what identifies the scroller; the hand-written\n// rail in ds-cart-photo-lightbox carries both on one element and still matches.\nfunction galleryRail(root) {\n  return root.querySelector('[data-ds-thumbnail-rail-orientation]') ||\n    root.querySelector('[data-ds-thumbnail-rail]');\n}\n\n// Centre the selected tile inside the rail. The rail hides its scrollbar\n// (matching the live storefronts), so a tile past the fold has no affordance\n// otherwise. Scrolls the RAIL only, never an ancestor, so a gallery sitting in a\n// sticky column doesn't drag the page. Clamped by scrollTo, so the first/last\n// tile simply rests against its end.\nfunction centerRailTile(rail, tile) {\n  var railBox = rail.getBoundingClientRect();\n  var tileBox = tile.getBoundingClientRect();\n  // Read the computed direction, not the orientation attribute: the responsive\n  // rail is a row on mobile and a column from lg, so the scroll axis flips.\n  var vertical = getComputedStyle(rail).flexDirection.indexOf('column') === 0;\n  var offset = vertical\n    ? tileBox.top - railBox.top - (railBox.height - tileBox.height) / 2\n    : tileBox.left - railBox.left - (railBox.width - tileBox.width) / 2;\n  // Honour a user who asked the OS for less motion; the jump still happens.\n  var smooth = !window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n  var target = vertical ? { top: rail.scrollTop + offset } : { left: rail.scrollLeft + offset };\n  target.behavior = smooth ? 'smooth' : 'auto';\n  rail.scrollTo(target);\n}\n\n// The active slide read back from the viewer's OWN scroll position \u2014 correct\n// after a native swipe that stopped between two snap points as much as after our\n// own scrollTo. Rounding picks the nearest snap point.\nfunction galleryIndexFromScroll(track) {\n  var w = track.clientWidth || 1;\n  return Math.round(track.scrollLeft / w);\n}\n\n// The neighbours are lazy (only the active slide renders eager), so the FIRST\n// swipe to each image would otherwise fetch+decode mid-scroll and stutter.\n// Warming the two adjacent slides the moment the active one changes keeps the\n// next swipe instant. Per the HTML spec, flipping an <img>'s loading from lazy\n// to eager starts the fetch immediately, even while the slide is off-screen.\nfunction preloadGalleryNeighbors(root, idx) {\n  var track = root.querySelector('[data-ds-gallery-track]');\n  if (!track) return;\n  [idx - 1, idx + 1].forEach(function (target) {\n    var img = track.querySelector('img[data-ds-gallery-slide][data-ds-gallery-index=\"' + target + '\"]');\n    if (img && img.loading === 'lazy') img.loading = 'eager';\n  });\n}\n\n// Silence a slide's video. Called for the slide being LEFT (and for every slide\n// when the lightbox closes), never for one that was not playing \u2014 an iframe\n// reload is not free, and a never-visited embed must not be touched.\n// A Shopify-hosted video is a real <video>, so pause() is enough. An\n// external_video is a third-party iframe we cannot control without its JS API\n// (YouTube ignores postMessage without enablejsapi), so reassigning src is the\n// portable stop: it drops the player back to its poster.\nfunction stopGalleryMedia(slide) {\n  if (!slide) return;\n  if (typeof slide.pause === 'function') {\n    slide.pause();\n    return;\n  }\n  var frame = slide.querySelector('iframe');\n  if (frame) frame.src = frame.src;\n}\n\n// Landing on a video slide starts it, so the shopper sees motion instead of a\n// still they have to press. Only the INLINE preview, which is the muted looping\n// one: the lightbox player is unmuted-capable and has controls, and a browser\n// blocks unprompted audio anyway, so starting that one would just be refused.\n// play() rejects on its own for a codec or policy the browser dislikes \u2014 the\n// poster stays, which is the honest fallback.\nfunction playGalleryMedia(slide) {\n  if (!slide || !slide.muted || typeof slide.play !== 'function') return;\n  var played = slide.play();\n  if (played && typeof played.catch === 'function') played.catch(function () {});\n}\n\n// The <video> for the slide that is currently active (null when the active slide\n// is an image or an external embed).\nfunction activeGalleryVideo(root) {\n  var idx = Number(root.dataset.dsGalleryActive || 0);\n  return root.querySelector('video[data-ds-gallery-video][data-ds-gallery-index=\"' + idx + '\"]');\n}\n\n// Play the active video only while the gallery is actually on-screen, pause it\n// otherwise. This is the \"scroll to the video and it plays\" behaviour: on the\n// PDP the gallery scrolls in and out of view as the shopper reads, and a muted\n// clip must not burn cycles behind the fold. Routed through here from BOTH a\n// slide change (syncGalleryUI) and a scroll (the observer below), so the two\n// share one play/pause decision. `__dsVisible` is undefined until the observer\n// first reports \u2014 and stays so where IntersectionObserver is missing \u2014 which we\n// read as visible, degrading to the plain \"play on active slide\" baseline.\nfunction updateGalleryPlayback(root) {\n  var video = activeGalleryVideo(root);\n  if (!video) return;\n  var hidden = galleryVisibility && root.__dsVisible === false;\n  if (hidden) {\n    video.pause();\n  } else {\n    playGalleryMedia(video);\n  }\n}\n\n// One observer for every gallery on the page: it flips each root's visibility\n// flag and re-evaluates playback. 0.5 = start once half the gallery is in view,\n// which on mobile means the shopper has actually scrolled the media into frame.\nvar galleryVisibility = typeof IntersectionObserver !== 'undefined'\n  ? new IntersectionObserver(function (entries) {\n      entries.forEach(function (entry) {\n        entry.target.__dsVisible = entry.isIntersecting;\n        updateGalleryPlayback(entry.target);\n      });\n    }, { threshold: 0.5 })\n  : null;\n\n// Watch a gallery for scroll-in/out. Idempotent \u2014 a re-render (Theme Editor) or\n// a second initGalleries pass must not stack duplicate observations.\nfunction observeGallery(root) {\n  if (!galleryVisibility || root.__dsObserved) return;\n  root.__dsObserved = true;\n  galleryVisibility.observe(root);\n}\n\n// Sync every derived bit of UI to `idx` WITHOUT scrolling. The scroll position\n// is the source of truth, so this is called FROM the scroll listener as the\n// track travels (native swipe or our own scrollTo alike) and once on init.\nfunction syncGalleryUI(root, idx, centerSelected) {\n  var count = Number(root.dataset.dsGalleryCount || 0);\n  if (!count) return;\n  idx = Math.max(0, Math.min(count - 1, idx));\n  var prev = Number(root.dataset.dsGalleryActive || 0);\n  if (prev !== idx) {\n    stopGalleryMedia(root.querySelector('[data-ds-gallery-video][data-ds-gallery-index=\"' + prev + '\"]'));\n  }\n  root.dataset.dsGalleryActive = String(idx);\n  // Play/pause the new active slide's video by whether the gallery is in view.\n  updateGalleryPlayback(root);\n  // Warm the slides on either side so the next swipe doesn't decode mid-scroll.\n  preloadGalleryNeighbors(root, idx);\n\n  var railWrap = root.querySelector('[data-ds-thumbnail-rail]');\n  if (railWrap) {\n    // `data-ds-thumbnail-rail` sits on a plain wrapper (ds-gallery.liquid); the\n    // element that actually overflows/scrolls is the ds-thumbnail-rail snippet\n    // root inside it (it carries the orientation attribute). Centre THAT one \u2014\n    // scrolling the non-overflowing wrapper is a no-op, so the active tile never\n    // came into view.\n    var rail = railWrap.querySelector('[data-ds-thumbnail-rail-orientation]') || railWrap;\n    rail.setAttribute('data-ds-thumbnail-rail-active', String(idx));\n    rail.querySelectorAll('[data-ds-thumbnail-rail-item]').forEach(function (item) {\n      var control = item.querySelector('[data-ds-thumbnail]');\n      if (!control) return;\n      var isActive = Number(item.getAttribute('data-ds-thumbnail-rail-index')) === idx;\n      // Toggle the ring atomically: strip both sets, re-add the right one.\n      TILE_SELECTED.forEach(function (c) { control.classList.remove(c); });\n      TILE_IDLE.forEach(function (c) { control.classList.remove(c); });\n      (isActive ? TILE_SELECTED : TILE_IDLE).forEach(function (c) { control.classList.add(c); });\n      control.setAttribute('aria-pressed', String(isActive));\n      if (isActive && centerSelected !== false) centerRailTile(rail, control);\n    });\n  }\n\n  var cur = root.querySelector('[data-ds-gallery-counter-current]');\n  if (cur) cur.textContent = String(idx + 1);\n  updateGalleryButtons(root);\n}\n\nfunction updateGalleryButtons(root) {\n  var count = Number(root.dataset.dsGalleryCount || 0);\n  var idx = Number(root.dataset.dsGalleryActive || 0);\n  var prev = root.querySelector('[data-ds-gallery-prev]');\n  var next = root.querySelector('[data-ds-gallery-next]');\n  if (prev) prev.disabled = count <= 1 || idx <= 0;\n  if (next) next.disabled = count <= 1 || idx >= count - 1;\n}\n\n// Scroll the viewer to slide `idx`. `instant` (and reduced-motion) override the\n// track's CSS scroll-behavior: smooth for a jump the shopper shouldn't watch\n// travel \u2014 opening the lightbox on the slide they swiped to, or the first paint.\nfunction scrollGalleryTo(root, idx, instant) {\n  var track = root.querySelector('[data-ds-gallery-track]');\n  if (!track) return;\n  var count = Number(root.dataset.dsGalleryCount || 0);\n  var clamped = Math.max(0, Math.min(Math.max(0, count - 1), idx));\n  var reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n  var left = clamped * (track.clientWidth || 1);\n  if (instant || reduce) {\n    // A real, single-frame jump \u2014 used to keep the inline gallery and its\n    // full-screen twin on the same slide when the lightbox opens/closes (and on\n    // first paint). scrollTo({behavior:'auto'}) is spec-instant, but Safari and\n    // in-app WebViews (Facebook/Instagram) still ANIMATE it on a track that is\n    // scroll-snap + scroll-behavior:smooth \u2014 that stray glide is the \"slide\" the\n    // paired gallery showed. Forcing scroll-behavior:auto inline (inline wins over\n    // the sf-scroll-smooth class) and writing scrollLeft lands it with no motion\n    // in every engine; restore the previous inline value next frame so ordinary\n    // swipes and chevron moves stay smooth.\n    var previousBehavior = track.style.scrollBehavior;\n    track.style.scrollBehavior = 'auto';\n    track.scrollLeft = left;\n    requestAnimationFrame(function () {\n      track.style.scrollBehavior = previousBehavior;\n    });\n    return;\n  }\n  track.scrollTo({ left: left, behavior: 'smooth' });\n}\n\n// Programmatic move (chevron, thumbnail, ds:gallery:set). Scrolls the viewer;\n// the scroll listener restages the UI as the track travels. An `instant` jump\n// may not fire a scroll event (already at target), so sync the UI directly too.\nfunction setGalleryActive(root, next, centerSelected, instant) {\n  var count = Number(root.dataset.dsGalleryCount || 0);\n  if (!count) return;\n  var idx = Math.max(0, Math.min(count - 1, next));\n  scrollGalleryTo(root, idx, instant);\n  if (instant) syncGalleryUI(root, idx, centerSelected);\n}\n\n// Restage the UI from the live scroll position, at most once per frame \u2014 a swipe\n// fires scroll far more often than the active slide actually changes.\nfunction bindGalleryScroll(root) {\n  if (root.__dsGalleryScrollBound) return;\n  var track = root.querySelector('[data-ds-gallery-track]');\n  if (!track) return;\n  root.__dsGalleryScrollBound = true;\n  var queued = false;\n  track.addEventListener('scroll', function () {\n    if (queued) return;\n    queued = true;\n    requestAnimationFrame(function () {\n      queued = false;\n      var idx = galleryIndexFromScroll(track);\n      if (idx !== Number(root.dataset.dsGalleryActive || 0)) syncGalleryUI(root, idx);\n    });\n  }, { passive: true });\n}\n\n// The media column sticks at `--space-6` from lg. The photo note hangs off\n// the bottom of that column, so while the column is pinned the last line sits\n// below the viewport until the column unsticks at the end of the section.\n// Cap the preview (`--ds-preview-max`) so the buttons and the full note fit.\n// If the chrome alone is already too tall, pull `top` up instead.\nfunction mediaStickyGap() {\n  var n = parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--space-6'));\n  return n > 0 ? n : 24;\n}\n\nfunction mediaBottomCover() {\n  var bar = document.querySelector('.sticky-btn-container');\n  if (!bar || !bar.getClientRects().length) return 0;\n  var pos = getComputedStyle(bar).position;\n  if (pos !== 'fixed' && pos !== 'sticky') return 0;\n  var rect = bar.getBoundingClientRect();\n  if (rect.top >= window.innerHeight) return 0;\n  return Math.max(0, window.innerHeight - rect.top);\n}\n\nfunction clearProductMediaFit(col) {\n  col.style.top = '';\n  col.style.removeProperty('--ds-preview-max');\n}\n\n// Natural height of the preview at the column's full width. Reading the\n// rendered height loops: a cap shrinks it, the next pass thinks it fits, and\n// the cap comes off.\nfunction canvasNaturalHeight(canvas) {\n  var parent = canvas.parentElement;\n  var fullW = parent ? parent.clientWidth : 0;\n  if (!canvas.width || !canvas.height || !fullW) return canvas.getBoundingClientRect().height;\n  return (canvas.height / canvas.width) * fullW;\n}\n\nfunction fitProductMedia(col) {\n  if (getComputedStyle(col).position !== 'sticky') {\n    clearProductMediaFit(col);\n    return;\n  }\n  var controls = col.querySelector('.customily-mobile-controls');\n  var canvas = col.querySelector('[data-ds-gallery-viewer] > .cl-canvas-container canvas');\n  if (!controls || !controls.getClientRects().length || !canvas) {\n    clearProductMediaFit(col);\n    return;\n  }\n  var gap = mediaStickyGap();\n  var cover = Math.max(gap, mediaBottomCover());\n  var rendered = canvas.getBoundingClientRect().height;\n  var chrome = col.getBoundingClientRect().height - rendered;\n  var maxH = window.innerHeight - gap - cover - chrome;\n  var natural = canvasNaturalHeight(canvas);\n  if (natural > maxH + 1 && maxH >= 160) {\n    var next = Math.floor(maxH) + 'px';\n    if (col.style.getPropertyValue('--ds-preview-max') !== next) {\n      col.style.setProperty('--ds-preview-max', next);\n    }\n    col.style.top = '';\n    return;\n  }\n  col.style.removeProperty('--ds-preview-max');\n  var top = Math.min(gap, window.innerHeight - cover - col.getBoundingClientRect().height);\n  if (top < gap - 0.5) col.style.top = Math.round(top) + 'px';\n  else col.style.top = '';\n}\n\nfunction bindProductMediaFit(root) {\n  var col = root.closest('[data-ds-product-media]');\n  if (!col || col.__dsMediaFit) return;\n  col.__dsMediaFit = true;\n  if (window.ResizeObserver) new ResizeObserver(function () { fitProductMedia(col); }).observe(col);\n  fitProductMedia(col);\n}\n\nfunction initGalleries() {\n  document.querySelectorAll('[data-ds-gallery]').forEach(function (root) {\n    observeGallery(root);\n    bindGalleryScroll(root);\n    bindProductMediaFit(root);\n    var idx = Number(root.dataset.dsGalleryActive || 0);\n    // Place the scroll at the server-rendered active slide with no animation,\n    // then sync the derived UI. rAF: the track's clientWidth is 0 until layout.\n    requestAnimationFrame(function () {\n      scrollGalleryTo(root, idx, true);\n      syncGalleryUI(root, idx, false);\n    });\n  });\n}\n\nif (!document.__dsGalleryBound) {\n  document.__dsGalleryBound = true;\n  if (document.readyState === 'loading') {\n    document.addEventListener('DOMContentLoaded', initGalleries, { once: true });\n  } else {\n    initGalleries();\n  }\n  // The Theme Editor re-renders a section into a fresh subtree; re-run so the new\n  // gallery gets its scroll listener bound and its position set. Idempotent:\n  // existing galleries just re-assert the slide they already show.\n  document.addEventListener('shopify:section:load', initGalleries);\n  window.addEventListener('resize', function () {\n    document.querySelectorAll('[data-ds-product-media]').forEach(fitProductMedia);\n  });\n  if (window.MutationObserver) {\n    new MutationObserver(function () {\n      document.querySelectorAll('[data-ds-product-media]').forEach(fitProductMedia);\n    }).observe(document.body, { childList: true });\n  }\n  document.addEventListener('click', function (e) {\n    var t = e.target;\n    if (!t || !t.closest) return;\n    var root = t.closest('[data-ds-gallery]');\n    if (!root) return;\n    var current = Number(root.dataset.dsGalleryActive || 0);\n\n    if (t.closest('[data-ds-gallery-prev]')) { setGalleryActive(root, current - 1); return; }\n    if (t.closest('[data-ds-gallery-next]')) { setGalleryActive(root, current + 1); return; }\n\n    var item = t.closest('[data-ds-thumbnail-rail-item]');\n    if (item) {\n      var idx = Number(item.getAttribute('data-ds-thumbnail-rail-index'));\n      if (!Number.isNaN(idx)) setGalleryActive(root, idx);\n    }\n  });\n\n  // Programmatic jump. media-lightbox.js dispatches this to keep the inline\n  // gallery and its full-screen twin on the same image; a variant switch could\n  // use it too. Delegated on document so galleries mounted later still respond.\n  document.addEventListener('ds:gallery:set', function (e) {\n    var root = e.target && e.target.closest && e.target.closest('[data-ds-gallery]');\n    if (!root) return;\n    var idx = Number(e.detail && e.detail.index);\n    // `instant` skips the slide animation \u2014 the open/close sync is a jump to a\n    // known slide, not a swipe the shopper should watch travel.\n    var instant = !!(e.detail && e.detail.instant);\n    if (!Number.isNaN(idx)) setGalleryActive(root, idx, false, instant);\n  });\n\n  // Stop everything playing in a gallery. media-lightbox.js dispatches this when\n  // the modal closes \u2014 otherwise a video keeps playing (audible) behind a modal\n  // that is only [aria-hidden], still painted and never unmounted.\n  document.addEventListener('ds:gallery:stop', function (e) {\n    var root = e.target && e.target.closest && e.target.closest('[data-ds-gallery]');\n    if (!root) return;\n    root.querySelectorAll('[data-ds-gallery-video]').forEach(stopGalleryMedia);\n  });\n}\n\n} catch (sfError) {\nif (typeof console !== 'undefined') console.error('[sf-core] behavior \"gallery\" failed to initialise:', sfError);\n}\n})();\n\n/* header-nav.js */\n(function () {\ntry {\n/* header-nav \u2014 adds Amazon-style menu aim to desktop mega menus and controls\n * the mobile hamburger drawer with its single-open accordion branches.\n * Delegated listeners keep it Theme-Editor-safe and work for async Storybook\n * mounts. No import/export: the build wraps this file in an IIFE.\n */\n\n(function () {\n  if (document.__dsHeaderNavBound) return;\n  document.__dsHeaderNavBound = true;\n\n  var DESKTOP_QUERY = '(min-width: 1024px) and (hover: hover) and (pointer: fine)';\n  var SAFE_TRIANGLE_TIMEOUT = 500;\n  var CLOSE_DELAY = 120;\n  var navState = new WeakMap();\n\n  // Marker: CSS keeps :hover/:focus-within as the no-JS fallback; once this\n  // attribute is set, header-nav.css hands fine-pointer hover to menu-aim.\n  document.documentElement.setAttribute('data-ds-header-menu-aim', '');\n\n  function desktopPointer(event) {\n    return event.pointerType === 'mouse' && window.matchMedia(DESKTOP_QUERY).matches;\n  }\n\n  function stateFor(nav) {\n    var state = navState.get(nav);\n    if (!state) {\n      state = {\n        active: null,\n        pending: null,\n        triangle: null,\n        lastPoint: null,\n        closeTimer: 0,\n        aimTimer: 0,\n      };\n      navState.set(nav, state);\n    }\n    return state;\n  }\n\n  function menuFor(item) {\n    return item && item.querySelector(':scope > [data-ds-header-mega-menu]');\n  }\n\n  function setDesktopItem(item, open) {\n    if (!item) return;\n    item.toggleAttribute('data-ds-menu-open', open);\n    var trigger = item.querySelector(':scope > [data-ds-header-nav-trigger]');\n    if (trigger) trigger.setAttribute('aria-expanded', open ? 'true' : 'false');\n  }\n\n  function cancelTimers(state) {\n    window.clearTimeout(state.closeTimer);\n    window.clearTimeout(state.aimTimer);\n    state.closeTimer = 0;\n    state.aimTimer = 0;\n  }\n\n  function activateDesktopItem(nav, item) {\n    var state = stateFor(nav);\n    cancelTimers(state);\n    if (state.active !== item) setDesktopItem(state.active, false);\n    state.active = menuFor(item) ? item : null;\n    state.pending = null;\n    state.triangle = null;\n    state.lastPoint = null;\n    nav.removeAttribute('data-ds-menu-aiming');\n    setDesktopItem(state.active, true);\n  }\n\n  function closeDesktopNav(nav) {\n    var state = stateFor(nav);\n    cancelTimers(state);\n    setDesktopItem(state.active, false);\n    state.active = null;\n    state.pending = null;\n    state.triangle = null;\n    state.lastPoint = null;\n    nav.removeAttribute('data-ds-menu-aiming');\n  }\n\n  function pointInTriangle(point, a, b, c) {\n    function sign(p1, p2, p3) {\n      return (p1.x - p3.x) * (p2.y - p3.y) - (p2.x - p3.x) * (p1.y - p3.y);\n    }\n    var d1 = sign(point, a, b);\n    var d2 = sign(point, b, c);\n    var d3 = sign(point, c, a);\n    var hasNegative = d1 < 0 || d2 < 0 || d3 < 0;\n    var hasPositive = d1 > 0 || d2 > 0 || d3 > 0;\n    return !(hasNegative && hasPositive);\n  }\n\n  function armSafeTriangle(nav, event) {\n    var state = stateFor(nav);\n    var menu = menuFor(state.active);\n    if (!menu) return;\n    var bounds = menu.getBoundingClientRect();\n    var edgeY = bounds.top + 8;\n    var apex = state.lastPoint || { x: event.clientX, y: event.clientY };\n    state.triangle = {\n      startedAt: Date.now(),\n      apex: { x: apex.x, y: apex.y },\n      left: { x: bounds.left - 12, y: edgeY },\n      right: { x: bounds.right + 12, y: edgeY },\n    };\n  }\n\n  function insideSafeTriangle(state, event) {\n    var triangle = state.triangle;\n    if (!triangle || Date.now() - triangle.startedAt > SAFE_TRIANGLE_TIMEOUT) return false;\n    return pointInTriangle(\n      { x: event.clientX, y: event.clientY },\n      triangle.apex,\n      triangle.left,\n      triangle.right,\n    );\n  }\n\n  function deferToSafeTriangle(nav, item) {\n    var state = stateFor(nav);\n    cancelTimers(state);\n    state.pending = item;\n    nav.setAttribute('data-ds-menu-aiming', '');\n    state.aimTimer = window.setTimeout(function () {\n      activateDesktopItem(nav, state.pending);\n    }, SAFE_TRIANGLE_TIMEOUT);\n  }\n\n  function scheduleDesktopClose(nav) {\n    var state = stateFor(nav);\n    window.clearTimeout(state.closeTimer);\n    state.closeTimer = window.setTimeout(function () {\n      var activeMenu = menuFor(state.active);\n      if (state.active && !state.active.matches(':hover') && (!activeMenu || !activeMenu.matches(':hover'))) {\n        closeDesktopNav(nav);\n      }\n    }, CLOSE_DELAY);\n  }\n\n  document.addEventListener('pointerover', function (event) {\n    if (!desktopPointer(event) || !event.target.closest) return;\n    var item = event.target.closest('[data-ds-header-nav-item]');\n    var nav = item && item.closest('[data-ds-header-nav]');\n    if (!nav || (event.relatedTarget && item.contains(event.relatedTarget))) return;\n\n    var state = stateFor(nav);\n    window.clearTimeout(state.closeTimer);\n    state.closeTimer = 0;\n\n    if (state.active === item) return;\n    if (state.active && insideSafeTriangle(state, event)) {\n      deferToSafeTriangle(nav, item);\n      return;\n    }\n    activateDesktopItem(nav, item);\n    stateFor(nav).lastPoint = { x: event.clientX, y: event.clientY };\n  });\n\n  document.addEventListener('pointerout', function (event) {\n    if (!desktopPointer(event) || !event.target.closest) return;\n    var item = event.target.closest('[data-ds-header-nav-item]');\n    var nav = item && item.closest('[data-ds-header-nav]');\n    if (!nav || stateFor(nav).active !== item) return;\n    if (event.relatedTarget && item.contains(event.relatedTarget)) return;\n    armSafeTriangle(nav, event);\n    scheduleDesktopClose(nav);\n  });\n\n  document.addEventListener('pointermove', function (event) {\n    if (!desktopPointer(event) || !event.target.closest) return;\n    var nav = event.target.closest('[data-ds-header-nav]');\n    if (!nav) return;\n    var state = stateFor(nav);\n    if (!state.pending) {\n      var activeItem = event.target.closest('[data-ds-header-nav-item]');\n      if (activeItem && activeItem === state.active) {\n        state.lastPoint = { x: event.clientX, y: event.clientY };\n      }\n      return;\n    }\n    if (insideSafeTriangle(state, event)) return;\n\n    var hoveredItem = event.target.closest('[data-ds-header-nav-item]');\n    if (hoveredItem && hoveredItem.closest('[data-ds-header-nav]') === nav) {\n      activateDesktopItem(nav, hoveredItem);\n    } else {\n      closeDesktopNav(nav);\n    }\n  });\n\n  // Keep aria-expanded aligned with the keyboard path.\n  document.addEventListener('focusin', function (event) {\n    if (!event.target.closest || !window.matchMedia('(min-width: 1024px)').matches) return;\n    var item = event.target.closest('[data-ds-header-nav-item]');\n    var nav = item && item.closest('[data-ds-header-nav]');\n    if (nav && menuFor(item)) activateDesktopItem(nav, item);\n  });\n\n  document.addEventListener('focusout', function (event) {\n    if (!event.target.closest) return;\n    var nav = event.target.closest('[data-ds-header-nav]');\n    if (!nav || (event.relatedTarget && nav.contains(event.relatedTarget))) return;\n    window.setTimeout(function () {\n      if (!nav.contains(document.activeElement) && !nav.matches(':hover')) closeDesktopNav(nav);\n    }, 0);\n  });\n\n  function parts(trigger) {\n    var header = trigger && trigger.closest('[data-ds-header]');\n    var menu = header && header.querySelector('[data-ds-header-mobile-menu]');\n    return { header: header, menu: menu };\n  }\n\n  function sizeDrawer(menu) {\n    if (!menu) return;\n    var header = menu.closest('[data-ds-header]');\n    var bar = header && header.querySelector('[data-ds-header-main-row]');\n    var edge = bar || header;\n    var top = edge ? edge.getBoundingClientRect().bottom : menu.getBoundingClientRect().top;\n    /* Floor so a fractional edge never leaves a hairline of the page between\n       the search row and the sheet. */\n    top = Math.max(0, Math.min(window.innerHeight, Math.floor(top)));\n    menu.style.setProperty('--ds-header-mobile-menu-top', top + 'px');\n    menu.style.setProperty('--ds-header-mobile-menu-max-height', Math.max(0, window.innerHeight - top) + 'px');\n  }\n\n  function syncPageScrollLock() {\n    var hasOpenDrawer = document.querySelector(\n      '[data-ds-header-mobile-menu][aria-hidden=\"false\"]',\n    );\n    document.documentElement.toggleAttribute('data-ds-header-nav-scroll-lock', Boolean(hasOpenDrawer));\n  }\n\n  function setDrawer(trigger, open) {\n    var found = parts(trigger);\n    if (!found.menu) return;\n    trigger.setAttribute('aria-controls', found.menu.id);\n    if (open) sizeDrawer(found.menu);\n    found.menu.setAttribute('aria-hidden', open ? 'false' : 'true');\n    trigger.setAttribute('aria-expanded', open ? 'true' : 'false');\n    trigger.setAttribute(\n      'aria-label',\n      open ? trigger.getAttribute('data-label-close') : trigger.getAttribute('data-label-open'),\n    );\n    var iconOpen = trigger.querySelector('[data-ds-header-menu-icon-open]');\n    var iconClose = trigger.querySelector('[data-ds-header-menu-icon-close]');\n    if (iconOpen) iconOpen.classList.toggle('sf-hidden', open);\n    if (iconClose) iconClose.classList.toggle('sf-hidden', !open);\n    syncPageScrollLock();\n  }\n\n  function setBranch(item, open) {\n    var toggle = item.querySelector('[data-ds-mobile-menu-toggle]');\n    var panel = item.querySelector('[data-ds-mobile-menu-panel]');\n    var label = item.querySelector('[data-ds-mobile-menu-label]');\n    var icon = item.querySelector('[data-ds-mobile-menu-icon]');\n    var plus = item.querySelector('[data-ds-mobile-menu-plus]');\n    var minus = item.querySelector('[data-ds-mobile-menu-minus]');\n    if (!toggle || !panel) return;\n\n    toggle.setAttribute('aria-expanded', open ? 'true' : 'false');\n    toggle.classList.toggle('sf-bg-badge-brand', open);\n    toggle.classList.toggle('hover:sf-bg-surface-hover', !open);\n    if (label) {\n      label.classList.toggle('sf-text-action-primary-strong', open);\n      label.classList.toggle('sf-text-primary', !open);\n    }\n    if (icon) {\n      icon.classList.toggle('sf-text-action-primary-strong', open);\n      icon.classList.toggle('sf-text-primary', !open);\n    }\n    if (plus) plus.classList.toggle('sf-hidden', open);\n    if (minus) minus.classList.toggle('sf-hidden', !open);\n\n    // Animate height instead of snapping display, so opening one branch and\n    // closing another (single-open accordion) doesn't jump the layout.\n    if (open) {\n      panel.style.maxHeight = 'none';\n      var h = panel.scrollHeight;\n      panel.style.maxHeight = '0px';\n      requestAnimationFrame(function () { panel.style.maxHeight = h + 'px'; });\n      panel.addEventListener('transitionend', function onEnd() {\n        panel.style.maxHeight = 'none';\n        panel.removeEventListener('transitionend', onEnd);\n      });\n    } else {\n      panel.style.maxHeight = panel.scrollHeight + 'px';\n      requestAnimationFrame(function () { panel.style.maxHeight = '0px'; });\n    }\n  }\n\n  document.addEventListener('click', function (event) {\n    if (!event.target.closest) return;\n\n    var menuTrigger = event.target.closest('[data-ds-header-menu-button]');\n    if (menuTrigger) {\n      event.preventDefault();\n      setDrawer(menuTrigger, menuTrigger.getAttribute('aria-expanded') !== 'true');\n      return;\n    }\n\n    var branchToggle = event.target.closest('[data-ds-mobile-menu-toggle]');\n    if (!branchToggle) return;\n    event.preventDefault();\n    var item = branchToggle.closest('[data-ds-mobile-menu-item]');\n    var drawer = branchToggle.closest('[data-ds-header-mobile-menu]');\n    if (!item || !drawer) return;\n    var willOpen = branchToggle.getAttribute('aria-expanded') !== 'true';\n    // The single-open accordion collapses whichever sibling is open. Capture it\n    // before closing so we can predict how far this heading will shift.\n    var closingPanel = null;\n    if (willOpen) {\n      var openSibling = drawer.querySelector('[data-ds-mobile-menu-toggle][aria-expanded=\"true\"]');\n      openSibling = openSibling && openSibling.closest('[data-ds-mobile-menu-item]');\n      if (openSibling && openSibling !== item) closingPanel = openSibling.querySelector('[data-ds-mobile-menu-panel]');\n    }\n    drawer.querySelectorAll('[data-ds-mobile-menu-item]').forEach(function (candidate) {\n      if (candidate !== item) setBranch(candidate, false);\n    });\n    setBranch(item, willOpen);\n    // Long panels can push the just-opened heading off-screen; pin it to the top\n    // of the drawer's own scroll area (not scrollIntoView \u2014 the drawer is\n    // overflow:hidden, and browsers scroll that ancestor too, corrupting layout).\n    var scroller = drawer.querySelector('[data-ds-header-mobile-menu-scroll]');\n    if (willOpen && scroller) {\n      var openPanel = item.querySelector('[data-ds-mobile-menu-panel]');\n      // Predict the settled scroll target and reposition there immediately.\n      // The panels already animate max-height; smooth-scrolling at the same\n      // time creates a competing animation against a layout that is moving.\n      // The heading only shifts by the height of a sibling collapsing ABOVE it;\n      // its own panel opens below the heading and never moves it. max-height:0\n      // does not hide a panel from scrollHeight, so the open/close heights are\n      // readable up front, before any transition runs.\n      var toggleTop = branchToggle.getBoundingClientRect().top\n        - scroller.getBoundingClientRect().top + scroller.scrollTop;\n      var closingH = closingPanel ? closingPanel.scrollHeight : 0;\n      var above = closingPanel\n        && !(branchToggle.compareDocumentPosition(closingPanel) & Node.DOCUMENT_POSITION_FOLLOWING);\n      var finalTop = toggleTop - (above ? closingH : 0);\n      var finalContent = scroller.scrollHeight - closingH + (openPanel ? openPanel.scrollHeight : 0);\n      var finalMax = finalContent - scroller.clientHeight;\n      scroller.style.paddingBottom = '';\n      if (finalMax > 0) {\n        // Reserve just enough trailing space that even the last, short branch\n        // can bring its heading all the way to the top.\n        var pad = Math.ceil(finalTop - finalMax);\n        if (pad > 0) scroller.style.paddingBottom = pad + 'px';\n        scroller.scrollTo({ top: Math.max(0, finalTop), behavior: 'auto' });\n      }\n    }\n  });\n\n  document.addEventListener('keydown', function (event) {\n    if (event.key !== 'Escape') return;\n    document.querySelectorAll('[data-ds-header-menu-button][aria-expanded=\"true\"]').forEach(function (trigger) {\n      setDrawer(trigger, false);\n      trigger.focus();\n    });\n  });\n\n  document.addEventListener(\n    'touchmove',\n    function (event) {\n      if (!document.documentElement.hasAttribute('data-ds-header-nav-scroll-lock')) return;\n      if (event.target.closest && event.target.closest('[data-ds-header-mobile-menu]')) return;\n      if (event.target.closest && event.target.closest('[data-ds-region-modal][aria-hidden=\"false\"]')) return;\n      event.preventDefault();\n    },\n    { passive: false },\n  );\n\n  function sizeOpenDrawers() {\n    document.querySelectorAll('[data-ds-header-mobile-menu][aria-hidden=\"false\"]').forEach(sizeDrawer);\n  }\n\n  /* The drawer top is the search row's bottom edge. Dismissing the\n     announcement bar shortens the header while the drawer stays open; a size\n     observer misses that when the bar's wrapper collapses without resizing\n     the header box, so also remeasure when header children change. */\n  if (typeof ResizeObserver === 'function') {\n    var headerBox = new ResizeObserver(sizeOpenDrawers);\n    function watchHeaders(root) {\n      (root || document).querySelectorAll('[data-ds-header]').forEach(function (header) {\n        headerBox.observe(header);\n        if (header.__dsDrawerWatch || typeof MutationObserver !== 'function') return;\n        header.__dsDrawerWatch = true;\n        new MutationObserver(sizeOpenDrawers).observe(header, { childList: true, subtree: true });\n      });\n    }\n    watchHeaders();\n    document.addEventListener('shopify:section:load', function (event) {\n      watchHeaders(event.target);\n    });\n  }\n\n  document.addEventListener('ds:header-announcement-dismiss', function () {\n    sizeOpenDrawers();\n    requestAnimationFrame(sizeOpenDrawers);\n  });\n\n  window.addEventListener('resize', function () {\n    if (window.innerWidth >= 1024) {\n      document.querySelectorAll('[data-ds-header-menu-button][aria-expanded=\"true\"]').forEach(function (trigger) {\n        setDrawer(trigger, false);\n      });\n      return;\n    }\n    document.querySelectorAll('[data-ds-header-nav]').forEach(closeDesktopNav);\n    sizeOpenDrawers();\n  });\n})();\n\n} catch (sfError) {\nif (typeof console !== 'undefined') console.error('[sf-core] behavior \"header-nav\" failed to initialise:', sfError);\n}\n})();\n\n/* header-search.js */\n(function () {\ntry {\n/* header-search \u2014 below lg, focusing the header search pill expands it to the\n * full row: the brand cluster and the utility actions give way and a Cancel\n * control appears (Figma 2477:10415 / 7209:5419). One attribute on the row\n * carries the state; the CSS in the markup does the rest, so with no JS the\n * field is simply an inline search box that still submits.\n * Delegated listeners keep it Theme-Editor-safe. No import/export.\n */\n\n(function () {\n  if (document.__dsHeaderSearchBound) return;\n  document.__dsHeaderSearchBound = true;\n\n  var MOBILE = '(max-width: 1023px)';\n\n  function rowFor(el) {\n    return el && el.closest ? el.closest('[data-ds-header-main-row] > div') : null;\n  }\n\n  function setOpen(row, open) {\n    if (!row) return;\n    row.toggleAttribute('data-ds-search-open', open);\n  }\n\n  function syncPlaceholder(form) {\n    var field = form && form.querySelector ? form.querySelector('input') : null;\n    if (!field) return;\n    var next = window.matchMedia(MOBILE).matches\n      ? form.getAttribute('data-placeholder-mobile')\n      : form.getAttribute('data-placeholder-desktop');\n    if (next) field.setAttribute('placeholder', next);\n  }\n\n  function syncPlaceholders() {\n    document.querySelectorAll('[data-ds-header-search]').forEach(syncPlaceholder);\n  }\n\n  syncPlaceholders();\n  document.addEventListener('shopify:section:load', syncPlaceholders);\n\n  document.addEventListener('focusin', function (event) {\n    if (!event.target.closest || !window.matchMedia(MOBILE).matches) return;\n    if (!event.target.closest('[data-ds-header-search]')) return;\n    // The expanded row hides the hamburger, and the drawer has no other close\n    // control \u2014 leaving both open would trap a touch user behind a scroll lock.\n    document.querySelectorAll('[data-ds-header-menu-button][aria-expanded=\"true\"]').forEach(function (trigger) {\n      trigger.click();\n    });\n    setOpen(rowFor(event.target), true);\n  });\n\n  // Dismissing the keyboard by tapping the page blurs the field without a\n  // Cancel click; without this the row stays expanded and the utility actions\n  // (account, cart) remain hidden with no way back. Deferred by a task so a\n  // Cancel click, which blurs the field first, still lands on its own handler.\n  document.addEventListener('focusout', function (event) {\n    if (!event.target.closest || !event.target.closest('[data-ds-header-search]')) return;\n    var row = rowFor(event.target);\n    window.setTimeout(function () {\n      if (row && !row.contains(document.activeElement)) setOpen(row, false);\n    }, 0);\n  });\n\n  document.addEventListener('click', function (event) {\n    if (!event.target.closest) return;\n    var cancel = event.target.closest('[data-ds-header-search-cancel]');\n    if (!cancel) return;\n    event.preventDefault();\n    var row = rowFor(cancel);\n    var field = row && row.querySelector('[data-ds-header-search] input');\n    if (field) {\n      field.value = '';\n      field.blur();\n    }\n    setOpen(row, false);\n  });\n\n  // The field's own clear X (shown by CSS only while there is text): empty it,\n  // tell anything listening on `input` (a predictive-search app) that it\n  // changed, and keep the caret in the field for the next query.\n  document.addEventListener('click', function (event) {\n    if (!event.target.closest) return;\n    var clear = event.target.closest('[data-ds-header-search-clear]');\n    if (!clear) return;\n    event.preventDefault();\n    var field = clear.closest('[data-ds-header-search]').querySelector('input');\n    if (!field) return;\n    field.value = '';\n    field.dispatchEvent(new Event('input', { bubbles: true }));\n    field.focus();\n  });\n\n  // The search button must never run an empty query: tapping it with a blank\n  // field focuses the field instead of navigating to a bare /search \u2014 and below\n  // lg that focus also expands the collapsed pill (via focusin). Universal, so it\n  // holds in the collapsed pill, the expanded row, and the desktop bar alike.\n  // Handled on the click (not the submit event) because a predictive-search app\n  // can swallow the submit in the capture phase; cancelling the button's click\n  // stops the form from ever submitting. A real query is left untouched, and\n  // with no JS the form still posts normally.\n  document.addEventListener('click', function (event) {\n    if (!event.target.closest) return;\n    var submit = event.target.closest('[data-ds-header-search] button[type=\"submit\"]');\n    if (!submit) return;\n    var field = submit.closest('[data-ds-header-search]').querySelector('input');\n    if (field && field.value.trim() !== '') return;\n    event.preventDefault();\n    if (field) {\n      field.focus();\n      // Forward the tap to the field itself so a predictive-search app that opens\n      // its panel on the input's click (not just focus) still opens \u2014 same as\n      // tapping the field directly. Safe: clicking an input submits nothing, and\n      // this click's target is the input, so the guard above skips it.\n      field.click();\n    }\n  });\n\n  // Enter in an empty field would submit past the click guard above; block it so\n  // a blank query never runs from the keyboard either.\n  document.addEventListener('keydown', function (event) {\n    if (event.key !== 'Enter' || !event.target.closest) return;\n    if (!event.target.closest('[data-ds-header-search]')) return;\n    if (event.target.value != null && event.target.value.trim() === '') {\n      event.preventDefault();\n    }\n  });\n\n  document.addEventListener('keydown', function (event) {\n    if (event.key !== 'Escape' || !event.target.closest) return;\n    if (!event.target.closest('[data-ds-header-search]')) return;\n    var row = rowFor(event.target);\n    if (event.target.blur) event.target.blur();\n    setOpen(row, false);\n  });\n\n  // Above lg the row is never in the expanded state \u2014 the pill always sits\n  // between the brand and the utility actions.\n  window.addEventListener('resize', function () {\n    syncPlaceholders();\n    if (window.matchMedia(MOBILE).matches) return;\n    document.querySelectorAll('[data-ds-search-open]').forEach(function (row) {\n      setOpen(row, false);\n    });\n  });\n})();\n\n} catch (sfError) {\nif (typeof console !== 'undefined') console.error('[sf-core] behavior \"header-search\" failed to initialise:', sfError);\n}\n})();\n\n/* hero-slider.js */\n(function () {\ntry {\n/* hero-slider \u2014 homepage hero carousel: arrows, dots, autoplay.\n *\n * Autoplay does not start with the section: it arms after `load` plus a grace\n * window, or on the shopper's first touch, whichever comes first \u2014 advancing\n * while the page still loads competes with it and moves the banner out from\n * under someone who has not read it yet. See arm() for the reasoning, and for\n * the LCP claim this is NOT.\n *\n * The track is a scroll-snap container (snippets/ds-hero-slider.liquid), so\n * swipe already works with no JS and the first slide is always visible. This\n * only adds the controls: scrollTo the neighbouring slide, restage the dots\n * from the observed scroll position, advance on a timer when the section asks\n * for autoplay, and loop in both directions through a clone at each end (see\n * the clone block in bindHero).\n *\n * Driving the native scroll position (rather than a transform) means the\n * enhanced and baseline states never disagree \u2014 a swipe and an arrow click end\n * up in the same place, and the dots follow either one.\n *\n * Authoring rules: src/behaviors/README.md. No import/export.\n */\n\nvar HERO = '[data-ds-hero-slider]';\n\nfunction bindHero(root) {\n  if (root.dataset.dsHeroBound) return;\n  root.dataset.dsHeroBound = '1';\n\n  var track = root.querySelector('[data-ds-hero-track]');\n  var slides = track ? Array.prototype.slice.call(track.children) : [];\n  if (!track || slides.length < 2) {\n    // One slide: the arrows would be dead controls, so remove the whole row.\n    var controls = root.querySelector('[data-ds-hero-controls]');\n    if (controls) controls.remove();\n    return;\n  }\n\n  var dots = Array.prototype.slice.call(root.querySelectorAll('[data-ds-hero-dot]'));\n  var interval = Number(root.dataset.dsHeroInterval || 0);\n  var timer = null;\n  var staged = -1;\n  var armed = false;\n\n  // Seamless loop in BOTH directions, the same construction product-rail uses:\n  // the track reads [clone of last][reals][clone of first] and rests one slide\n  // in. Leaving either end lands on a slide that is pixel-identical to the one\n  // the shopper expects, and once that scroll has come to rest the offset is\n  // teleported to the real twin \u2014 so a wrap never sweeps the whole run, which\n  // reads as a reset rather than a loop.\n  //\n  // Both clones are made by JS and never by Liquid: the no-JS baseline must\n  // paint the real first slide at offset 0, and it is the page's LCP element.\n  var count = slides.length;\n  var lastReal = count; // offset index of the last real slide\n  var firstReal = 1; // offset index of the first real slide\n\n  function makeClone(slide) {\n    var clone = slide.cloneNode(true);\n    clone.setAttribute('aria-hidden', 'true');\n    clone.setAttribute('data-ds-hero-clone', '');\n    clone.querySelectorAll('a, button, input, select, textarea, [tabindex]').forEach(function (el) {\n      el.setAttribute('tabindex', '-1');\n    });\n    clone.querySelectorAll('img').forEach(function (img) {\n      // A clone rests off-screen; inheriting the first slide's eager/high\n      // priority would make it compete with the real LCP image.\n      img.setAttribute('loading', 'lazy');\n      img.removeAttribute('fetchpriority');\n    });\n    return clone;\n  }\n\n  // Where offset slide i starts, in the track's own scroll coordinates: read\n  // off the slide itself, not computed as i \u00D7 a width. Under a real browser\n  // zoom the band's width is fractional, and i \u00D7 any width the DOM reports\n  // (clientWidth, the computed width) drifted further from slide i with every\n  // step \u2014 a growing sliver of the neighbouring image, by the last slide wider\n  // than the pixels src/hero.css clips. offsetLeft is in the same unzoomed CSS\n  // px as scrollLeft (a rect is not, under CSS zoom) and is rounded per slide,\n  // so its error stays under half a pixel instead of adding up.\n  function slideStart(i) {\n    var slide = track.children[i];\n    if (!slide) return track.scrollLeft;\n    return slide.offsetLeft - track.firstElementChild.offsetLeft;\n  }\n\n  track.appendChild(makeClone(slides[0]));\n  track.insertBefore(makeClone(slides[count - 1]), slides[0]);\n  // Prepending shifts the reals one slide right while the browser keeps\n  // scrollLeft at 0 \u2014 which is now the leading clone. Correct it in the SAME\n  // task, before a paint can show the last slide as the hero.\n  track.scrollTo({ left: slideStart(firstReal), behavior: 'instant' });\n\n  function offsetIndex() {\n    // The slide whose start is nearest the scroll offset.\n    var at = track.scrollLeft;\n    var best = firstReal;\n    var bestGap = Infinity;\n    for (var i = 0; i < track.children.length; i += 1) {\n      var gap = Math.abs(slideStart(i) - at);\n      if (gap < bestGap) { bestGap = gap; best = i; }\n    }\n    return best;\n  }\n\n  function activeIndex() {\n    // Nearest slide to the current scroll offset \u2014 correct after a swipe that\n    // stopped between two snap points as well as after our own scrollTo. The\n    // modulo folds each clone onto its real twin, so the dots read the slide\n    // the shopper is actually looking at.\n    return ((offsetIndex() - firstReal) % count + count) % count;\n  }\n\n  function goTo(offset) {\n    // Takes an OFFSET index, so a caller can aim at a clone (0 or count + 1)\n    // deliberately; anything else is clamped into the real run.\n    var target = offset < 0 || offset > count + 1\n      ? ((offset % count) + count) % count + firstReal\n      : offset;\n    track.scrollTo({\n      left: slideStart(target),\n      behavior: window.matchMedia('(prefers-reduced-motion: reduce)').matches ? 'auto' : 'smooth',\n    });\n  }\n\n  // Swap a clone for its real twin once the track has come to REST on it.\n  // Never on a scroll frame: a smooth scroll still in flight RESUMES towards\n  // its old target after the offset is rewritten (measured \u2014 the track parked\n  // itself between two slides), so this only runs on `scrollend`, or on a short\n  // idle debounce where that event does not exist.\n  //\n  // `behavior: 'instant'`, not 'auto' and not a scrollLeft write: the track\n  // carries CSS scroll-behavior: smooth (sf-scroll-smooth), which BOTH of those\n  // honour \u2014 the jump animated the whole run, i.e. exactly the sweep the clones\n  // exist to avoid. 'instant' is the only value that overrides it.\n  function recycle() {\n    var at = offsetIndex();\n    var jump = null;\n    if (at === 0) jump = lastReal;\n    else if (at === count + 1) jump = firstReal;\n    if (jump === null) return;\n    track.scrollTo({ left: slideStart(jump), behavior: 'instant' });\n    restage();\n  }\n\n  function restage() {\n    var active = activeIndex();\n    // A swipe fires scroll every frame while the index changes only at snap\n    // points, so without this the whole subtree's attributes were rewritten ~60\n    // times a second and each write invalidated the layout the next frame read.\n    if (active === staged) return;\n    staged = active;\n    slides.forEach(function (slide, i) {\n      // Off-screen slides stay in the a11y tree only as much as a carousel\n      // should: hidden from AT, and their links out of the tab order. Neither\n      // clone is in `slides`, so both stay hidden for good.\n      slide.setAttribute('aria-hidden', i === active ? 'false' : 'true');\n    });\n    dots.forEach(function (dot, i) {\n      var on = i === active;\n      dot.setAttribute('aria-selected', on ? 'true' : 'false');\n      var pip = dot.querySelector('[data-ds-hero-dot-pip]');\n      // The pip's two sizes live in src/hero.css, selected off this attribute.\n      // JS binds data-ds-* only: duplicating the class lists here meant a token\n      // change in the snippet left the JS toggling dead classes.\n      if (pip) pip.setAttribute('data-ds-hero-dot-pip', on ? 'on' : 'off');\n    });\n  }\n\n  function schedule() {\n    window.clearInterval(timer);\n    if (!armed || !interval) return;\n    timer = window.setInterval(function () {\n      if (!root.isConnected) { window.clearInterval(timer); return; }\n      // Straight onto the next OFFSET, clone included: recycle() swaps it for\n      // the real twin once the scroll has settled.\n      goTo(offsetIndex() + 1);\n    }, Math.max(2000, interval));\n  }\n\n  // Autoplay does not start with the section, because advancing while the page\n  // is still loading costs the shopper twice: the timer competes for the main\n  // thread with everything still parsing, it pulls the next slide's lazy\n  // picture into a network that is still fetching what is on screen, and it\n  // moves the banner out from under someone who has not finished reading the\n  // first one.\n  //\n  // NOT an LCP fix, though it was written believing it was. Measured: a\n  // three-slide hero at a 2s interval over 12s reports ONE candidate, at\n  // 256ms, and never another \u2014 LCP only records a candidate that is LARGER\n  // than the standing one, and every slide paints at the same size. See\n  // check-hero.mjs, which pins that measurement so the claim cannot come back.\n  var AUTOPLAY_GRACE_MS = 4000;\n\n  function arm() {\n    if (armed) return;\n    armed = true;\n    schedule();\n  }\n\n  function armAfterLoad() {\n    window.setTimeout(arm, AUTOPLAY_GRACE_MS);\n  }\n\n  // A shopper who reaches for the carousel has finished waiting for the page,\n  // so their first touch arms it too rather than making them sit out the grace.\n  ['pointerdown', 'keydown'].forEach(function (name) {\n    root.addEventListener(name, arm, { passive: true, once: true });\n  });\n\n  if (document.readyState === 'complete') armAfterLoad();\n  else window.addEventListener('load', armAfterLoad, { once: true });\n\n  var prev = root.querySelector('[data-ds-hero-prev]');\n  var next = root.querySelector('[data-ds-hero-next]');\n  if (prev) prev.addEventListener('click', function () { goTo(offsetIndex() - 1); schedule(); });\n  if (next) next.addEventListener('click', function () { goTo(offsetIndex() + 1); schedule(); });\n  dots.forEach(function (dot, i) {\n    dot.addEventListener('click', function () { goTo(i + firstReal); schedule(); });\n  });\n\n  // Throttle to one restage per frame: scroll fires far more often than the\n  // dots can meaningfully change.\n  var queued = false;\n  var hasScrollEnd = 'onscrollend' in window;\n  var idle = null;\n  track.addEventListener('scroll', function () {\n    if (!hasScrollEnd) {\n      window.clearTimeout(idle);\n      idle = window.setTimeout(recycle, 150);\n    }\n    if (queued) return;\n    queued = true;\n    window.requestAnimationFrame(function () { queued = false; restage(); });\n  }, { passive: true });\n  if (hasScrollEnd) track.addEventListener('scrollend', recycle);\n\n  // Autoplay must not run away while the shopper is reading or tabbing through.\n  root.addEventListener('mouseenter', function () { window.clearInterval(timer); });\n  root.addEventListener('mouseleave', schedule);\n  root.addEventListener('focusin', function () { window.clearInterval(timer); });\n  root.addEventListener('focusout', function (event) {\n    if (!root.contains(event.relatedTarget)) schedule();\n  });\n\n  // Neither hover nor focus exists on a touch device, so without the two gates\n  // below autoplay kept scrolling the largest element on the page after the\n  // shopper had left it behind, and a backgrounded tab woke up mid-burst.\n  var onScreen = true;\n  document.addEventListener('visibilitychange', function () {\n    if (document.hidden) window.clearInterval(timer);\n    else if (onScreen) schedule();\n  });\n\n  if (typeof IntersectionObserver !== 'undefined') {\n    new IntersectionObserver(function (entries) {\n      onScreen = entries[entries.length - 1].isIntersecting;\n      if (onScreen) schedule();\n      else window.clearInterval(timer);\n    }).observe(root);\n  }\n\n  restage();\n  schedule();\n}\n\nfunction initHeroSliders(scope) {\n  if (!scope) return;\n  if (scope.matches && scope.matches(HERO)) bindHero(scope);\n  if (scope.querySelectorAll) scope.querySelectorAll(HERO).forEach(bindHero);\n}\n\nif (!document.__dsHeroSliderBound) {\n  document.__dsHeroSliderBound = true;\n  var startHeroSliders = function () { initHeroSliders(document); };\n  if (document.readyState === 'loading') {\n    document.addEventListener('DOMContentLoaded', startHeroSliders, { once: true });\n  } else {\n    startHeroSliders();\n  }\n  // Shopify Theme Editor re-renders a section into a fresh DOM subtree.\n  document.addEventListener('shopify:section:load', startHeroSliders);\n\n  // Sections injected after load (async section rendering, Storybook mount):\n  // re-scan on the next frame, then stop. Watching the whole document forever\n  // meant every unrelated mutation on the storefront \u2014 cart drawer, apps \u2014 woke\n  // this up to re-query for a slider count that is fixed once the section is in.\n  // The Theme Editor is covered by shopify:section:load above.\n  if (typeof MutationObserver !== 'undefined') {\n    var rescanQueued = false;\n    var observer = new MutationObserver(function () {\n      if (rescanQueued) return;\n      rescanQueued = true;\n      requestAnimationFrame(function () {\n        rescanQueued = false;\n        startHeroSliders();\n        if (document.querySelector(HERO)) observer.disconnect();\n      });\n    });\n    observer.observe(document.documentElement, { childList: true, subtree: true });\n  }\n}\n\n} catch (sfError) {\nif (typeof console !== 'undefined') console.error('[sf-core] behavior \"hero-slider\" failed to initialise:', sfError);\n}\n})();\n\n/* image-fallback.js */\n(function () {\ntry {\n/* image-fallback \u2014 a ds-image <img> that fails to load retries twice with\n * backoff (a composited preview can 404 briefly while it finishes generating\n * server-side, e.g. cart \"Save as new\" \u2014 CART-60). Its data-ds-image-loading\n * spinner covers the frame until load succeeds or retries are exhausted, then\n * the <img> hides itself so the tinted frame shows instead of a broken icon.\n *\n * Authoring rules: src/behaviors/README.md. No import/export.\n */\n\nif (!document.__dsImageFallbackBound) {\n  document.__dsImageFallbackBound = true;\n  var MAX_RETRIES = 2;\n\n  // A blur-up placeholder (data-ds-image-lqip) fades off the photo instead of\n  // vanishing; `instant` skips that for an image already there before the page\n  // was interactive, and for a failure, where there is no photo to reveal.\n  function hideSpinner(img, instant) {\n    var wrap = img.closest('[data-ds-image]');\n    var spinner = wrap && wrap.querySelector('[data-ds-image-loading]');\n    if (!spinner) return;\n    if (!instant && spinner.hasAttribute('data-ds-image-lqip')) {\n      spinner.setAttribute('data-ds-image-lqip', 'done');\n      spinner.removeAttribute('role');\n      spinner.setAttribute('aria-hidden', 'true');\n      return;\n    }\n    spinner.hidden = true;\n  }\n\n  // This script is deferred, so a cached image can finish loading \u2014 firing its\n  // one-time 'load' event \u2014 before the listener below is even attached, leaving\n  // the spinner stuck over a photo that's already there.\n  // The blur-up preview is an <img> inside the frame too; its own load or error\n  // says nothing about the photo, so every path below skips it.\n  function isPreview(img) {\n    return img.hasAttribute('data-ds-image-lqip-img');\n  }\n\n  document.querySelectorAll('[data-ds-image] img').forEach(function (img) {\n    if (isPreview(img)) return;\n    if (img.complete && img.naturalWidth > 0) hideSpinner(img, true);\n  });\n\n  document.addEventListener(\n    'load',\n    function (e) {\n      if (e.target instanceof HTMLImageElement && !isPreview(e.target)) hideSpinner(e.target);\n    },\n    true,\n  );\n\n  document.addEventListener(\n    'error',\n    function (e) {\n      var img = e.target;\n      if (!(img instanceof HTMLImageElement) || !img.src || img.hidden || isPreview(img)) return;\n      if (!img.closest('[data-ds-image]')) return;\n      var retries = Number(img.dataset.dsImageRetries || 0);\n      if (retries >= MAX_RETRIES) {\n        img.hidden = true;\n        hideSpinner(img, true);\n        return;\n      }\n      img.dataset.dsImageRetries = String(retries + 1);\n      var src = img.src;\n      var sep = src.indexOf('?') === -1 ? '?' : '&';\n      setTimeout(function () {\n        img.src = src + sep + '_retry=' + Date.now();\n      }, 800 * (retries + 1));\n    },\n    true,\n  );\n}\n\n} catch (sfError) {\nif (typeof console !== 'undefined') console.error('[sf-core] behavior \"image-fallback\" failed to initialise:', sfError);\n}\n})();\n\n/* image-lightbox.js */\n(function () {\ntry {\n/* image-lightbox \u2014 a shared single-image zoom. Any element with\n * [data-ds-image-zoom] and a data-ds-image-zoom-src opens the ds-image-lightbox\n * dialog showing that one image full size (add-on / cross-sell thumbnails today).\n * One dialog serves the whole page; the clicked element's src/alt are injected on\n * open.\n *\n * It owns only the dialog: open/close, portal to <body> (so a sticky/transformed\n * ancestor never traps its z-index \u2014 same reason as media-lightbox.js), scroll\n * lock, focus trap + restore. Progressive enhancement: without this script the\n * thumbnails are plain images, nothing breaks.\n *\n * Authoring rules: src/behaviors/README.md. No import/export.\n */\n\n(function () {\n  if (document.__dsImageLightboxBound) return;\n  document.__dsImageLightboxBound = true;\n\n  var lastTrigger = null;\n\n  function getModal() {\n    // A fixed overlay must live at the root to stack above everything; it is\n    // authored inside the add-on block, which can sit in a stacking context.\n    var all = document.querySelectorAll('[data-ds-image-lightbox]');\n    for (var i = 1; i < all.length; i++) {\n      all[i].parentNode && all[i].parentNode.removeChild(all[i]); // drop duplicates\n    }\n    var modal = all[0] || null;\n    if (modal && modal.parentNode !== document.body) document.body.appendChild(modal);\n    return modal;\n  }\n\n  function isOpen(modal) {\n    return !!modal && modal.getAttribute('aria-hidden') === 'false';\n  }\n\n  function open(trigger) {\n    var modal = getModal();\n    if (!modal) return;\n    var src = trigger.getAttribute('data-ds-image-zoom-src');\n    if (!src) return;\n\n    var alt = trigger.getAttribute('data-ds-image-zoom-alt') || '';\n    var img = modal.querySelector('[data-ds-image-lightbox-img]');\n    if (img) {\n      img.setAttribute('src', src);\n      img.setAttribute('alt', alt);\n    }\n\n    // Heading: cart preview keeps \"Preview\"; else an explicit title, else alt.\n    var titleEl = modal.querySelector('[data-ds-image-lightbox-title]');\n    if (titleEl && !modal.hasAttribute('data-ds-image-lightbox-cart')) {\n      var title = trigger.getAttribute('data-ds-image-zoom-title') || alt;\n      if (title) titleEl.textContent = title;\n    }\n\n    lastTrigger = trigger;\n    modal.setAttribute('aria-hidden', 'false');\n    document.documentElement.style.overflow = 'hidden';\n\n    var close = modal.querySelector('[data-ds-image-lightbox-close]');\n    if (close) window.requestAnimationFrame(function () { close.focus({ focusVisible: false }); });\n  }\n\n  function close() {\n    var modal = getModal();\n    if (!modal) return;\n    modal.setAttribute('aria-hidden', 'true');\n    document.documentElement.style.overflow = '';\n    var img = modal.querySelector('[data-ds-image-lightbox-img]');\n    if (img) img.removeAttribute('src'); // don't keep the last image decoded/visible\n    if (lastTrigger && typeof lastTrigger.focus === 'function') lastTrigger.focus();\n    lastTrigger = null;\n  }\n\n  function trapTab(e, modal) {\n    var dialog = modal.querySelector('[data-ds-image-lightbox-dialog]') || modal;\n    var focusable = Array.prototype.filter.call(\n      dialog.querySelectorAll(\n        'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])',\n      ),\n      function (el) {\n        return el.offsetParent !== null || el === document.activeElement;\n      },\n    );\n    if (!focusable.length) return;\n    var first = focusable[0];\n    var last = focusable[focusable.length - 1];\n    if (e.shiftKey && document.activeElement === first) {\n      e.preventDefault();\n      last.focus();\n    } else if (!e.shiftKey && document.activeElement === last) {\n      e.preventDefault();\n      first.focus();\n    } else if (!dialog.contains(document.activeElement)) {\n      e.preventDefault();\n      first.focus();\n    }\n  }\n\n  document.addEventListener('click', function (e) {\n    if (!e.target.closest) return;\n\n    if (e.target.closest('[data-ds-image-lightbox-close]')) {\n      e.preventDefault();\n      close();\n      return;\n    }\n\n    var trigger = e.target.closest('[data-ds-image-zoom]');\n    if (trigger) {\n      e.preventDefault();\n      open(trigger);\n      return;\n    }\n\n    var modal = getModal();\n    if (isOpen(modal) && e.target === modal) close();\n  });\n\n  // Keyboard-open for the non-button triggers (an <img role=\"button\" tabindex=0>).\n  document.addEventListener('keydown', function (e) {\n    var modal = getModal();\n    if (isOpen(modal)) {\n      if (e.key === 'Escape') { close(); return; }\n      if (e.key === 'Tab') { trapTab(e, modal); return; }\n    }\n    if (e.key === 'Enter' || e.key === ' ' || e.key === 'Spacebar') {\n      var trigger = e.target.closest && e.target.closest('[data-ds-image-zoom]');\n      if (trigger) {\n        e.preventDefault();\n        open(trigger);\n      }\n    }\n  });\n\n  // Portal up front so the first open never flashes trapped under a sticky column.\n  if (document.readyState === 'loading') {\n    document.addEventListener('DOMContentLoaded', getModal, { once: true });\n  } else {\n    getModal();\n  }\n})();\n\n} catch (sfError) {\nif (typeof console !== 'undefined') console.error('[sf-core] behavior \"image-lightbox\" failed to initialise:', sfError);\n}\n})();\n\n/* input-counter.js */\n(function () {\ntry {\n/* input-counter \u2014 live \"N/max\" character count for a maxlength text field.\n * Binds [data-ds-input] with an [data-ds-input-counter] readout and a field\n * carrying maxlength (see snippets/ds-input.liquid). Progressive enhancement:\n * the field enforces maxlength natively; the readout just stays at its initial\n * server-rendered value without JS.\n *\n * Authoring rules: src/behaviors/README.md. No import/export.\n */\n\nfunction syncInputCounter(input) {\n  var wrap = input.closest('[data-ds-input]');\n  var counter = wrap && wrap.querySelector('[data-ds-input-counter]');\n  var max = input.getAttribute('maxlength');\n  if (!counter || !max) return;\n  counter.textContent = input.value.length + '/' + max;\n}\n\nif (!document.__dsInputCounterBound) {\n  document.__dsInputCounterBound = true;\n  document.addEventListener('input', function (e) {\n    var field = e.target;\n    if (field instanceof HTMLInputElement && field.matches('[data-ds-input] input[maxlength]')) {\n      syncInputCounter(field);\n    }\n  });\n}\n\n} catch (sfError) {\nif (typeof console !== 'undefined') console.error('[sf-core] behavior \"input-counter\" failed to initialise:', sfError);\n}\n})();\n\n/* jump.js */\n(function () {\ntry {\n/* jump \u2014 scroll to the element a control names in [data-ds-jump].\n *\n * The value is a CSS selector, not an id, because the target is usually another\n * section whose id is generated by Shopify (the reviews band is\n * `ds-product-reviews-<section.id>-band`), so the caller can only name its hook.\n * A target inside a closed ds-accordion is opened first \u2014 scrolling to content\n * of zero height lands nowhere.\n *\n * Progressive enhancement: without this the control is inert, which is exactly\n * what the row was before it became a control.\n *\n * Authoring rules: src/behaviors/README.md. No import/export.\n */\n\nif (!document.__dsJumpBound) {\n  document.__dsJumpBound = true;\n\n  document.addEventListener('click', function (e) {\n    var control = e.target.closest && e.target.closest('[data-ds-jump]');\n    if (!control) return;\n    var target = document.querySelector(control.getAttribute('data-ds-jump') || '');\n    if (!target) return;\n    e.preventDefault();\n\n    var panel = target.closest('[data-ds-accordion-content]');\n    if (panel) {\n      var trigger = panel.parentNode.querySelector('[data-ds-accordion-trigger]');\n      if (trigger && trigger.getAttribute('aria-expanded') === 'false') trigger.click();\n    }\n\n    target.scrollIntoView({ behavior: 'smooth', block: 'start' });\n  });\n}\n\n} catch (sfError) {\nif (typeof console !== 'undefined') console.error('[sf-core] behavior \"jump\" failed to initialise:', sfError);\n}\n})();\n\n/* media-lightbox.js */\n(function () {\ntry {\n/* media-lightbox \u2014 opens the full-screen product image viewer (ds-media-lightbox)\n * from the gallery's main image or its ds-media-zoom trigger, and closes it\n * again. Rebuilds the \"click the image \u2192 see it big in a modal\" UX the PDP was\n * missing (T2147), modelled on the live wanderprints.com behaviour.\n *\n * It owns only the dialog: open/close, body scroll-lock, focus (trap + restore)\n * and keeping the modal on the same image as the inline gallery. The viewer\n * itself \u2014 track, chevrons, counter \u2014 is a plain ds-gallery in `bare` mode, so\n * behaviors/gallery.js drives it. The two galleries are paired by id: the inline\n * root carries data-ds-gallery-zoom=\"<id>\" and the overlay is #<id>. Index is\n * synced both ways with the `ds:gallery:set` event gallery.js listens for.\n *\n * Open/closed is one attribute, [aria-hidden]; src/media-lightbox.css carries the\n * fade off it so a display utility never cuts the transition. Progressive\n * enhancement: without this script the gallery still swaps images inline, the\n * modal just never opens.\n *\n * Authoring rules: src/behaviors/README.md. No import/export.\n */\n\n(function () {\n  if (document.__dsMediaLightboxBound) return;\n  document.__dsMediaLightboxBound = true;\n\n  var lastTrigger = null;\n\n  function isOpen(overlay) {\n    return !!overlay && overlay.getAttribute('aria-hidden') === 'false';\n  }\n\n  // Portal the overlay to <body>. It is authored inside ds-gallery, which on the\n  // PDP sits in a `position: sticky` column \u2014 and sticky creates its own stacking\n  // context, trapping the overlay's z-index below the buy-box column that paints\n  // after it (the variant pills would show over the modal). A fixed overlay must\n  // live at the root to stack above everything. Also drops any stale twin a\n  // section re-render (Theme Editor) may have left with the same id.\n  function hoist(overlay) {\n    if (!overlay) return overlay;\n    if (overlay.id) {\n      document.querySelectorAll('[data-ds-media-lightbox]').forEach(function (el) {\n        if (el !== overlay && el.id === overlay.id && el.parentNode) {\n          el.parentNode.removeChild(el);\n        }\n      });\n    }\n    if (overlay.parentNode !== document.body) document.body.appendChild(overlay);\n    return overlay;\n  }\n\n  function hoistAll() {\n    document.querySelectorAll('[data-ds-media-lightbox]').forEach(hoist);\n  }\n\n  function openOverlayEl() {\n    var list = document.querySelectorAll('[data-ds-media-lightbox]');\n    for (var i = 0; i < list.length; i++) {\n      if (isOpen(list[i])) return list[i];\n    }\n    return null;\n  }\n\n  // The active-slide <video> of a gallery root \u2014 the inline preview or the\n  // modal's own copy. null when the active slide is an image or an iframe embed.\n  function activeVideo(galleryRoot) {\n    if (!galleryRoot) return null;\n    var idx = Number(galleryRoot.dataset.dsGalleryActive || 0);\n    return galleryRoot.querySelector(\n      'video[data-ds-gallery-video][data-ds-gallery-index=\"' + idx + '\"]',\n    );\n  }\n\n  // Continue a clip on the OTHER element at the position it was left. The inline\n  // preview and the modal are two <video>s pointing at the same media, so this is\n  // what \"don't reload, keep playing\" means across the boundary: seek to `time`\n  // and, if it was rolling, resume. `preload=\"none\"` leaves readyState 0 with no\n  // seekable range yet \u2014 setting currentTime then is stored as the default start\n  // position, and the loadedmetadata re-seek covers browsers that drop it, so the\n  // element opens at `time` instead of flashing frame 0. Range requests + the\n  // HTTP cache mean the bytes come back without a fresh full download.\n  function resumeVideoAt(video, time, play) {\n    if (!video) return;\n    var seek = function () {\n      try {\n        if (Math.abs((video.currentTime || 0) - time) > 0.25) video.currentTime = time;\n      } catch (e) {}\n    };\n    seek();\n    if (video.readyState < 1) video.addEventListener('loadedmetadata', seek, { once: true });\n    if (play) {\n      var played = video.play();\n      if (played && typeof played.catch === 'function') played.catch(function () {});\n    }\n  }\n\n  function openFor(root, opener) {\n    if (!root) return;\n    var id = root.getAttribute('data-ds-gallery-zoom');\n    var overlay = id ? hoist(document.getElementById(id)) : null;\n    if (!overlay) return;\n\n    lastTrigger = opener || root.querySelector('[data-ds-media-zoom-trigger]') || null;\n\n    // Snapshot the inline preview BEFORE stopping it, so the modal picks the clip\n    // up where the shopper left it instead of restarting at 0.\n    var fromVideo = activeVideo(root);\n    var snapshot = fromVideo\n      ? { time: fromVideo.currentTime || 0, playing: !fromVideo.paused }\n      : null;\n\n    // Whatever the inline gallery was playing must not keep playing under the\n    // modal \u2014 the shopper has moved to the big viewer.\n    root.dispatchEvent(new CustomEvent('ds:gallery:stop', { bubbles: true }));\n    overlay.setAttribute('aria-hidden', 'false');\n    document.documentElement.style.overflow = 'hidden';\n\n    // Start the modal on whatever image the inline gallery is showing.\n    var inner = overlay.querySelector('[data-ds-gallery]');\n    if (inner) {\n      inner.dispatchEvent(\n        new CustomEvent('ds:gallery:set', {\n          bubbles: true,\n          // instant: the modal must open already ON this slide, never slide to it.\n          detail: { index: Number(root.dataset.dsGalleryActive || 0), instant: true },\n        }),\n      );\n      // Hand the position to the modal's own player. A preview that was rolling\n      // continues MUTED (the modal's controls are where the shopper unmutes), so\n      // there is neither a jump back to frame 0 nor surprise audio. The click is\n      // a user gesture, so even the play() call is allowed.\n      var toVideo = activeVideo(inner);\n      if (toVideo && snapshot) {\n        if (snapshot.playing) toVideo.muted = true;\n        resumeVideoAt(toVideo, snapshot.time, snapshot.playing);\n      }\n    }\n\n    // The image opens at fit; wire the slide-change reset and clear any zoom the\n    // last open left on this overlay's images.\n    ensureZoomObserver(overlay);\n    resetZoom(overlay);\n\n    var closeBtn = overlay.querySelector('[data-ds-media-zoom-close]');\n    if (closeBtn) window.requestAnimationFrame(function () { closeBtn.focus({ focusVisible: false }); });\n  }\n\n  function closeOverlay(overlay) {\n    if (!overlay) return;\n\n    // Leave the inline gallery on the image the shopper stepped to in the modal.\n    var inner = overlay.querySelector('[data-ds-gallery]');\n    var outer = overlay.id\n      ? document.querySelector('[data-ds-gallery-zoom=\"' + overlay.id + '\"]')\n      : null;\n\n    // Snapshot the modal player before it is stopped, to carry the position back\n    // to the inline preview \u2014 closing must continue the clip, not rewind it to\n    // where it stood when the modal opened.\n    var fromVideo = inner ? activeVideo(inner) : null;\n    var snapshot = fromVideo\n      ? { time: fromVideo.currentTime || 0, playing: !fromVideo.paused }\n      : null;\n\n    if (inner && outer) {\n      outer.dispatchEvent(\n        new CustomEvent('ds:gallery:set', {\n          bubbles: true,\n          // instant: the inline gallery behind the modal jumps to match, so it is\n          // not caught mid-slide when the overlay fades away.\n          detail: { index: Number(inner.dataset.dsGalleryActive || 0), instant: true },\n        }),\n      );\n      // ds:gallery:set already had gallery.js play or pause the inline preview by\n      // whether the gallery is on screen; only correct its position and let that\n      // visibility decision stand (never unmute \u2014 the inline stays a muted\n      // preview, so `!toVideo.paused` here is that decision, not the modal's).\n      var toVideo = activeVideo(outer);\n      if (toVideo && snapshot) resumeVideoAt(toVideo, snapshot.time, !toVideo.paused);\n    }\n    // The overlay is only [aria-hidden] \u2014 still in the DOM and still painted \u2014\n    // so a video left playing keeps its audio going over the page. gallery.js\n    // owns the media, so ask it to stop rather than reaching into the slides.\n    if (inner) inner.dispatchEvent(new CustomEvent('ds:gallery:stop', { bubbles: true }));\n\n    // Drop any zoom so the next open starts at fit, and abandon a gesture that was\n    // still mid-flight when the modal was dismissed.\n    resetZoom(overlay);\n    clearZoomGesture();\n\n    overlay.setAttribute('aria-hidden', 'true');\n    document.documentElement.style.overflow = '';\n    if (lastTrigger && typeof lastTrigger.focus === 'function') lastTrigger.focus();\n    lastTrigger = null;\n  }\n\n  // Tab must cycle inside the dialog while open; the page behind is still painted\n  // ([aria-hidden] only), so focus would otherwise walk out of the viewer.\n  function trapTab(e, overlay) {\n    var dialog = overlay.querySelector('[data-ds-media-lightbox-dialog]') || overlay;\n    var focusable = Array.prototype.filter.call(\n      dialog.querySelectorAll(\n        'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])',\n      ),\n      function (el) {\n        return el.offsetParent !== null || el === document.activeElement;\n      },\n    );\n    if (!focusable.length) return;\n    var first = focusable[0];\n    var last = focusable[focusable.length - 1];\n    if (e.shiftKey && document.activeElement === first) {\n      e.preventDefault();\n      last.focus();\n    } else if (!e.shiftKey && document.activeElement === last) {\n      e.preventDefault();\n      first.focus();\n    } else if (!dialog.contains(document.activeElement)) {\n      e.preventDefault();\n      first.focus();\n    }\n  }\n\n  // \u2500\u2500\u2500 Zoom (pinch / drag on mobile, wheel + double-click + drag on desktop) \u2500\u2500\u2500\n  // The modal is where a shopper inspects the mockup up close, so its active image\n  // gets a hand-driven zoom. The zoom lives entirely on the active\n  // <img data-ds-gallery-main> as a `translate3d(...) scale(...)` transform \u2014 it is\n  // layered ON TOP of the track's own translateX (which owns slide position), so\n  // the two never fight: the track slides, the image zooms within its frame (the\n  // viewer's overflow-hidden clips the magnified image).\n  //\n  // gallery.js already binds a one-finger swipe on the same viewer to change\n  // slides. So zoom must defer to swipe at scale 1 (a plain drag still flips the\n  // image) and TAKE OVER once zoomed or on a two-finger pinch. It does that from\n  // the capture phase on `document`: a capture listener runs before gallery's\n  // bubble listener on the viewer, so calling stopPropagation there keeps the\n  // swipe from ever seeing the gesture \u2014 but only when the gesture is ours.\n  var ZOOM_MAX = 3;\n  var zPointers = {}; // pointerId -> {x,y}  (live touches over the viewer)\n  var zImg = null; // image the current gesture drives\n  var zMode = null; // 'pan' | 'pinch' | null\n  var zLast = { x: 0, y: 0 }; // last single-pointer position (pan)\n  var zLastDist = 0; // last two-finger distance (pinch)\n  var zLastMid = { x: 0, y: 0 }; // last two-finger midpoint (pinch)\n\n  function zState(img) {\n    return img.__dsZoom || (img.__dsZoom = { scale: 1, tx: 0, ty: 0 });\n  }\n\n  // The active image of the bare gallery inside an overlay (null when the active\n  // slide is a video \u2014 zoom is images only, so gallery.js keeps the swipe there).\n  function activeZoomImg(viewer) {\n    var root = viewer && viewer.closest('[data-ds-gallery]');\n    if (!root) return null;\n    var idx = Number(root.dataset.dsGalleryActive || 0);\n    var img = root.querySelector(\n      'img[data-ds-gallery-main][data-ds-gallery-index=\"' + idx + '\"]',\n    );\n    if (img) return img;\n    // Storybook / placeholder path: a single main image carries no index.\n    var mains = root.querySelectorAll('img[data-ds-gallery-main]');\n    return mains.length === 1 ? mains[0] : null;\n  }\n\n  // Keep the magnified image from panning past its own edges: at scale s the image\n  // box is s\u00D7 the viewer, so it may travel at most (s-1)\u00B7half in each axis before a\n  // gutter would show. Snaps back to centred at scale 1.\n  function clampZoom(img) {\n    var z = zState(img);\n    if (z.scale <= 1) {\n      z.scale = 1;\n      z.tx = 0;\n      z.ty = 0;\n      return;\n    }\n    var v = img.closest('[data-ds-gallery-viewer]');\n    var w = (v ? v.clientWidth : img.clientWidth) || 1;\n    var h = (v ? v.clientHeight : img.clientHeight) || 1;\n    var maxX = ((z.scale - 1) * w) / 2;\n    var maxY = ((z.scale - 1) * h) / 2;\n    z.tx = Math.max(-maxX, Math.min(maxX, z.tx));\n    z.ty = Math.max(-maxY, Math.min(maxY, z.ty));\n  }\n\n  function applyZoom(img, animate) {\n    var z = zState(img);\n    var viewer = img.closest('[data-ds-gallery-viewer]');\n    img.style.transition = animate\n      ? 'transform var(--duration-base) var(--ease-standard)'\n      : 'none';\n    img.style.transformOrigin = 'center center';\n    img.style.transform =\n      'translate3d(' + z.tx + 'px,' + z.ty + 'px,0) scale(' + z.scale + ')';\n    img.style.cursor = z.scale > 1 ? 'grab' : 'zoom-in';\n    img.style.willChange = z.scale > 1 ? 'transform' : 'auto';\n    if (z.scale > 1) img.setAttribute('data-ds-zoomed', 'true');\n    else img.removeAttribute('data-ds-zoomed');\n    // Own the touches only while zoomed; at scale 1 hand pan-y back so gallery.js\n    // keeps its one-finger swipe (and vertical page gestures) working.\n    if (viewer) viewer.style.touchAction = z.scale > 1 ? 'none' : 'pan-y';\n  }\n\n  // Focal point of a client coord expressed as an offset from the viewer centre \u2014\n  // the space the transform's translate lives in.\n  function focal(img, clientX, clientY) {\n    var v = img.closest('[data-ds-gallery-viewer]') || img;\n    var r = v.getBoundingClientRect();\n    return { x: clientX - (r.left + r.width / 2), y: clientY - (r.top + r.height / 2) };\n  }\n\n  // Scale toward a fixed focal point: solve t1 so the screen point under the\n  // cursor/pinch-centre stays put across the scale change (t1 = F \u2212 (s1/s0)(F \u2212 t0)).\n  function zoomToward(img, newScale, fx, fy) {\n    var z = zState(img);\n    newScale = Math.max(1, Math.min(ZOOM_MAX, newScale));\n    var s0 = z.scale || 1;\n    z.tx = fx - (newScale / s0) * (fx - z.tx);\n    z.ty = fy - (newScale / s0) * (fy - z.ty);\n    z.scale = newScale;\n    clampZoom(img);\n  }\n\n  // Return every zoomed image in scope to its resting (unzoomed, centred) state.\n  // Called on open, close, and any slide change so a fresh slide never inherits\n  // the previous one's zoom.\n  function resetZoom(scope) {\n    (scope || document)\n      .querySelectorAll('img[data-ds-gallery-main]')\n      .forEach(function (img) {\n        if (!img.__dsZoom) return;\n        img.__dsZoom = { scale: 1, tx: 0, ty: 0 };\n        applyZoom(img, false);\n      });\n  }\n\n  function clearZoomGesture() {\n    zMode = null;\n    zImg = null;\n    zPointers = {};\n  }\n\n  // Settle the end of a gesture: snap a barely-zoomed image back to 1 (so a tiny\n  // pinch does not leave it stuck at 1.01) and animate the last frame.\n  function finishZoomGesture() {\n    if (zImg) {\n      var z = zState(zImg);\n      if (z.scale <= 1.02) {\n        z.scale = 1;\n        z.tx = 0;\n        z.ty = 0;\n      }\n      clampZoom(zImg);\n      applyZoom(zImg, true);\n    }\n    clearZoomGesture();\n  }\n\n  function twoPoints() {\n    var ks = Object.keys(zPointers);\n    return [zPointers[ks[0]], zPointers[ks[1]]];\n  }\n  function ptDist(a, b) {\n    return Math.hypot(a.x - b.x, a.y - b.y);\n  }\n  function ptMid(a, b) {\n    return { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 };\n  }\n\n  // Reset the active image whenever the modal slides to a different one \u2014 covers\n  // chevrons, the open-time index sync, and any programmatic ds:gallery:set.\n  function ensureZoomObserver(overlay) {\n    var root = overlay.querySelector('[data-ds-gallery]');\n    if (!root || root.__dsZoomObserved || typeof MutationObserver === 'undefined') return;\n    root.__dsZoomObserved = true;\n    // Only a REAL index change resets \u2014 the pinch-start snap rewrites the\n    // attribute to its current value, and that must not clear a zoom in flight.\n    var last = root.dataset.dsGalleryActive;\n    new MutationObserver(function () {\n      var now = root.dataset.dsGalleryActive;\n      if (now === last) return;\n      last = now;\n      resetZoom(overlay);\n    }).observe(root, { attributes: true, attributeFilter: ['data-ds-gallery-active'] });\n  }\n\n  // pointerdown \u2014 decide who owns the gesture. Two fingers \u2192 pinch; one finger on\n  // an already-zoomed image \u2192 pan; otherwise leave it for gallery.js's swipe.\n  document.addEventListener(\n    'pointerdown',\n    function (e) {\n      var overlay = e.target.closest && e.target.closest('[data-ds-media-lightbox]');\n      if (!overlay || !isOpen(overlay)) return;\n      // Never hijack the chevrons / close / any control.\n      if (e.target.closest('button, a, [data-ds-gallery-prev], [data-ds-gallery-next]')) return;\n      if (e.button != null && e.button > 0) return;\n      var viewer = e.target.closest('[data-ds-gallery-viewer]');\n      if (!viewer) return;\n      var img = activeZoomImg(viewer);\n      if (!img) return; // video slide \u2192 gallery.js\n\n      zPointers[e.pointerId] = { x: e.clientX, y: e.clientY };\n      var ids = Object.keys(zPointers);\n\n      if (ids.length >= 2) {\n        // Pinch: a first finger may already have started a gallery drag \u2014 snap the\n        // track back to the current slide so the pinch starts from a clean frame.\n        var root = viewer.closest('[data-ds-gallery]');\n        if (root) {\n          root.dispatchEvent(\n            new CustomEvent('ds:gallery:set', {\n              bubbles: true,\n              detail: { index: Number(root.dataset.dsGalleryActive || 0), instant: true },\n            }),\n          );\n        }\n        zImg = img;\n        zMode = 'pinch';\n        var p = twoPoints();\n        zLastDist = ptDist(p[0], p[1]);\n        zLastMid = ptMid(p[0], p[1]);\n        img.style.transition = 'none';\n        try { viewer.setPointerCapture(e.pointerId); } catch (err) {}\n        e.stopPropagation();\n        e.preventDefault();\n      } else if (zState(img).scale > 1) {\n        zImg = img;\n        zMode = 'pan';\n        zLast = { x: e.clientX, y: e.clientY };\n        img.style.transition = 'none';\n        img.style.cursor = 'grabbing';\n        try { viewer.setPointerCapture(e.pointerId); } catch (err) {}\n        e.stopPropagation();\n        e.preventDefault();\n      } else {\n        // Scale 1, single pointer: this is a swipe \u2014 gallery.js owns it.\n        zMode = null;\n      }\n    },\n    true,\n  );\n\n  document.addEventListener(\n    'pointermove',\n    function (e) {\n      if (!zMode || !zImg || !(e.pointerId in zPointers)) return;\n      zPointers[e.pointerId] = { x: e.clientX, y: e.clientY };\n      e.stopPropagation();\n      e.preventDefault();\n\n      var z = zState(zImg);\n      if (zMode === 'pinch') {\n        if (Object.keys(zPointers).length < 2) return;\n        var p = twoPoints();\n        var d = ptDist(p[0], p[1]);\n        var m = ptMid(p[0], p[1]);\n        var v = zImg.closest('[data-ds-gallery-viewer]');\n        var r = v.getBoundingClientRect();\n        var cx = r.left + r.width / 2;\n        var cy = r.top + r.height / 2;\n        var fx = m.x - cx;\n        var fy = m.y - cy;\n        // Follow the midpoint (two-finger drag)\u2026\n        z.tx += fx - (zLastMid.x - cx);\n        z.ty += fy - (zLastMid.y - cy);\n        // \u2026then scale about that same midpoint.\n        var s0 = z.scale || 1;\n        var s1 = Math.max(1, Math.min(ZOOM_MAX, s0 * (d / (zLastDist || d))));\n        z.tx = fx - (s1 / s0) * (fx - z.tx);\n        z.ty = fy - (s1 / s0) * (fy - z.ty);\n        z.scale = s1;\n        clampZoom(zImg);\n        applyZoom(zImg, false);\n        zLastDist = d;\n        zLastMid = m;\n      } else if (zMode === 'pan') {\n        z.tx += e.clientX - zLast.x;\n        z.ty += e.clientY - zLast.y;\n        zLast = { x: e.clientX, y: e.clientY };\n        clampZoom(zImg);\n        applyZoom(zImg, false);\n      }\n    },\n    true,\n  );\n\n  function zoomPointerEnd(e) {\n    if (!(e.pointerId in zPointers)) return;\n    delete zPointers[e.pointerId];\n    if (zMode) e.stopPropagation();\n    var ids = Object.keys(zPointers);\n    if (zMode === 'pinch' && ids.length === 1) {\n      // Second finger lifted mid-pinch: keep dragging with the one that remains.\n      if (zState(zImg).scale > 1) {\n        zMode = 'pan';\n        zLast = zPointers[ids[0]];\n      } else {\n        finishZoomGesture();\n      }\n    } else if (zMode === 'pan' && ids.length >= 1) {\n      zLast = zPointers[ids[0]];\n    } else if (ids.length === 0) {\n      finishZoomGesture();\n    }\n  }\n  document.addEventListener('pointerup', zoomPointerEnd, true);\n  document.addEventListener('pointercancel', zoomPointerEnd, true);\n\n  // Desktop wheel \u2014 zoom toward the cursor. The modal already locks page scroll,\n  // so the wheel is free to mean \"zoom\".\n  document.addEventListener(\n    'wheel',\n    function (e) {\n      var overlay = e.target.closest && e.target.closest('[data-ds-media-lightbox]');\n      if (!overlay || !isOpen(overlay)) return;\n      var viewer = e.target.closest('[data-ds-gallery-viewer]');\n      if (!viewer) return;\n      var img = activeZoomImg(viewer);\n      if (!img) return;\n      e.preventDefault();\n      var f = focal(img, e.clientX, e.clientY);\n      zoomToward(img, zState(img).scale * Math.exp(-e.deltaY * 0.0015), f.x, f.y);\n      applyZoom(img, false);\n    },\n    { passive: false },\n  );\n\n  // Desktop double-click \u2014 toggle between fit and 2\u00D7, zooming toward the cursor.\n  document.addEventListener('dblclick', function (e) {\n    var overlay = e.target.closest && e.target.closest('[data-ds-media-lightbox]');\n    if (!overlay || !isOpen(overlay)) return;\n    var viewer = e.target.closest('[data-ds-gallery-viewer]');\n    if (!viewer) return;\n    var img = activeZoomImg(viewer);\n    if (!img) return;\n    e.preventDefault();\n    var z = zState(img);\n    if (z.scale > 1) {\n      z.scale = 1;\n      z.tx = 0;\n      z.ty = 0;\n      applyZoom(img, true);\n    } else {\n      var f = focal(img, e.clientX, e.clientY);\n      zoomToward(img, 2, f.x, f.y);\n      applyZoom(img, true);\n    }\n  });\n\n  document.addEventListener('click', function (e) {\n    var t = e.target;\n    if (!t || !t.closest) return;\n\n    // Close: the X control inside the overlay.\n    var closeBtn = t.closest('[data-ds-media-lightbox-close]');\n    if (closeBtn) {\n      e.preventDefault();\n      closeOverlay(closeBtn.closest('[data-ds-media-lightbox]'));\n      return;\n    }\n\n    // Open: the explicit zoom trigger button over the image.\n    var trigger = t.closest('[data-ds-media-zoom-trigger]');\n    if (trigger) {\n      e.preventDefault();\n      openFor(trigger.closest('[data-ds-gallery]'), trigger);\n      return;\n    }\n\n    // Close: clicking the dimmed backdrop (the overlay itself, not its card).\n    var overlay = t.closest('[data-ds-media-lightbox]');\n    if (overlay && isOpen(overlay) && t === overlay) {\n      closeOverlay(overlay);\n      return;\n    }\n\n    // Open: ONLY a real gallery slide opens the zoom \u2014 never the whole viewer.\n    // Personalization apps (Customily) drop an interactive <canvas> over the main\n    // image; keying on [data-ds-gallery-main] means a click on that canvas (or any\n    // app overlay sitting above the image) is not ours to catch, so it never\n    // hijacks into an empty modal. The explicit zoom button above still works.\n    // The inline video preview carries the same marker on purpose: it has no\n    // controls, so a click on it is the same \"see it big\" gesture as on an image,\n    // and the modal is where that video gets its real player.\n    var mainImg = t.closest('[data-ds-gallery-main]');\n    if (mainImg) {\n      var imgRoot = mainImg.closest('[data-ds-gallery]');\n      // Only the inline gallery is zoomable (it carries data-ds-gallery-zoom); the\n      // bare gallery inside the modal has none, so its own images never re-open it.\n      if (imgRoot && imgRoot.getAttribute('data-ds-gallery-zoom')) openFor(imgRoot, null);\n    }\n  });\n\n  document.addEventListener('keydown', function (e) {\n    var overlay = openOverlayEl();\n    if (!overlay) return;\n    if (e.key === 'Escape') {\n      closeOverlay(overlay);\n      return;\n    }\n    if (e.key === 'Tab') trapTab(e, overlay);\n  });\n\n  // Portal overlays out of their sticky/stacking-context columns up front, so the\n  // first open never flashes trapped-under-the-buy-box.\n  if (document.readyState === 'loading') {\n    document.addEventListener('DOMContentLoaded', hoistAll, { once: true });\n  } else {\n    hoistAll();\n  }\n})();\n\n} catch (sfError) {\nif (typeof console !== 'undefined') console.error('[sf-core] behavior \"media-lightbox\" failed to initialise:', sfError);\n}\n})();\n\n/* number-input.js */\n(function () {\ntry {\n/* number-input \u2014 stepper +/- buttons around a native <input type=number>.\n * Binds [data-ds-number-input] and data-ds-number-input-{decrease,increase}\n * (see snippets/ds-number-input.liquid). The native field stays the value\n * store; buttons dispatch input/change on it so theme code listens on the field.\n * Progressive enhancement: the field is usable by keyboard without JS.\n *\n * Authoring rules: src/behaviors/README.md. No import/export.\n */\n\nvar NUMBER = '[data-ds-number-input]';\nvar DECREASE = '[data-ds-number-input-decrease]';\nvar INCREASE = '[data-ds-number-input-increase]';\n// Settle time before a stepper click commits its 'change' \u2014 the event other\n// behaviors (e.g. cart-lines.js) treat as \"fire the network request\". Without\n// this, holding + fires one fetch per click instead of one per burst.\nvar CHANGE_SETTLE = 400;\n\nfunction numBound(input, attr, fallback) {\n  var raw = input.getAttribute(attr);\n  if (raw === null || raw === '') return fallback;\n  var value = Number(raw);\n  return Number.isNaN(value) ? fallback : value;\n}\n\nfunction numCurrent(input, min) {\n  var value = Number(input.value);\n  if (!Number.isNaN(value)) return value;\n  return min === -Infinity ? 0 : min;\n}\n\nfunction numSync(root) {\n  var input = root.querySelector('input[type=\"number\"]');\n  if (!input) return;\n  var min = numBound(input, 'min', -Infinity);\n  var max = numBound(input, 'max', Infinity);\n  var value = numCurrent(input, min);\n  var decrease = root.querySelector(DECREASE);\n  var increase = root.querySelector(INCREASE);\n  if (decrease) decrease.disabled = input.disabled || value <= min;\n  if (increase) increase.disabled = input.disabled || value >= max;\n}\n\nfunction numStep(root, direction) {\n  var input = root.querySelector('input[type=\"number\"]');\n  if (!input || input.disabled) return;\n  var min = numBound(input, 'min', -Infinity);\n  var max = numBound(input, 'max', Infinity);\n  var size = numBound(input, 'step', 1) || 1;\n  var value = numCurrent(input, min);\n  var next = Math.min(max, Math.max(min, value + direction * size));\n  if (next === value) return;\n  input.value = String(next);\n  input.dispatchEvent(new Event('input', { bubbles: true }));\n  numSync(root);\n  if (input.__dsChangeTimer) window.clearTimeout(input.__dsChangeTimer);\n  input.__dsChangeTimer = window.setTimeout(function () {\n    input.__dsChangeTimer = 0;\n    input.dispatchEvent(new Event('change', { bubbles: true }));\n  }, CHANGE_SETTLE);\n}\n\n// A typed value bypasses numStep's own clamp entirely, and PDP add-to-cart forms\n// carry novalidate (so the browser's own min/max check never runs) \u2014 so -1 or a\n// blank field would otherwise reach /cart/add.js untouched. Runs on the capture\n// phase so it lands before any other 'change' listener (cart-lines.js, form\n// submit) reads the field, regardless of script load order.\n// ponytail: no upper bound when the field carries no `max` (PDP/cart never set\n// one today) \u2014 inventory-based caps would be invented business logic. Add a\n// `max` in ds-number-input.liquid callers once inventory limits are wired.\nfunction numClamp(input) {\n  var min = numBound(input, 'min', -Infinity);\n  var max = numBound(input, 'max', Infinity);\n  var value = Number(input.value);\n  if (!Number.isFinite(value)) value = min === -Infinity ? 0 : min;\n  var clamped = Math.min(max, Math.max(min, value));\n  if (String(clamped) !== input.value) input.value = String(clamped);\n}\n\nif (!document.__dsNumberInputBound) {\n  document.__dsNumberInputBound = true;\n\n  document.addEventListener('click', function (e) {\n    var target = e.target;\n    if (!target || !target.closest) return;\n    var button = target.closest(DECREASE + ',' + INCREASE);\n    if (!button || button.disabled) return;\n    var root = button.closest(NUMBER);\n    if (root) numStep(root, button.matches(INCREASE) ? 1 : -1);\n  });\n\n  document.addEventListener('input', function (e) {\n    var field = e.target;\n    if (field instanceof HTMLInputElement && field.matches(NUMBER + ' input[type=\"number\"]')) {\n      var root = field.closest(NUMBER);\n      if (root) numSync(root);\n    }\n  });\n\n  document.addEventListener('change', function (e) {\n    var field = e.target;\n    if (field instanceof HTMLInputElement && field.matches(NUMBER + ' input[type=\"number\"]')) {\n      numClamp(field);\n      var root = field.closest(NUMBER);\n      if (root) numSync(root);\n    }\n  }, true);\n}\n\n} catch (sfError) {\nif (typeof console !== 'undefined') console.error('[sf-core] behavior \"number-input\" failed to initialise:', sfError);\n}\n})();\n\n/* order-lookup.js */\n(function () {\ntry {\n/**\n * order-lookup \u2014 busy state on the Track order CTA (SF0026 #1651).\n *\n * Swaps the idle / busy button pair Liquid already rendered. Does not perform\n * a real order lookup \u2014 that lives outside this theme. Prevents a second press\n * while busy; restores idle if the page stays put (no navigation).\n */\n(function () {\n  function setBusy(root, on) {\n    var idle = root.querySelector('[data-ds-order-lookup-idle]');\n    var busy = root.querySelector('[data-ds-order-lookup-busy]');\n    if (!idle || !busy) return;\n\n    var from = on ? idle : busy;\n    var to = on ? busy : idle;\n    var carry = from.contains(document.activeElement);\n\n    idle.classList.toggle('sf-hidden', on);\n    busy.classList.toggle('sf-hidden', !on);\n    idle.classList.toggle('sf-block', !on);\n    busy.classList.toggle('sf-block', on);\n\n    if (!carry) return;\n    var target = to.querySelector('button');\n    if (target && typeof target.focus === 'function') target.focus();\n  }\n\n  function onSubmit(event) {\n    var root = event.target.closest('[data-ds-order-lookup]');\n    if (!root) return;\n\n    var order = root.querySelector('[data-ds-order-lookup-order]');\n    var email = root.querySelector('[data-ds-order-lookup-email]');\n    if (order && !String(order.value || '').trim()) {\n      event.preventDefault();\n      return;\n    }\n    if (email && !String(email.value || '').trim()) {\n      event.preventDefault();\n      return;\n    }\n\n    setBusy(root, true);\n  }\n\n  function bind(root) {\n    if (!root || root.hasAttribute('data-ds-order-lookup-bound')) return;\n    root.setAttribute('data-ds-order-lookup-bound', '');\n    root.addEventListener('submit', onSubmit);\n  }\n\n  function scan() {\n    document.querySelectorAll('[data-ds-order-lookup]').forEach(bind);\n  }\n\n  scan();\n  document.addEventListener('shopify:section:load', scan);\n})();\n\n} catch (sfError) {\nif (typeof console !== 'undefined') console.error('[sf-core] behavior \"order-lookup\" failed to initialise:', sfError);\n}\n})();\n\n/* page-lock.js */\n(function () {\ntry {\n/* page-lock \u2014 holds the page still behind an open sheet, modal or lightbox,\n * iOS Safari included.\n *\n * Contract: the behavior that owns an overlay sets [data-ds-page-lock] on it\n * when it opens and removes it when it closes. That is the whole API. The page\n * stays locked while any element in the document carries the attribute, so\n * nested overlays need no bookkeeping, and an open overlay removed by a Theme\n * Editor re-render unlocks the page on its own.\n *\n * `overflow: hidden` on html or body does not hold on iOS: a drag on the scrim\n * pans the page, and focusing a field scrolls it (BY-115). So the body is\n * pinned in place at its current offset, and html carries [data-ds-page-locked]\n * while it is \u2014 pinning resets the window scroll to 0, and scroll listeners\n * (scroll-top.js) skip that jump. Unlocking puts the page back where it was.\n *\n * While locked, a one-finger drag may only move something that can still\n * scroll that way inside a locked overlay; anything else (the scrim, a header,\n * a list at its end) is dropped. Two fingers are a pinch and pass through.\n *\n * Opt-in [data-ds-page-lock-fit] on the overlay pins it to the visual viewport\n * (top and height), so it stays above the iOS keyboard; its CSS can cap heights\n * from --ds-page-lock-viewport. Each change fires `ds:page-lock:fit` on the\n * overlay with { top, height, resized } for the owner to react to.\n *\n * Authoring rules: src/behaviors/README.md. No import/export.\n */\n\n(function () {\n  if (document.__dsPageLockBound) return;\n  document.__dsPageLockBound = true;\n\n  var ATTR = 'data-ds-page-lock';\n  var LOCKED = 'data-ds-page-locked';\n  var FIT = 'data-ds-page-lock-fit';\n  var STYLE = ['position', 'top', 'left', 'right', 'width', 'overflow'];\n  var html = document.documentElement;\n  var viewport = window.visualViewport;\n  var saved = null;\n  var fitted = [];\n\n  function lock() {\n    var body = document.body;\n    saved = { scroll: window.pageYOffset, style: {} };\n    STYLE.forEach(function (prop) { saved.style[prop] = body.style[prop]; });\n    html.setAttribute(LOCKED, '');\n    body.style.position = 'fixed';\n    body.style.top = -saved.scroll + 'px';\n    body.style.left = '0';\n    body.style.right = '0';\n    body.style.width = '100%';\n    body.style.overflow = 'hidden';\n  }\n\n  function unlock() {\n    var body = document.body;\n    STYLE.forEach(function (prop) { body.style[prop] = saved.style[prop]; });\n    window.scrollTo({ top: saved.scroll, behavior: 'instant' });\n    html.removeAttribute(LOCKED);\n    saved = null;\n  }\n\n  // \u2500\u2500 Visual-viewport fit \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n  function fit(entry) {\n    // Pinch-zoom shrinks the visual viewport too; the overlay stays as it is\n    // until the page is back at its own scale.\n    if (viewport.scale > 1.01) return;\n    var top = viewport.offsetTop;\n    var height = viewport.height;\n    if (entry.top === top && entry.height === height) return;\n    var resized = entry.height !== height;\n    entry.top = top;\n    entry.height = height;\n    var el = entry.el;\n    el.style.top = top + 'px';\n    el.style.bottom = 'auto';\n    el.style.height = height + 'px';\n    el.style.setProperty('--ds-page-lock-viewport', height + 'px');\n    el.dispatchEvent(new CustomEvent('ds:page-lock:fit', {\n      detail: { top: top, height: height, resized: resized },\n    }));\n  }\n\n  function unfit(entry) {\n    var el = entry.el;\n    el.style.removeProperty('top');\n    el.style.removeProperty('bottom');\n    el.style.removeProperty('height');\n    el.style.removeProperty('--ds-page-lock-viewport');\n  }\n\n  function refit() {\n    fitted.forEach(fit);\n  }\n\n  if (viewport) {\n    viewport.addEventListener('resize', refit);\n    viewport.addEventListener('scroll', refit);\n  }\n\n  // \u2500\u2500 State \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n  function sync() {\n    var open = document.querySelectorAll('[' + ATTR + ']');\n    if (open.length && !saved) lock();\n    else if (!open.length && saved) unlock();\n\n    if (!viewport) return;\n    var next = [];\n    Array.prototype.forEach.call(open, function (el) {\n      if (!el.hasAttribute(FIT)) return;\n      var entry = null;\n      for (var i = 0; i < fitted.length; i++) {\n        if (fitted[i].el === el) entry = fitted[i];\n      }\n      next.push(entry || { el: el, top: null, height: null });\n    });\n    fitted.forEach(function (entry) {\n      if (next.indexOf(entry) === -1) unfit(entry);\n    });\n    fitted = next;\n    refit();\n  }\n\n  // The observer runs right after the handler that flipped the attribute,\n  // before the next paint, and between the listeners of one user event.\n  new MutationObserver(sync).observe(html, {\n    subtree: true,\n    childList: true,\n    attributes: true,\n    attributeFilter: [ATTR],\n  });\n  sync();\n\n  // \u2500\u2500 Touch guard \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n  function canScroll(el, axis, delta) {\n    var style = getComputedStyle(el);\n    if (axis === 'y') {\n      if (!/auto|scroll/.test(style.overflowY) || el.scrollHeight <= el.clientHeight + 1) return false;\n      if (delta > 0) return el.scrollTop > 0;\n      return el.scrollTop + el.clientHeight < el.scrollHeight - 1;\n    }\n    if (!/auto|scroll/.test(style.overflowX) || el.scrollWidth <= el.clientWidth + 1) return false;\n    // Logical start/end: scrollLeft runs 0 \u2192 negative in a right-to-left box.\n    var left = Math.abs(el.scrollLeft);\n    var start = style.direction === 'rtl' ? delta < 0 : delta > 0;\n    if (start) return left > 0;\n    return left + el.clientWidth < el.scrollWidth - 1;\n  }\n\n  var lastX = 0;\n  var lastY = 0;\n\n  document.addEventListener('touchstart', function (event) {\n    lastX = event.touches[0].clientX;\n    lastY = event.touches[0].clientY;\n  }, { passive: true });\n\n  document.addEventListener('touchmove', function (event) {\n    if (!saved || event.touches.length > 1 || !event.cancelable) return;\n    var x = event.touches[0].clientX;\n    var y = event.touches[0].clientY;\n    var dx = x - lastX;\n    var dy = y - lastY;\n    lastX = x;\n    lastY = y;\n    var target = event.target.nodeType === 1 ? event.target : event.target.parentElement;\n    var root = target && target.closest('[' + ATTR + ']');\n    if (!root) {\n      event.preventDefault();\n      return;\n    }\n    var axis = Math.abs(dx) > Math.abs(dy) ? 'x' : 'y';\n    var delta = axis === 'x' ? dx : dy;\n    if (!delta) return;\n    for (var el = target; el; el = el.parentElement) {\n      if (canScroll(el, axis, delta)) return;\n      if (el === root) break;\n    }\n    event.preventDefault();\n  }, { passive: false });\n})();\n\n} catch (sfError) {\nif (typeof console !== 'undefined') console.error('[sf-core] behavior \"page-lock\" failed to initialise:', sfError);\n}\n})();\n\n/* product-rail.js */\n(function () {\ntry {\n/* product-rail \u2014 category/product bands' finite carousel: arrows and swipe.\n *\n * Binds EVERY rail, not just the always-scrolling one. The other shape wraps\n * into a grid from md but still scrolls below it, and arrows follow the\n * scrolling rather than the shape \u2014 so the same file drives both and simply\n * asks, at each width, whether this track scrolls at all.\n *\n * The track is already a native horizontal scroller (snippets/ds-product-rail\n * .liquid), so swipe works with no JS and every tile is reachable by keyboard\n * before this file runs. This adds the two things the markup cannot express:\n * the arrows scroll by a whole number of tiles, and the buttons disable at the\n * real start/end of the row.\n *\n * How far one click travels is the section's call, and it differs by end \u2014 the\n * mobile track shows fewer tiles, so the desktop jump would overshoot the whole\n * viewport. Both counts arrive as attributes and the breakpoint is read from\n * `--breakpoint-md` rather than typed here, so a token change moves the JS with\n * the CSS.\n *\n * Authoring rules: src/behaviors/README.md. No import/export.\n */\n\nvar RAIL = '[data-ds-product-rail]';\n\nfunction desktopQuery() {\n  var token = window\n    .getComputedStyle(document.documentElement)\n    .getPropertyValue('--breakpoint-md')\n    .trim();\n  return '(min-width: ' + (token || '768px') + ')';\n}\n\nfunction bindRail(root) {\n  if (root.dataset.dsRailBound) return;\n  root.dataset.dsRailBound = '1';\n\n  var track = root.querySelector('[data-ds-product-rail-track]');\n  var nav = root.querySelector('[data-ds-carousel-nav]');\n  if (!track) return;\n\n  var reals = Array.prototype.slice.call(track.children);\n  if (!reals.length) {\n    if (nav) nav.remove();\n    return;\n  }\n\n  // Read once: a section cannot change its own settings without the Theme\n  // Editor re-rendering the whole subtree, which re-binds from scratch.\n  var stepDesktop = Math.max(1, Number(root.dataset.dsRailStep) || 1);\n  var stepMobile = Math.max(1, Number(root.dataset.dsRailStepMobile) || 1);\n  var wide = window.matchMedia(desktopQuery());\n  var prev = root.querySelector('[data-ds-carousel-nav-prev]');\n  var next = root.querySelector('[data-ds-carousel-nav-next]');\n\n  function tilesPerClick() {\n    return Math.max(1, wide.matches ? stepDesktop : stepMobile);\n  }\n\n  function step() {\n    // One tile plus one gutter, measured rather than assumed: the tile width is\n    // the `auto-cols-rail-*` calc off the per-view count and the gutter changes\n    // at md, so any number hardcoded here would be wrong at one end or the\n    // other.\n    // The track's CURRENT first tile, not the one captured at bind: the\n    // homepage reviews band refills its track from Judge.me after this runs,\n    // and a detached tile measures 0.\n    var first = track.firstElementChild || reals[0];\n    var box = first.getBoundingClientRect().width;\n    var gap = parseFloat(window.getComputedStyle(track).columnGap) || 0;\n    return box + gap;\n  }\n\n  function scrollable() {\n    // Asked of the layout, not of the summed tile widths. A wrapped grid's\n    // tiles add up far wider than the track while the track itself does not\n    // scroll at all \u2014 summing them reported overflow on a grid.\n    return track.scrollWidth > track.clientWidth + 1;\n  }\n\n  function updateNav() {\n    if (!nav) return;\n    var canScroll = scrollable();\n    nav.hidden = !canScroll;\n    var atStart = !canScroll || track.scrollLeft <= 1;\n    var atEnd = !canScroll || track.scrollLeft >= track.scrollWidth - track.clientWidth - 1;\n    if (prev) prev.disabled = atStart;\n    if (next) next.disabled = atEnd;\n  }\n\n  function restage() {\n    track.scrollLeft = Math.min(track.scrollLeft, Math.max(0, track.scrollWidth - track.clientWidth));\n    updateNav();\n  }\n\n  function scrollBy(direction) {\n    var max = Math.max(0, track.scrollWidth - track.clientWidth);\n    var target = Math.max(0, Math.min(max,\n      track.scrollLeft + direction * step() * tilesPerClick()));\n    // A smooth scroll reports its first scroll event on a later frame. Update\n    // from the clamped destination immediately so Prev becomes usable as soon\n    // as Next is pressed instead of looking stuck during the animation.\n    if (prev) prev.disabled = target <= 1;\n    if (next) next.disabled = target >= max - 1;\n    track.scrollTo({\n      left: target,\n      behavior: window.matchMedia('(prefers-reduced-motion: reduce)').matches ? 'auto' : 'smooth',\n    });\n  }\n\n  if (prev) prev.addEventListener('click', function () { scrollBy(-1); });\n  if (next) next.addEventListener('click', function () { scrollBy(1); });\n\n  var scrollQueued = false;\n  track.addEventListener('scroll', function () {\n    if (scrollQueued) return;\n    scrollQueued = true;\n    window.requestAnimationFrame(function () {\n      scrollQueued = false;\n      updateNav();\n    });\n  }, { passive: true });\n\n  // The per-view count is a media query, so a width change moves both the tile\n  // width and the gutter \u2014 every number above has to be re-measured.\n  // Width only. restage() clamps the scroll position, so running it on every\n  // observed box change \u2014 a tile's image settling, a label wrapping \u2014 would\n  // yank the row back to the start mid-browse.\n  var lastWidth = 0;\n  function onResize() {\n    var width = track.clientWidth;\n    if (width === lastWidth) return;\n    lastWidth = width;\n    restage();\n  }\n\n  if (typeof ResizeObserver !== 'undefined') {\n    var queued = false;\n    new ResizeObserver(function () {\n      if (queued) return;\n      queued = true;\n      window.requestAnimationFrame(function () { queued = false; onResize(); });\n    }).observe(track);\n  } else {\n    window.addEventListener('resize', onResize);\n  }\n\n  lastWidth = track.clientWidth;\n  restage();\n}\n\nfunction initRails(scope) {\n  if (!scope) return;\n  if (scope.matches && scope.matches(RAIL)) bindRail(scope);\n  if (scope.querySelectorAll) scope.querySelectorAll(RAIL).forEach(bindRail);\n}\n\nif (!document.__dsProductRailBound) {\n  document.__dsProductRailBound = true;\n  var startRails = function () { initRails(document); };\n  if (document.readyState === 'loading') {\n    document.addEventListener('DOMContentLoaded', startRails, { once: true });\n  } else {\n    startRails();\n  }\n  document.addEventListener('shopify:section:load', startRails);\n\n  if (typeof MutationObserver !== 'undefined') {\n    var rescanQueued = false;\n    var observer = new MutationObserver(function () {\n      if (rescanQueued) return;\n      rescanQueued = true;\n      requestAnimationFrame(function () {\n        rescanQueued = false;\n        startRails();\n        if (document.querySelector(RAIL)) observer.disconnect();\n      });\n    });\n    observer.observe(document.documentElement, { childList: true, subtree: true });\n  }\n}\n\n} catch (sfError) {\nif (typeof console !== 'undefined') console.error('[sf-core] behavior \"product-rail\" failed to initialise:', sfError);\n}\n})();\n\n/* product-reviews.js */\n(function () {\ntry {\n/* product-reviews \u2014 the reviews band's UI: summary rail, list, pager / Load\n * more, filter + sort panels, photo strip and lightbox.\n *\n * The UI is shared; the DATA is not. A band reads its reviews from one source,\n * picked once on mount, and every request after that goes to the same source:\n *\n *   product-reviews-data-sf.js       WanderCo SF API (the app's own mirror)\n *   product-reviews-data-judgeme.js  Judge.me's public widget feed \u2014 the fallback\n *\n * Each source hands this file the same plain review objects ({ id, author,\n * rating, dateIso, body, photos, \u2026 }) and answers the same questions \u2014 list\n * page N, strip page N, how many a draft filter holds \u2014 so nothing below knows\n * which one it is talking to, beyond the capability flags a source declares\n * (`photoServer`, `countsByRequest`, \u2026). SF is used when the shop has it on, the\n * API answers, and it holds reviews for a product Judge.me has reviews for;\n * otherwise Judge.me (`data-ds-reviews-sf` on the root records the choice).\n *\n * Authoring rules: src/behaviors/README.md. No import/export.\n */\n\nvar ROOT = '[data-ds-product-reviews]';\n/* Five was read off the Figma frame, and it turns out to be the merchant's own\n * setting rather than a coincidence: `shop.metafields.judgeme.settings` carries\n * jdgmSettings, which opens `{\"pagination\":5,\u2026}`. So this page size is what a\n * shopper already sees in Judge.me's widget, and the two do not disagree.\n * Left as a literal on purpose \u2014 that metafield is a 39KB <script> tag, and\n * regexing a number out of it in Liquid to arrive back at 5 buys nothing. If a\n * merchant ever changes it there, change it here. */\nvar PER_PAGE = 5;\n\n/* \u2500\u2500 Shared rendering helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n/* The flag as an SVG from flag-icons (MIT, flag.<cc>.svg in the theme's own\n * assets), not the prototype's emoji: Windows ships no flag glyphs, so the\n * emoji came out as the two letters \"US\" on most desktops, and the prototype\n * marks the emoji as a stopgap until real flag icons exist. The name comes\n * from Intl in the page's language, falling back to the code where\n * Intl.DisplayNames is missing. */\nfunction countryName(code) {\n  try {\n    var lang = document.documentElement.lang || 'en';\n    return new Intl.DisplayNames([lang], { type: 'region' }).of(code) || code;\n  } catch (error) {\n    return code;\n  }\n}\n\n/* Shared by the review list and the customer-photos lightbox. Null when the\n * review has no code, the code is Vietnam (merchant call), or the theme did\n * not ship flag-icons. The caller decides whether a miss removes the slot\n * (a cloned review) or only hides it (the one lightbox, reused per photo). */\nfunction countryFlagImage(review, src) {\n  if (!review.country || review.country === 'VN' || !src || src.indexOf('flag.us.svg') === -1) return null;\n  var name = countryName(review.country);\n  var img = document.createElement('img');\n  img.src = src.replace('flag.us.svg', 'flag.' + review.country.toLowerCase() + '.svg');\n  img.alt = name;\n  img.title = name;\n  img.width = 4;\n  img.height = 3;\n  img.loading = 'lazy';\n  img.decoding = 'async';\n  return img;\n}\n\n/* SF0126 \u2014 display form for a product average rating.\n * Ceil to one decimal (Math.ceil(avg * 10) / 10), then always print one decimal\n * place so \"5\" stays \"5.0\" and 4.96 becomes \"5.0\". Shared by the reviews-band\n * summary and (via the same Liquid rule) the buy-box title rating so both\n * surfaces never disagree. */\nfunction formatAverageRating(avg) {\n  var n = typeof avg === 'number' ? avg : parseFloat(avg);\n  if (avg == null || avg === '' || isNaN(n)) return null;\n  return (Math.ceil(n * 10) / 10).toFixed(1);\n}\n\n/* Write a source's aggregate ({ averageValue, count, dist }) into the summary\n * rail: the big score, the star row, the \"Based on N reviews\" line and the five\n * distribution bars. Idempotent, so it runs whether the Liquid seed was already\n * right (the common case) or stale. Null leaves the server render alone. */\nfunction applyAggregate(root, agg) {\n  if (!agg) return;\n\n  /* A number the merchant typed in the theme editor is the final word \u2014 the\n   * section stamps it on the root, and the widget metafield may not overwrite\n   * it. */\n  /* SF0126: ceil to 1 decimal + always one decimal digit (see formatAverageRating). */\n  var averageText = formatAverageRating(agg.averageValue);\n  if (averageText != null && !root.hasAttribute('data-ds-reviews-score-pinned')) {\n    var scoreEl = root.querySelector('[data-ds-reviews-score]');\n    if (scoreEl) scoreEl.textContent = averageText;\n  }\n\n  if (agg.averageValue != null && !isNaN(agg.averageValue)) {\n    var stars = root.querySelector('[data-ds-reviews-stars] [data-ds-rating]');\n    if (stars) setStars(stars, agg.averageValue);\n  }\n\n  if (agg.count != null && !root.hasAttribute('data-ds-reviews-count-pinned')) {\n    // Swap only the number, leaving the localized wording (\"Based on \u2026\", \"Bas\u00E9\n    // sur \u2026\") untouched \u2014 the count is the one thing that moved.\n    var summaryEl = root.querySelector('[data-ds-reviews-summary]');\n    if (summaryEl && /\\d/.test(summaryEl.textContent)) {\n      summaryEl.textContent = summaryEl.textContent.replace(/\\d[\\d.,]*/, String(agg.count));\n    }\n  }\n\n  var order = [5, 4, 3, 2, 1];\n  var peak = Math.max(agg.dist[5], agg.dist[4], agg.dist[3], agg.dist[2], agg.dist[1], 0);\n  // No histogram in the widget markup (peak 0) means we have no published\n  // distribution \u2014 leave whatever the server rendered rather than zeroing every\n  // bar. A genuinely review-less product shows the empty card, not bars, anyway.\n  var bars = peak > 0 ? root.querySelector('[data-ds-rating-bars]') : null;\n  if (bars) {\n    bars.querySelectorAll('[data-ds-rating-bar-row]').forEach(function (row, index) {\n      var freq = agg.dist[order[index]] || 0;\n      // Percent of the peak row, matching ds-rating-bars' own scaling. Set as an\n      // inline width so it wins over the attribute-keyed rules in rating-bars.css\n      // without needing a class for every possible percentage.\n      var fill = row.querySelector('[data-ds-rating-bar-fill]');\n      if (fill) fill.style.width = (peak > 0 ? Math.round((freq * 100) / peak) : 0) + '%';\n      var spans = row.querySelectorAll(':scope > span');\n      var count = spans[spans.length - 1];\n      if (count) count.textContent = String(freq);\n    });\n  }\n}\n\n/* The filter panel's star rows carry each level's count\n * (`data-ds-reviews-rating-total`, read by the \"Show N reviews\" button) and lock\n * a level with no reviews. The Liquid draws them from Judge.me's histogram; a\n * source that counts for itself rewrites them from its own `dist`, so the\n * button, the lock and the list agree with the source the band reads. */\nfunction applyRatingRows(root, dist) {\n  if (!dist) return;\n  root.querySelectorAll('[data-ds-reviews-rating-row]').forEach(function (row) {\n    var stars = parseInt(row.getAttribute('data-ds-reviews-rating-row'), 10);\n    if (dist[stars] === undefined) return;\n    var total = parseInt(dist[stars], 10) || 0;\n    row.setAttribute('data-ds-reviews-rating-total', String(total));\n    var input = row.querySelector('input[name=\"ds-reviews-rating\"]');\n    if (!input) return;\n    input.disabled = total === 0;\n    var label = row.querySelector('label[for=\"' + input.id + '\"][aria-hidden=\"true\"]');\n    if (!label) return;\n    label.classList.toggle('sf-opacity-50', total === 0);\n    label.classList.toggle('sf-cursor-not-allowed', total === 0);\n    label.classList.toggle('sf-cursor-pointer', total !== 0);\n  });\n}\n\n/* 57231 \u2192 \"57.2k\", 1284 \u2192 \"1.3k\", 950 \u2192 \"950\" \u2014 the hub header's Liquid rule. */\nfunction compactCount(n) {\n  if (n < 1000) return String(n);\n  var tenths = Math.round(n / 100);\n  var tenth = tenths % 10;\n  return Math.floor(tenths / 10) + (tenth ? '.' + tenth : '') + 'k';\n}\n\n/* The reviews page's header (ds-reviews-hub-header: score, total, five bars) is\n * drawn by the Liquid from Judge.me's shop metafields. A source that counts for\n * itself redraws it from its own aggregate, in place: only the numbers move,\n * the translated wording stays (the old numbers ride on data-ds-reviews-hub-*). */\nfunction applyHubHeader(root, agg) {\n  if (!agg || !root.id) return;\n  var row = document.querySelector('[data-ds-reviews-star][data-ds-reviews-for=\"' + root.id + '\"]');\n  var hub = row && row.closest('[data-ds-reviews-hub]');\n  if (!hub) return;\n\n  var avgText = formatAverageRating(agg.averageValue);\n  var score = hub.querySelector('[data-ds-reviews-hub-score-value]');\n  if (score && avgText) {\n    var oldScore = score.textContent.trim();\n    score.textContent = avgText;\n    var rated = score.getAttribute('aria-label');\n    if (rated && oldScore) score.setAttribute('aria-label', rated.replace(oldScore, avgText));\n  }\n\n  if (typeof agg.count === 'number') {\n    var short = hub.querySelector('[data-ds-reviews-hub-count-short]');\n    if (short) short.textContent = '(' + compactCount(agg.count) + ')';\n    var abbr = hub.querySelector('[data-ds-reviews-hub-count-abbr]');\n    if (abbr) {\n      var oldShort = abbr.getAttribute('data-ds-reviews-hub-count-abbr');\n      var oldFull = abbr.getAttribute('data-ds-reviews-hub-count-full');\n      var full = String(agg.count).replace(/\\B(?=(\\d{3})+(?!\\d))/g, ',');\n      if (oldShort) abbr.textContent = abbr.textContent.replace(oldShort, compactCount(agg.count));\n      if (oldFull) abbr.setAttribute('title', (abbr.getAttribute('title') || '').replace(oldFull, full));\n      abbr.setAttribute('data-ds-reviews-hub-count-abbr', compactCount(agg.count));\n      abbr.setAttribute('data-ds-reviews-hub-count-full', full);\n    }\n  }\n\n  var dist = agg.dist || {};\n  var total = (dist[5] || 0) + (dist[4] || 0) + (dist[3] || 0) + (dist[2] || 0) + (dist[1] || 0);\n  if (!total) return;\n  hub.querySelectorAll('[data-ds-reviews-star][data-ds-reviews-hub-count]').forEach(function (button) {\n    var stars = parseInt(button.getAttribute('data-ds-reviews-star'), 10);\n    var n = dist[stars] || 0;\n    var pct = Math.round((n * 100) / total);\n    var label = n > 0 && pct === 0 ? '<1%' : pct + '%';\n    var fill = button.querySelector('[data-ds-rating-bar-fill]');\n    if (fill) {\n      fill.setAttribute('data-ds-rating-bar-fill', String(pct));\n      fill.style.width = pct + '%';\n    }\n    var pctEl = button.querySelector('[data-ds-reviews-hub-pct]');\n    var oldLabel = pctEl ? pctEl.textContent.trim() : '';\n    if (pctEl) pctEl.textContent = label;\n    var oldCount = button.getAttribute('data-ds-reviews-hub-count');\n    var aria = button.getAttribute('aria-label') || '';\n    if (oldLabel) aria = aria.replace(oldLabel, label);\n    if (oldCount !== null) aria = aria.replace('(' + oldCount + ')', '(' + n + ')');\n    button.setAttribute('aria-label', aria);\n    button.setAttribute('data-ds-reviews-hub-count', String(n));\n  });\n}\n\n/* Judge.me renders its timestamps client-side, so the DOM text is usually\n * empty and only the ISO attribute is trustworthy. Absolute English wording\n * (e.g. \"Sep 18, 2026\") is built here from that stamp \u2014 short month, day\n * without a leading zero, comma, four-digit year. UTC calendar day so a\n * Z-suffixed ISO does not shift the date by local offset. */\nvar REVIEW_MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];\nfunction absoluteDate(iso) {\n  if (!iso) return '';\n  var m = /^(\\d{4})-(\\d{2})-(\\d{2})/.exec(String(iso));\n  var y, mo, day;\n  if (m) {\n    y = Number(m[1]);\n    mo = Number(m[2]);\n    day = Number(m[3]);\n  } else {\n    var then = Date.parse(iso);\n    if (isNaN(then)) return '';\n    var d = new Date(then);\n    y = d.getUTCFullYear();\n    mo = d.getUTCMonth() + 1;\n    day = d.getUTCDate();\n  }\n  if (mo < 1 || mo > 12 || day < 1) return '';\n  return REVIEW_MONTHS[mo - 1] + ' ' + day + ', ' + y;\n}\n\n/* \u2500\u2500 Rendering \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n/* Fill one cloned ds-review-item. Every optional node is REMOVED rather than\n * hidden when its data is missing, so the article's flex gaps close up the way\n * they would have if Liquid had never rendered it. */\nfunction fillReview(node, review, photoTemplate, flagSrc) {\n  // photoTemplate here is the REVIEW tile (76 -> 140), not the strip's (84).\n  var author = node.querySelector('[data-ds-review-author]');\n  if (author) author.textContent = review.author;\n\n\n  /* Removed, not blanked: an empty <p> still takes a flex gap, and no review on\n   * this store carries a title today. */\n  var title = node.querySelector('[data-ds-review-title]');\n  if (title) {\n    if (!review.title) title.remove();\n    else title.textContent = review.title;\n  }\n\n  // A rating-only review (SF keeps them: they are in its counts) drops the empty paragraph, as the title does.\n  var body = node.querySelector('[data-ds-review-body]');\n  if (body) {\n    if (!review.body) body.remove();\n    else body.textContent = review.body;\n  }\n\n  /* \"Purchased item: {name}\" \u2014 reviews page only; a PDP review has no product\n   * and the line goes. */\n  var product = node.querySelector('[data-ds-review-product]');\n  if (product) {\n    var productLink = product.querySelector('[data-ds-review-product-link]');\n    if (!review.product || !productLink) product.remove();\n    else {\n      productLink.textContent = review.product.title;\n      productLink.setAttribute('href', review.product.href || '#');\n    }\n  }\n\n  var date = node.querySelector('[data-ds-review-date]');\n  if (date) {\n    date.setAttribute('datetime', review.dateIso);\n    date.textContent = absoluteDate(review.dateIso);\n  }\n\n  var stars = node.querySelector('[data-ds-rating]');\n  if (stars) setStars(stars, review.rating);\n\n  /* \"Verified Buyer\" is hardcoded for every review (SF0341 issue 5): the badge stays\n   * on the node and nothing here reads a verified flag \u2014 there is no verified\n   * filter or sort either. */\n\n  var flag = node.querySelector('[data-ds-review-flag]');\n  if (flag) {\n    /* No Vietnam flag: the merchant's call (SF0026 review) \u2014 those reviews\n     * show the name without one, as if Judge.me had sent no code. A code\n     * flag-icons has no file for leaves no broken-image box. */\n    var img = countryFlagImage(review, flagSrc);\n    if (!img) flag.remove();\n    else {\n      img.onerror = function () { flag.remove(); };\n      flag.textContent = '';\n      flag.appendChild(img);\n    }\n  }\n\n  var photos = node.querySelector('[data-ds-review-photos]');\n  if (photos) {\n    if (!review.photos.length) {\n      photos.remove();\n    } else {\n      photos.innerHTML = '';\n      review.photos.forEach(function (photo) {\n        var tile = photoTemplate.content.firstElementChild.cloneNode(true);\n        var img = tile.querySelector('img');\n        if (img) {\n          img.setAttribute('src', photo.src);\n          img.removeAttribute('srcset');\n          img.setAttribute('alt', '');\n        }\n        // The lightbox walks the whole product's strip, not just this review's,\n        // so a body photo opens at its position in that flat list.\n        tile.setAttribute('data-ds-reviews-photo-src', photo.src);\n        photos.appendChild(tile);\n      });\n    }\n  }\n\n  var reply = node.querySelector('[data-ds-review-reply]');\n  if (reply) {\n    if (!review.reply) {\n      reply.remove();\n    } else {\n      var replyBody = reply.querySelector('[data-ds-review-reply-body]');\n      if (replyBody) replyBody.textContent = review.reply;\n    }\n  }\n  return node;\n}\n\n/* ds-rating draws five stacked star pairs and reveals the overlay with a width\n * utility. Re-pointing it at another score means toggling those same classes \u2014\n * no new markup, no second star implementation here. */\nfunction setStars(container, score) {\n  var halves = Math.max(0, Math.min(10, Math.round(score * 2)));\n  container.setAttribute('data-ds-rating-value', String(score));\n  // Each slot is `<span class=\"sf-relative\">{svg}{overlay?}</span>`; the overlay\n  // is the only child <span>, and it exists on every slot because the template\n  // renders ds-rating at its default 5.\n  container.querySelectorAll('[role=\"img\"] > span').forEach(function (slot, index) {\n    var overlay = slot.querySelector('span');\n    if (!overlay) return;\n    var filled = (index + 1) * 2;\n    overlay.classList.remove('sf-w-full', 'sf-w-1/2', 'sf-hidden');\n    if (halves >= filled) overlay.classList.add('sf-w-full');\n    else if (halves === filled - 1) overlay.classList.add('sf-w-1/2');\n    else overlay.classList.add('sf-hidden');\n  });\n}\n\n/* \u2500\u2500 One band \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n/* Page size per band: the PDP keeps the merchant's 5, the reviews page asks for\n * 10 (prototype ReviewsBlock `pageSize={10}`). One UI page is still one request\n * \u2014 the all-reviews feed honours per_page up to 25. */\nfunction bandPageSize(root) {\n  return parseInt(root.getAttribute('data-ds-reviews-per-page'), 10) || 5;\n}\n\n// Pair the sort radios with the machine values the section listed beside their\n// labels \u2014 see the sort_values param on ds-product-reviews. Same positional\n// contract the <select> had; only the control changed.\nfunction pairSortValues(root) {\n  var sortBox = root.querySelector('[data-ds-reviews-sort]');\n  var sortInputs = root.querySelectorAll('[data-ds-reviews-sort-option]');\n  if (!sortBox || !sortInputs.length) return;\n  var values = (sortBox.getAttribute('data-ds-reviews-sort') || '').split(',');\n  sortInputs.forEach(function (input, index) {\n    if (values[index]) input.value = values[index];\n  });\n}\n\n/* Pick the band's data source, once, then mount the UI on it.\n *\n * SF first, when the shop has it on (`__WC_REVIEW_SF_SOURCE__`), the API URL was\n * injected and it is not known to be down: one round trip asks for the stats\n * and list page 1 together. SF answers and holds reviews \u2014 or Judge.me holds\n * none either \u2014 and the band is SF's. SF failing, slow, or empty for a product\n * Judge.me has reviews for (not synced into the app yet), and the band is\n * Judge.me's. The skeleton covers the wait. */\nfunction setup(root) {\n  var source = root.querySelector('[data-ds-reviews-source]');\n  if (!source) return;\n  var sources = window.WcReviewData || {};\n  if (!sources.judgeme) {\n    // The data layer did not load: leave Judge.me's own widget showing.\n    source.classList.remove('sf-hidden');\n    return;\n  }\n  pairSortValues(root);\n  var perPage = bandPageSize(root);\n  var feedAll = root.getAttribute('data-ds-reviews-feed') === 'all';\n  var jm = sources.judgeme(root, { perPage: perPage, feedAll: feedAll, source: source });\n  var sf = sources.sf ? sources.sf(root, { perPage: perPage }) : null;\n  if (!sf) {\n    root.setAttribute('data-ds-reviews-sf', '0');\n    mount(root, jm);\n    return;\n  }\n  root.setAttribute('data-ds-reviews-sf', 'pending');\n  var picked = root.querySelector('[data-ds-reviews-sort-option]:checked');\n  sf.load(picked ? picked.value : '').then(function (ok) {\n    var jmCount = jm.aggregate && typeof jm.aggregate.count === 'number' ? jm.aggregate.count : jm.seed.length;\n    var useSf = ok && (sf.aggregate.count > 0 || jmCount === 0);\n    root.setAttribute('data-ds-reviews-sf', useSf ? '1' : '0');\n    mount(root, useSf ? sf : jm);\n  }, function () {\n    root.setAttribute('data-ds-reviews-sf', '0');\n    mount(root, jm);\n  });\n}\n\n/* The band's UI over one data source (see the header). */\nfunction mount(root, data) {\n  // Shadows the file-level default for everything inside this band.\n  var PER_PAGE = bandPageSize(root);\n  var feedAll = root.getAttribute('data-ds-reviews-feed') === 'all';\n  /* The strip is fetched page by page from the server (SF, PDP and reviews page alike) rather than as one whole set. */\n  var stripPaged = feedAll || !!data.stripPaged;\n  var source = root.querySelector('[data-ds-reviews-source]');\n  var list = root.querySelector('[data-ds-reviews-list]');\n  var template = root.querySelector('[data-ds-review-template]');\n  var photoTemplate = root.querySelector('[data-ds-reviews-photo-template]');\n  var reviewPhotoTemplate = root.querySelector('[data-ds-review-photo-template]');\n  var flagSrc = root.getAttribute('data-ds-reviews-flag-src') || '';\n  var morePhotoTemplate = root.querySelector('[data-ds-reviews-photo-more-template]');\n  var pageTemplate = root.querySelector('[data-ds-reviews-page-template]');\n  var ellipsisTemplate = root.querySelector('[data-ds-reviews-ellipsis-template]');\n  if (!source || !list || !template) return;\n  if (!photoTemplate || !morePhotoTemplate || !pageTemplate || !ellipsisTemplate) return;\n  if (!reviewPhotoTemplate) return;\n\n  // Reconcile the summary rail with the source's numbers first \u2014 this is\n  // independent of the list below, so it also corrects a rating-only product\n  // where the source has nothing to render.\n  var aggregate = data.aggregate;\n  applyAggregate(root, aggregate);\n  if (data.rewritesRatingRows && aggregate) {\n    applyRatingRows(root, aggregate.dist);\n    if (feedAll) applyHubHeader(root, aggregate);\n  }\n\n  var all = data.seed;\n  // Nothing to render: fall back to Judge.me's own widget and stand down. The\n  // source node ships hidden (the Liquid `sf-hidden`, to kill the first-paint\n  // flash), so un-hide it here \u2014 otherwise a widget the source can't read would\n  // leave a blank column instead of Judge.me's working one. A source that can\n  // fetch its first reviews another way (Judge.me with an empty metafield) gets\n  // one try first.\n  if (!all.length) {\n    if (data.fillSeed && !root.dataset.dsSeeded) {\n      root.dataset.dsSeeded = '1';\n      data.fillSeed().then(function (ok) {\n        if (ok) mount(root, data);\n        else source.classList.remove('sf-hidden');\n      });\n      return;\n    }\n    source.classList.remove('sf-hidden');\n    return;\n  }\n\n  var state = { sort: '', rating: 0, photo: false, variants: [], page: 1, shown: PER_PAGE, complete: false };\n  // Every photo on the product, flattened, each remembering the review it came\n  // from \u2014 the lightbox steps through this rather than through the reviews.\n  var strip = [];\n  /* Which review each rendered row shows, so a photo inside a review body can\n   * find its own review when the strip does not hold that photo (see the\n   * lightbox click handler). */\n  var reviewOfNode = typeof WeakMap !== 'undefined' ? new WeakMap() : null;\n\n  source.classList.add('sf-hidden');\n\n  /* The CONTIGUOUS prefix from page 1 of the current query. Used by mobile\n   * (which appends) and by the photo filter; desktop reads `rowsForPage`\n   * directly.\n   *\n   * Contiguous, not \"every page loaded\": desktop fetches per click, so the cache\n   * can be {1, 5, 6}. Concatenating all three puts page 5's first review at\n   * index 6 \u2014 mobile's `state.shown` and `appendMore` (next page = length + 1)\n   * both count by position, so a sparse cache yields rows in the wrong place and\n   * asks for the wrong page. Cutting at the first gap is enough to keep both\n   * honest. */\n  function loadedRows() {\n    syncQuery();\n    var out = [];\n    for (var n = 1; pages[n]; n += 1) out = out.concat(pages[n]);\n    return out;\n  }\n\n  /* The set the LOCAL filters run on. Judge.me has no parameter that filters by\n   * photo \u2014 all of them were probed (`with_pictures`, `has_pictures`,\n   * `pictures_only`, `filter_pictures`, `media`, `only_pictures`: `total_count`\n   * never moved) \u2014 so \"which reviews have photos\" can only be answered by\n   * reading reviews.\n   *\n   * The source is the reviews `loadStrip` ALREADY fetched, not whichever pages\n   * the shopper happened to click through. It costs no extra request and is wide\n   * enough to still have a pager. The shop sets\n   * `default_sort_method = pictures-first`, so reviews with photos sit in one\n   * block at the front \u2014 the first 20 are the densest stretch of photos a single\n   * request can reach.\n   *\n   * The remaining limit, stated plainly: a review with photos beyond that block\n   * is out of this filter's reach. Reaching it means reading every review, which\n   * is sequential pagination. */\n  function photoPool() {\n    /* The same source as `stripVisible`, and they may not diverge. The old\n     * version fell back to `loadedRows()` when `loadStrip` failed while the strip\n     * still drew from `all` \u2014 so the shopper could click a photo the filter could\n     * not reach: forcing `loadStrip` to fail with 7 photo reviews gave a list of\n     * 5 rows, NO pager, and \"See this review\" on the seventh tile silently did\n     * nothing. The filter's set must be exactly the strip's, never smaller. */\n    return stripRows && stripRows.length ? stripRows : all;\n  }\n\n  /* A review's position in an array, matched on Judge.me's `id`, falling back to\n   * object identity only when the review carries no id. */\n  function indexOfReview(rows, wanted) {\n    if (!wanted) return -1;\n    if (!wanted.id) return rows.indexOf(wanted);\n    for (var i = 0; i < rows.length; i += 1) {\n      if (rows[i] === wanted || rows[i].id === wanted.id) return i;\n    }\n    return -1;\n  }\n\n  /* The strip's set with the active filters applied. The underlying request\n   * carries no filter parameters, so rating/variant have to be applied\n   * locally \u2014 the same thing `visible()` does when it runs on this set. */\n  function stripVisible() {\n    return (stripRows || all).filter(function (review) {\n      if (!review.photos.length) return false;\n      if (state.rating && review.rating !== state.rating) return false;\n      if (state.variants.length && state.variants.indexOf(review.variant || '') === -1) return false;\n      return true;\n    });\n  }\n\n  /* `filters` defaults to the committed `state`. An open filter panel passes\n   * a draft read from the form so \"Show N reviews\" can count a selection that\n   * has not been applied yet. */\n  function visibleWith(filters) {\n    var f = filters || state;\n    /* Turning on the photo filter swaps the source: from the list (which the\n     * server already filtered by sort/rating) to the strip's set (one request\n     * carrying NO filter parameters). So on that swap the rating must be\n     * applied LOCALLY \u2014 otherwise it vanishes in silence. Filtering those 20\n     * is exact, not an approximation. */\n    var onPool = f.photo && !data.photoServer;\n    if (onPool) return sortPool(photoPool().filter(keep));\n    return loadedRows().filter(keep);\n\n    function keep(review) {\n      if (f.photo && !review.photos.length) return false;\n      if (onPool && f.rating && review.rating !== f.rating) return false;\n      if (f.variants.length && f.variants.indexOf(review.variant || '') === -1) return false;\n      return true;\n    }\n  }\n\n  function visible() {\n    return visibleWith(state);\n  }\n\n  /* Whether \"With photos\" runs on the strip's set in memory (Judge.me's PDP\n   * widget endpoint cannot filter by photo) rather than as a server query (SF\n   * and Judge.me's all-reviews feed can) \u2014 the source's `photoServer`. */\n  function photoLocal(filters) {\n    return !!(filters || state).photo && !data.photoServer;\n  }\n\n  /* Sort LOCALLY, and only for the strip's set.\n   *\n   * That set arrives from a request carrying no `sort_by` (deliberately \u2014 it is\n   * the only way to ask for the shop's pictures-first order), so without a local\n   * sort, changing the sort while \"With photos\" is on is a click that does\n   * nothing: the trigger reads \"Lowest rating\" while the list sits still. The set\n   * is 20 reviews already in memory, so sorting it is exact rather than\n   * approximate.\n   *\n   * \"Pictures first\" keeps the server's order \u2014 that IS pictures-first. */\n  function sortPool(rows) {\n    var parts = String(currentSort() || '').split('|');\n    var key = parts[0];\n    var dir = parts[1] === 'asc' ? 1 : -1;\n    if (!key || key === 'pictures') return rows;\n    return rows.slice().sort(function (a, b) {\n      if (key === 'rating') return (a.rating - b.rating) * dir;\n      if (key === 'helpful') {\n        var votes = (a.helpful - b.helpful) * dir;\n        if (votes) return votes;\n        return Date.parse(b.dateIso || 0) - Date.parse(a.dateIso || 0);\n      }\n      return (Date.parse(a.dateIso || 0) - Date.parse(b.dateIso || 0)) * dir;\n    });\n  }\n\n  /* Two breakpoints, read from tokens rather than typed here so the JS moves\n   * with the CSS (prototype ReviewsBlock / ReviewFilterPicker):\n   *   - the list paginates from tablet-air (820) and appends with Load more\n   *     below it \u2014 Figma 3445:26357 vs 2787:6734;\n   *   - Filter and Sort open as a dropdown from tablet-mini (744) and as a\n   *     bottom sheet below it: every tablet gets the desktop picker. */\n  function atLeast(token, fallback) {\n    var value = getComputedStyle(document.documentElement).getPropertyValue(token).trim();\n    var px = parseInt(value, 10) || fallback;\n    return window.matchMedia('(min-width: ' + px + 'px)').matches;\n  }\n  function isDesktop() {\n    return atLeast('--breakpoint-tablet-air', 820);\n  }\n  function panelsDrop() {\n    return atLeast('--breakpoint-tablet-mini', 744);\n  }\n\n  /* The real list and the skeleton trade places. `aria-busy` goes on the real\n   * list rather than the skeleton: the skeleton is aria-hidden, so to a screen\n   * reader the thing that is busy IS the list. */\n  function setBusy(on) {\n    var skeleton = root.querySelector('[data-ds-reviews-skeleton]');\n    list.setAttribute('aria-busy', on ? 'true' : 'false');\n\n    /* Lock Sort and Filter while waiting. They run on the loaded set, so using\n     * them before the data is in returns an answer that is true-of-what-arrived\n     * and then changes itself when the rest lands \u2014 measured: the same \"With\n     * photos\" box read \"Show 5 reviews\" and became \"Show 16 reviews\" with the\n     * shopper touching nothing. Better unusable for a few seconds than an answer\n     * that will move. */\n    ['filter', 'sort'].forEach(function (key) {\n      var trigger = root.querySelector('[data-ds-reviews-toggle=\"' + key + '\"]');\n      if (trigger) trigger.disabled = on;\n    });\n\n    if (!skeleton) return;\n    skeleton.classList.toggle('sf-hidden', !on);\n    list.classList.toggle('sf-hidden', on);\n  }\n\n  function renderList() {\n    var desktop = isDesktop();\n    /* Photo mode runs on what is already loaded (photos sit in one block at the\n     * front under `pictures_first`), so it slices the flat array. Otherwise\n     * desktop reads EXACTLY the matching API page \u2014 one page, one request, no\n     * accumulation. */\n    var totalPages = photoLocal()\n      ? Math.max(1, Math.ceil(visible().length / PER_PAGE))\n      : pageCount();\n    if (state.page > totalPages) state.page = totalPages;\n    var slice = desktop\n      ? (photoLocal()\n          ? visible().slice((state.page - 1) * PER_PAGE, state.page * PER_PAGE)\n          : rowsForPage(state.page))\n      : visible().slice(0, state.shown);\n\n    list.innerHTML = '';\n    slice.forEach(function (review) {\n      var node = template.content.firstElementChild.cloneNode(true);\n      list.appendChild(fillReview(node, review, reviewPhotoTemplate, flagSrc));\n      if (reviewOfNode) reviewOfNode.set(node, review);\n    });\n\n    /* \"No reviews match\" is a CONCLUSION, so it must stay quiet while the list\n     * is waiting on the API \u2014 there is nothing to conclude yet \u2014 and equally\n     * quiet when the request just failed: \"could not ask\" and \"asked, and there\n     * are none\" are two different statements. */\n    var empty = root.querySelector('[data-ds-reviews-empty]');\n    if (empty) {\n      var busy = list.getAttribute('aria-busy') === 'true';\n      empty.classList.toggle('sf-hidden', slice.length > 0 || busy || !!failed[state.page]);\n    }\n\n    renderPagination(totalPages);\n\n    syncMore();\n  }\n\n  /* The mobile \"Load more\" pill only ever grows the list, so it appends rather\n   * than going through renderList's clear-and-rebuild \u2014 that would replay the\n   * fade-in on every row already on screen, not just the new ones. The new\n   * nodes go in with data-ds-review-enter set (review-list.css's opacity 0 /\n   * translateY state), then a forced reflow before it is cleared so the\n   * browser commits that starting frame first \u2014 without it, the add and the\n   * remove would coalesce into one paint and the transition would never run.\n   * Not requestAnimationFrame: rAF is paused while the tab is backgrounded,\n   * which would strand the row invisible until the shopper returns to it. */\n  /* Mobile appends. Each press adds exactly one page, which is exactly one\n   * request \u2014 `per_page=5` matches this button's step, so nothing has to be\n   * gathered. */\n  function appendMore() {\n    var need = state.shown + PER_PAGE;\n    var have = visible().length;\n    if (have < need && !photoLocal()) {\n      var next = Math.floor(state.shown / PER_PAGE) + 1;\n      var token = ++ticket;\n      setMoreBusy(true);\n      return fetchUiPage(next).then(function () {\n        setMoreBusy(false);\n        if (token !== ticket) return;\n        appendSlice();\n      });\n    }\n    return appendSlice();\n  }\n\n  function appendSlice() {\n    var rows = visible();\n    var slice = rows.slice(state.shown, state.shown + PER_PAGE);\n    /* Advance by exactly the rows just added. Adding a hard PER_PAGE moves the\n     * cursor even on a press that appended nothing, and from then on the button\n     * skips real reviews. */\n    state.shown += slice.length;\n\n    var added = slice.map(function (review) {\n      var node = template.content.firstElementChild.cloneNode(true);\n      fillReview(node, review, reviewPhotoTemplate, flagSrc);\n      if (reviewOfNode) reviewOfNode.set(node, review);\n      node.setAttribute('data-ds-review-enter', '');\n      list.appendChild(node);\n      return node;\n    });\n\n    if (added.length) added[0].offsetHeight; // eslint-disable-line no-unused-expressions\n    added.forEach(function (node) { node.removeAttribute('data-ds-review-enter'); });\n\n    syncMore();\n  }\n\n  /* The load-more button while the network is out: swap to the spinner variant\n   * Liquid already rendered. Both variants are real ds-buttons, so no class is\n   * hand-written here, and the busy one carries `sf-pointer-events-none` so a\n   * second press cannot get through. */\n  function setMoreBusy(on) {\n    var idle = root.querySelector('[data-ds-reviews-more-idle]');\n    var busy = root.querySelector('[data-ds-reviews-more-busy]');\n    if (!idle || !busy) return;\n\n    /* Swapping the button must CARRY FOCUS with it. The button just pressed is\n     * the one about to be `display:none`, so without this focus falls to\n     * `<body>`: a keyboard shopper's second Enter does nothing and Tab restarts\n     * from the top of the page. The old version set `button.disabled = true`, so\n     * focus stayed \u2014 swapping two nodes means doing that work by hand.\n     *\n     * Only move focus when it is actually inside this pair; a shopper who\n     * pressed and then went elsewhere is left alone. */\n    var from = on ? idle : busy;\n    var to = on ? busy : idle;\n    var carry = from.contains(document.activeElement);\n\n    idle.classList.toggle('sf-hidden', on);\n    busy.classList.toggle('sf-hidden', !on);\n\n    if (!carry) return;\n    var target = to.querySelector('button');\n    if (target && typeof target.focus === 'function') target.focus();\n  }\n\n  /* Whether the \"Load more\" pill stays or goes \u2014 one rule shared by `renderList`\n   * and `appendSlice`.\n   *\n   * The yardstick is the query's TOTAL, not the row count in memory. Measured\n   * against what is loaded, `state.shown` after the first press always equals\n   * exactly the rows just fetched, so the pill hides itself on that first press\n   * with 415 reviews still behind it \u2014 the reported \"load more on mobile\n   * disappears after one click\".\n   *\n   * The photo filter is a genuine exception: it runs locally on the strip's set,\n   * so that set is the whole of what the filter has. */\n  function syncMore() {\n    var pill = root.querySelector('[data-ds-reviews-more]');\n    if (!pill) return;\n    var total;\n    if (photoLocal()) {\n      total = visible().length;\n    } else {\n      total = pagesTotal || all.length;\n      total = Math.min(total, data.maxReviews);\n      /* Once the real bottom is known, use the real bottom rather than the number\n       * the server claims. `total_count` can exceed what the endpoint will serve\n       * (measured: claims 210, serves 192). Measured only against 210, the pill\n       * is still showing at review 192 while the next press lands on a page\n       * already loaded \u2014 a button that lives forever and adds nothing.\n       *\n       * The yardstick is the ROWS loaded contiguously, not\n       * `lastKnownPage * PER_PAGE`: the bottom page is usually shorter than a\n       * full one (the 6-review fixture ends with a page of 1), so multiplying\n       * overshoots in exactly the place this is meant to fix. */\n      if (lastKnownPage && pages[lastKnownPage]) total = Math.min(total, loadedRows().length);\n    }\n    /* Hide Load more after a real fail so aggregate pageCount does not imply\n     * more pages to fetch. Also hide when the next mobile page already failed\n     * and is not cached \u2014 pressing again would only re-fail. */\n    var nextMobile = Math.floor(state.shown / PER_PAGE) + 1;\n    var hideForFail = !!failed[state.page]\n      || (!photoLocal() && !!failed[nextMobile] && !pages[nextMobile]);\n    pill.classList.toggle('sf-hidden', isDesktop() || state.shown >= total || hideForFail);\n  }\n\n  /* Which page numbers to draw. Figma's `1 2 3 \u2026 10` is the first-page case of\n   * one rule: the run always shows page 1, the current page with a neighbour\n   * either side, and the last page \u2014 and near the start that collapses to the\n   * first three. Everything else becomes a gap marker. Without this a product\n   * with 35 reviews would draw seven buttons, and one with 300 would draw\n   * sixty. */\n  function pageRun(pages) {\n    /* A window of THREE numbers, and it only slides when the shopper clicks the\n     * number at its EDGE \u2014 the one next to the ellipsis. Clicking the middle\n     * number leaves the window where it is, because the next page is already\n     * sitting there and no room needs making.\n     *\n     *   1 2 3 \u2026 86   click 2 (middle) \u2192 1 2 3 \u2026 86   window holds\n     *                click 3 (edge)   \u2192 2 3 4 \u2026 86   slides one step\n     *                click 4 (edge)   \u2192 3 4 5 \u2026 86\n     *\n     * Symmetric in both directions: clicking the LEFT edge slides it back, so\n     * walking backwards also always leaves a number to click rather than sending\n     * the shopper hunting for the Prev arrow.\n     *\n     * That is why the window carries STATE \u2014 `runStart` remembers where it\n     * stands instead of being recomputed from `state.page`. Derived purely from\n     * the current page, the current page either always leads (sliding on every\n     * click, including middle ones) or always centres (never showing an edge).\n     * Neither is this.\n     *\n     * A long jump \u2014 straight to the last page, or a filter change that returns to\n     * page 1 \u2014 makes the window follow rather than crawl there one step at a\n     * time. */\n    var cur = state.page;\n    var maxStart = Math.max(1, pages - 2);\n    if (runStart > maxStart) runStart = maxStart;\n\n    if (cur < runStart) runStart = cur;\n    else if (cur > runStart + 2) runStart = cur - 2;\n    else if (cur === runStart + 2) runStart = cur - 1;\n    else if (cur === runStart && cur > 1) runStart = cur - 1;\n\n    runStart = Math.max(1, Math.min(runStart, maxStart));\n\n    var run = [];\n    for (var n = runStart; n <= Math.min(runStart + 2, pages); n += 1) run.push(n);\n    var last = run[run.length - 1];\n    if (last < pages) {\n      if (pages - last > 1) run.push(null); // gap marker\n      run.push(pages);\n    }\n    return run;\n  }\n\n  /* Every interaction goes through here: fetch the page that is wanted, then\n   * draw. A response whose ticket is stale is dropped \u2014 on two quick clicks, the\n   * first result arriving late would paint over the page the shopper is actually\n   * looking at. */\n  function show(uiPage) {\n    state.page = Math.max(1, uiPage);\n    if (photoLocal() || rowsForPage(state.page).length) {\n      setBusy(false);\n      renderList();\n      renderFilterTrigger();\n      renderApply();\n      return Promise.resolve();\n    }\n    var token = ++ticket;\n    setBusy(true);\n    return fetchUiPage(state.page).then(function () {\n      if (token !== ticket) return;\n      /* An empty page means we went past the bottom. `per_page=5` returns empty\n       * in exactly the right place, so `lastKnownPage` already knows \u2014 just fall\n       * back to it. */\n      if (!rowsForPage(state.page).length && lastKnownPage) state.page = Math.min(state.page, lastKnownPage);\n      setBusy(false);\n      renderList();\n      renderFilterTrigger();\n      renderApply();\n    });\n  }\n\n  function renderPagination(pages) {\n    var nav = root.querySelector('[data-ds-reviews-pagination]');\n    var slot = root.querySelector('[data-ds-reviews-pages]');\n    if (!nav || !slot) return;\n    /* Hide pager after a real fail so aggregate pageCount does not imply a\n     * browsable list. */\n    var show = pages >= 2 && isDesktop() && !failed[state.page];\n    nav.classList.toggle('sf-hidden', !show);\n    nav.classList.toggle('sf-flex', show);\n    slot.innerHTML = '';\n    if (!show) return;\n\n    pageRun(pages).forEach(function (page) {\n      if (page === null) {\n        slot.appendChild(ellipsisTemplate.content.firstElementChild.cloneNode(true));\n        return;\n      }\n      var button = pageTemplate.content.firstElementChild.cloneNode(true);\n      button.textContent = String(page);\n      button.setAttribute('data-page', String(page));\n      if (page === state.page) {\n        button.setAttribute('aria-current', 'page');\n        /* Reviews page: the current page is not a stop for Tab, as in the\n         * prototype's Pagination (`disabled` on the active button). */\n        if (root.getAttribute('data-ds-reviews-layout') === 'page') button.disabled = true;\n      }\n      slot.appendChild(button);\n    });\n\n    setArrow('[data-ds-reviews-prev]', state.page <= 1);\n    setArrow('[data-ds-reviews-next]', state.page >= pages);\n  }\n\n  function setArrow(selector, disabled) {\n    var control = root.querySelector(selector + ' button, ' + selector + ' a');\n    if (control) control.disabled = disabled;\n  }\n\n  function renderPhotoStrip() {\n    var block = root.querySelector('[data-ds-reviews-photos]');\n    var track = root.querySelector('[data-ds-reviews-photos-track]');\n    if (!block || !track) return;\n\n    /* The strip MUST follow the active filter. It used to show every photo\n     * regardless, so filtering to 1\u2605 and then clicking a 5\u2605 photo was routine \u2014\n     * and \"See this review\" would then hunt for a review outside the filtered\n     * set and land on the wrong page. Filtering the strip by the same state is\n     * the only fix at the root: whatever the shopper can click is guaranteed to\n     * be in the list. */\n    var photos = [];\n    stripVisible().forEach(function (review) {\n      review.photos.forEach(function (photo) {\n        photos.push({ src: photo.src, full: photo.full, review: review });\n      });\n    });\n    /* An open lightbox points into the old `strip` by INDEX, so replacing the\n     * array under it makes that index meaningless: open tile 13 of the\n     * metafield's 15 photos, let `loadStrip` land with 25 in a different order,\n     * and Next jumps to an unrelated photo while the counter goes from \"13 / 15\"\n     * to \"14 / 25\". Re-anchor by `src` \u2014 the same photo is the same URL \u2014 and\n     * only fall back to 0 when the photo on screen is gone from the new set. */\n    /* A lightbox walking one review's own photos (`shots`) does not point into\n     * the strip, so there is nothing to re-anchor. */\n    var pinned = !shots && lightbox && lightbox.getAttribute('aria-hidden') === 'false'\n      ? strip[lightboxAt] && strip[lightboxAt].src\n      : null;\n\n    strip = photos;\n\n    if (pinned) {\n      var found = -1;\n      for (var s = 0; s < photos.length; s += 1) {\n        if (photos[s].src === pinned) { found = s; break; }\n      }\n      lightboxAt = found < 0 ? 0 : found;\n      /* The counter must state the new total immediately, not wait for an arrow\n       * press. */\n      var counter = lightbox.querySelector('[data-ds-photo-lightbox-counter]');\n      if (counter && photos.length) counter.textContent = stripPosition(lightboxAt) + ' / ' + stripPhotoTotal();\n    }\n\n    if (!photos.length) {\n      /* Do NOT conclude anything before the answer arrives. A null `stripRows`\n       * means `loadStrip` has not returned, and the set under consideration is\n       * still the metafield's five reviews \u2014 on a product whose first five carry\n       * no photos, the whole block would be hidden and then reappear seconds\n       * later, shoving the list and everything under it down. That is precisely\n       * the jump the placeholders exist to prevent. Keep the block and its\n       * placeholders until there is a real answer.\n       *\n       * Once the answer is in and still empty (filtered to 1\u2605 with no 1\u2605 review\n       * carrying photos), then remove it, rather than leaving an empty rail with\n       * a badge reading 0. */\n      if (!stripSettled) return;\n      track.innerHTML = '';\n      block.classList.add('sf-hidden');\n      block.classList.remove('sf-flex');\n      return;\n    }\n    track.innerHTML = '';\n\n    /* Figma shows ten tiles and folds the remainder into the last one as\n     * \"+7\" \u2014 so the strip never grows past ten no matter how many photos the\n     * product has collected. The tenth stands for itself plus the hidden ones,\n     * which is why the count subtracts nine and not ten. */\n    var MAX_TILES = 10;\n    /* Before `loadStrip` returns, this set is only the photos of the metafield's\n     * reviews \u2014 LOADING, not an answer. Say so: measured, the strip drew 7 tiles\n     * and quietly became 19, with the count following and nothing in between to\n     * signal it.\n     *\n     * So while waiting, the rest of the row is placeholders placed AFTER the real\n     * photos: the first-paint data stays viewable immediately, and the room for\n     * what is coming is already reserved \u2014 when it lands it replaces in place\n     * rather than growing. The \"+N\" overflow tile waits too: do not print N\n     * before N is known. */\n    var loading = !stripSettled;\n    /* While loading, the LAST tile is always a placeholder, even when ten photos\n     * are already in hand.\n     *\n     * Without this rule, a first strip page that returns ten photos looks\n     * finished \u2014 ten real photos, no placeholders, badge hidden \u2014 and then the\n     * second request lands and the tenth tile quietly becomes \"+31\" while the\n     * badge appears. Measured exactly that: three states, the middle one carrying\n     * no sign at all that more was on the way.\n     *\n     * Reserving the last tile makes that slot visibly pending for the whole wait,\n     * and when the data lands it becomes either a real photo or the \"+N\" chip \u2014\n     * replaced in place, not grown. */\n    var cap = loading ? MAX_TILES - 1 : MAX_TILES;\n    var shown = photos.slice(0, cap);\n    var photoTotal = stripPhotoTotal();\n    /* \"+N\" is the badge less the nine tiles before it, in the badge's own\n     * unit, so the two always read off one number (QC r7). */\n    var hidden = photoTotal - (MAX_TILES - 1);\n    var overflowing = photoTotal > MAX_TILES;\n\n    shown.forEach(function (photo, index) {\n      var overflow = !loading && overflowing && index === MAX_TILES - 1;\n      var source = overflow ? morePhotoTemplate : photoTemplate;\n      var tile = source.content.firstElementChild.cloneNode(true);\n      var img = tile.querySelector('img');\n      if (img) {\n        img.setAttribute('src', photo.src);\n        img.removeAttribute('srcset');\n        img.setAttribute('alt', '');\n      }\n      if (overflow) {\n        var badge = tile.querySelector('[data-ds-reviews-photo-more]');\n        if (badge) badge.textContent = '+' + hidden;\n      }\n      tile.setAttribute('data-ds-reviews-photo-index', String(index));\n      track.appendChild(tile);\n    });\n\n    /* Liquid's placeholder block covers the stretch BEFORE JS runs. From here on\n     * the placeholders live inside the track itself, after the real photos, so\n     * take one of them as a template before removing the block \u2014 the classes stay\n     * written in Liquid rather than retyped in JS. */\n    var strip_skeleton = root.querySelector('[data-ds-reviews-photos-skeleton]');\n    if (strip_skeleton) {\n      if (!stripPlaceholder && strip_skeleton.firstElementChild) {\n        stripPlaceholder = strip_skeleton.firstElementChild.cloneNode(true);\n      }\n      strip_skeleton.remove();\n    }\n\n    if (loading && stripPlaceholder) {\n      for (var g = shown.length; g < MAX_TILES; g += 1) {\n        var ghost = stripPlaceholder.cloneNode(true);\n        ghost.setAttribute('aria-hidden', 'true');\n        ghost.setAttribute('data-ds-reviews-photo-ghost', '');\n        track.appendChild(ghost);\n      }\n    }\n\n    /* The badge counts EXACTLY the photos in the strip \u2014 which is also the number\n     * the lightbox can page through, so the label and the thing being clicked\n     * tell one story. It is NOT the product's total: knowing that would mean\n     * reading every review, and Judge.me has no endpoint that counts them.\n     * Redrawn whenever the strip changes, so it starts as the metafield's photos\n     * and becomes the count of whatever `loadStrip` brought back.\n     *\n     * ds-badge keeps its copy in a `.label` span beside optional icons \u2014 write to\n     * that, not to the badge, or the icons go with the text. */\n    var badge = root.querySelector('[data-ds-reviews-photos-count]');\n    if (badge) {\n      var label = badge.querySelector('.label') || badge.querySelector('[data-ds-badge] > span') || badge;\n      label.textContent = String(photoTotal);\n      /* While loading, show NO number. The first-paint figure counts the\n       * metafield's five reviews, not the strip's set \u2014 publishing it promises\n       * something about to be corrected, and it was measured going from 7 to 19\n       * under the shopper's hand. */\n      badge.classList.toggle('sf-hidden', loading || strip.length === 0);\n    }\n\n    block.classList.remove('sf-hidden');\n    block.classList.add('sf-flex');\n    syncStripNav();\n  }\n\n  /* Arrows only where they do something. A strip of three photos on a desktop\n   * column never scrolls, and ds-product-rail already set the precedent: drop\n   * the nav rather than leave two dead controls on a row that cannot move. */\n  function syncStripNav() {\n    var track = root.querySelector('[data-ds-reviews-photos-track]');\n    var nav = root.querySelector('[data-ds-reviews-photos] [data-ds-carousel-nav]');\n    if (!track || !nav) return;\n\n    var scrolls = track.scrollWidth > track.clientWidth + 1;\n    nav.hidden = !scrolls;\n    if (!scrolls) return;\n\n    var prev = nav.querySelector('[data-ds-carousel-nav-prev]');\n    var next = nav.querySelector('[data-ds-carousel-nav-next]');\n    // A fractional column (DPR 2, a 538.5 strip) stops the scroll a pixel or\n    // so short of scrollWidth, so a 1px tolerance never reached the end.\n    var atStart = track.scrollLeft <= 2;\n    var atEnd = Math.ceil(track.scrollLeft + track.clientWidth) >= track.scrollWidth - 2;\n    /* Reviews page: both arrows stay live at the ends, as the prototype's\n     * strip draws them (QC r7) \u2014 a press at an end simply has nowhere to go. */\n    var keepLive = root.getAttribute('data-ds-reviews-layout') === 'page';\n    if (prev) prev.disabled = keepLive ? false : atStart;\n    if (next) next.disabled = keepLive ? false : atEnd;\n  }\n\n  /* The two CONTENT checkboxes carry no number.\n   *\n   * \"With photos\": Judge.me has no endpoint, no parameter and no aggregate for\n   * photos \u2014 all 34 operations in their docs were checked, and no path contains\n   * media/picture/photo. The only thing close is `has_published_pictures` on\n   * `GET /reviews`, and that endpoint needs a private token, so it cannot ship in\n   * a theme. The number is therefore only obtainable by reading every review.\n   *\n   * A bare label works fine; a filter need not announce its result before you\n   * use it. */\n\n  /* The VARIANT group only exists when Judge.me actually carried one \u2014 it is a\n   * custom question the merchant may never have asked, so the Liquid ships the\n   * fieldset empty and hidden and this fills it from the parsed set or leaves it\n   * out entirely. Cloning the checkbox already in the CONTENT group keeps every\n   * class in Liquid, the same rule the photo tiles follow. */\n  function renderVariants() {\n    var group = root.querySelector('[data-ds-reviews-group=\"variant\"]');\n    var slot = root.querySelector('[data-ds-reviews-variants]');\n    var sample = root.querySelector('[data-ds-reviews-filter=\"photo\"] [data-ds-checkbox]');\n    if (!group || !slot || !sample) return;\n\n    var names = [];\n    (stripRows || all).forEach(function (review) {\n      if (review.variant && names.indexOf(review.variant) === -1) names.push(review.variant);\n    });\n    if (!names.length) return;\n\n    slot.innerHTML = '';\n    names.forEach(function (name, index) {\n      var wrap = document.createElement('span');\n      wrap.setAttribute('data-ds-reviews-variant', name);\n      var box = sample.cloneNode(true);\n      var input = box.querySelector('input');\n      var id = root.id + '-variant-' + index;\n      if (input) {\n        input.id = id;\n        input.name = 'ds-reviews-variant';\n        /* Keep whatever is already selected. `renderVariants` runs again when\n         * `loadStrip` returns, and hard-coding `false` here makes the shopper's\n         * tick vanish from the panel while `state.variants` is still filtering \u2014\n         * the next interaction reads the DOM back and silently drops that\n         * filter. */\n        input.checked = state.variants.indexOf(name) !== -1;\n        input.removeAttribute('data-ds-checkbox-indeterminate');\n      }\n      var label = box.querySelector('label');\n      if (label) label.setAttribute('for', id);\n      var text = box.querySelector('label > span:last-child > span:first-child');\n      if (text) text.textContent = name;\n      wrap.appendChild(box);\n      slot.appendChild(wrap);\n    });\n\n    group.classList.remove('sf-hidden');\n    group.classList.add('sf-flex');\n  }\n\n  /* \u2500\u2500 Panels \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   * A dropdown from md up and a bottom sheet below it, one tree either way.\n   * Open/closed is [aria-hidden] alone \u2014 src/reviews-panel.css carries the fade\n   * and the slide off it, so toggling a display utility here would cut the\n   * close animation off (experience.md, \"Toggling sf-hidden killed the close\n   * animation\"). Body scroll is only locked for the sheet: locking it for a\n   * desktop dropdown would freeze the page behind a 240px panel.\n   */\n  function panel(name) {\n    return root.querySelector('[data-ds-reviews-panel=\"' + name + '\"]');\n  }\n\n  function panelOpen(name) {\n    var box = panel(name);\n    return !!box && box.getAttribute('aria-hidden') === 'false';\n  }\n\n  /* One at a time, expressed as \"which panel is open\" rather than as a toggle\n   * per panel: opening one has to close the other (two dropdowns overlap on a\n   * narrow column, two sheets stack), and writing that as setPanel calling\n   * itself made opening Sort close Sort \u2014 the nested call re-ran the same loop\n   * and shut the panel that had just been opened. One writer over both panels\n   * cannot have that bug. `name` is null for \"none open\". */\n  function showPanel(name) {\n    // Which presentation this panel is in. The breakpoint comes from\n    // panelsDrop, which reads the token, so the CSS and the JS cannot disagree\n    // about where the sheet becomes a dropdown; src/reviews-panel.css hangs the\n    // full-height slide off this attribute rather than off an @media whose 744\n    // would have to be typed a second time.\n    var sheet = !panelsDrop();\n\n    ['filter', 'sort'].forEach(function (key) {\n      var box = panel(key);\n      if (!box) return;\n      var open = key === name;\n\n      if (sheet) box.setAttribute('data-ds-reviews-sheet', '');\n      else box.removeAttribute('data-ds-reviews-sheet');\n      var was = box.getAttribute('aria-hidden') === 'false';\n      box.setAttribute('aria-hidden', open ? 'false' : 'true');\n      // Only the mobile sheet locks the page (page-lock.js); a 240px desktop\n      // dropdown that froze the whole document behind it would be a bug, not\n      // a modal.\n      box.toggleAttribute('data-ds-page-lock', open && sheet);\n\n      var trigger = root.querySelector('[data-ds-reviews-toggle=\"' + key + '\"]');\n      if (trigger) trigger.setAttribute('aria-expanded', String(open));\n\n      // Opening moves focus onto the panel's first control \u2014 otherwise the\n      // trigger reads \"expanded\" while keyboard/AT focus is still sitting on\n      // the trigger itself, one Tab short of the content that just appeared.\n      // Closing mirrors it back, but only if focus is still inside the panel\n      // being closed \u2014 otherwise closing one panel would steal focus from the\n      // control the shopper just moved to.\n      if (open) {\n        /* The first VISIBLE one, not the first in the markup. The panel header\n         * carries `md:sf-hidden`, so from md up its close button is display:none\n         * \u2014 calling `focus()` on it silently does nothing. */\n        var fields = box.querySelectorAll(\n          'input, select, textarea, button, [href], [tabindex]:not([tabindex=\"-1\"])'\n        );\n        for (var f = 0; f < fields.length; f++) {\n          if (fields[f].offsetParent === null) continue;\n          if (typeof fields[f].focus === 'function') fields[f].focus();\n          break;\n        }\n      } else if (was && trigger && typeof trigger.focus === 'function' && box.contains(document.activeElement)) {\n        trigger.focus();\n      }\n    });\n  }\n\n  function closePanels() {\n    showPanel(null);\n  }\n\n  /* The Sort trigger reads the choice back (\"Sort: Most Helpful\"), which is\n   * the whole point of collapsing a select into a button \u2014 the value has to stay\n   * visible while the panel is shut. */\n  function renderSortTrigger() {\n    var trigger = root.querySelector('[data-ds-reviews-toggle=\"sort\"]');\n    var picked = root.querySelector('[data-ds-reviews-sort-option]:checked');\n    if (!trigger || !picked) return;\n    var label = picked.closest('label');\n    var text = label && label.querySelector('span');\n    // ds-button wraps its copy in a <span> beside the optional icons; writing to\n    // the control itself would take the chevron out with the text.\n    var slot = trigger.querySelector(':scope > span');\n    if (!slot || !text) return;\n    var pattern = root.getAttribute('data-ds-reviews-sort-pattern') || '%s';\n\n    /* SoT `.wp-rsp__trigger`: muted \"Sort:\" + medium-weight value. Built from\n     * nodes rather than innerHTML \u2014 the label comes from the translation file,\n     * and a translation file is not a place that should carry markup. */\n    var parts = pattern.split('%s');\n    var value = document.createElement('span');\n    value.className = 'sf-font-medium';\n    value.setAttribute('data-ds-reviews-sort-value', '');\n    value.textContent = text.textContent.trim();\n\n    slot.textContent = '';\n    slot.appendChild(document.createTextNode(parts[0]));\n    slot.appendChild(value);\n    if (parts[1]) slot.appendChild(document.createTextNode(parts[1]));\n  }\n\n  /* The Filter trigger carries the number of reviews the current selection\n   * yields, the way Figma 7262:24752 draws it (\"Filter (6)\"). With nothing\n   * selected it is just \"Filter\". */\n  function renderFilterTrigger() {\n    var trigger = root.querySelector('[data-ds-reviews-toggle=\"filter\"]');\n    if (!trigger) return;\n    var slot = trigger.querySelector(':scope > span');\n    if (!slot) return;\n    if (!trigger.dataset.dsLabel) trigger.dataset.dsLabel = slot.textContent.trim();\n\n    var picked =\n      (state.rating ? 1 : 0) + (state.photo ? 1 : 0) + state.variants.length;\n    if (!picked) {\n      slot.textContent = trigger.dataset.dsLabel;\n      return;\n    }\n    var total = queryTotal();\n    if (total === null) {\n      slot.textContent = trigger.dataset.dsLabel;\n      return;\n    }\n    var pattern = root.getAttribute('data-ds-reviews-count-pattern') || '%1 (%2)';\n    slot.textContent = pattern\n      .replace('%1', trigger.dataset.dsLabel)\n      .replace('%2', String(total));\n  }\n\n  /* How many reviews the current selection will yield \u2014 or `null` when that\n   * cannot be known.\n   *\n   * It can be known because each star row already carries its own count\n   * (`data-ds-reviews-rating-total`, server-rendered), and that count lives in\n   * the same universe as the server's: measured on product 9902457880859\n   * (2026-09-17) the histogram says 4 stars = 41, and `filter_rating=4` returns\n   * exactly `total_count=41`. `filter_rating` matches EXACTLY rather than \"and\n   * up\", so one lookup settles it \u2014 no request.\n   *\n   * \"With photos\" and the variant filter have no such cell to\n   * read, and knowing them would mean reading every review. Return `null` so the\n   * display drops the number. A figure that is true-of-what-loaded is a figure\n   * that will change under the shopper's hand, which is worse than none. */\n  function queryTotal(filters) {\n    var f = filters || state;\n    /* The photo and variant filters run LOCALLY, so the server's `total_count`\n     * says nothing about them. But a number does exist: the button reads \"Show N\n     * reviews\", and the N it promises is the row count the filter will put on\n     * screen \u2014 `visible().length`. Not an approximation of \"how many reviews on\n     * this product carry photos\"; that is a different question and Judge.me\n     * cannot answer it.\n     *\n     * The condition is that the set must be STILL, or the number moves under the\n     * shopper's hand. For the photo filter the set is only still once `loadStrip`\n     * has FULLY finished \u2014 it may take up to two requests, see\n     * `STRIP_MAX_PAGES`. Until then, no number. */\n    if (f.photo && !data.photoServer) {\n      /* Gate on `stripSettled`, NOT on `stripRows`: `loadStrip` assigns\n       * `stripRows` right after the first page and then fetches a second, so\n       * treating `stripRows` as \"done\" publishes a number that is still growing \u2014\n       * ticking \"With photos\" in that window takes the button from \"Show 20\n       * reviews\" to \"Show 23 reviews\" with the shopper touching nothing. Exactly\n       * what this gate exists to prevent. */\n      if (!stripSettled) return null;\n      /* Settled with no photos means the answer is 0, not \"unknown\" \u2014 `visible()`\n       * returns [] and that 0 goes straight to the button. */\n      return visibleWith(f).length;\n    }\n    /* The variant filter yields NO number. Its set is `loadedRows()`, and that\n     * set grows every time the shopper turns a page \u2014 the button would walk from\n     * \"Show 2 reviews\" to \"Show 9 reviews\" with nobody touching it. The same\n     * instability the branch above rejects, so reject it identically. The photo\n     * branch can produce a number because its set is `stripRows`, a finished\n     * fetch that does not grow; variants have no equivalent, and Judge.me has no\n     * parameter that filters by variant either. */\n    if (f.variants.length) return null;\n    /* Once this query has made a request, the server has stated its total. That\n     * is the real number for every sort/rating combination, including the ones\n     * no table lookup can answer. It only appears after the data lands, and\n     * once landed it does not move.\n     *\n     * Only trust the committed query's total when the draft matches what was\n     * fetched \u2014 otherwise a mobile draft rating would still show the previous\n     * query's count. */\n    if (pagesTotalFromServer && filtersMatchState(f)) return pagesTotal;\n    /* `peekDraftTotal` caches the server's `total_count` for a draft so the\n     * button can say \"Show N reviews\" the first time a box is ticked \u2014 not\n     * only after the list has already been filtered once. `0` is an answer. */\n    /* The star rows' counts come from the header histogram, which on the\n     * all-reviews feed includes store reviews the list never shows (QC r4: 3\u2605\n     * read \"Show 524\", the list held 506). So there a star draft is counted by\n     * the server too. */\n    if (data.countsByRequest(f)) {\n      var peeked = draftTotals[queryKey(f)];\n      return typeof peeked === 'number' ? peeked : null;\n    }\n    if (f.rating) return ratingTotal(f.rating);\n    return aggregate && typeof aggregate.count === 'number' ? aggregate.count : null;\n  }\n\n  function filtersMatchState(f) {\n    if (f.rating !== state.rating) return false;\n    if (f.photo !== state.photo) return false;\n    if (f.variants.length !== state.variants.length) return false;\n    for (var i = 0; i < f.variants.length; i++) {\n      if (state.variants.indexOf(f.variants[i]) === -1) return false;\n    }\n    return true;\n  }\n\n  /* The sheet's primary action counts what the shopper is about to see, so the\n   * number has to move with every tick rather than only on open. While the\n   * filter panel is open the ticks are a draft \u2014 count from the form, do not\n   * apply yet. */\n  function renderApply() {\n    var button = root.querySelector('[data-ds-reviews-apply]');\n    if (!button) return;\n    var slot = button.querySelector(':scope > span') || button;\n    var draft = panelOpen('filter') ? readFilterDraft() : null;\n    var total = queryTotal(draft);\n    /* Reviews page: Clear is inert while the draft picks nothing (prototype\n     * `.wp-rfp__clear:disabled`). */\n    var clear = root.getAttribute('data-ds-reviews-layout') === 'page' && root.querySelector('[data-ds-reviews-clear]');\n    if (clear) {\n      var d = draft || state;\n      clear.disabled = !(d.rating || d.photo || d.variants.length);\n    }\n    /* A draft the table cannot answer has no number until its count request\n     * lands. Ask now; the reply calls back into here. Photo and variant stay numberless\n     * on purpose \u2014 their sets are not a Judge.me `total_count`. */\n    var serverCounts = draft && !draft.variants.length && data.countsByRequest(draft);\n    if (total === null && serverCounts) {\n      peekDraftTotal(draft);\n    }\n    if (total === null) {\n      slot.textContent = root.getAttribute('data-ds-reviews-apply-plain') || button.textContent;\n      return;\n    }\n    // \"Show 1 review\", not \"Show 1 reviews\": the singular is its own translated pattern.\n    var pattern = (total === 1 && root.getAttribute('data-ds-reviews-apply-pattern-one')) ||\n      root.getAttribute('data-ds-reviews-apply-pattern') || '%s';\n    slot.textContent = pattern.replace('%s', String(total));\n  }\n\n  /* \u2500\u2500 Data: one UI page = one API page \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   *\n   * `per_page=5` is the number Judge.me's own widget uses, and it paginates\n   * CORRECTLY: measured on a 420-review product, pages 1\u219284 each returned five\n   * distinct reviews and page 85 came back empty \u2014 matching `total_count`\n   * exactly.\n   *\n   * Asking for a large `per_page` does the opposite: the windows overlap. On the\n   * same product, 20 pages at `per_page=100` returned 567 raw reviews but only\n   * 277 distinct ones, and never an empty page to signal the end. The whole\n   * earlier \"gather the set\" architecture was built on that broken mode, which is\n   * why the pager read 56 pages instead of 84.\n   *\n   * Since the UI also shows five per page, the two line up: UI page N is API page\n   * N \u2014 one request, no accumulation, no offset arithmetic, no de-duplication. */\n  var pages = {};\n  var stripRows = null;\n  /* One placeholder sampled from Liquid's skeleton block, kept so it can be\n   * cloned. */\n  var stripPlaceholder = null;\n  /* Whether `loadStrip` has an answer yet \u2014 including when the answer is\n   * \"failed\".\n   *\n   * Do not use `!stripRows` as the loading flag: it merges \"not back yet\" with\n   * \"came back and failed\", so a failed request leaves a row of placeholders\n   * pulsing forever and a badge that never states a number. Failure is also an\n   * ending, just one with less data. */\n  var stripSettled = false;\n  var pagesQuery = '';\n  var pagesTotalFromServer = false;\n  /* `total_count` for a draft, keyed like `currentQuery`. The list\n   * stays on the committed query; this map only fills the Show N label. */\n  var draftTotals = {};\n  var draftInflight = {};\n  var pagesTotal = aggregate && aggregate.count ? aggregate.count : all.length;\n  var lastKnownPage = 0;\n  /* Where the pager's window currently stands \u2014 see `pageRun`. */\n  var runStart = 1;\n  var inflight = {};\n  /* Which pages just failed (network error / non-OK response). Used to tell\n   * \"nothing yet\" apart from \"asked, and there are no reviews\" \u2014 those say two\n   * different things to the shopper. */\n  var failed = {};\n  var ticket = 0;\n\n  /* The strip's set comes from the source's `strip`: Judge.me pages 20 reviews\n   * at a time in its pictures-first default order; SF answers with every photo\n   * review in one request (`/sf/media`) and has no page 2.\n   *\n   * The number of strip pages we may request, page 1 included. Two, no more:\n   * enough to cover the photo block on every product in this store (the longest\n   * measured is 23, and two pages cover 40) while staying a FIXED number \u2014 not\n   * \"ask until empty\", which on another product could become hundreds of\n   * requests. */\n  var STRIP_MAX_PAGES = 2;\n\n  /* Paged strip (the all-reviews feed, and a PDP whose source pages it \u2014 SF). The strip asks for page 1 of the photo reviews that\n   * match the active star filter and takes the TOTAL from the\n   * response \u2014 the strip draws ten tiles at most (\"+N\" on the last), so one\n   * page fills it and the count is what the shopper needs. Further pages are\n   * fetched only by the lightbox, when the shopper steps toward the end of what\n   * is loaded (`loadMoreStrip`). The PDP keeps its two-page pictures-first\n   * read below: its endpoint has no photo filter and no photo count. */\n  var stripTotal = null;\n  /* PDP on a source that pages the strip (SF): every photo of the product under the active star filter. The header\n   * and the \"+N\" tile count photos while only a page or two of them is loaded. Null = unknown, count what is loaded. */\n  var stripPhotoCount = null;\n  var stripPagesLoaded = 0;\n  /* Reviews the paged strip has received, photo or not: what `stripTotal` counts. Strip rows keep photo reviews only,\n   * so a video-only review still moves paging forward. */\n  var stripSeen = 0;\n  var stripKey = '';\n  var stripToken = 0;\n  var stripMore = null;\n\n  /* Never empty: `stripKey` doubles as \"the strip has been loaded\", and '' reads as \"not yet\". */\n  function stripFilterKey() {\n    return 'r' + (state.rating || 0);\n  }\n\n  /* The filters a server-filtered strip (the all-reviews feed) is asked with. */\n  function stripQuery() {\n    return { rating: state.rating };\n  }\n\n  function loadStripAll() {\n    var token = ++stripToken;\n    stripKey = stripFilterKey();\n    stripRows = null;\n    stripTotal = null;\n    stripPhotoCount = null;\n    stripPagesLoaded = 0;\n    stripSeen = 0;\n    stripMore = null;\n    stripSettled = false;\n    renderPhotoStrip();\n    return data.strip(1, stripQuery()).then(function (res) {\n      if (token !== stripToken) return;\n      stripSettled = true;\n      if (res) {\n        var rows = res.rows;\n        var t = res.total;\n        stripTotal = isNaN(t) ? rows.length : Math.max(t, rows.length);\n        stripPhotoCount = typeof res.photos === 'number' ? res.photos : null;\n        stripRows = rows;\n        stripPagesLoaded = 1;\n        stripSeen = stripGot(res);\n        renderVariants();\n      }\n      renderPhotoStrip();\n      renderFilterTrigger();\n      renderApply();\n      // Page 1 held only video reviews: the strip has nothing to draw, so walk on to the first photo.\n      if (res && !res.rows.length && stripHasMore()) loadMoreStrip();\n    });\n  }\n\n  /* Reviews a strip page carried; a source that does not say (Judge.me) is counted by its rows. */\n  function stripGot(res) {\n    return typeof res.got === 'number' ? res.got : res.rows.length;\n  }\n\n  function stripHasMore() {\n    return stripPaged && stripSettled && !!stripRows && stripTotal !== null &&\n      Math.max(stripSeen, stripRows.length) < Math.min(stripTotal, data.maxReviews);\n  }\n\n  /* The next page of photo reviews, for the lightbox. One request at a time.\n   * A source that states how many reviews a page carried (SF, true server\n   * paging): a page with no review ends the set, one with only video reviews\n   * (no photo row) moves straight on to the next. Any other source (Judge.me,\n   * whose capped feed repeats pages): a page that brings nothing new ends it. */\n  function loadMoreStrip() {\n    if (!stripHasMore()) return Promise.resolve();\n    if (stripMore) return stripMore;\n    var token = stripToken;\n    stripMore = data.strip(stripPagesLoaded + 1, stripQuery()).then(function (res) {\n      stripMore = null;\n      if (token !== stripToken || !res) return;\n      var seen = {};\n      stripRows.forEach(function (r) { if (r.id) seen[r.id] = true; });\n      var fresh = res.rows.filter(function (r) { return !r.id || !seen[r.id]; });\n      var counted = typeof res.got === 'number';\n      var got = stripGot(res);\n      stripPagesLoaded += 1;\n      stripSeen += got;\n      if (!fresh.length && (!counted || !got)) stripTotal = Math.max(stripSeen, stripRows.length);\n      else if (fresh.length) stripRows = stripRows.concat(fresh);\n      renderPhotoStrip();\n      if (counted && got && !fresh.length && stripHasMore()) return loadMoreStrip();\n    });\n    return stripMore;\n  }\n\n  /* What the strip counts. The badge, the \"+N\" tile and the lightbox counter\n   * all read these two functions, so they tell one story, as on the PDP.\n   *\n   * On the all-reviews feed the unit is the REVIEW with photos, not the photo.\n   * The one number Judge.me states up front is how many reviews carry photos\n   * (`number_of_product_reviews` under `sort_by=with_pictures`); the number of\n   * photos is only known after reading every page \u2014 50 requests for the\n   * 1000 reviews the feed will serve. A photo count built from \"loaded photos +\n   * one per review still to come\" moved as pages landed (QC r6: 1001 \u2192 1003),\n   * so the strip counts reviews instead: fixed from the first response, capped\n   * at the source's `maxReviews` like everything the feed serves. Elsewhere (PDP): photos\n   * loaded, as before. */\n  function stripCountsReviews() {\n    return feedAll && stripTotal !== null && !!stripRows;\n  }\n\n  function stripPhotoTotal() {\n    if (stripCountsReviews()) return Math.min(stripTotal, data.maxReviews);\n    /* A paged PDP strip loads ten reviews at a time, so what is loaded undercounts \u2014 the source states the real\n     * number of photos. */\n    if (stripPhotoCount !== null) return Math.max(stripPhotoCount, strip.length);\n    return strip.length;\n  }\n\n  /* 1-based position of photo `index` in that unit: on the all-reviews feed,\n   * the position of its review among the strip's reviews (every photo of one\n   * review shares it); elsewhere the photo's own position. */\n  function stripPosition(index) {\n    if (!stripCountsReviews()) return index + 1;\n    var seen = {};\n    var n = 0;\n    for (var i = 0; i <= index && i < strip.length; i += 1) {\n      var r = strip[i].review || {};\n      var key = r.id || ('#' + i);\n      if (!seen[key]) { seen[key] = true; n += 1; }\n    }\n    return Math.max(1, n);\n  }\n\n  /* Strip page N's reviews, or null when the request failed. */\n  function fetchStripPage(n) {\n    if (!data.hasTarget) return Promise.resolve(null);\n    return data.strip(n, stripQuery()).then(function (res) {\n      return res ? res.rows : null;\n    });\n  }\n\n  function loadStrip() {\n    if (stripPaged && data.hasTarget) return loadStripAll();\n    if (!data.hasTarget) {\n      stripSettled = true;\n      return Promise.resolve();\n    }\n\n    /* Ask for another page ONLY when the last one was all photo reviews \u2014 that is\n     * evidence the photo block runs longer than what just arrived, not a guess.\n     * The shop sets `default_sort_method = pictures-first`, so reviews with\n     * photos sit in one block at the front (measured on three products: blocks of\n     * 23 / 4 / 1), which makes \"this page is all photos\" mean the block's bottom\n     * has not been reached.\n     *\n     * A page SHORTER than 20 is also the end, so stop. And the page count is\n     * capped at `STRIP_MAX_PAGES`, making the worst case two requests, known in\n     * advance. On a product with more than 40 photo reviews the filter still\n     * cannot reach the tail \u2014 reaching it means reading every review, which is\n     * sequential pagination. */\n    function step(n, gathered) {\n      return fetchStripPage(n).then(function (rows) {\n        if (rows === null) return gathered;\n        var merged = gathered.concat(rows);\n        var everyHasPhoto = rows.length > 0 && rows.every(function (r) { return r.photos.length > 0; });\n        if (n < STRIP_MAX_PAGES && rows.length === 20 && everyHasPhoto) {\n          /* Draw what is already in hand before asking for more: the shopper sees\n           * photos immediately, and the rest lands in the slots the placeholders\n           * are holding. */\n          stripRows = merged;\n          renderPhotoStrip();\n          return step(n + 1, merged);\n        }\n        return merged;\n      });\n    }\n\n    return step(1, []).then(function (rows) {\n      stripSettled = true;\n      if (rows && rows.length) {\n        stripRows = rows;\n        renderVariants();\n      }\n      /* With nothing received, the strip keeps the metafield reviews' photos \u2014\n       * fewer, but real photos, which beats a row of empty boxes. */\n      renderPhotoStrip();\n      /* And redraw the two places that carry a NUMBER. The photo filter's count\n       * is withheld until `stripSettled`, so if the shopper ticked \"With photos\"\n       * during the wait, this is the only moment the button gets to learn the\n       * answer \u2014 without this call it sits at \"Show reviews\" until the next\n       * interaction. */\n      renderFilterTrigger();\n      renderApply();\n    });\n  }\n\n  function queryKey(filters) {\n    var f = filters || state;\n    return currentSort() + '|' + (f.rating || '') + '|' + (data.photoServer && f.photo ? 'p' : '');\n  }\n\n  function currentQuery() {\n    return queryKey(state);\n  }\n\n\n  /* What the source is asked for: the sort plus the filters it can apply\n   * (`filters` defaults to the committed `state`). */\n  function queryOf(filters) {\n    var f = filters || state;\n    return { sort: currentSort(), rating: f.rating, photo: f.photo };\n  }\n\n  /* Ask the source how many reviews a draft will show, without changing the\n   * list. The result is cached, including 0. A failed request does not call\n   * back into `renderApply`, or a null total would ask again forever. */\n  function peekDraftTotal(filters) {\n    var key = queryKey(filters);\n    if (typeof draftTotals[key] === 'number' || draftInflight[key]) return;\n    if (!data.hasTarget) return;\n    draftInflight[key] = true;\n    data.count(queryOf(filters)).then(function (t) {\n      delete draftInflight[key];\n      if (t === null) return;\n      draftTotals[key] = t;\n      renderApply();\n    }, function () {\n      delete draftInflight[key];\n    });\n  }\n\n  /* Changing the query changes the whole set. Drop the old query's loaded pages\n   * rather than keeping a cache per sort/rating combination \u2014 returning\n   * to an earlier choice costs exactly one request, which is not worth holding\n   * memory for. */\n  function syncQuery() {\n    var q = currentQuery();\n    if (q === pagesQuery) return;\n    pagesQuery = q;\n    pages = {};\n    inflight = {};\n    failed = {};\n    lastKnownPage = 0;\n    pagesTotalFromServer = false;\n    /* The new query's total \u2014 do NOT reset it to 0. At 0, `pageCount` falls back\n     * to `all.length`, which is the metafield's five reviews, which is one page,\n     * which means the pager disappears; that was the \"change sort, come back to\n     * Most helpful, lose pagination\" bug, because the default query does not\n     * fetch and so nothing rebuilt it.\n     *\n     * And read only sources that do NOT depend on the cache \u2014 the cache was\n     * cleared a few lines above. `queryTotal()` is unusable here: it has branches\n     * that return `visible().length`, and `visible()` reads `loadedRows()`, which\n     * reads the very `pages` object just emptied \u2192 0 \u2192 the pager disappears\n     * again, only triggered by a variant instead of a sort.\n     *\n     * Rating has its count sitting on the star row already (server-rendered).\n     * Everything else takes the whole set's total as the mark, and the first\n     * request's `total_count` corrects it. */\n    var rated = ratingTotal();\n    pagesTotal = rated === null ? (aggregate && aggregate.count) || all.length : rated;\n    /* A count the server already gave for this exact query (a draft's \"Show\n     * N\") beats the histogram where the source says so \u2014 SF, and Judge.me's\n     * all-reviews feed, whose histogram includes store reviews. */\n    if (data.trustDraftTotals && typeof draftTotals[q] === 'number') pagesTotal = draftTotals[q];\n  }\n\n  /* How many reviews sit at the selected star level, read from the\n   * server-rendered attribute.\n   *\n   * `null` means UNKNOWN (no star selected, or the attribute could not be read).\n   * `0` is an answer, not a blank \u2014 and it has to be returnable: a `0` folded\n   * into `null` makes the button read \"Show reviews\" for a star level whose\n   * correct answer is \"Show 0 reviews\", while `pagesTotal` jumps to the whole\n   * product's total and draws a pager for an empty query. Measured on product\n   * 9902474002715: 9 reviews, with 4 of the 5 star levels at zero. */\n  function ratingTotal(rating) {\n    var stars = rating == null ? state.rating : rating;\n    if (!stars) return null;\n    var row = root.querySelector('[data-ds-reviews-rating-row=\"' + stars + '\"]');\n    var raw = row && row.getAttribute('data-ds-reviews-rating-total');\n    if (raw == null || raw === '') return null;\n    var n = parseInt(raw, 10);\n    return isNaN(n) ? null : n;\n  }\n\n  function fetchUiPage(n) {\n    syncQuery();\n    if (pages[n]) return Promise.resolve(pages[n]);\n    if (inflight[n]) return inflight[n];\n    /* No product/shop attrs \u2192 nothing to ask. Do not retry an empty URL. */\n    if (!data.hasTarget) return Promise.resolve([]);\n    var q = pagesQuery;\n\n    function accept(res) {\n      delete failed[n];\n      var rows = res.rows;\n      pages[n] = rows;\n      /* `!isNaN` rather than truthiness: `total_count: 0` is the server ANSWERING\n       * that this query is empty, not the server staying silent. Discard it and\n       * `pagesTotalFromServer` never turns on, `pagesTotal` keeps the guess made\n       * before asking, the Load more pill measures against the wrong mark and\n       * the chip loses its number. */\n      var t = res.total;\n      if (!isNaN(t)) {\n        pagesTotal = t;\n        pagesTotalFromServer = true;\n        renderFilterTrigger();\n        renderApply();\n      }\n      /* The REAL bottom, measured by what the server returns rather than what it\n       * claims.\n       *\n       * An empty page is the bottom: `per_page=5` returns empty in exactly the\n       * right place (measured: page 85 of a 420-review product), unlike a large\n       * `per_page`, which keeps returning rows forever.\n       *\n       * A SHORT page is also the bottom, and has to count \u2014 otherwise a product\n       * whose `total_count` exceeds what it serves (measured: claims 210, serves\n       * 192) stops on a page of 2 rows with `lastKnownPage` still 0, because\n       * nobody ever asks for the empty page behind it. `per_page=5` returns\n       * exactly 5 for every page that has data (measured pages 1\u219284), so fewer\n       * than 5 means the end. */\n      var got = res.got;\n      if (!got) lastKnownPage = Math.max(1, n - 1);\n      else if (got < PER_PAGE) lastKnownPage = n;\n      delete inflight[n];\n      return rows;\n    }\n\n    function giveUp() {\n      /* `inflight[n]` must be cleared: keeping a promise already resolved to []\n       * means every later click on that page resolves empty instantly, leaving\n       * the page dead until the shopper changes sort/rating or reloads.\n       * And do NOT write `pages[n]` \u2014 a 503 / network blip is not \"this page has\n       * no reviews\". */\n      delete inflight[n];\n      if (q === pagesQuery) failed[n] = true;\n      return [];\n    }\n\n    /* One promise covers every attempt (see RETRY_DELAYS), so concurrent callers\n     * still share a single `inflight[n]`. Busy/skeleton stay up across the\n     * retries \u2014 `show` awaits this promise and only clears them when it settles. */\n    var req = data.page(n, queryOf()).then(function (res) {\n      /* The query changed: this result is meaningless, and `syncQuery` already\n       * replaced `inflight` with a fresh object, so there is nothing to clean\n       * up. */\n      if (q !== pagesQuery) return [];\n      return res ? accept(res) : giveUp();\n    });\n\n    inflight[n] = req;\n    return req;\n  }\n\n  /* Page N's reviews, or empty if it has not been fetched. There is NO exception\n   * for page 1: the metafield is Judge.me's widget rendered in the shop's default\n   * order, while the list asks in the selected sort \u2014 taking page 1 from one and\n   * pages 2+ from the other stitches two different orderings together, measured\n   * as 30 rendered rows holding only 23 distinct reviews. */\n  function rowsForPage(n) {\n    syncQuery();\n    return pages[n] || [];\n  }\n\n  function pageCount() {\n    var reach = pagesTotal || all.length;\n    reach = Math.min(reach, data.maxReviews);\n    var n = Math.max(1, Math.ceil(reach / PER_PAGE));\n    if (lastKnownPage) n = Math.min(n, lastKnownPage);\n    return n;\n  }\n\n  // The sort radios already carry their machine values (pairSortValues, setup).\n  function currentSort() {\n    var picked = root.querySelector('[data-ds-reviews-sort-option]:checked');\n    return picked ? picked.value : '';\n  }\n\n  /* Read the filter form without committing. Ticks stay a draft until\n   * \"Show N reviews\" on every breakpoint. */\n  function readFilterDraft() {\n    var rating = root.querySelector('[name=\"ds-reviews-rating\"]:checked');\n    var photo = root.querySelector('[data-ds-reviews-filter=\"photo\"] input');\n    var variants = [];\n    root.querySelectorAll('[data-ds-reviews-variant] input:checked').forEach(function (input) {\n      var wrap = input.closest('[data-ds-reviews-variant]');\n      if (wrap) variants.push(wrap.getAttribute('data-ds-reviews-variant'));\n    });\n    return {\n      rating: rating ? parseInt(rating.value, 10) || 0 : 0,\n      photo: !!(photo && photo.checked),\n      variants: variants,\n    };\n  }\n\n  /* Put the committed filters back on the form \u2014 discard a draft when the\n   * panel closes without Show N reviews. */\n  function writeFilterDraft(filters) {\n    root.querySelectorAll('[name=\"ds-reviews-rating\"]').forEach(function (input) {\n      input.checked = !!filters.rating && String(filters.rating) === String(input.value);\n    });\n    var photo = root.querySelector('[data-ds-reviews-filter=\"photo\"] input');\n    if (photo) photo.checked = !!filters.photo;\n    root.querySelectorAll('[data-ds-reviews-variant]').forEach(function (wrap) {\n      var input = wrap.querySelector('input');\n      var name = wrap.getAttribute('data-ds-reviews-variant');\n      if (input) input.checked = filters.variants.indexOf(name) !== -1;\n    });\n  }\n\n  /* Every control in both panels reports through here: read the DOM back into\n   * `state`, re-sort, and repaint. Reading the inputs rather than tracking each\n   * one's own toggle means \"Clear all\" only has to reset the form controls. */\n  function syncFromPanels() {\n    var draft = readFilterDraft();\n    state.rating = draft.rating;\n    state.photo = draft.photo;\n    state.variants = draft.variants;\n\n    state.shown = PER_PAGE;\n    renderSortTrigger();\n    // The strip must follow the filter, or the shopper can click a photo that is\n    // not in the list and \"See this review\" lands on the wrong page. On the\n    // all-reviews feed it follows by asking the server again.\n    if (stripPaged && stripKey && stripFilterKey() !== stripKey) loadStripAll();\n    else renderPhotoStrip();\n    renderStarFilter();\n    // A new query starts at its own page 1; `show` fetches exactly that page.\n    return show(1);\n  }\n\n  /* \u2500\u2500 Reviews page: header star rows + the active-star chip \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   * ds-reviews-hub-header draws the distribution OUTSIDE this band (above the\n   * summary band), each row a button naming this band in\n   * `data-ds-reviews-for`. A press sets the same rating filter the Filter\n   * panel's Rating group sets \u2014 pressing the active row again clears it \u2014 so\n   * the two controls can never disagree: both write the form, both commit\n   * through `syncFromPanels`. */\n  var starRows = root.id\n    ? document.querySelectorAll('[data-ds-reviews-star][data-ds-reviews-for=\"' + root.id + '\"]')\n    : [];\n  var starChip = root.querySelector('[data-ds-reviews-star-chip]');\n\n  function setStarFilter(stars) {\n    writeFilterDraft({\n      rating: stars,\n      photo: state.photo,\n      variants: state.variants.slice(),\n    });\n    syncFromPanels();\n  }\n\n  /* Mirror the committed rating onto the header rows (`aria-pressed`) and the\n   * chip under the controls (\"5 stars \u00D7\", prototype `.wp-reviews__star-chip`).\n   * The chip's wording comes from Liquid as a pattern, like the sort trigger's. */\n  function renderStarFilter() {\n    starRows.forEach(function (row) {\n      var on = String(state.rating) === row.getAttribute('data-ds-reviews-star');\n      row.setAttribute('aria-pressed', on ? 'true' : 'false');\n    });\n    if (!starChip) return;\n    starChip.classList.toggle('sf-hidden', !state.rating);\n    if (!state.rating) return;\n    var label = state.rating === 1\n      ? root.getAttribute('data-ds-reviews-star-chip-one') || '1 star'\n      : (root.getAttribute('data-ds-reviews-star-chip-pattern') || '%s stars').replace('%s', state.rating);\n    var button = starChip.querySelector('[data-ds-chip]');\n    var text = button && button.querySelector('span > span');\n    if (text) text.textContent = label;\n    if (button) {\n      button.setAttribute('aria-label', (root.getAttribute('data-ds-reviews-star-chip-remove') || '%s').replace('%s', label));\n    }\n  }\n\n  starRows.forEach(function (row) {\n    row.addEventListener('click', function () {\n      var stars = parseInt(row.getAttribute('data-ds-reviews-star'), 10) || 0;\n      setStarFilter(state.rating === stars ? 0 : stars);\n    });\n  });\n  if (starChip) {\n    starChip.addEventListener('click', function (event) {\n      if (!event.target.closest('[data-ds-chip]')) return;\n      setStarFilter(0);\n    });\n  }\n\n  /* Filter sheet: discard an un-applied draft before closing. Sort commits on\n   * change, so there is nothing to restore for it. */\n  function dismissFilterDraft() {\n    if (!panelOpen('filter')) return;\n    writeFilterDraft({\n      rating: state.rating,\n      photo: state.photo,\n      variants: state.variants.slice(),\n    });\n  }\n\n  function closeFilterOrPanels() {\n    dismissFilterDraft();\n    closePanels();\n  }\n\n  root.addEventListener('change', function (event) {\n    if (!event.target.closest('[data-ds-reviews-panel]')) return;\n    // Sort always commits immediately \u2014 one choice, then close.\n    if (event.target.hasAttribute('data-ds-reviews-sort-option')) {\n      syncFromPanels();\n      closePanels();\n      return;\n    }\n    // Filter ticks are a draft. Show N reviews is what commits them, on every\n    // breakpoint \u2014 a tick must not change the list underneath the open panel.\n    if (event.target.closest('[data-ds-reviews-panel=\"filter\"]')) {\n      renderApply();\n      return;\n    }\n    syncFromPanels();\n  });\n\n  root.addEventListener('click', function (event) {\n    var toggle = event.target.closest('[data-ds-reviews-toggle]');\n    if (toggle) {\n      event.preventDefault();\n      var name = toggle.getAttribute('data-ds-reviews-toggle');\n      if (panelOpen(name)) {\n        if (name === 'filter') dismissFilterDraft();\n        showPanel(null);\n      } else {\n        // Opening filter: form must mirror committed state (a prior discard or\n        // desktop edit could leave inputs ahead of state).\n        if (name === 'filter') {\n          writeFilterDraft({\n            rating: state.rating,\n            photo: state.photo,\n            variants: state.variants.slice(),\n          });\n        }\n        showPanel(name);\n        renderApply();\n      }\n      return;\n    }\n\n    // \"Show N reviews\" commits the draft, then closes.\n    var apply = event.target.closest('[data-ds-reviews-apply]');\n    if (apply) {\n      event.preventDefault();\n      syncFromPanels();\n      closePanels();\n      return;\n    }\n\n    var dismiss = event.target.closest('[data-ds-reviews-panel-close]');\n    if (dismiss) {\n      event.preventDefault();\n      closeFilterOrPanels();\n      return;\n    }\n\n    var clear = event.target.closest('[data-ds-reviews-clear]');\n    if (clear) {\n      event.preventDefault();\n      root.querySelectorAll('[data-ds-reviews-panel=\"filter\"] input').forEach(function (input) {\n        input.checked = false;\n      });\n      // Clear only resets the draft. Show N reviews is what changes the list.\n      renderApply();\n      return;\n    }\n\n    // Clicking the overlay of the mobile sheet, or anywhere outside an open\n    // desktop dropdown, closes it. Inside the card, nothing.\n    if (!event.target.closest('[data-ds-reviews-panel-card]')) closeFilterOrPanels();\n\n    var more = event.target.closest('[data-ds-reviews-more]');\n    if (more) {\n      event.preventDefault();\n      appendMore();\n      return;\n    }\n\n    var page = event.target.closest('[data-ds-reviews-page]');\n    if (page) {\n      event.preventDefault();\n      show(parseInt(page.getAttribute('data-page'), 10) || 1);\n      root.scrollIntoView({ behavior: 'smooth', block: 'start' });\n      return;\n    }\n\n    var pager = event.target.closest('[data-ds-reviews-prev], [data-ds-reviews-next]');\n    if (pager) {\n      event.preventDefault();\n      var step = pager.hasAttribute('data-ds-reviews-next') ? 1 : -1;\n      show(state.page + step);\n      return;\n    }\n\n    var arrow = event.target.closest('[data-ds-carousel-nav-prev], [data-ds-carousel-nav-next]');\n    if (arrow) {\n      var track = root.querySelector('[data-ds-reviews-photos-track]');\n      if (track) {\n        var step = track.clientWidth * 0.8;\n        track.scrollBy({ left: arrow.hasAttribute('data-ds-carousel-nav-next') ? step : -step, behavior: 'smooth' });\n      }\n    }\n  });\n\n  /* A dropdown has to close on a click anywhere else on the page, not just\n   * elsewhere in the band \u2014 the handler above only sees clicks inside `root`. */\n  document.addEventListener('click', function (event) {\n    if (!panelOpen('filter') && !panelOpen('sort')) return;\n    if (root.contains(event.target)) return;\n    closeFilterOrPanels();\n  });\n\n  document.addEventListener('keydown', function (event) {\n    if (event.key !== 'Escape') return;\n    if (!panelOpen('filter') && !panelOpen('sort')) return;\n    closeFilterOrPanels();\n  });\n\n  /* The write-a-review button is NOT wired here. It used to open Judge.me's own\n   * form, because posting a review looked like it needed a token. It does not:\n   * behaviors/review-form.js posts straight to their public endpoint, and owns\n   * every [data-ds-reviews-write] control on the page. */\n\n\n  /* \u2500\u2500 Lightbox \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n   * Opened from any photo tile \u2014 the strip's or one inside a review body. It\n   * walks the flat `strip`, not the reviews, so \"next\" moves to the next photo\n   * on the product the way the Figma frame's \"2 / 14\" implies, even when that\n   * crosses into another review.\n   */\n\n  var lightbox = document.querySelector('[data-ds-photo-lightbox]');\n  var lightboxAt = 0;\n  /* The set the lightbox walks. Null = the strip, which is what a strip tile\n   * and any body photo the strip holds open into. A body photo the strip does\n   * NOT hold \u2014 the strip is one capped request (40 photo reviews on the\n   * reviews page, two pages of 20 on a PDP), while the list pages through all\n   * of them \u2014 opens into its own review's photos instead. Falling back to the\n   * strip's first photo showed another reviewer and another product (QC r1). */\n  var shots = null;\n  function lightboxSet() {\n    return shots || strip;\n  }\n  var lightboxOpener = null;\n\n  function lightboxShow(index) {\n    var set = lightboxSet();\n    if (!lightbox || !set.length) return;\n    lightboxAt = (index + set.length) % set.length;\n    var shot = set[lightboxAt];\n    var review = shot.review || {};\n\n    var img = lightbox.querySelector('[data-ds-photo-lightbox-image]');\n    if (img) img.src = shot.full || shot.src;\n\n    var author = lightbox.querySelector('[data-ds-photo-lightbox-author]');\n    if (author) author.textContent = review.author || '';\n\n    /* Same flag as the review list. The lightbox is one node reused for every\n     * photo, so a missing flag is hidden rather than removed, and it is put\n     * back on the credentials line before each paint. \"Verified Buyer\" is\n     * shown for every review (SF0341), so that line is never empty. */\n    var credentials = lightbox.querySelector('[data-ds-photo-lightbox-credentials]');\n    var flag = lightbox.querySelector('[data-ds-photo-lightbox-flag]');\n    var verified = lightbox.querySelector('[data-ds-photo-lightbox-verified]');\n    if (verified) verified.classList.remove('sf-hidden');\n    if (credentials) credentials.classList.remove('sf-hidden');\n    if (flag && credentials && verified) credentials.insertBefore(flag, verified);\n    if (flag) {\n      var flagImg = countryFlagImage(review, flagSrc);\n      flag.textContent = '';\n      if (!flagImg) flag.classList.add('sf-hidden');\n      else {\n        flagImg.onerror = function () { flag.textContent = ''; flag.classList.add('sf-hidden'); };\n        flag.appendChild(flagImg);\n        flag.classList.remove('sf-hidden');\n      }\n    }\n\n    var date = lightbox.querySelector('[data-ds-photo-lightbox-date]');\n    if (date) {\n      date.setAttribute('datetime', review.dateIso || '');\n      date.textContent = absoluteDate(review.dateIso || '');\n    }\n\n    var stars = lightbox.querySelector('[data-ds-rating]');\n    if (stars) setStars(stars, review.rating || 0);\n\n    var title = lightbox.querySelector('[data-ds-photo-lightbox-title]');\n    if (title) {\n      title.textContent = review.title || '';\n      title.classList.toggle('sf-hidden', !review.title);\n    }\n\n    var body = lightbox.querySelector('[data-ds-photo-lightbox-body]');\n    if (body) body.textContent = review.body || '';\n\n    // Judge.me only carries a variant when the merchant asks for one as a\n    // custom question, so this line is usually absent rather than empty.\n    var variant = lightbox.querySelector('[data-ds-photo-lightbox-variant]');\n    if (variant) {\n      variant.textContent = review.variant || '';\n      variant.classList.toggle('sf-hidden', !review.variant);\n    }\n\n    var counter = lightbox.querySelector('[data-ds-photo-lightbox-counter]');\n    var setTotal = shots ? set.length : stripPhotoTotal();\n    var setAt = shots ? lightboxAt + 1 : stripPosition(lightboxAt);\n    if (counter) counter.textContent = setAt + ' / ' + setTotal;\n\n    /* One photo has nowhere to step to: hide \u2039 \u203A rather than leave two\n     * controls that do nothing (QC r2 note). */\n    ['[data-ds-photo-lightbox-prev]', '[data-ds-photo-lightbox-next]'].forEach(function (selector) {\n      var arrow = lightbox.querySelector(selector);\n      if (arrow) arrow.classList.toggle('sf-hidden', set.length <= 1 && !(!shots && stripHasMore()));\n    });\n\n    renderLightboxProduct(review.product);\n  }\n\n  /* \"Purchased item\" in the lightbox \u2014 shop-wide reviews only: thumb, name,\n   * price, one link to the PDP. Judge.me's markup carries the name, the link\n   * and the image but no price, so the price is read from the storefront's own\n   * `/products/<handle>.js` the first time a product is shown, cached per URL,\n   * and the line simply stays empty if that fails. A PDP review has no\n   * product and the block stays hidden. */\n  var priceCache = {};\n\n  function formatPrice(cents) {\n    var format = (root.getAttribute('data-ds-reviews-money') || '${{amount}}').replace(/<[^>]*>/g, '');\n    return format.replace(/\\{\\{\\s*amount[a-z_]*\\s*\\}\\}/, ((Number(cents) || 0) / 100).toFixed(2));\n  }\n\n  /* Price and featured image off the storefront's own `/products/<handle>.js`,\n   * cached per URL. The image is only used when the source sent none (SF\n   * carries the product's name and handle, not its picture). */\n  function productInfo(href) {\n    var path = String(href || '').split('?')[0].split('#')[0];\n    if (!/\\/products\\/[^/]+$/.test(path)) return Promise.resolve(null);\n    if (!priceCache[path]) {\n      priceCache[path] = fetch(path + '.js', { headers: { Accept: 'application/json' } })\n        .then(function (response) { return response.ok ? response.json() : null; })\n        .then(function (json) {\n          if (!json) return null;\n          return {\n            price: typeof json.price_min === 'number' ? formatPrice(json.price_min) : null,\n            image: typeof json.featured_image === 'string' ? json.featured_image : '',\n          };\n        })\n        .catch(function () { return null; });\n    }\n    return priceCache[path];\n  }\n\n  function renderLightboxProduct(product) {\n    var block = lightbox && lightbox.querySelector('[data-ds-photo-lightbox-product]');\n    if (!block) return;\n    var show = !!(product && product.title);\n    block.classList.toggle('sf-hidden', !show);\n    block.classList.toggle('sf-flex', show);\n    if (!show) return;\n\n    block.setAttribute('href', product.href || '#');\n    var title = block.querySelector('[data-ds-photo-lightbox-product-title]');\n    if (title) title.textContent = product.title;\n    var img = block.querySelector('[data-ds-photo-lightbox-product-image]');\n    if (img) {\n      img.classList.toggle('sf-hidden', !product.image);\n      if (product.image) img.src = product.image;\n    }\n    var price = block.querySelector('[data-ds-photo-lightbox-product-price]');\n    if (price) {\n      price.textContent = '';\n      price.classList.add('sf-hidden');\n    }\n    var wanted = product.href;\n    productInfo(product.href).then(function (info) {\n      // The shopper may have stepped to another photo while this was out.\n      if (!info || block.getAttribute('href') !== wanted) return;\n      if (price && info.price) {\n        price.textContent = info.price;\n        price.classList.remove('sf-hidden');\n      }\n      if (img && !product.image && info.image) {\n        img.src = info.image;\n        img.classList.remove('sf-hidden');\n      }\n    });\n  }\n\n  /* One step through the lightbox. Walking the strip on the all-reviews feed,\n   * the next page of photo reviews is fetched three photos before the end of\n   * what is loaded, and a step past the end waits for it rather than wrapping\n   * back to the first photo. */\n  /* While the next page is on its way, both arrows are disabled and the viewer\n   * reports busy, so a press neither sits there silently nor queues up jumps\n   * that all land at once (QC r6). */\n  var lightboxWaiting = false;\n  function setLightboxWaiting(on) {\n    lightboxWaiting = on;\n    if (!lightbox) return;\n    var viewer = lightbox.querySelector('[data-ds-photo-lightbox-viewer]');\n    if (viewer) viewer.setAttribute('aria-busy', on ? 'true' : 'false');\n    ['[data-ds-photo-lightbox-prev] button', '[data-ds-photo-lightbox-next] button'].forEach(function (selector) {\n      var button = lightbox.querySelector(selector);\n      if (button) button.disabled = on;\n    });\n  }\n\n  function lightboxStep(delta) {\n    if (lightboxWaiting) return;\n    if (!shots && delta > 0 && stripHasMore()) {\n      if (lightboxAt + delta >= strip.length) {\n        var from = lightboxAt;\n        setLightboxWaiting(true);\n        loadMoreStrip().then(function () {\n          setLightboxWaiting(false);\n          /* A failed page leaves the shopper where they were rather than\n           * wrapping back to the first photo. */\n          if (from + delta < strip.length) lightboxShow(lightboxAt + delta);\n        });\n        return;\n      }\n      if (lightboxAt + delta >= strip.length - 3) loadMoreStrip();\n    }\n    lightboxShow(lightboxAt + delta);\n  }\n\n  function lightboxOpen(index, trigger) {\n    if (!lightbox) return;\n    lightboxOpener = trigger || null;\n    lightboxShow(index);\n    lightbox.setAttribute('aria-hidden', 'false');\n    lightbox.setAttribute('data-ds-page-lock', '');\n    var close = lightbox.querySelector('[data-ds-photo-lightbox-close] button');\n    if (close) close.focus({ focusVisible: false });\n  }\n\n  function lightboxClose() {\n    if (!lightbox) return;\n    lightbox.setAttribute('aria-hidden', 'true');\n    lightbox.removeAttribute('data-ds-page-lock');\n    if (lightboxOpener && typeof lightboxOpener.focus === 'function') lightboxOpener.focus();\n    lightboxOpener = null;\n  }\n\n  /* Bring row `at` of `visible()` on screen: desktop turns to its page, mobile\n   * appends far enough to hold it. */\n  function revealReview(at) {\n    var node;\n    if (isDesktop()) {\n      state.page = Math.floor(at / PER_PAGE) + 1;\n      renderList();\n      node = list.children[at % PER_PAGE];\n    } else {\n      if (at >= state.shown) state.shown = at + 1;\n      renderList();\n      node = list.children[at];\n    }\n    if (node) node.scrollIntoView({ behavior: 'smooth', block: 'center' });\n  }\n\n  /* A tile in the strip knows its index; a tile in a review body knows only its\n   * URL, so it finds itself in the strip by that. */\n  root.addEventListener('click', function (event) {\n    var tile = event.target.closest('[data-ds-reviews-photo]');\n    if (!tile || !lightbox) return;\n    var index = tile.getAttribute('data-ds-reviews-photo-index');\n    if (index !== null) {\n      if (!strip.length) return;\n      event.preventDefault();\n      shots = null;\n      lightboxOpen(parseInt(index, 10) || 0, tile);\n      return;\n    }\n    var src = tile.getAttribute('data-ds-reviews-photo-src');\n    var found = -1;\n    strip.forEach(function (shot, i) {\n      if (found < 0 && shot.src === src) found = i;\n    });\n    if (found >= 0) {\n      event.preventDefault();\n      shots = null;\n      lightboxOpen(found, tile);\n      return;\n    }\n    var item = tile.closest('[data-ds-review-item]');\n    var review = reviewOfNode && item ? reviewOfNode.get(item) : null;\n    if (!review || !review.photos.length) return;\n    event.preventDefault();\n    shots = review.photos.map(function (photo) {\n      return { src: photo.src, full: photo.full, review: review };\n    });\n    var at = 0;\n    shots.forEach(function (shot, i) {\n      if (shot.src === src) at = i;\n    });\n    lightboxOpen(at, tile);\n  });\n\n  if (lightbox) {\n    lightbox.addEventListener('click', function (event) {\n      if (event.target === lightbox || event.target.closest('[data-ds-photo-lightbox-close]')) {\n        event.preventDefault();\n        lightboxClose();\n        return;\n      }\n      if (event.target.closest('[data-ds-photo-lightbox-prev]')) {\n        event.preventDefault();\n        lightboxStep(-1);\n        return;\n      }\n      if (event.target.closest('[data-ds-photo-lightbox-next]')) {\n        event.preventDefault();\n        lightboxStep(1);\n        return;\n      }\n      if (event.target.closest('[data-ds-photo-lightbox-see]')) {\n        event.preventDefault();\n        var seen = lightboxSet();\n        var wanted = seen[lightboxAt] && seen[lightboxAt].review;\n        lightboxClose();\n        if (!wanted) return;\n        // The review is rarely one of the rows already on screen, and the two\n        // breakpoints hide it differently: desktop paginates, so the review sits\n        // on a page that is not the current one; mobile appends, so it sits past\n        // what has been appended so far. Reaching it therefore means moving the\n        // page on desktop and raising the appended count on mobile \u2014 running the\n        // desktop branch on mobile lands on the wrong row, or on no row at all.\n        /* The review behind a photo comes from the strip's set (one `per_page=20`\n         * request), while the list can be on any page of the current query \u2014 the\n         * two barely overlap, so searching the list directly almost always misses\n         * and the button silently does nothing. That was the bug.\n         *\n         * Turning on \"With photos\" is the correct and cheapest route: that\n         * filter's set IS the strip's set, so the review is certainly present and\n         * it costs no request. The shopper can see which mode they are in because\n         * the Filter chip changes with it \u2014 carrying someone to the review they\n         * clicked without saying so is the confusing version.\n         *\n         * Matched on `id` rather than object identity: the review in `strip` and\n         * the one in the list are parsed from two different responses, so the same\n         * review is two objects. When `loadStrip` fails, the strip is built from\n         * the metafield while `photoPool()` returns the list's rows \u2014 `indexOf`\n         * never matches and the button goes silent again, the very bug this\n         * exists to fix. */\n        var at = indexOfReview(visible(), wanted);\n        if (at >= 0) {\n          revealReview(at);\n          return;\n        }\n        if (!wanted.photos || !wanted.photos.length) return;\n        var box = root.querySelector('[data-ds-reviews-filter=\"photo\"] input');\n        if (!box || box.checked) return;\n        box.checked = true;\n        /* Through `syncFromPanels` rather than assigning `state.photo`\n         * directly: it is the one place that reads the DOM back into state, so\n         * the Filter chip, the Done button and the list all tell one story. */\n        var shown = syncFromPanels();\n        if (!data.photoServer) {\n          at = indexOfReview(visible(), wanted);\n          if (at >= 0) revealReview(at);\n          return;\n        }\n        /* Where \"With photos\" is a server query (SF, the all-reviews feed), its\n         * rows arrive a page at a time, so walk them in order until the review\n         * turns up \u2014 no further than the strip reaches, since that is where the\n         * review came from. */\n        var reach = Math.ceil(((stripRows && stripRows.length) || PER_PAGE) / PER_PAGE) + 1;\n        var seek = function (n) {\n          var found = indexOfReview(visible(), wanted);\n          if (found >= 0) return revealReview(found);\n          if (n > reach || n > pageCount()) return;\n          return fetchUiPage(n).then(function () { seek(n + 1); });\n        };\n        shown.then(function () { seek(2); });\n      }\n    });\n\n    document.addEventListener('keydown', function (event) {\n      if (lightbox.getAttribute('aria-hidden') !== 'false') return;\n      if (event.key === 'Escape') lightboxClose();\n      if (event.key === 'ArrowLeft') lightboxStep(-1);\n      if (event.key === 'ArrowRight') lightboxStep(1);\n    });\n\n    /* Touch/pen swipe over the photo \u2014 the same step the arrows do. The viewer\n     * is a single <img> whose src is swapped (no scroll track), so this can't\n     * follow the finger; it commits on release past a threshold, and Judge.me's\n     * strip wraps via the modulo in lightboxShow. touch-action: pan-y keeps the\n     * mobile bottom-sheet vertically scrollable while horizontal drags are ours. */\n    var swipeArea = lightbox.querySelector('[data-ds-photo-lightbox-viewer]');\n    if (swipeArea) {\n      swipeArea.style.touchAction = 'pan-y';\n      var sx = 0, sy = 0, sdx = 0, sid = null, sDown = false, sHor = false;\n\n      // Kill the browser's native image drag while a mouse drag is ours.\n      swipeArea.addEventListener('dragstart', function (event) {\n        if (sDown) event.preventDefault();\n      });\n\n      swipeArea.addEventListener('pointerdown', function (event) {\n        // Primary button only; touch, pen and mouse all drag to step photos.\n        if (event.button != null && event.button > 0) return;\n        sDown = true; sHor = false; sdx = 0;\n        sx = event.clientX; sy = event.clientY; sid = event.pointerId;\n      });\n\n      swipeArea.addEventListener('pointermove', function (event) {\n        if (!sDown || event.pointerId !== sid) return;\n        var mx = event.clientX - sx;\n        var my = event.clientY - sy;\n        if (!sHor) {\n          if (Math.abs(mx) < 10 && Math.abs(my) < 10) return;\n          // A mostly-vertical drag is the shopper scrolling the sheet \u2014 bow out.\n          if (Math.abs(mx) <= Math.abs(my)) { sDown = false; return; }\n          sHor = true;\n          try { swipeArea.setPointerCapture(sid); } catch (err) {}\n        }\n        sdx = mx;\n        event.preventDefault();\n      });\n\n      var endSwipe = function (event) {\n        if (!sDown || (sid !== null && event.pointerId !== sid)) return;\n        var moved = sdx;\n        var wasHorizontal = sHor;\n        sDown = false; sHor = false;\n        try { if (sid !== null) swipeArea.releasePointerCapture(sid); } catch (err) {}\n        sid = null;\n        if (!wasHorizontal) return;\n        var threshold = Math.min(80, (swipeArea.clientWidth || 1) * 0.2);\n        if (moved <= -threshold) lightboxStep(1);\n        else if (moved >= threshold) lightboxStep(-1);\n      };\n\n      swipeArea.addEventListener('pointerup', endSwipe);\n      swipeArea.addEventListener('pointercancel', endSwipe);\n    }\n  }\n\n  /* Rotating a phone or dragging a desktop window across tablet-air swaps which control\n   * owns the list. Without this the shopper keeps whichever they had \u2014 a pager\n   * on a 375 screen, or a stranded \"Load more\" beside page numbers. */\n  var stripTrack = root.querySelector('[data-ds-reviews-photos-track]');\n  if (stripTrack) stripTrack.addEventListener('scroll', syncStripNav, { passive: true });\n\n  var wasDesktop = isDesktop();\n  var wasDrop = panelsDrop();\n  window.addEventListener('resize', function () {\n    syncStripNav();\n    var now = isDesktop();\n    var drop = panelsDrop();\n    // The same panel is a sheet on one side of tablet-mini and a dropdown on\n    // the other, and the two lock body scroll differently. Closing on the way\n    // across is cheaper than reconciling that, and leaves no scroll lock\n    // stranded on a desktop page.\n    if (drop !== wasDrop) {\n      wasDrop = drop;\n      closePanels();\n    }\n    if (now === wasDesktop) return;\n    wasDesktop = now;\n    closePanels();\n    state.page = 1;\n    state.shown = PER_PAGE;\n    renderList();\n  });\n\n  /* Mount costs exactly TWO requests and no more: one for the list's page 1, one\n   * for the photo strip (`per_page=20`). Later pages are fetched as they are\n   * clicked. On SF the list's page 1 already came with the source choice (the\n   * source hands it over without asking again), so only the strip remains.\n   *\n   * The page count comes from the source's aggregate, so the pager is there\n   * immediately, and the first request's `total_count` corrects it if they\n   * disagree. */\n  renderSortTrigger();\n  renderFilterTrigger();\n\n  /* The list does NOT come from the seed, not even its first page. Judge.me's\n   * seed is the widget metafield rendered in the shop's default order\n   * (`pictures-first`), while the list asks in the selected sort \u2014 `helpful` by\n   * default. Two different orderings. Taking page 1 from one and pages 2+ from\n   * the other stitches two lists together: measured on product 9902457880859,\n   * 30 rendered rows held only 23 distinct reviews \u2014 7 repeats \u2014 and photos\n   * appeared only in the first five rows, because exactly those five came from\n   * the pictures-first ordering.\n   *\n   * So page 1 goes through the source like every other page: one ordering,\n   * nothing repeated, nothing missed. The skeleton covers that stretch. */\n  setBusy(true);\n  var firstPage = show(1);\n  // Draw the strip immediately from the seed's photos; `loadStrip` then\n  // replaces it with the wider set. Drawing it up front keeps the block present\n  // from the start rather than appearing late and shoving the list down.\n  renderPhotoStrip();\n  renderVariants();\n  renderApply();\n  // After the list's page 1, not beside it: two parallel requests on arrival\n  // were enough, with a couple of hard reloads, to trip Judge.me's burst limit\n  // (its RETRY_DELAYS). The strip already shows the seed's photos meanwhile.\n  firstPage.then(loadStrip, loadStrip);\n}\n\nfunction init(scope) {\n  (scope || document).querySelectorAll(ROOT).forEach(function (root) {\n    if (root.dataset.dsBound) return;\n    root.dataset.dsBound = '1';\n    setup(root);\n  });\n}\n\n/* The data sources (product-reviews-data-*.js) are concatenated AFTER this file\n * in sf-core.js, so mount once the whole bundle has run: on DOMContentLoaded, or\n * one microtask from now when the document is already parsed. */\nif (document.readyState === 'loading') {\n  document.addEventListener('DOMContentLoaded', function () { init(); });\n} else {\n  Promise.resolve().then(function () { init(); });\n}\n\n// Theme editor re-renders a section into a fresh subtree; bind that one too.\ndocument.addEventListener('shopify:section:load', function (event) {\n  init(event.target);\n});\n\n} catch (sfError) {\nif (typeof console !== 'undefined') console.error('[sf-core] behavior \"product-reviews\" failed to initialise:', sfError);\n}\n})();\n\n/* product-reviews-data-judgeme.js */\n(function () {\ntry {\n/* product-reviews-data-judgeme \u2014 Judge.me as the data source of a reviews band.\n *\n * The UI (product-reviews.js) is shared; the data is not. Each source turns its\n * own feed into the same plain review objects the UI renders, and this file is\n * the Judge.me one: the public widget endpoints, their URLs, retries and the\n * `.jdgm-rev` HTML they answer with. The WanderCo SF API is the other source\n * (product-reviews-data-sf.js); a band uses SF when it can and falls back to\n * this one. Nothing here knows SF exists.\n *\n * Why parse HTML at all. Judge.me publishes no JSON a storefront can read: the\n * `judgeme.review_widget_data` metafield does not exist, the cache server and\n * `api.judge.me/api/v1/widgets/*` answer with pre-rendered HTML, and the one\n * real JSON endpoint needs a private token that cannot ship in a theme. What IS\n * open is `api.judge.me/reviews/reviews_for_widget` \u2014 no token at all \u2014 and it\n * returns `{\"html\": \u2026}`. So HTML is the only feed, and reading it is the price\n * of not standing up an App Proxy.\n *\n * What makes that safe enough. Judge.me stamps the review's actual data onto\n * attributes \u2014 data-review-id, data-verified-buyer, data-score, and a <time>\n * with a real datetime \u2014 and those change far less often than class names. The\n * parser reads attributes first and falls back to a class only for the body,\n * the author and the reply, where there is nothing else. If a page yields no\n * parsable reviews, the original widget is left visible: the failure mode is\n * Judge.me's own design, never an empty column.\n *\n * Exposes `window.WcReviewData.judgeme(root, { perPage, feedAll, source })` (the\n * reviews band) and `window.WcReviewData.judgemeShopCards(source)` (homepage\n * cards, ds-customer-reviews).\n * Authoring rules: src/behaviors/README.md. No import/export.\n */\n\nvar JM_API = 'https://api.judge.me/reviews/reviews_for_widget';\n\n/* The shop-wide feed: what Judge.me's own all-reviews widget pages with (its\n * \"Load More\" on /pages/reviews-1). Same `page` / `per_page` / `filter_rating` /\n * `verified_only` / `sort_by` / `sort_dir` levers, measured 2026-10-10; the\n * differences are listed where they bite \u2014 see `allSort` and `payloadTotal`.\n * A band opts in with `data-ds-reviews-feed=\"all\"` (ds-product-reviews\n * `feed: 'all'`, the reviews page). */\nvar JM_ALL_API = 'https://api.judge.me/reviews/all_reviews_js_based';\n\n/* The band's sort values (`helpful|desc`, \u2026, shared with the PDP) as this\n * endpoint names them. Measured, first 10 reviews each:\n *   no sort_by / sort_by=most_helpful \u2192 thumbs-up 53, 34, 31\u2026 (the default)\n *   sort_by=helpful                   \u2192 silently newest first (unknown value)\n *   sort_by=created_at                \u2192 newest first, whatever sort_dir says\n *   sort_by=rating + sort_dir=asc     \u2192 1\u2605 first; desc \u2192 5\u2605 first\n *   sort_by=pictures_first            \u2192 photo reviews first, count unchanged\n * So `helpful` and `pictures` must be renamed, and `date` maps to created_at. */\nfunction allSort(chosen) {\n  var parts = String(chosen || '').split('|');\n  var key = { helpful: 'most_helpful', date: 'created_at', rating: 'rating', pictures: 'pictures_first' }[parts[0]] || '';\n  if (!key) return '';\n  var dir = key === 'rating' || key === 'created_at' ? parts[1] : '';\n  return '&sort_by=' + key + (dir ? '&sort_dir=' + encodeURIComponent(dir) : '');\n}\n\n/* How many reviews the query holds. reviews_for_widget says `total_count`; the\n * all-reviews feed has no such key and instead splits the FILTERED set into\n * `number_of_product_reviews` + `number_of_shop_reviews` (filter_rating=1 \u2192\n * 322 + 43, the histogram's 365). The list only shows product reviews, so the\n * product half is its total. NaN when the payload carries neither. */\nfunction payloadTotal(payload) {\n  if (!payload) return NaN;\n  var t = parseInt(payload.total_count, 10);\n  if (!isNaN(t)) return t;\n  return parseInt(payload.number_of_product_reviews, 10);\n}\n\n/* The ceiling for ONE request. Judge.me promises nothing about timing, and a\n * page that hangs forever leaves a permanent skeleton and two locked controls \u2014\n * the shopper cannot get out. On timeout we abort and retry (RETRY_DELAYS);\n * only once every attempt has failed do we release the controls \u2014 an empty page\n * is at least one you can ask for again. */\nvar PAGE_TIMEOUT = 8000;\n\n/* How long to wait before each retry of a failed Judge.me request.\n *\n * Judge.me rate-limits the widget endpoint per IP and answers 429 with no\n * Retry-After. Measured on product 9902457880859 (2026-09-25): 30 requests one\n * after another (~2/s) all came back 200, while three rounds of 3 in parallel\n * hit 429 on the second round \u2014 it is the burst that trips it, not the count \u2014\n * and the block had lifted within a second. So an immediate retry lands inside\n * the same burst and fails again; a short wait is what clears it. Jittered so two\n * tabs reloaded together do not retry in lockstep. */\nvar RETRY_DELAYS = [1000, 2000, 4000];\n\n/* One request with retries. Resolves to the JSON payload, or null once every\n * attempt failed. Only 429, 5xx, a network error or the timeout are retried \u2014 a\n * 4xx other than 429 will say the same thing the next time. */\nfunction fetchReviewsJson(url) {\n  function attempt(index) {\n    var controller = typeof AbortController !== 'undefined' ? new AbortController() : null;\n    var timer = setTimeout(function () { if (controller) controller.abort(); }, PAGE_TIMEOUT);\n    return fetch(url, controller ? { signal: controller.signal } : undefined)\n      .then(function (response) {\n        clearTimeout(timer);\n        if (response.ok) return response.json();\n        var retryable = response.status === 429 || response.status >= 500;\n        return retryable ? again(index) : null;\n      }, function () {\n        clearTimeout(timer);\n        return again(index);\n      })\n      .catch(function () { return null; });\n  }\n  function again(index) {\n    if (index >= RETRY_DELAYS.length) return null;\n    var delay = RETRY_DELAYS[index] * (0.75 + Math.random() * 0.5);\n    return new Promise(function (resolve) { setTimeout(resolve, delay); })\n      .then(function () { return attempt(index + 1); });\n  }\n  return attempt(0);\n}\n\n/* \u2500\u2500 Parsing \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\nfunction text(node, selector) {\n  var found = node.querySelector(selector);\n  return found ? found.textContent.trim() : '';\n}\n\n/* One `.jdgm-rev` element to a plain object. Returns null for anything that\n * carries no body \u2014 Judge.me emits placeholder rows we have no design for. */\nfunction parseReview(el) {\n  var body = text(el, '.jdgm-rev__body');\n  if (!body) return null;\n\n  var title = text(el, '.jdgm-rev__title');\n  var rating = el.querySelector('.jdgm-rev__rating');\n  var time = el.querySelector('.jdgm-rev__timestamp');\n  var reply = text(el, '.jdgm-rev__reply');\n\n  var photos = [];\n  var product = null;\n  /* The all-reviews feed names the product each review is about, and puts the\n   * shop's own product image into the same picture row as the customer's\n   * photos, told apart only by `jdgm-rev__product-picture`. That image is the\n   * PRODUCT, not evidence (spec 12-reviews: #2557), so it leaves the photo set\n   * and travels with the product instead. */\n  var prodLink = el.querySelector('.jdgm-rev__prod-link');\n  if (prodLink && prodLink.textContent.trim()) {\n    var prodPic = el.querySelector('.jdgm-rev__product-picture');\n    var prodImg = prodPic && prodPic.querySelector('img');\n    product = {\n      title: prodLink.textContent.trim(),\n      href: prodLink.getAttribute('href') || '',\n      image: (prodImg && (prodImg.getAttribute('data-src') || prodImg.getAttribute('src'))) || (prodPic && prodPic.getAttribute('href')) || '',\n    };\n  }\n  el.querySelectorAll('.jdgm-rev__pic-link').forEach(function (link) {\n    if (link.classList.contains('jdgm-rev__product-picture')) return;\n    var img = link.querySelector('img');\n    // Judge.me lazy-loads its own thumbnails, so the real URL sits in data-src\n    // on the <img> while the anchor holds the full-size one.\n    var src = (img && (img.getAttribute('data-src') || img.getAttribute('src'))) || link.getAttribute('href');\n    if (src) photos.push({ src: src, full: link.getAttribute('href') || src });\n  });\n\n  return {\n    id: el.getAttribute('data-review-id') || '',\n    helpful: parseInt(el.getAttribute('data-thumb-up-count'), 10) || 0,\n    author: text(el, '.jdgm-rev__author'),\n    /* The variant the review was left on, which Judge.me only carries when the\n     * merchant configured it as a custom question \u2014 `.jdgm-rev__custom-form` is\n     * present but EMPTY on this store. So this is '' far more often than not,\n     * and the filter panel's VARIANT group stays hidden on that evidence rather\n     * than drawing an empty fieldset. */\n    variant: text(el, '.jdgm-rev__variant') || text(el, '.jdgm-rev__custom-form'),\n    // Shown only by the lightbox \u2014 the reviews list's Figma frame hides it.\n    title: title,\n    /* Two-letter code Judge.me stamps on `.jdgm-rev__location` once the shop\n     * turns on \"Show reviewer country flag\"; '' when the review has none. */\n    country: countryCode(el),\n    rating: rating ? parseInt(rating.getAttribute('data-score'), 10) || 0 : 0,\n    /* The all-reviews feed stamps `data-content='2025-11-08 14:04:04 UTC'`\n     * where the product widget has a datetime; the UI's date formatter reads\n     * either. */\n    dateIso: time ? time.getAttribute('datetime') || time.getAttribute('data-content') || '' : '',\n    body: body,\n    photos: photos,\n    reply: reply,\n    product: product,\n  };\n}\n\nfunction countryCode(el) {\n  var location = el.querySelector('.jdgm-rev__location');\n  var code = location ? (location.getAttribute('data-country-code') || '').toUpperCase() : '';\n  return /^[A-Z]{2}$/.test(code) ? code : '';\n}\n\nfunction parseAll(root) {\n  var out = [];\n  root.querySelectorAll('.jdgm-rev').forEach(function (el) {\n    var review = parseReview(el);\n    if (review) out.push(review);\n  });\n  return out;\n}\n\n/* Judge.me's newer Review Widget answers reviews_for_widget with `reviews[]`\n * (JSON) and no `html`. Same review object as `parseReview`, read straight off\n * the fields; a review with no body is skipped the same way. */\nfunction jsonReview(r) {\n  var box = document.createElement('div');\n  box.innerHTML = r.body_html || '';\n  var body = box.textContent.trim();\n  if (!body) return null;\n  var code = String(r.location_country_code || '').toUpperCase();\n  return {\n    id: r.uuid ? String(r.uuid) : '',\n    helpful: parseInt(r.thumb_up, 10) || 0,\n    author: r.reviewer_name || '',\n    variant: r.product_variant_title || '',\n    title: r.title || '',\n    country: /^[A-Z]{2}$/.test(code) ? code : '',\n    rating: parseInt(r.rating, 10) || 0,\n    dateIso: r.created_at || '',\n    body: body,\n    photos: (r.pictures_urls || []).map(function (p) {\n      var src = typeof p === 'string' ? p : (p && (p.compact || p.small || p.url || p.original)) || '';\n      var full = typeof p === 'string' ? p : (p && (p.original || p.huge || p.url)) || src;\n      return { src: src, full: full };\n    }).filter(function (p) { return p.src; }),\n    reply: r.reply_content || '',\n    product: null,\n  };\n}\n\n/* What a list / strip response holds: the parsed reviews, the query's total\n * (NaN when unstated) and how many reviews the server SENT (`got`, counting\n * the ones the parser skips \u2014 it is what tells the UI it reached the bottom). */\nfunction readPayload(payload) {\n  if (payload.html || !Array.isArray(payload.reviews)) {\n    var holder = document.createElement('div');\n    holder.innerHTML = payload.html || '';\n    return { rows: parseAll(holder), total: payloadTotal(payload), got: holder.querySelectorAll('.jdgm-rev').length };\n  }\n  var rows = [];\n  payload.reviews.forEach(function (r) {\n    var review = jsonReview(r);\n    if (review) rows.push(review);\n  });\n  // The JSON widget may state its total under `pagination`; failing that, the\n  // page count times the page size (exact on the last page).\n  var pg = payload.pagination || {};\n  var exact = [payload.total_count, pg.total_count, pg.total_entries, pg.total]\n    .map(function (v) { return parseInt(v, 10); })\n    .filter(function (v) { return v >= 0; })[0];\n  var per = pg.per_page || payload.reviews.length;\n  var total = exact !== undefined ? exact\n    : pg.total_pages ? (pg.current_page >= pg.total_pages\n      ? (pg.current_page - 1) * per + payload.reviews.length\n      : pg.total_pages * per)\n    : NaN;\n  return { rows: rows, total: total, got: payload.reviews.length };\n}\n\n/* The published aggregate, read off the widget metafield the section already\n * parked in the source node. Judge.me stamps its DISPLAYED numbers onto the\n * `.jdgm-rev-widg` root (data-average-rating / data-number-of-reviews) and the\n * per-star frequencies onto `.jdgm-histogram__row` (data-rating + data-\n * frequency). These are the same values the Liquid seeds the summary with;\n * re-reading them keeps the rail correct after a theme-editor re-render and\n * self-heals if the server seed ever parsed the markup wrong. Returns null when\n * the widget markup carries no aggregate, so the server render is left alone. */\nfunction readAggregate(source) {\n  var widg = source.querySelector('.jdgm-rev-widg') || source.querySelector('.jdgm-all-reviews__header');\n  if (!widg) return null;\n  var avgAttr = widg.getAttribute('data-average-rating');\n  /* The all-reviews header counts product AND store reviews; the list it feeds\n   * holds product reviews only, so its own total is the product half (QC r4:\n   * Clear all read \"Show 58193\", the list held 56106). */\n  var cntAttr = widg.getAttribute('data-number-of-product-reviews') || widg.getAttribute('data-number-of-reviews');\n  if (avgAttr == null && cntAttr == null) return null;\n\n  var dist = { 5: 0, 4: 0, 3: 0, 2: 0, 1: 0 };\n  source.querySelectorAll('.jdgm-histogram__row').forEach(function (row) {\n    var stars = parseInt(row.getAttribute('data-rating'), 10);\n    if (dist[stars] === undefined) return;\n    dist[stars] = parseInt(row.getAttribute('data-frequency'), 10) || 0;\n  });\n\n  var avgNum = avgAttr != null ? parseFloat(avgAttr) : NaN;\n  return {\n    averageValue: avgAttr != null && !isNaN(avgNum) ? avgNum : null,\n    count: cntAttr != null ? parseInt(cntAttr, 10) || 0 : null,\n    dist: dist,\n  };\n}\n\n/* \u2500\u2500 One band \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\nfunction createJudgeMe(root, opts) {\n  var perPage = opts.perPage;\n  var feedAll = !!opts.feedAll;\n  var source = opts.source;\n  var productId = root.getAttribute('data-ds-reviews-product');\n  var shop = root.getAttribute('data-ds-reviews-shop');\n\n  /* `q` is the UI's query: { sort, rating, photo } \u2014 `sort` is the\n   * band's radio value (`helpful|desc`, \u2026). */\n  function pageUrl(n, q) {\n    if (feedAll && shop) {\n      return (\n        JM_ALL_API +\n        '?url=' + encodeURIComponent(shop) +\n        '&shop_domain=' + encodeURIComponent(shop) +\n        '&platform=shopify&per_page=' + perPage + '&page=' + n +\n        /* \"With photos\" is a server filter on this feed (`with_pictures`, it\n         * counts and pages like any other query), so the list is every photo\n         * review, not the strip's first page. It travels as a sort_by, so it\n         * takes the sort's place while it is on. */\n        (q.photo ? '&sort_by=with_pictures' : allSort(q.sort)) +\n        (q.rating ? '&filter_rating=' + encodeURIComponent(q.rating) : '')\n      );\n    }\n    if (!productId || !shop) return '';\n    /* Same names as the shop-wide feed. The radios still say `helpful|desc`,\n     * `date|desc`, `pictures|desc`; `allSort` renames them. The old names are\n     * ignored and the list stays newest. */\n    return (\n      JM_API +\n      '?url=' + encodeURIComponent(shop) +\n      '&shop_domain=' + encodeURIComponent(shop) +\n      '&platform=shopify&product_id=' + encodeURIComponent(productId) +\n      '&per_page=' + perPage + '&page=' + n +\n      allSort(q.sort) +\n      (q.rating ? '&filter_rating=' + encodeURIComponent(q.rating) : '')\n    );\n  }\n\n  /* Photo strip requests carry `per_page=20` and no `sort_by`.\n   *\n   * Omitting the sort is deliberate \u2014 the shop sets\n   * `default_sort_method = pictures-first`, so the default already pushes reviews\n   * with photos to the front; sending any `sort_by` overwrites that (measured:\n   * no sort \u2192 20 reviews all with photos; with a sort \u2192 27 reviews, 1 with a\n   * photo).\n   *\n   * `per_page=20` is a MEASURED number, not a chosen one: the endpoint caps a\n   * response at 20 rows. Measured on product 9902457880859 (2026-09-17) \u2014\n   * per_page 5/10/15/20 returned exactly 5/10/15/20, while 25/30/50/100 all\n   * returned 20.\n   *\n   * Shop-wide, the strip asks for `sort_by=with_pictures` \u2014 on that feed it is a\n   * real filter (only reviews carrying a customer photo, counted 5,923 of\n   * 56,087) \u2014 narrowed by the active star filter. */\n  function stripUrl(n, q) {\n    if (feedAll && shop) {\n      return (\n        JM_ALL_API +\n        '?url=' + encodeURIComponent(shop) +\n        '&shop_domain=' + encodeURIComponent(shop) +\n        '&platform=shopify&sort_by=with_pictures&per_page=20&page=' + n +\n        (q.rating ? '&filter_rating=' + encodeURIComponent(q.rating) : '')\n      );\n    }\n    if (!productId || !shop) return '';\n    return (\n      JM_API +\n      '?url=' + encodeURIComponent(shop) +\n      '&shop_domain=' + encodeURIComponent(shop) +\n      '&platform=shopify&product_id=' + encodeURIComponent(productId) +\n      '&per_page=20&page=' + n\n    );\n  }\n\n  var data = {\n    name: 'judgeme',\n    /* \"With photos\" is a server query only on the all-reviews feed. The product\n     * widget endpoint has no photo filter \u2014 every parameter was probed\n     * (`with_pictures`, `has_pictures`, `pictures_only`, `filter_pictures`,\n     * `media`, `only_pictures`: `total_count` never moved) \u2014 so on a PDP the UI\n     * filters the strip's set in memory. */\n    photoServer: feedAll,\n    /* A draft count the server gave for this exact query may stand in for the\n     * histogram only on the all-reviews feed, whose histogram includes store\n     * reviews the list never shows. */\n    trustDraftTotals: feedAll,\n    // The strip is read 20 at a time (see `stripUrl`).\n    stripWhole: false,\n    /* How deep the all-reviews feed goes: the first 1000 reviews of any query,\n     * whatever the sort or filter. Measured 2026-10-10 \u2014 per_page=10 serves\n     * distinct pages up to 100, per_page=20 up to 50, and every page past that\n     * returns the last page again (most_helpful, created_at, verified_only,\n     * filter_rating=4 alike). The reviews page stops its pager, Load more and\n     * lightbox there rather than repeat (QC r4). Counts still state the true size\n     * of the query. The product widget has no such ceiling. */\n    maxReviews: feedAll ? 1000 : Infinity,\n    hasTarget: !!pageUrl(1, {}),\n    aggregate: readAggregate(source),\n    /* The widget metafield's reviews, rendered server-side in the shop's\n     * default order. */\n    seed: parseAll(source),\n\n    /* Which draft counts need one request: on the all-reviews feed a star or\n     * photo draft is counted by the server. Photo on a PDP is counted from the\n     * strip instead. */\n    countsByRequest: function (f) {\n      return !!(feedAll && (f.photo || f.rating));\n    },\n\n    /* List page `n` of query `q`: { rows, total, got }, or null once every\n     * attempt failed. */\n    page: function (n, q) {\n      return fetchReviewsJson(pageUrl(n, q)).then(function (payload) {\n        return payload ? readPayload(payload) : null;\n      });\n    },\n\n    /* Strip page `n`: { rows, total }, or null on failure. */\n    strip: function (n, q) {\n      return fetchReviewsJson(stripUrl(n, q || {})).then(function (payload) {\n        if (!payload) return null;\n        var res = readPayload(payload);\n        return { rows: res.rows, total: res.total };\n      });\n    },\n\n    /* How many reviews a draft will show, without changing the list: one\n     * request, no retry \u2014 a failed count only leaves the button without a\n     * number. Resolves to the total, or null. */\n    count: function (q) {\n      var controller = typeof AbortController !== 'undefined' ? new AbortController() : null;\n      var timer = setTimeout(function () { if (controller) controller.abort(); }, PAGE_TIMEOUT);\n      return fetch(pageUrl(1, q), controller ? { signal: controller.signal } : undefined)\n        .then(function (response) { return response.ok ? response.json() : null; })\n        .then(function (payload) {\n          clearTimeout(timer);\n          var t = payloadTotal(payload);\n          return isNaN(t) ? null : t;\n        }, function () {\n          clearTimeout(timer);\n          return null;\n        });\n    },\n\n    /* An empty widget metafield (the newer Review Widget renders client-side and\n     * leaves it blank) gets page 1 from the API instead. Resolves true when that\n     * produced reviews (now in `seed`). */\n    fillSeed: function () {\n      if (feedAll || !productId || !shop) return Promise.resolve(false);\n      var url = JM_API +\n        '?url=' + encodeURIComponent(shop) +\n        '&shop_domain=' + encodeURIComponent(shop) +\n        '&platform=shopify&product_id=' + encodeURIComponent(productId) +\n        '&per_page=' + perPage + '&page=1';\n      return fetchReviewsJson(url).then(function (payload) {\n        if (!payload) return false;\n        if (payload.html) source.innerHTML = payload.html;\n        data.seed = readPayload(payload).rows;\n        return data.seed.length > 0;\n      });\n    },\n  };\n  return data;\n}\n\n/* \u2500\u2500 Homepage cards (ds-customer-reviews) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n * The homepage band reads Judge.me's shop metafields the section parked in its\n * hidden source node. TWO feeds, read in this order:\n *\n *   `.jdgm-rev`          \u2014 the all-reviews page (all_reviews_0 + all_reviews_1).\n *                          The SAME markup the PDP widget uses, so the same\n *                          attributes: data-verified-buyer, data-score,\n *                          data-product-title, data-product-url. Preferred,\n *                          because an attribute survives a restyle where a\n *                          class does not.\n *   `.jdgm-carousel-item` \u2014 the featured carousel. Every field is a class with\n *                          no attribute twin and the rating has no data-score\n *                          at all, so this is only the fallback.\n *\n * A card: { body, author, product, href, image, rating, verified }. */\n\n/* One `.jdgm-rev` to a card. Everything but the body and the author comes off\n * an attribute, which is why this feed is preferred. */\nfunction parseShopReview(el) {\n  var body = text(el, '.jdgm-rev__body');\n  if (!body) return null;\n\n  var rating = el.querySelector('.jdgm-rev__rating');\n  var pic = el.querySelector('.jdgm-rev__pic-link img');\n\n  return {\n    body: body,\n    author: text(el, '.jdgm-rev__author'),\n    product: el.getAttribute('data-product-title') || '',\n    href: el.getAttribute('data-product-url') || '',\n    image: pic ? pic.getAttribute('data-src') || pic.getAttribute('src') || '' : '',\n    rating: rating ? parseInt(rating.getAttribute('data-score'), 10) || 5 : 5,\n  };\n}\n\n/* One `.jdgm-carousel-item` to a card, or null when it carries no review body \u2014\n * the carousel pads itself with product-only tiles. */\nfunction parseCarouselItem(el) {\n  var body = text(el, '.jdgm-carousel-item__review-body');\n  if (!body) return null;\n\n  var link = el.querySelector('a.jdgm-carousel-item__product, .jdgm-carousel-item__product a, a.jdgm-carousel-item__full-review-link');\n\n  /* The carousel's rating carries NO `data-score` \u2014 unlike the review widget's,\n   * which does. It states the score in `aria-label` (\"5 stars\") over a run of\n   * `.jdgm-star.jdgm--on` spans. Counting the lit stars is the reading that\n   * cannot be broken by a locale change; the label is the fallback. Reading\n   * data-score here (which is what shipped first) silently made every card five\n   * stars, and a wall of fives is exactly the thing a shopper stops believing. */\n  var ratingEl = el.querySelector('.jdgm-carousel-item__review-rating');\n  var rating = 5;\n  if (ratingEl) {\n    var lit = ratingEl.querySelectorAll('.jdgm-star.jdgm--on').length;\n    if (lit > 0) {\n      rating = lit;\n    } else {\n      var label = parseInt(ratingEl.getAttribute('aria-label'), 10);\n      if (label > 0) rating = label;\n    }\n  }\n\n  /* `jdgm-carousel-item__product-image` is the class on the <img> ITSELF, not on\n   * a wrapper around one. Its `alt` is the product title, which is the only\n   * place the title appears when the carousel's theme drops the product line. */\n  var image = el.querySelector('img.jdgm-carousel-item__product-image, .jdgm-carousel-item__image-wrapper img');\n\n  return {\n    body: body,\n    author: text(el, '.jdgm-carousel-item__reviewer-name'),\n    product: text(el, '.jdgm-carousel-item__product-title') || (image ? image.getAttribute('alt') || '' : ''),\n    href: link ? link.getAttribute('href') || '' : '',\n    // Judge.me lazy-loads, so the real URL is usually on data-src.\n    image: image ? image.getAttribute('data-src') || image.getAttribute('src') || '' : '',\n    rating: rating,\n  };\n}\n\n/* The homepage cards in the source node: the all-reviews feed, else the\n * featured carousel. [] when neither parses. */\nfunction shopCards(source) {\n  var cards = [];\n  source.querySelectorAll('.jdgm-rev').forEach(function (el) {\n    var card = parseShopReview(el);\n    if (card) cards.push(card);\n  });\n  if (!cards.length) {\n    source.querySelectorAll('.jdgm-carousel-item').forEach(function (el) {\n      var card = parseCarouselItem(el);\n      if (card) cards.push(card);\n    });\n  }\n  return cards;\n}\n\nif (typeof window !== 'undefined') {\n  window.WcReviewData = window.WcReviewData || {};\n  window.WcReviewData.judgeme = createJudgeMe;\n  window.WcReviewData.judgemeShopCards = shopCards;\n}\n\n} catch (sfError) {\nif (typeof console !== 'undefined') console.error('[sf-core] behavior \"product-reviews-data-judgeme\" failed to initialise:', sfError);\n}\n})();\n\n/* product-reviews-data-sf.js */\n(function () {\ntry {\n/* product-reviews-data-sf \u2014 the WanderCo SF API as the data source of a reviews\n * band.\n *\n * The UI (product-reviews.js) is shared; the data is not. This source reads the\n * app's own review mirror (co-review-sf-api) and hands the UI the same plain\n * review objects the Judge.me source does \u2014 straight from JSON, no HTML in\n * between. The API does its own filtering, sorting and counting, so every\n * query goes to the server as itself: `page`, `sort`, `rating`,\n * `with_photos`, and `total_count` is the answer.\n *\n * The band falls back to Judge.me (product-reviews-data-judgeme.js) when SF is\n * off for the shop, not configured, failing, or holds no reviews for a product\n * Judge.me has reviews for. That choice is made once per band on mount;\n * afterwards every request of the band goes to the source it picked.\n *\n *   GET {apiUrl}/sf/stats?store_id&product_external_id\n *     \u2192 { count, avg, per_rating: {1..5}, with_media_count }\n *   GET {apiUrl}/sf/reviews?store_id&product_external_id&limit&page&sort\n *       [&rating=1..5][&with_photos=true]\n *     \u2192 { items: [...], total_count, page, per_page, total_pages }\n *   GET {apiUrl}/sf/media?store_id&product_external_id\n *     \u2192 { media: [...], reviews: [...] } \u2014 every review with photos, one request\n *\n * Without `product_external_id`, all three cover the WHOLE shop (every product\n * review, each item carrying its `product: { title, handle }`) \u2014 the reviews\n * page (`data-ds-reviews-feed=\"all\"`). There `/sf/media` pages (`page`,\n * `limit=20`, `rating` \u2192 `total_count`): thousands of photo reviews\n * do not fit one request. The strip always has its own endpoint, so\n * `/sf/reviews` serves the list only.\n *\n * `store_id` is the shop's permanent domain; the product id is sent as a GID.\n * The API URL is injected at build time into `#wc-review-config`\n * (ds-product-reviews section); the shop metafield `wanderco.review_source =\n * judgeme` turns SF off (`window.__WC_REVIEW_SF_SOURCE__`).\n *\n * Exposes `window.WcReviewData.sf(root, { perPage })` \u2192 source, or null when SF\n * must not be used for this band. The homepage band (ds-customer-reviews) does\n * not use SF: its cards stay Judge.me's / the merchant's.\n * Authoring rules: src/behaviors/README.md. No import/export.\n */\n\n/* One request's ceiling. Past it the band would rather use Judge.me than keep\n * the shopper looking at a skeleton. */\nvar SF_TIMEOUT = 4000;\n\n/* A failing SF API is remembered for the tab (sessionStorage) for this long, so\n * every product page during an outage goes straight to Judge.me instead of\n * waiting out the timeout first. */\nvar DOWN_TTL = 5 * 60 * 1000;\nvar DOWN_KEY = 'wc-review-sf-down-until';\n\n/* The band's sort radios (`helpful|desc`, \u2026) as the API names them. */\nvar SORTS = {\n  'helpful|desc': 'helpful',\n  'date|desc': 'newest',\n  'date|asc': 'oldest',\n  'rating|desc': 'rating_desc',\n  'rating|asc': 'rating_asc',\n  'pictures|desc': 'pictures_first',\n};\n\nfunction sfSort(value) {\n  return SORTS[String(value || '')] || 'helpful';\n}\n\nfunction apiBase() {\n  var el = document.getElementById('wc-review-config');\n  if (!el) return null;\n  try {\n    var cfg = JSON.parse(el.textContent);\n    var url = cfg && cfg.apiUrl ? String(cfg.apiUrl).trim() : '';\n    // An un-injected token (`__REVIEW_SF_API_URL__`) is not a URL: SF stays off.\n    return /^https?:\\/\\//.test(url) ? url.replace(/\\/+$/, '') : null;\n  } catch (error) {\n    return null;\n  }\n}\n\nfunction markDown() {\n  try {\n    window.sessionStorage.setItem(DOWN_KEY, String(Date.now() + DOWN_TTL));\n  } catch (error) {\n    // Storage blocked: only this page falls back.\n  }\n}\n\nfunction isDown() {\n  try {\n    return parseInt(window.sessionStorage.getItem(DOWN_KEY), 10) > Date.now();\n  } catch (error) {\n    return false;\n  }\n}\n\n/* GET with a timeout. Resolves to the JSON, or null on a non-2xx, a network\n * error, the timeout or a body that is not JSON. Only the mount-time loads\n * (`load`) mark the API down: a later page / count / strip request failing is\n * that one request's problem \u2014 it must not switch every band in the tab to\n * Judge.me for five minutes. */\nfunction getJson(url) {\n  var controller = typeof AbortController !== 'undefined' ? new AbortController() : null;\n  var timer = setTimeout(function () { if (controller) controller.abort(); }, SF_TIMEOUT);\n  return fetch(url, { headers: { accept: 'application/json' }, signal: controller ? controller.signal : undefined })\n    .then(function (response) { return response.ok ? response.json() : null; })\n    .catch(function () { return null; })\n    .then(function (json) {\n      clearTimeout(timer);\n      return json;\n    });\n}\n\n/* One API item to the review object the UI renders. A rating-only review (no\n * body) is kept: SF's `total_count` and stats count it, so dropping it would\n * leave a page short and the counts promising rows that never show.\n * `withProduct`: the shop-wide feed, where each review names its product\n * (\"Purchased item\"); a PDP review does not. */\nfunction toReview(item, withProduct) {\n  if (!item || item.id == null) return null;\n  var body = item.body ? String(item.body).trim() : '';\n  var code = String(item.country_code || '').toUpperCase();\n  return {\n    id: String(item.id || ''),\n    helpful: parseInt(item.helpful_count, 10) || 0,\n    author: item.reviewer_name || '',\n    variant: '',\n    title: item.title || '',\n    country: /^[A-Z]{2}$/.test(code) ? code : '',\n    rating: parseInt(item.rating, 10) || 0,\n    dateIso: item.external_created_at || '',\n    body: body,\n    photos: (Array.isArray(item.pictures) ? item.pictures : []).map(function (p) {\n      var full = typeof p === 'string' ? p : (p && p.url) || '';\n      var src = typeof p === 'string' ? p : (p && (p.thumbnail_url || p.url)) || '';\n      return { src: src, full: full || src };\n    }).filter(function (p) { return p.src; }),\n    reply: item.reply_body || '',\n    product: withProduct ? toProduct(item.product) : null,\n  };\n}\n\n/* The image is left to the UI, which reads it off `/products/<handle>.js`. */\nfunction toProduct(p) {\n  if (!p || !p.title) return null;\n  return { title: String(p.title), href: p.handle ? '/products/' + encodeURIComponent(p.handle) : '', image: '' };\n}\n\n/* A list response to { rows, total, got }, or null when it is not one. */\nfunction readList(json, withProduct) {\n  if (!json || !Array.isArray(json.items)) return null;\n  var rows = [];\n  json.items.forEach(function (item) {\n    var review = toReview(item, withProduct);\n    if (review) rows.push(review);\n  });\n  var total = parseInt(json.total_count, 10);\n  return { rows: rows, total: isNaN(total) ? NaN : total, got: json.items.length };\n}\n\n/* /sf/stats to the aggregate the UI's summary rail takes. */\nfunction readStats(json) {\n  if (!json || typeof json.count !== 'number') return null;\n  var per = json.per_rating || {};\n  var avg = parseFloat(json.avg);\n  return {\n    averageValue: isNaN(avg) ? null : avg,\n    count: json.count,\n    dist: {\n      5: parseInt(per[5], 10) || 0,\n      4: parseInt(per[4], 10) || 0,\n      3: parseInt(per[3], 10) || 0,\n      2: parseInt(per[2], 10) || 0,\n      1: parseInt(per[1], 10) || 0,\n    },\n  };\n}\n\nfunction createSf(root, opts) {\n  if (!window.__WC_REVIEW_SF_SOURCE__ || isDown()) return null;\n  var base = apiBase();\n  var shop = root.getAttribute('data-ds-reviews-shop');\n  var productId = root.getAttribute('data-ds-reviews-product');\n  var feedAll = root.getAttribute('data-ds-reviews-feed') === 'all';\n  if (!base || !shop || (!feedAll && !productId)) return null;\n  var perPage = opts.perPage;\n  // No product = the whole shop.\n  var target = '?store_id=' + encodeURIComponent(shop) +\n    (feedAll ? '' : '&product_external_id=' + encodeURIComponent('gid://shopify/Product/' + productId));\n\n  /* `q` is the UI's query: { sort, rating, photo }. */\n  function listUrl(n, limit, q) {\n    return base + '/sf/reviews' + target +\n      '&limit=' + limit + '&page=' + n + '&sort=' + sfSort(q.sort) +\n      (q.rating ? '&rating=' + encodeURIComponent(q.rating) : '') +\n      (q.photo ? '&with_photos=true' : '');\n  }\n\n  /* Page 1 that `load` already fetched, handed out once to the list's first\n   * request for that same query instead of asking again. */\n  var primed = {};\n\n  function list(url) {\n    if (primed[url]) {\n      var hit = primed[url];\n      delete primed[url];\n      return Promise.resolve(hit);\n    }\n    return getJson(url).then(function (json) {\n      return readList(json, feedAll);\n    });\n  }\n\n  var data = {\n    name: 'sf',\n    // The API filters by photo (`with_photos`) and counts it.\n    photoServer: true,\n    trustDraftTotals: true,\n    // `aggregate.dist` is SF's own count: the rating rows are redrawn from it.\n    rewritesRatingRows: true,\n    // PDP: `strip` returns every photo review at once (`/sf/media`); the shop-wide\n    // strip pages (see `strip`).\n    /* The strip is paged on the server in both layouts: ten photo reviews at a time on a PDP, twenty on the\n     * reviews page (`/sf/media`, product- or shop-wide) \u2014 the lightbox asks for more as it nears the end. */\n    stripPaged: true,\n    // The API pages as deep as the query goes.\n    maxReviews: Infinity,\n    hasTarget: true,\n    aggregate: null,\n    seed: [],\n    // Raw /sf/stats, kept for the counts it already holds (with media).\n    stats: null,\n\n    /* Star counts come from the stats histogram (written onto the rating rows);\n     * anything with photos is one count request. */\n    countsByRequest: function (f) {\n      return !!f.photo;\n    },\n\n    /* Stats + list page 1 in the band's default sort, together. Resolves true\n     * when both answered, filling `aggregate` and `seed`. */\n    load: function (sort) {\n      var firstUrl = listUrl(1, perPage, { sort: sort });\n      return Promise.all([getJson(base + '/sf/stats' + target), list(firstUrl)]).then(function (r) {\n        var stats = readStats(r[0]);\n        var first = r[1];\n        if (!stats || !first) {\n          // The band's choice failed on SF: remember the outage for the tab.\n          markDown();\n          return false;\n        }\n        data.stats = r[0];\n        data.aggregate = stats;\n        data.seed = first.rows;\n        primed[firstUrl] = first;\n        return true;\n      });\n    },\n\n    page: function (n, q) {\n      return list(listUrl(n, perPage, q));\n    },\n\n    /* The photo strip, from `/sf/media`, one page of photo reviews under the active star filter: ten a page on a\n     * PDP, twenty shop-wide. Both come back with the query's review total, so the lightbox can ask for the next\n     * page as it nears the end; the PDP also gets the product's photo count for its header. */\n    strip: function (n, q) {\n      var f = q || {};\n      var url = base + '/sf/media' + target +\n        '&limit=' + (feedAll ? 20 : 10) + '&page=' + n +\n        (f.rating ? '&rating=' + encodeURIComponent(f.rating) : '');\n      return getJson(url).then(function (json) {\n        if (!json || !Array.isArray(json.reviews)) return null;\n        var rows = [];\n        json.reviews.forEach(function (item) {\n          var review = toReview(item, feedAll);\n          if (review && review.photos.length) rows.push(review);\n        });\n        var total = parseInt(json.total_count, 10);\n        var photos = parseInt(json.photo_count, 10);\n        /* `total` = reviews with media for the query (what paging walks); `got` = reviews this page carried,\n         * video-only ones included, so a page with no photo row is not mistaken for the end; `photos` = every\n         * photo of the product under the active star filter (the PDP's \"Customer photos (N)\" and \"+N\" read it).\n         * Shop-wide has no photo count \u2014 that feed counts reviews. */\n        return {\n          rows: rows,\n          total: isNaN(total) ? rows.length : total,\n          got: json.reviews.length,\n          photos: feedAll || isNaN(photos) ? null : photos,\n        };\n      });\n    },\n\n    /* A single filter the stats already counted costs nothing; a combination\n     * is one request. */\n    count: function (q) {\n      var s = data.stats;\n      if (s && !q.rating && q.photo) {\n        if (typeof s.with_media_count === 'number') return Promise.resolve(s.with_media_count);\n      }\n      return list(listUrl(1, 1, q)).then(function (res) {\n        return res && !isNaN(res.total) ? res.total : null;\n      });\n    },\n  };\n  return data;\n}\n\nif (typeof window !== 'undefined') {\n  window.WcReviewData = window.WcReviewData || {};\n  window.WcReviewData.sf = createSf;\n}\n\n} catch (sfError) {\nif (typeof console !== 'undefined') console.error('[sf-core] behavior \"product-reviews-data-sf\" failed to initialise:', sfError);\n}\n})();\n\n/* product-variants.js */\n(function () {\ntry {\n/* product-variants \u2014 fully client-side PDP variant selection.\n * Reads every variant's option values + availability from a JSON payload\n * embedded once by ds-product-variants.liquid, so changing ANY option (Style,\n * Color, Size, ...) resolves the matching variant and updates selection,\n * cross-option availability, price, gallery and URL instantly \u2014 no server\n * round-trip, even when the change invalidates other options' selections.\n * Progressive enhancement: without JS every value remains a normal variant\n * URL, server-resolved to its best matching variant.\n * Authoring rules: src/behaviors/README.md. No import/export.\n *\n * ponytail: cross-option fallback keeps a later option when the new variant\n * has the same value. Colour is matched by name, not the whole string, so\n * \"Tshirt Forest Green\" survives a switch to \"Hoodie Forest Green\". Size and\n * every other later option still have to match exactly. A colour the new\n * style does not sell falls through to the first available match.\n */\n\nfunction dsParseVariants(picker) {\n  var script = picker.querySelector('script[data-ds-product-variants-json]');\n  if (!script) return [];\n  try {\n    var parsed = JSON.parse(script.textContent);\n    return Array.isArray(parsed) ? parsed : [];\n  } catch (e) {\n    return [];\n  }\n}\n\nfunction dsInitialSelection(picker, variants) {\n  var id = Number(picker.dataset.dsSelectedVariantId);\n  var current = null;\n  for (var i = 0; i < variants.length; i++) {\n    if (variants[i].id === id) { current = variants[i]; break; }\n  }\n  if (!current) current = variants[0];\n  return { 1: current.option1, 2: current.option2, 3: current.option3 };\n}\n\n// \"Tshirt Forest Green\" and \"Hoodie Forest Green\" are the same colour. The\n// style prefix is option1, in both the hyphenated form and the squashed one\n// (\"T-shirt\" -> \"Tshirt\"), matching ds-product-variant-value.liquid.\nfunction dsColorName(value, style) {\n  var cleaned = value == null ? '' : String(value);\n  var prefix = style == null ? '' : String(style);\n  var squash = prefix.replace(/[-\\s]/g, '');\n  function strip(token) {\n    if (!token) return;\n    var at = cleaned.indexOf(token);\n    if (at < 0) return;\n    cleaned = cleaned.slice(0, at) + cleaned.slice(at + token.length);\n  }\n  strip(prefix);\n  if (squash !== prefix) strip(squash);\n  return cleaned.trim().toLowerCase();\n}\n\n// Resolve the variant to select after `position` changes to `value`, keeping\n// as much of the rest of `selection` as possible: options before `position`\n// must match exactly (mirrors the picker's own colour/size scoping), options\n// after are kept on a best-effort basis, available beats unavailable.\n// `colorPosition` is the colour option (0 when the product has none).\n// `styleForColor` is the style the current colour string was chosen under \u2014\n// the style BEFORE this click \u2014 so the name strip uses the right prefix.\nfunction dsPickVariant(variants, selection, position, colorPosition, styleForColor) {\n  var best = null;\n  var bestScore = -1;\n  var i, p, v, score, priorOk;\n  var colorStyle = styleForColor != null ? styleForColor : selection[1];\n\n  for (i = 0; i < variants.length; i++) {\n    v = variants[i];\n    if (v['option' + position] !== selection[position]) continue;\n\n    priorOk = true;\n    for (p = 1; p < position; p++) {\n      if (selection[p] != null && v['option' + p] !== selection[p]) { priorOk = false; break; }\n    }\n    if (!priorOk) continue;\n\n    score = v.available ? 1000 : 0;\n    for (p = position + 1; p <= 3; p++) {\n      if (selection[p] == null) continue;\n      if (colorPosition && p === colorPosition) {\n        var want = dsColorName(selection[p], colorStyle);\n        var got = dsColorName(v['option' + p], v.option1);\n        if (want && want === got) score += 100;\n      } else if (v['option' + p] === selection[p]) {\n        score += 10;\n      }\n    }\n    if (score > bestScore) { bestScore = score; best = v; }\n  }\n\n  if (!best) {\n    for (i = 0; i < variants.length; i++) {\n      if (variants[i]['option' + position] === selection[position]) { best = variants[i]; break; }\n    }\n  }\n  return best;\n}\n\n// Does some available variant carry `value` at `position` given only the\n// options BEFORE `position` (mirrors dsPickVariant's priorOk) \u2014 later\n// options are best-effort, not a filter. Checking ALL positions instead would\n// deadlock: e.g. after Style changes and auto-corrects Color, the old Style\n// value would depend on the new Color matching it, which it never will, so\n// it would stay hidden forever with nothing left to reveal it.\nfunction dsValueAvailable(variants, selection, position, value) {\n  for (var i = 0; i < variants.length; i++) {\n    var v = variants[i];\n    if (v['option' + position] !== value || !v.available) continue;\n\n    var ok = true;\n    for (var p = 1; p < position; p++) {\n      if (selection[p] != null && v['option' + p] !== selection[p]) { ok = false; break; }\n    }\n    if (ok) return true;\n  }\n  return false;\n}\n\nfunction dsSetLinkSelected(link, selected) {\n  // \"true\", the value Liquid renders: toggleAttribute writes \"\", which the\n  // [aria-current='true'] rules (variants.css, bmsm.css) do not match, so a\n  // clicked chip fell back to the 1px rest border.\n  if (selected) link.setAttribute('aria-current', 'true');\n  else link.removeAttribute('aria-current');\n\n  if (link.hasAttribute('data-ds-bmsm-pill')) return;\n\n  if (link.hasAttribute('data-ds-color-swatch')) {\n    link.classList.toggle('sf-border-transparent', selected);\n    link.classList.toggle('sf-outline', selected);\n    link.classList.toggle('sf-outline-ring', selected);\n    link.classList.toggle('sf-outline-secondary', selected);\n    link.classList.toggle('sf-outline-offset-selected', selected);\n    link.classList.toggle('sf-border-default', !selected);\n    link.classList.toggle('hover:sf-border-strong', !selected);\n  } else {\n    // Border width stays fixed (sf-border-emphasis, set server-side); only\n    // colour toggles, so selecting a value never resizes its box and shifts\n    // the row (was device-dependent since it hinged on text-wrap timing).\n    link.classList.toggle('sf-border-action-secondary', selected);\n    link.classList.toggle('sf-text-badge-secondary', selected);\n    link.classList.toggle('sf-border-default', !selected);\n    link.classList.toggle('sf-text-primary', !selected);\n  }\n}\n\nfunction dsSyncDroplistTriggers(picker) {\n  picker.querySelectorAll('[data-ds-variant-droplist]').forEach(function (droplist) {\n    var selectedRow = droplist.querySelector('[data-ds-variants-layout=\"row\"][aria-current]');\n    var trigger = droplist.querySelector('[data-ds-variants-layout=\"trigger\"]');\n    if (!selectedRow || !trigger) return;\n\n    var rowLabel = selectedRow.querySelector('[data-ds-variants-label]');\n    var triggerLabel = trigger.querySelector('[data-ds-variants-label]');\n    if (rowLabel && triggerLabel) triggerLabel.textContent = rowLabel.textContent;\n\n    var rowPrice = selectedRow.querySelector('[data-ds-variants-price]');\n    var triggerPrice = trigger.querySelector('[data-ds-variants-price]');\n    if (triggerPrice) triggerPrice.textContent = rowPrice ? rowPrice.textContent : '';\n\n    trigger.setAttribute('data-ds-variants-value', selectedRow.getAttribute('data-ds-variants-value') || '');\n  });\n}\n\n// wanderprints-theme main, VariantSelector.getUnitPrice: parseInt the option\n// value (\"2 PCS\" -> 2, \"2-pack\" -> 2). More than one piece floors the cents;\n// anything else, including a value that does not start with a number, is the\n// variant price itself.\nfunction dsUnitCents(cents, value) {\n  var quantity = parseInt(value, 10);\n  if (quantity > 1) return Math.floor(cents / quantity);\n  return cents;\n}\n\n// wanderprints-theme main, VariantSelector.findVariant: the first available\n// variant whose set options all match. A later option is not a best-effort\n// colour match; if it is set, it has to be that value.\nfunction dsFindAvailableVariant(variants, selection) {\n  for (var i = 0; i < variants.length; i++) {\n    var variant = variants[i];\n    if (variant.available === false) continue;\n    var ok = true;\n    for (var level = 1; level <= 3; level++) {\n      var value = selection[level];\n      var got = variant['option' + level];\n      if (value && got && got !== value) { ok = false; break; }\n    }\n    if (ok) return variant;\n  }\n  return null;\n}\n\nfunction dsVariantMoney(picker, cents) {\n  var format = (picker.getAttribute('data-ds-product-variants-money') || '${{amount}}').replace(/<[^>]*>/g, '');\n  var amount = (Number(cents) || 0) / 100;\n  var text = amount.toFixed(2);\n  if (/\\{\\{\\s*amount_no_decimals/.test(format)) text = String(Math.round(amount));\n  else if (/\\{\\{\\s*amount_with_comma_separator/.test(format)) text = text.replace('.', ',');\n  return format.replace(/\\{\\{\\s*amount[a-z_]*\\s*\\}\\}/, text);\n}\n\n// Pack pills are rendered once, against the style that was selected at page\n// load. A later Style click changes the real variant price, so each pill's\n// \"/pc\" has to be recomputed from the variant that pairs that pack with the\n// selection now on the page.\nfunction dsSyncUnitPrices(picker, variants, selection) {\n  picker.querySelectorAll('[data-ds-product-variant-option][data-ds-unit-suffix]').forEach(function (wrapper) {\n    var node = wrapper.querySelector('[data-ds-bmsm-pill-bot], [data-ds-variants-unit-price]');\n    if (!node) return;\n    var position = Number(wrapper.dataset.optionPosition);\n    var value = wrapper.dataset.optionValue;\n    if (!position) return;\n\n    var trial = { 1: selection[1], 2: selection[2], 3: selection[3] };\n    trial[position] = value;\n    var variant = dsFindAvailableVariant(variants, trial);\n    if (!variant || !Number.isFinite(variant.priceCents)) return;\n\n    node.textContent = dsVariantMoney(picker, dsUnitCents(variant.priceCents, value)) + (wrapper.getAttribute('data-ds-unit-suffix') || '');\n  });\n}\n\nfunction dsUpdatePrice(productSection, variant) {\n  var price = productSection.querySelector('[data-ds-product-price]');\n  if (!price) return;\n\n  var amount = price.querySelector('[data-ds-price-amount]');\n  if (amount) amount.textContent = variant.price || '';\n\n  var compare = price.querySelector('[data-ds-price-compare]');\n  if (compare) {\n    compare.textContent = variant.compareAtPrice || '';\n    compare.hidden = !variant.compareAtPrice;\n  }\n\n  var discount = price.querySelector('[data-ds-price-discount]');\n  if (discount) {\n    var label = discount.querySelector('[data-ds-badge] .label');\n    if (label) label.textContent = variant.discount || '';\n    discount.hidden = !variant.discount;\n  }\n}\n\nfunction dsUpdateForms(productSection, variantId) {\n  productSection.querySelectorAll('input[name=\"id\"]').forEach(function (input) {\n    input.value = variantId;\n    input.dispatchEvent(new Event('change', { bubbles: true }));\n  });\n}\n\nfunction dsUpdateGallery(productSection, imageId) {\n  if (!imageId) return;\n  var tile = productSection.querySelector('[data-ds-thumbnail-rail-image-id=\"' + imageId + '\"] [data-ds-thumbnail]');\n  if (tile) tile.click();\n}\n\nfunction dsApplyVariant(picker, productSection, variants, selection, variant) {\n  [1, 2, 3].forEach(function (p) {\n    if (variant['option' + p] != null) selection[p] = variant['option' + p];\n  });\n\n  picker.querySelectorAll('[data-ds-product-variant-option]').forEach(function (wrapper) {\n    var position = Number(wrapper.dataset.optionPosition);\n    var value = wrapper.dataset.optionValue;\n    if (!position || value == null) return;\n\n    var selected = selection[position] === value;\n    var available = dsValueAvailable(variants, selection, position, value);\n    wrapper.hidden = !available && !selected;\n\n    var link = wrapper.querySelector('[data-ds-variants], [data-ds-color-swatch]');\n    if (link) dsSetLinkSelected(link, selected);\n  });\n\n  dsSyncDroplistTriggers(picker);\n  dsSyncUnitPrices(picker, variants, selection);\n  dsUpdatePrice(productSection, variant);\n  dsUpdateForms(productSection, variant.id);\n  dsUpdateGallery(productSection, variant.imageId);\n\n  var url = new URL(window.location.href);\n  url.searchParams.set('variant', variant.id);\n  url.searchParams.delete('option_values');\n  url.searchParams.delete('section_id');\n  window.history.replaceState({}, '', url.toString());\n\n  productSection.dispatchEvent(new CustomEvent('variant:change', {\n    detail: { variantId: variant.id, url: url.toString() },\n    bubbles: true,\n  }));\n}\n\nif (!document.__dsProductVariantsBound) {\n  document.__dsProductVariantsBound = true;\n\n  document.addEventListener('click', function (event) {\n    var target = event.target;\n    if (!target || !target.closest) return;\n\n    var link = target.closest(\n      '[data-ds-product-variants] a[data-ds-variants], [data-ds-product-variants] a[data-ds-color-swatch]'\n    );\n    if (!link) return;\n\n    var wrapper = link.closest('[data-ds-product-variant-option]');\n    var picker = link.closest('[data-ds-product-variants]');\n    var productSection = link.closest('[data-ds-product-section]');\n    if (!wrapper || !picker || !productSection) return;\n\n    var position = Number(wrapper.dataset.optionPosition);\n    var value = wrapper.dataset.optionValue;\n    if (!position || value == null) return;\n\n    if (!picker.__dsVariants) picker.__dsVariants = dsParseVariants(picker);\n    var variants = picker.__dsVariants;\n    if (!variants.length) return; // no JSON payload \u2014 fall back to native navigation\n\n    if (!picker.__dsSelection) picker.__dsSelection = dsInitialSelection(picker, variants);\n    var selection = picker.__dsSelection;\n    var previous = selection[position];\n    var styleForColor = selection[1];\n    var colorPosition = Number(picker.dataset.dsColorPosition) || 0;\n    selection[position] = value;\n\n    var variant = dsPickVariant(variants, selection, position, colorPosition, styleForColor);\n    if (!variant) { selection[position] = previous; return; }\n\n    event.preventDefault();\n    dsApplyVariant(picker, productSection, variants, selection, variant);\n  });\n}\n\n} catch (sfError) {\nif (typeof console !== 'undefined') console.error('[sf-core] behavior \"product-variants\" failed to initialise:', sfError);\n}\n})();\n\n/* region-modal.js */\n(function () {\ntry {\n/* region-modal \u2014 opens the shared \"Region and language\" dialog (ds-region-modal)\n * from any [data-ds-locale-open] trigger (header / footer locale selector),\n * filters the country list, and on row-click sets the localization form's\n * country_code and submits it (POST /localization) to switch country + currency.\n *\n * Singleton: ds-region-modal is meant to render once per page, but the header and\n * footer selectors may each include one. On init we keep the first modal in the\n * DOM and remove any extras so every trigger drives the same dialog.\n *\n * Progressive enhancement: without this script the triggers are inert and the\n * modal stays hidden \u2014 nothing breaks, the country just can't be changed from the\n * selector until JS loads.\n *\n * Open/closed is one attribute, [aria-hidden]. src/region-modal.css carries the\n * fade + slide off it so the sheet stays painted while it animates out; toggling\n * a display utility here would cut that off.\n *\n * Authoring rules: src/behaviors/README.md. No import/export.\n */\n\n(function () {\n  if (document.__dsRegionModalBound) return;\n  document.__dsRegionModalBound = true;\n\n  var lastTrigger = null;\n\n  function getModal() {\n    return document.querySelector('[data-ds-region-modal]');\n  }\n\n  function dedupe() {\n    var modals = document.querySelectorAll('[data-ds-region-modal]');\n    for (var i = 1; i < modals.length; i++) {\n      modals[i].parentNode && modals[i].parentNode.removeChild(modals[i]);\n    }\n  }\n\n  function openModal(trigger) {\n    // Sections can mount after this behavior initializes (Theme Editor and\n    // Storybook do this), so enforce the singleton again at interaction time.\n    dedupe();\n    var modal = getModal();\n    if (!modal) return;\n    lastTrigger = trigger || null;\n    modal.setAttribute('aria-hidden', 'false');\n    document.body.style.overflow = 'hidden';\n\n    var search = modal.querySelector('[data-ds-region-search]');\n    if (search) {\n      search.value = '';\n      applyFilter(modal, '');\n      // Focus after paint so the dialog is visible first.\n      window.requestAnimationFrame(function () {\n        search.focus();\n      });\n    }\n  }\n\n  function closeModal() {\n    var modal = getModal();\n    if (!modal) return;\n    modal.setAttribute('aria-hidden', 'true');\n    document.body.style.overflow = '';\n    var dialog = modal.querySelector('[data-ds-region-dialog]');\n    if (dialog) dialog.style.minHeight = '';\n    if (lastTrigger && typeof lastTrigger.focus === 'function') {\n      lastTrigger.focus();\n    }\n    lastTrigger = null;\n  }\n\n  function isOpen(modal) {\n    return !!modal && modal.getAttribute('aria-hidden') === 'false';\n  }\n\n  // Tab must cycle inside the dialog while it is open, otherwise focus walks the\n  // page behind the sheet \u2014 which is still rendered ([aria-hidden] only).\n  function trapTab(e) {\n    var modal = getModal();\n    if (!isOpen(modal)) return;\n    var dialog = modal.querySelector('[data-ds-region-dialog]') || modal;\n    var focusable = Array.prototype.filter.call(\n      dialog.querySelectorAll(\n        'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])',\n      ),\n      function (el) {\n        return el.offsetParent !== null || el === document.activeElement;\n      },\n    );\n    if (!focusable.length) return;\n    var first = focusable[0];\n    var last = focusable[focusable.length - 1];\n    if (e.shiftKey && document.activeElement === first) {\n      e.preventDefault();\n      last.focus();\n    } else if (!e.shiftKey && document.activeElement === last) {\n      e.preventDefault();\n      first.focus();\n    } else if (!dialog.contains(document.activeElement)) {\n      e.preventDefault();\n      first.focus();\n    }\n  }\n\n  function applyFilter(modal, rawQuery) {\n    var query = (rawQuery || '').trim().toLowerCase();\n    var searching = query.length > 0;\n\n    // Lock the card at the height it had before the list shrinks, measured\n    // before rows hide. Clearing the query releases it.\n    var dialog = modal.querySelector('[data-ds-region-dialog]');\n    if (dialog) {\n      if (searching) {\n        if (!dialog.style.minHeight) {\n          dialog.style.minHeight = Math.round(dialog.getBoundingClientRect().height) + 'px';\n        }\n      } else {\n        dialog.style.minHeight = '';\n      }\n    }\n\n    var popularGroup = modal.querySelector('[data-ds-region-group=\"popular\"]');\n    var popularLabel = modal.querySelector('[data-ds-region-popular-label]');\n    var allLabel = modal.querySelector('[data-ds-region-all-label]');\n    var allGroup = modal.querySelector('[data-ds-region-group=\"all\"]');\n    var popularSection = modal.querySelector('[data-ds-region-section=\"popular\"]');\n    var allSection = modal.querySelector('[data-ds-region-section=\"all\"]');\n    var empty = modal.querySelector('[data-ds-region-empty]');\n\n    // ALL COUNTRIES excludes popular rows (SoT rest filter). Search both groups\n    // so popular countries stay findable; hide section titles while querying.\n    if (popularLabel) popularLabel.hidden = searching;\n    if (allLabel) allLabel.hidden = searching;\n\n    var visible = 0;\n    var popularVisible = 0;\n    var allVisible = 0;\n    [popularGroup, allGroup].forEach(function (group) {\n      if (!group) return;\n      var options = group.querySelectorAll('[data-ds-region-option]');\n      options.forEach(function (opt) {\n        var haystack = opt.getAttribute('data-search') || '';\n        var match = !searching || haystack.indexOf(query) !== -1;\n        var li = opt.closest('li') || opt;\n        li.hidden = !match;\n        if (match) {\n          visible++;\n          if (group === popularGroup) popularVisible++;\n          else allVisible++;\n        }\n      });\n    });\n\n    // Hide an empty section shell (title + list) so search results don't leave\n    // a blank POPULAR block when only ALL matches.\n    if (popularSection) popularSection.hidden = searching && popularVisible === 0;\n    if (allSection) allSection.hidden = searching && allVisible === 0;\n    if (!searching) {\n      if (popularSection) popularSection.hidden = false;\n      if (allSection) allSection.hidden = false;\n    }\n\n    if (empty) empty.classList.toggle('sf-hidden', visible > 0);\n  }\n\n  function selectCountry(option) {\n    var modal = getModal();\n    if (!modal || modal.getAttribute('aria-busy') === 'true') return;\n    var form = modal.querySelector('[data-ds-region-form]');\n    var input = modal.querySelector('[data-ds-region-country-input]');\n    var value = option.getAttribute('data-value');\n    if (!form || !input || !value) return;\n    input.value = value;\n    modal.setAttribute('aria-busy', 'true');\n    var pending = modal.querySelector('[data-ds-region-pending]');\n    if (pending) pending.hidden = false;\n    // Paint the scrim before the localization POST locks the page.\n    requestAnimationFrame(function () {\n      if (typeof form.requestSubmit === 'function') form.requestSubmit();\n      else form.submit();\n    });\n  }\n\n  dedupe();\n\n  // Open\n  document.addEventListener('click', function (e) {\n    var trigger = e.target.closest && e.target.closest('[data-ds-locale-open]');\n    if (trigger) {\n      e.preventDefault();\n      openModal(trigger);\n    }\n  });\n\n  // Close (X button) + close on overlay (click outside the dialog) + select row\n  document.addEventListener('click', function (e) {\n    if (!e.target.closest) return;\n\n    if (e.target.closest('[data-ds-region-close]')) {\n      e.preventDefault();\n      closeModal();\n      return;\n    }\n\n    var option = e.target.closest('[data-ds-region-option]');\n    if (option) {\n      e.preventDefault();\n      selectCountry(option);\n      return;\n    }\n\n    var modal = getModal();\n    if (isOpen(modal) && e.target === modal) {\n      closeModal();\n    }\n  });\n\n  // Filter as the user types\n  document.addEventListener('input', function (e) {\n    if (!e.target.closest) return;\n    if (!e.target.matches('[data-ds-region-search]')) return;\n    var modal = e.target.closest('[data-ds-region-modal]');\n    if (modal) applyFilter(modal, e.target.value);\n  });\n\n  // Esc closes; Tab stays inside the dialog\n  document.addEventListener('keydown', function (e) {\n    if (e.key === 'Escape') {\n      var modal = getModal();\n      if (isOpen(modal)) closeModal();\n      return;\n    }\n    if (e.key === 'Tab') trapTab(e);\n  });\n})();\n\n} catch (sfError) {\nif (typeof console !== 'undefined') console.error('[sf-core] behavior \"region-modal\" failed to initialise:', sfError);\n}\n})();\n\n/* review-form.js */\n(function () {\ntry {\n/* review-form \u2014 opens the write-a-review sheet and posts it to Judge.me.\n *\n * This one CAN submit for real, which the rest of the reviews work could not.\n * Judge.me's own widget posts a review to `api.judge.me/api/v1/reviews` with a\n * body of `{url, shop_domain, platform}` plus the fields \u2014 and no api_token\n * anywhere (their `shopParams()` returns exactly those three). So a storefront\n * may post directly: no App Proxy, no secret in the theme. Photos use the same\n * public pair \u2014 GET a presigned target, PUT the file, send the returned key\n * back as `picture_keys[n]`.\n *\n * Moderation is why the success state says \"once our team has checked it over\"\n * rather than \"your review is live\": Judge.me queues new reviews, and a shopper\n * who does not know that submits again.\n *\n * Open/close follows region-modal.js exactly \u2014 [aria-hidden] on the overlay\n * drives both the animation (review-form.css) and the a11y tree, focus is\n * trapped while open and handed back to whatever opened it on close.\n *\n * Authoring rules: src/behaviors/README.md. No import/export.\n */\n\nvar FORM = '[data-ds-review-form]';\nvar TRIGGER = '[data-ds-reviews-write]';\nvar API = 'https://api.judge.me/api/v1/reviews';\nvar PRESIGN = 'https://api.judge.me/api/v1/pictures/presigned_data';\n// The ceiling in Judge.me's own presigned policy (content-length-range).\nvar MAX_PHOTO_BYTES = 10 * 1024 * 1024;\n\nvar FOCUSABLE =\n  'a[href],button:not([disabled]),textarea:not([disabled]),input:not([disabled]),select:not([disabled]),[tabindex]:not([tabindex=\"-1\"])';\n\nfunction visible(el) {\n  return !!(el.offsetWidth || el.offsetHeight || el.getClientRects().length);\n}\n\nfunction setup(overlay) {\n  var dialog = overlay.querySelector('[data-ds-review-form-dialog]');\n  var form = overlay.querySelector('[data-ds-review-form-element]');\n  var body = overlay.querySelector('[data-ds-review-form-body]');\n  var counter = overlay.querySelector('[data-ds-review-form-counter]');\n  var submitBox = overlay.querySelector('[data-ds-review-form-submit]');\n  var errorBox = overlay.querySelector('[data-ds-review-form-error]');\n  var done = overlay.querySelector('[data-ds-review-form-done]');\n  if (!dialog || !form || !body) return;\n\n  var submitOff = overlay.querySelector('[data-ds-review-form-submit-off]');\n  var submitOn = overlay.querySelector('[data-ds-review-form-submit-on]');\n  var submit = submitOn && submitOn.querySelector('button');\n  var max = parseInt(overlay.getAttribute('data-ds-review-form-max'), 10) || 200;\n  var min = parseInt(overlay.getAttribute('data-ds-review-form-min'), 10) || 10;\n  var maxPhotos = parseInt(overlay.getAttribute('data-ds-review-form-photos'), 10) || 5;\n  var files = [];\n  var opener = null;\n  // Copy the behaviour needs but must not contain: a behaviour cannot call `| t`,\n  // so every string rides in on an attribute.\n  var copy = {\n    helper: overlay.getAttribute('data-ds-review-form-helper') || '',\n    helperShort: overlay.getAttribute('data-ds-review-form-helper-short') || '',\n    removePhoto: overlay.getAttribute('data-ds-review-form-photo-remove') || '',\n    photosFull: overlay.getAttribute('data-ds-review-form-photos-full') || '',\n    photoTooBig: overlay.getAttribute('data-ds-review-form-photo-too-big') || '',\n  };\n\n  /* \u2500\u2500 open / close \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n  function open(trigger) {\n    opener = trigger || null;\n    overlay.setAttribute('aria-hidden', 'false');\n    // Lock the page behind the modal on <body>, the same handle region-modal.js\n    // and the cart/header drawers use \u2014 locking <html> here left the page\n    // scrolling underneath on the storefronts this actually ships to.\n    document.body.style.overflow = 'hidden';\n    var first = dialog.querySelector(FOCUSABLE);\n    if (first) first.focus();\n  }\n\n  function close() {\n    overlay.setAttribute('aria-hidden', 'true');\n    document.body.style.overflow = '';\n    if (opener && typeof opener.focus === 'function') opener.focus();\n    opener = null;\n  }\n\n  function isOpen() {\n    return overlay.getAttribute('aria-hidden') === 'false';\n  }\n\n  // Any write-a-review control on the page opens this one sheet \u2014 the band's\n  // button and the empty state's both carry the same hook. The hook sits on a\n  // full-width wrapper (ds-button has no attributes slot), so only a click that\n  // actually lands on the button/link inside opens \u2014 not the empty flex space.\n  document.addEventListener('click', function (event) {\n    var trigger = event.target.closest(TRIGGER);\n    if (!trigger || overlay.contains(trigger)) return;\n    var control = event.target.closest('button, a');\n    if (!control || !trigger.contains(control)) return;\n    event.preventDefault();\n    open(control);\n  });\n\n  overlay.addEventListener('click', function (event) {\n    // Clicking the scrim closes; clicking anywhere inside the card does not.\n    if (event.target === overlay) {\n      close();\n      return;\n    }\n    if (event.target.closest('[data-ds-review-form-close], [data-ds-review-form-cancel]')) {\n      event.preventDefault();\n      close();\n    }\n  });\n\n  document.addEventListener('keydown', function (event) {\n    if (!isOpen()) return;\n    if (event.key === 'Escape') {\n      close();\n      return;\n    }\n    if (event.key !== 'Tab') return;\n    // Tab must cycle inside the dialog, or focus walks the page behind it.\n    var stops = Array.prototype.filter.call(dialog.querySelectorAll(FOCUSABLE), visible);\n    if (!stops.length) return;\n    var first = stops[0];\n    var last = stops[stops.length - 1];\n    if (event.shiftKey && document.activeElement === first) {\n      event.preventDefault();\n      last.focus();\n    } else if (!event.shiftKey && document.activeElement === last) {\n      event.preventDefault();\n      first.focus();\n    }\n  });\n\n  /* \u2500\u2500 stars \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n  /* Fill every star up to the chosen one. Both glyphs are already in the label\n   * (see ds-review-form.liquid) \u2014 the colour stays orange throughout and only\n   * the hollow/solid pair swaps, which is what Figma draws. */\n  function paintStars(value) {\n    overlay.querySelectorAll('[data-ds-review-form-star-label]').forEach(function (label) {\n      var index = parseInt(label.getAttribute('data-ds-review-form-star-label'), 10);\n      var hollow = label.querySelector('[data-ds-review-form-star-hollow]');\n      var solid = label.querySelector('[data-ds-review-form-star-solid]');\n      if (hollow) hollow.classList.toggle('sf-hidden', index <= value);\n      if (solid) solid.classList.toggle('sf-hidden', index > value);\n    });\n  }\n\n  overlay.addEventListener('change', function (event) {\n    var star = event.target.closest('[data-ds-review-form-star]');\n    if (!star) return;\n    paintStars(parseInt(star.value, 10) || 0);\n    refresh();\n  });\n\n  /* \u2500\u2500 body counter \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n  var helper = overlay.querySelector('[data-ds-review-form-body-helper]');\n\n  body.addEventListener('input', function () {\n    if (counter) counter.textContent = body.value.length + '/' + max;\n    // Figma's desktop frame shows the budget and its mobile frame shows the\n    // floor; both are true, so the helper says whichever one the shopper is\n    // currently failing. Silent while the field is still untouched.\n    if (helper) {\n      var length = body.value.trim().length;\n      helper.textContent = length > 0 && length < min ? copy.helperShort : copy.helper;\n    }\n    refresh();\n  });\n\n  /* \u2500\u2500 photos \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n  var fileInput = overlay.querySelector('[data-ds-review-form-file]');\n  var thumbs = overlay.querySelector('[data-ds-review-form-thumbs]');\n\n  // The dashed dropzone is a <label> wrapping this screen-reader-only file input\n  // (ds-review-form.liquid), so a click anywhere in the box opens the native\n  // picker with no JS \u2014 nothing to bind here, only its `change` to read below.\n\n  if (fileInput) {\n    fileInput.addEventListener('change', function () {\n      var overflowed = false;\n      var oversized = false;\n      Array.prototype.forEach.call(fileInput.files || [], function (file) {\n        if (files.length >= maxPhotos) {\n          overflowed = true;\n          return;\n        }\n        // Judge.me's presigned policy carries content-length-range 0..10485760,\n        // so a bigger file is rejected by S3 with nothing useful to show for it.\n        if (file.size > MAX_PHOTO_BYTES) {\n          oversized = true;\n          return;\n        }\n        files.push(file);\n      });\n      fileInput.value = '';\n      // Dropping the extras silently looks like the picker failed.\n      if ((overflowed || oversized) && errorBox) {\n        errorBox.textContent = oversized ? copy.photoTooBig : copy.photosFull;\n        errorBox.classList.remove('sf-hidden');\n      }\n      renderThumbs();\n      refresh();\n    });\n  }\n\n  function renderThumbs() {\n    if (!thumbs) return;\n    thumbs.innerHTML = '';\n    thumbs.classList.toggle('sf-hidden', files.length === 0);\n    thumbs.classList.toggle('sf-flex', files.length > 0);\n    files.forEach(function (file, index) {\n      var item = document.createElement('li');\n      item.className = 'sf-relative sf-h-review-photo-strip sf-w-review-photo-strip sf-overflow-hidden sf-rounded-md sf-bg-subtle';\n      var img = document.createElement('img');\n      img.className = 'sf-h-full sf-w-full sf-object-cover';\n      img.alt = '';\n      img.src = URL.createObjectURL(file);\n      item.appendChild(img);\n      var remove = document.createElement('button');\n      remove.type = 'button';\n      remove.className = 'sf-absolute sf-right-1 sf-top-1 sf-inline-flex sf-h-6 sf-w-6 sf-cursor-pointer sf-items-center sf-justify-center sf-rounded-circle sf-border-0 sf-bg-inverse sf-text-inverse';\n      remove.textContent = '\u00D7';\n      remove.setAttribute('aria-label', copy.removePhoto);\n      remove.setAttribute('data-ds-review-form-remove', String(index));\n      item.appendChild(remove);\n      thumbs.appendChild(item);\n    });\n  }\n\n  overlay.addEventListener('click', function (event) {\n    var remove = event.target.closest('[data-ds-review-form-remove]');\n    if (!remove) return;\n    event.preventDefault();\n    files.splice(parseInt(remove.getAttribute('data-ds-review-form-remove'), 10), 1);\n    renderThumbs();\n  });\n\n  /* Judge.me's public two-step, matched to what their own uploader actually\n   * sends (widget_form.js `_prepareFormData`). The shape is easy to get wrong\n   * and fails silently, which is what happened here first: the presigned\n   * response has NO `fields.key` \u2014 the key is built from a top-level\n   * `key_prefix` plus the filename, appended BEFORE the other fields, with the\n   * file last. Reading `fields.key` threw, the catch swallowed it, and every\n   * photo was quietly dropped from the review.\n   *\n   * S3 answers 201 (the policy sets success_action_status), and the key we\n   * built is what Judge.me wants back as `picture_keys[n]`.\n   */\n\n  // Their normalisation, kept identical so a name we accept is a name S3's\n  // policy accepts: strip anything outside [a-z0-9-_.], and if that leaves no\n  // basename at all, stand a timestamp in for it.\n  function safeName(file) {\n    var stripped = String(file.name || '').replace(/[^a-z0-9-_.]/gi, '');\n    var parts = stripped.split('.');\n    var ext = parts.pop();\n    if (!parts.join('').length) stripped = Date.now() + '.' + ext;\n    return encodeURIComponent(stripped);\n  }\n\n  function uploadPhotos() {\n    if (!files.length) return Promise.resolve([]);\n    var shop = overlay.getAttribute('data-ds-review-form-shop');\n    var query =\n      '?url=' + encodeURIComponent(shop) +\n      '&shop_domain=' + encodeURIComponent(shop) +\n      '&platform=shopify';\n\n    return fetch(PRESIGN + query)\n      .then(function (r) { return r.ok ? r.json() : null; })\n      .then(function (presigned) {\n        if (!presigned || !presigned.url || !presigned.key_prefix) return [];\n        return Promise.all(\n          files.map(function (file) {\n            var key = presigned.key_prefix + safeName(file);\n            var data = new FormData();\n            data.append('key', key);\n            Object.keys(presigned.fields || {}).forEach(function (field) {\n              data.append(field, presigned.fields[field]);\n            });\n            data.append('file', file);\n            return fetch(presigned.url, { method: 'POST', body: data })\n              .then(function (r) { return r.ok ? key : null; })\n              .catch(function () { return null; });\n          }),\n        ).then(function (keys) { return keys.filter(Boolean); });\n      })\n      .catch(function () { return []; });\n  }\n\n  /* \u2500\u2500 validity \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n  function rating() {\n    var picked = overlay.querySelector('[data-ds-review-form-star]:checked');\n    return picked ? parseInt(picked.value, 10) : 0;\n  }\n\n  function refresh() {\n    var name = overlay.querySelector('[data-ds-review-form-name]');\n    var email = overlay.querySelector('[data-ds-review-form-email]');\n    var ready = !!(\n      rating() > 0 &&\n      body.value.trim().length >= min &&\n      name && name.value.trim() &&\n      email && email.value.trim() && email.checkValidity()\n    );\n    if (submitOff) submitOff.classList.toggle('sf-hidden', ready);\n    if (submitOn) submitOn.classList.toggle('sf-hidden', !ready);\n  }\n\n  form.addEventListener('input', refresh);\n\n  /* \u2500\u2500 submit \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n  form.addEventListener('submit', function (event) {\n    event.preventDefault();\n    if (!submit || submit.disabled) return;\n\n    var shop = overlay.getAttribute('data-ds-review-form-shop');\n    var productId = overlay.getAttribute('data-ds-review-form-product');\n    var name = overlay.querySelector('[data-ds-review-form-name]');\n    var email = overlay.querySelector('[data-ds-review-form-email]');\n\n    submit.disabled = true;\n    if (errorBox) errorBox.classList.add('sf-hidden');\n\n    uploadPhotos().then(function (keys) {\n      var payload = new FormData();\n      payload.append('url', shop);\n      payload.append('shop_domain', shop);\n      payload.append('platform', 'shopify');\n      payload.append('id', productId);\n      payload.append('rating', String(rating()));\n      payload.append('body', body.value.trim());\n      payload.append('name', name ? name.value.trim() : '');\n      payload.append('email', email ? email.value.trim() : '');\n      keys.forEach(function (key, index) {\n        payload.append('picture_keys[' + index + ']', key);\n      });\n\n      return fetch(API, { method: 'POST', body: payload })\n        .then(function (response) {\n          if (!response.ok) throw new Error('rejected');\n          form.classList.add('sf-hidden');\n          if (done) {\n            done.classList.remove('sf-hidden');\n            done.classList.add('sf-flex');\n          }\n        })\n        .catch(function () {\n          if (errorBox) {\n            errorBox.textContent = overlay.getAttribute('data-ds-review-form-error-text') || errorBox.textContent;\n            errorBox.classList.remove('sf-hidden');\n          }\n          submit.disabled = false;\n        });\n    });\n  });\n\n  paintStars(0);\n  refresh();\n}\n\nfunction init(scope) {\n  (scope || document).querySelectorAll(FORM).forEach(function (overlay) {\n    if (overlay.dataset.dsBound) return;\n    overlay.dataset.dsBound = '1';\n    setup(overlay);\n  });\n}\n\nif (document.readyState === 'loading') {\n  document.addEventListener('DOMContentLoaded', function () { init(); });\n} else {\n  init();\n}\n\ndocument.addEventListener('shopify:section:load', function (event) {\n  init(event.target);\n});\n\n} catch (sfError) {\nif (typeof console !== 'undefined') console.error('[sf-core] behavior \"review-form\" failed to initialise:', sfError);\n}\n})();\n\n/* save-for-later.js */\n(function () {\ntry {\n/* save-for-later \u2014 the cart's \"Save for later\" list.\n *\n * WHERE IT LIVES (DEFAULT0260): localStorage, not a customer metafield. A theme\n * has no backend, and a metafield can only be written for a logged-in customer\n * through the Admin API \u2014 impossible for the guest who is most of a POD store's\n * traffic (Baymard: save-for-later is a cart-abandonment tool, so it has to work\n * before sign-in). localStorage keeps it client-only and guest-first. The\n * trade-off, accepted here: the list is per-browser, not cross-device, and is\n * cleared with site data. The cart itself stays the source of truth for the\n * purchase; this is only a holding shelf.\n *\n * Snapshot: saving a line reads what the cart page renders \u2014 title, image,\n * price, variant, and the VISIBLE personalization (line item properties). Hidden\n * `_`-properties are not in the DOM (Liquid drops them), so Move to Cart cannot\n * restore them; a proof URL a POD app stamped as `_proof` would be re-generated\n * by that app on the new line, not carried. Documented, not silent.\n *\n * Moving to cart re-adds by variant id (the part of the line key before ':')\n * with the saved properties, then reloads so the line list and Order Summary\n * reflect the new line \u2014 no partial client re-render to drift from the server.\n *\n * Reads/writes the grid rendered by snippets/ds-cart-saved.liquid, cloning its\n * <template> per entry. Binds data-ds-* only. Authoring rules: README.md.\n */\nvar SFL_CART_ITEM = '[data-ds-cart-item]';\n\nfunction sflKey() {\n  var host = '';\n  try { host = window.location.host || ''; } catch (e) { host = ''; }\n  return 'ds:cart:saved:' + host;\n}\n\nfunction sflRead() {\n  try {\n    var raw = window.localStorage.getItem(sflKey());\n    var list = raw ? JSON.parse(raw) : [];\n    return Array.isArray(list) ? list : [];\n  } catch (e) {\n    return [];\n  }\n}\n\nfunction sflWrite(list) {\n  try {\n    window.localStorage.setItem(sflKey(), JSON.stringify(list));\n  } catch (e) {\n    /* private mode / quota \u2014 the shelf is a convenience, so fail quiet */\n  }\n}\n\nfunction sflUrl(path) {\n  var root = window.Shopify && window.Shopify.routes && window.Shopify.routes.root;\n  return (root || '/') + path;\n}\n\n// The line key is `<variant_id>:<properties_hash>`; the id is what /cart/add.js\n// wants back.\nfunction sflVariantId(key) {\n  return String(key || '').split(':')[0];\n}\n\nfunction sflText(root, selector) {\n  var el = root.querySelector(selector);\n  return el ? el.textContent.trim() : '';\n}\n\nfunction sflSnapshot(line) {\n  var key = line.getAttribute('data-ds-cart-item-key') || '';\n  var img = line.querySelector('img[src]');\n  var titleEl = line.querySelector('[data-ds-cart-item-title]');\n  var options = [];\n  // The chosen options render as the caption spans directly under the title.\n  var optionWrap = titleEl && titleEl.parentNode ? titleEl.parentNode.querySelector('div') : null;\n  if (optionWrap) {\n    optionWrap.querySelectorAll('span').forEach(function (span) {\n      var text = span.textContent.trim();\n      if (text) options.push(text);\n    });\n  }\n  var properties = {};\n  line.querySelectorAll('[data-ds-cart-item-properties] > div').forEach(function (row) {\n    var dt = row.querySelector('dt');\n    var dd = row.querySelector('dd');\n    if (dt && dd) properties[dt.textContent.trim()] = dd.textContent.trim();\n  });\n  return {\n    id: sflVariantId(key),\n    key: key,\n    title: titleEl ? titleEl.textContent.trim() : '',\n    url: titleEl && titleEl.getAttribute('href') ? titleEl.getAttribute('href') : '',\n    image: img ? img.getAttribute('src') : '',\n    price: sflText(line, '[data-ds-price-amount]'),\n    compareAt: sflText(line, '[data-ds-price-compare]'),\n    variant: options.join(' / '),\n    properties: properties,\n  };\n}\n\nfunction sflAddEntry(entry) {\n  if (!entry.id) return;\n  var list = sflRead().filter(function (e) { return e.key !== entry.key; });\n  list.unshift(entry);\n  sflWrite(list);\n}\n\nfunction sflRemoveEntry(key) {\n  sflWrite(sflRead().filter(function (e) { return e.key !== key; }));\n}\n\n// Fill one cloned card from an entry, toggling the optional rows off when the\n// entry has nothing for them.\nfunction sflFillCard(card, entry) {\n  card.setAttribute('data-ds-cart-favorite-key', entry.key);\n  var title = card.querySelector('[data-ds-cart-favorite-title]');\n  if (title) {\n    title.textContent = entry.title || '';\n    if (entry.url && title.tagName === 'A') title.setAttribute('href', entry.url);\n    else if (title.tagName === 'A') title.removeAttribute('href');\n  }\n  var img = card.querySelector('img');\n  if (img) {\n    if (entry.image) { img.setAttribute('src', entry.image); img.setAttribute('alt', entry.title || ''); }\n  }\n  var toggle = function (selector, value) {\n    var el = card.querySelector(selector);\n    if (!el) return;\n    if (value) { el.textContent = value; el.hidden = false; el.style.display = ''; }\n    else { el.hidden = true; el.style.display = 'none'; }\n  };\n  toggle('[data-ds-cart-favorite-variant]', entry.variant);\n  toggle('[data-ds-cart-favorite-type]', entry.productType);\n  var amount = card.querySelector('[data-ds-price-amount]');\n  if (amount) amount.textContent = entry.price || '';\n  var compare = card.querySelector('[data-ds-price-compare]');\n  if (compare) {\n    if (entry.compareAt) { compare.textContent = entry.compareAt; compare.hidden = false; compare.style.display = ''; }\n    else { compare.hidden = true; compare.style.display = 'none'; }\n  }\n  var unit = card.querySelector('[data-ds-price-unit]');\n  if (unit) { unit.hidden = !entry.unit; unit.style.display = entry.unit ? '' : 'none'; if (entry.unit) unit.textContent = entry.unit; }\n  return card;\n}\n\nfunction sflRender() {\n  var section = document.querySelector('[data-ds-cart-saved]');\n  if (!section) return;\n  var grid = section.querySelector('[data-ds-cart-saved-grid]');\n  var template = section.querySelector('[data-ds-cart-saved-template]');\n  if (!grid || !template) return;\n  var list = sflRead();\n  var count = section.querySelector('[data-ds-cart-saved-count]');\n  if (count) count.textContent = String(list.length);\n\n  if (!list.length) {\n    section.hidden = true;\n    grid.textContent = '';\n    return;\n  }\n  section.hidden = false;\n\n  var pageSize = Number(section.getAttribute('data-ds-cart-saved-page-size')) || 4;\n  var expanded = section.getAttribute('data-ds-cart-saved-expanded') === 'true';\n  var shown = expanded ? list : list.slice(0, pageSize);\n\n  grid.textContent = '';\n  shown.forEach(function (entry) {\n    var clone = template.content.firstElementChild.cloneNode(true);\n    sflFillCard(clone, entry);\n    grid.appendChild(clone);\n  });\n\n  var moreWrap = section.querySelector('[data-ds-cart-saved-more-wrap]');\n  if (moreWrap) moreWrap.hidden = expanded || list.length <= pageSize;\n}\n\nfunction sflSave(line) {\n  var key = line.getAttribute('data-ds-cart-item-key');\n  if (!key) return;\n  sflAddEntry(sflSnapshot(line));\n  // Take the line out of the cart. Saving is intentional \u2014 no undo window; the\n  // way back is Move to Cart \u2014 so this commits straight away.\n  fetch(sflUrl('cart/change.js'), {\n    method: 'POST',\n    headers: { Accept: 'application/json', 'Content-Type': 'application/json', 'X-Requested-With': 'XMLHttpRequest' },\n    body: JSON.stringify({ id: key, quantity: 0 }),\n  })\n    .then(function (res) { return res.json().then(function (data) { return { ok: res.ok, data: data }; }); })\n    .then(function (result) {\n      if (!result.ok) {\n        // Put the entry back on the shelf's failure: the line is still in the cart.\n        sflRemoveEntry(key);\n        document.dispatchEvent(new CustomEvent('ds:cart:error', { detail: result.data }));\n        return;\n      }\n      if (line.parentNode) line.parentNode.removeChild(line);\n      document.dispatchEvent(new CustomEvent('ds:cart:updated', { detail: result.data }));\n      sflRender();\n    })\n    .catch(function (error) {\n      sflRemoveEntry(key);\n      document.dispatchEvent(new CustomEvent('ds:cart:error', { detail: { error: String(error) } }));\n    });\n}\n\nfunction sflMoveToCart(card) {\n  var key = card.getAttribute('data-ds-cart-favorite-key');\n  var entry = sflRead().filter(function (e) { return e.key === key; })[0];\n  if (!entry || !entry.id) return;\n  card.setAttribute('aria-busy', 'true');\n  fetch(sflUrl('cart/add.js'), {\n    method: 'POST',\n    headers: { Accept: 'application/json', 'Content-Type': 'application/json', 'X-Requested-With': 'XMLHttpRequest' },\n    body: JSON.stringify({ id: entry.id, quantity: 1, properties: entry.properties || {} }),\n  })\n    .then(function (res) { return res.json().then(function (data) { return { ok: res.ok, data: data }; }); })\n    .then(function (result) {\n      card.removeAttribute('aria-busy');\n      if (!result.ok) {\n        document.dispatchEvent(new CustomEvent('ds:cart:error', { detail: result.data }));\n        return;\n      }\n      sflRemoveEntry(key);\n      document.dispatchEvent(new CustomEvent('ds:cart:moved', { detail: { id: entry.id } }));\n      // Reload so the line list and Order Summary come from the server, not a\n      // client guess. Tests set __dsSflNoReload to inspect the call instead.\n      if (!window.__dsSflNoReload) window.location.reload();\n      else sflRender();\n    })\n    .catch(function (error) {\n      card.removeAttribute('aria-busy');\n      document.dispatchEvent(new CustomEvent('ds:cart:error', { detail: { error: String(error) } }));\n    });\n}\n\nif (!document.__dsSflBound) {\n  document.__dsSflBound = true;\n\n  document.addEventListener('click', function (event) {\n    var target = event.target && event.target.closest ? event.target : null;\n    if (!target) return;\n\n    var save = target.closest('[data-ds-cart-item-save]');\n    if (save) {\n      var line = save.closest(SFL_CART_ITEM);\n      if (line) { event.preventDefault(); sflSave(line); }\n      return;\n    }\n\n    var move = target.closest('[data-ds-cart-favorite-move]');\n    if (move) {\n      var card = move.closest('[data-ds-cart-favorite]');\n      if (card) { event.preventDefault(); sflMoveToCart(card); }\n      return;\n    }\n\n    var remove = target.closest('[data-ds-cart-favorite-remove]');\n    if (remove) {\n      var rcard = remove.closest('[data-ds-cart-favorite]');\n      if (rcard) { event.preventDefault(); sflRemoveEntry(rcard.getAttribute('data-ds-cart-favorite-key')); sflRender(); }\n      return;\n    }\n\n    var more = target.closest('[data-ds-cart-saved-more]');\n    if (more) {\n      var section = more.closest('[data-ds-cart-saved]');\n      if (section) { event.preventDefault(); section.setAttribute('data-ds-cart-saved-expanded', 'true'); sflRender(); }\n    }\n  });\n\n  sflRender();\n}\n\n} catch (sfError) {\nif (typeof console !== 'undefined') console.error('[sf-core] behavior \"save-for-later\" failed to initialise:', sfError);\n}\n})();\n\n/* scroll-top.js */\n(function () {\ntry {\n/* scroll-top \u2014 reveals the site-wide floating \"scroll to top\" FAB once the page\n * has been scrolled past a threshold, and smooth-scrolls to the top on click.\n * Binds [data-ds-scroll-top] (see snippets/ds-scroll-to-top.liquid). Each button\n * carries its own reveal threshold in data-ds-scroll-top (px, merchant setting).\n *\n * Progressive enhancement: the button server-renders hidden (inline display:none)\n * and this reveals it on scroll, so with no JS it never shows \u2014 a scroll-to-top\n * control can only smooth-scroll with JS anyway (same reveal-on-scroll shape as\n * sticky-bar.js). Honours prefers-reduced-motion by jumping instead of animating.\n *\n * Authoring rules: src/behaviors/README.md. No import/export.\n */\n\nif (!document.__dsScrollTopBound) {\n  document.__dsScrollTopBound = true;\n\n  // Toggle each FAB's inline display in step with the scroll position. Inline\n  // display (not a class) so it overrides the responsive device-visibility\n  // utilities the button carries; '' restores those, 'none' hides.\n  function dsScrollTopUpdate() {\n    var buttons = document.querySelectorAll('[data-ds-scroll-top]');\n    if (!buttons.length) return;\n    var y = window.pageYOffset || document.documentElement.scrollTop || 0;\n    for (var i = 0; i < buttons.length; i++) {\n      var after = Number(buttons[i].getAttribute('data-ds-scroll-top')) || 0;\n      buttons[i].style.display = y > after ? '' : 'none';\n    }\n  }\n\n  // Delegate on document so a button added by a Theme-Editor section re-render is\n  // handled without re-binding.\n  document.addEventListener('click', function (event) {\n    var trigger = event.target.closest('[data-ds-scroll-top]');\n    if (!trigger) return;\n    event.preventDefault();\n    var reduce =\n      window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n    window.scrollTo({ top: 0, behavior: reduce ? 'auto' : 'smooth' });\n  });\n\n  window.addEventListener('scroll', dsScrollTopUpdate, { passive: true });\n  window.addEventListener('resize', dsScrollTopUpdate);\n  // Theme editor re-adds/reorders sections without a full reload.\n  document.addEventListener('shopify:section:load', dsScrollTopUpdate);\n\n  dsScrollTopUpdate();\n}\n\n} catch (sfError) {\nif (typeof console !== 'undefined') console.error('[sf-core] behavior \"scroll-top\" failed to initialise:', sfError);\n}\n})();\n\n/* seasonal-feature.js */\n(function () {\ntry {\n/* seasonal-feature \u2014 squares the seasonal feature tile to the product card\n * beside it, on both tablets.\n *\n * Between tablet-mini (744) and lg the inline_feature band puts the campaign\n * tile beside a track of three product cards, and the tile is a square as\n * tall as one card \u2014 image, title, rating and price (prototype EventRow.jsx\n * reads the first cell's offsetHeight in a layout effect). A card's height\n * depends on its width, which depends on how much room the tile leaves, so no\n * single width in CSS lands it at every size. This measures the first card\n * and sets the tile's width to that height; the tile's own square image then\n * makes its height match. Setting the width moves the cards (a narrower tile\n * leaves them more room), so it re-measures and re-assigns until the two\n * agree within a pixel, and checks once more on the next frame in case the\n * last reflow moved the card again.\n *\n * Below 744 the tile is a banner over the scroller and from lg a fixed 300\n * tile; both are pure CSS, so the inline width is removed there. Without this\n * file the tablet tile falls back to --seasonal-feature-tile.\n *\n * The range is read from --breakpoint-tablet-mini / --breakpoint-lg rather\n * than typed here, so a token change moves the JS with the CSS.\n *\n * Authoring rules: src/behaviors/README.md. No import/export.\n */\n\nvar SEASONAL_ROOT = '[data-ds-seasonal-showcase]';\n\nfunction seasonalRange() {\n  var style = window.getComputedStyle(document.documentElement);\n  var from = style.getPropertyValue('--breakpoint-tablet-mini').trim() || '744px';\n  var until = parseFloat(style.getPropertyValue('--breakpoint-lg').trim() || '1024') - 0.02;\n  return '(min-width: ' + from + ') and (max-width: ' + until + 'px)';\n}\n\nfunction bindSeasonalFeature(root) {\n  if (root.dataset.dsSeasonalFeatureBound) return;\n  var tile = root.querySelector('[data-ds-seasonal-tile]');\n  var track = root.querySelector('[data-ds-product-rail-track]');\n  if (!tile || !track || !track.firstElementChild) return;\n  root.dataset.dsSeasonalFeatureBound = '1';\n\n  var query = window.matchMedia(seasonalRange());\n  var queued = false;\n  var retries = 0;\n\n  // One pending frame at a time. Nothing is dropped: every trigger that\n  // arrives while a frame is queued is covered by that frame's measurement.\n  function schedule() {\n    if (queued) return;\n    queued = true;\n    requestAnimationFrame(function () {\n      queued = false;\n      fit();\n    });\n  }\n\n  function fit() {\n    if (!query.matches) {\n      tile.style.removeProperty('width');\n      retries = 0;\n      return;\n    }\n    var card = track.firstElementChild;\n    if (!card) return;\n    // Each read forces a fresh layout, so every pass sees the card as the\n    // previous assignment left it.\n    for (var pass = 0; pass < 8; pass += 1) {\n      var side = card.offsetHeight;\n      if (!side || Math.abs(tile.offsetWidth - side) < 1) break;\n      tile.style.width = side + 'px';\n    }\n    if (Math.abs(tile.offsetWidth - card.offsetHeight) >= 1 && retries < 4) {\n      retries += 1;\n      schedule();\n    } else {\n      retries = 0;\n    }\n  }\n\n  fit();\n  if (query.addEventListener) query.addEventListener('change', schedule);\n  else if (query.addListener) query.addListener(schedule);\n  if (typeof ResizeObserver !== 'undefined') {\n    // The band's width (viewport) and the card's height (fonts, images) move\n    // the answer. A settled fit writes nothing, so its own echo ends here.\n    var observer = new ResizeObserver(schedule);\n    observer.observe(root);\n    observer.observe(track.firstElementChild);\n  } else {\n    window.addEventListener('resize', schedule);\n  }\n}\n\nfunction initSeasonalFeatures(scope) {\n  (scope || document).querySelectorAll(SEASONAL_ROOT).forEach(bindSeasonalFeature);\n}\n\nif (!document.__dsSeasonalFeatureBound) {\n  document.__dsSeasonalFeatureBound = true;\n  var startSeasonalFeatures = function () { initSeasonalFeatures(document); };\n  if (document.readyState === 'loading') {\n    document.addEventListener('DOMContentLoaded', startSeasonalFeatures, { once: true });\n  } else {\n    startSeasonalFeatures();\n  }\n  document.addEventListener('shopify:section:load', startSeasonalFeatures);\n}\n\n} catch (sfError) {\nif (typeof console !== 'undefined') console.error('[sf-core] behavior \"seasonal-feature\" failed to initialise:', sfError);\n}\n})();\n\n/* size-guide.js */\n(function () {\ntry {\n/* size-guide \u2014 opens the size-guide dialog (ds-size-guide) from its\n * [data-ds-size-guide-open] link on the size option's label row, closes it on X,\n * overlay click and Esc, and keeps Tab inside the dialog while it is open.\n *\n * Not a singleton: the picker renders one guide per product, but a page may hold\n * more than one product block (quick view, bundles), so every handler resolves\n * the dialog from the trigger's own subtree and falls back to the only one in the\n * document. Nothing is deduped or removed.\n *\n * Progressive enhancement: without this script the link is inert and the dialog\n * stays hidden \u2014 the variant picker itself is untouched.\n *\n * Open/closed is one attribute, [aria-hidden]. src/size-guide.css carries the\n * fade + slide off it, so the sheet stays painted while it animates out;\n * toggling a display utility here would cut that off.\n *\n * Authoring rules: src/behaviors/README.md. No import/export.\n */\n\n(function () {\n  if (document.__dsSizeGuideBound) return;\n  document.__dsSizeGuideBound = true;\n\n  var lastTrigger = null;\n\n  // The trigger sits next to its own dialog, so walk up to the nearest shared\n  // ancestor that holds one before falling back to the page's only guide.\n  function modalFor(el) {\n    var node = el;\n    while (node && node !== document) {\n      var found = node.querySelector && node.querySelector('[data-ds-size-guide]');\n      if (found) return found;\n      node = node.parentNode;\n    }\n    return document.querySelector('[data-ds-size-guide]');\n  }\n\n  function isOpen(modal) {\n    return !!modal && modal.getAttribute('aria-hidden') === 'false';\n  }\n\n  function openModal(trigger) {\n    var modal = modalFor(trigger);\n    if (!modal) return;\n    lastTrigger = trigger || null;\n    modal.setAttribute('aria-hidden', 'false');\n    document.body.style.overflow = 'hidden';\n\n    var close = modal.querySelector('[data-ds-size-guide-close]');\n    if (close) {\n      // Focus after paint so the dialog is visible first.\n      window.requestAnimationFrame(function () {\n        close.focus({ focusVisible: false });\n      });\n    }\n  }\n\n  function closeModal(modal) {\n    if (!modal) return;\n    modal.setAttribute('aria-hidden', 'true');\n    document.body.style.overflow = '';\n    if (lastTrigger && typeof lastTrigger.focus === 'function') {\n      lastTrigger.focus();\n    }\n    lastTrigger = null;\n  }\n\n  function openModalEl() {\n    var modals = document.querySelectorAll('[data-ds-size-guide]');\n    for (var i = 0; i < modals.length; i++) {\n      if (isOpen(modals[i])) return modals[i];\n    }\n    return null;\n  }\n\n  // Tab must cycle inside the dialog while it is open, otherwise focus walks the\n  // page behind the sheet \u2014 which is still rendered ([aria-hidden] only).\n  function trapTab(e) {\n    var modal = openModalEl();\n    if (!modal) return;\n    var dialog = modal.querySelector('[data-ds-size-guide-dialog]') || modal;\n    var focusable = Array.prototype.filter.call(\n      dialog.querySelectorAll(\n        'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])',\n      ),\n      function (el) {\n        return el.offsetParent !== null || el === document.activeElement;\n      },\n    );\n    if (!focusable.length) return;\n    var first = focusable[0];\n    var last = focusable[focusable.length - 1];\n    if (e.shiftKey && document.activeElement === first) {\n      e.preventDefault();\n      last.focus();\n    } else if (!e.shiftKey && document.activeElement === last) {\n      e.preventDefault();\n      first.focus();\n    } else if (!dialog.contains(document.activeElement)) {\n      e.preventDefault();\n      first.focus();\n    }\n  }\n\n  document.addEventListener('click', function (e) {\n    if (!e.target.closest) return;\n\n    var trigger = e.target.closest('[data-ds-size-guide-open]');\n    if (trigger) {\n      e.preventDefault();\n      openModal(trigger);\n      return;\n    }\n\n    var close = e.target.closest('[data-ds-size-guide-close]');\n    if (close) {\n      e.preventDefault();\n      closeModal(close.closest('[data-ds-size-guide]') || openModalEl());\n      return;\n    }\n\n    // Overlay: the click landed on the backdrop itself, not inside the card.\n    if (e.target.matches && e.target.matches('[data-ds-size-guide]') && isOpen(e.target)) {\n      closeModal(e.target);\n    }\n  });\n\n  document.addEventListener('keydown', function (e) {\n    if (e.key === 'Escape') {\n      var modal = openModalEl();\n      if (modal) closeModal(modal);\n      return;\n    }\n    if (e.key === 'Tab') trapTab(e);\n  });\n})();\n\n} catch (sfError) {\nif (typeof console !== 'undefined') console.error('[sf-core] behavior \"size-guide\" failed to initialise:', sfError);\n}\n})();\n\n/* sticky-bar.js */\n(function () {\ntry {\n/* sticky-bar \u2014 mobile fixed add-to-cart bar for the PDP.\n * Binds [data-ds-product-sticky-bar] (see snippets/ds-product-sticky-bar.liquid).\n * Reveals the bar once the main buy box ([data-ds-product-buy-buttons]) scrolls\n * out of view upward, and hides it again when the buy box returns. The bar's\n * \"Add to Cart\" ([data-ds-sticky-atc]) DELEGATES to the real submit, so native\n * and BuildYou (personalized) add-to-cart both run unchanged \u2014 the bar never\n * posts to the cart itself.\n *\n * Generic activator only: no pricing/variant logic (that lives in the theme).\n * The bar starts hidden (sf-hidden); this reveals it, so with no JS it never\n * shows and the in-flow buy button still works.\n *\n * Authoring rules: src/behaviors/README.md. No import/export.\n */\n\nif (!document.__dsStickyBarBound) {\n  document.__dsStickyBarBound = true;\n\n  var stickyBar = document.querySelector('[data-ds-product-sticky-bar]');\n  var stickyBuyBox = document.querySelector('[data-ds-product-buy-buttons]');\n\n  if (stickyBar) {\n    if (stickyBuyBox && 'IntersectionObserver' in window) {\n      var stickyObserver = new IntersectionObserver(\n        function (entries) {\n          var entry = entries[0];\n          if (!entry) return;\n          // Reveal only when the buy box has scrolled ABOVE the viewport. While\n          // it is on screen (or still below, before the shopper reaches it),\n          // the in-flow button is enough and the bar stays hidden.\n          if (!entry.isIntersecting && entry.boundingClientRect.top < 0) {\n            stickyBar.classList.remove('sf-hidden');\n          } else {\n            stickyBar.classList.add('sf-hidden');\n          }\n        },\n        { threshold: 0 }\n      );\n      stickyObserver.observe(stickyBuyBox);\n    }\n\n    stickyBar.addEventListener('click', function (event) {\n      var trigger = event.target.closest('[data-ds-sticky-atc]');\n      if (!trigger) return;\n      event.preventDefault();\n      // Forward to the real control: the SDK's aliased button when personalized,\n      // else the native form submit. Clicking a disabled one is a no-op (the SDK\n      // disables it until the customization form is ready).\n      var main =\n        (stickyBuyBox && stickyBuyBox.querySelector('.cus-add_to_cart')) ||\n        (stickyBuyBox && stickyBuyBox.querySelector('button[type=\"submit\"]'));\n      if (main) {\n        if (main.disabled) return;\n        main.click();\n      } else if (stickyBuyBox) {\n        stickyBuyBox.scrollIntoView({ behavior: 'smooth', block: 'center' });\n      }\n    });\n  }\n}\n\n} catch (sfError) {\nif (typeof console !== 'undefined') console.error('[sf-core] behavior \"sticky-bar\" failed to initialise:', sfError);\n}\n})();\n\n/* tags-input.js */\n(function () {\ntry {\n/* tags-input \u2014 add a tag on Enter (as a removable chip + hidden input) and\n * remove it on chip click. Binds [data-ds-tags-input] and its children (see\n * snippets/ds-tags-input.liquid). New chips are cloned from the snippet's\n * [data-ds-tags-input-template] (a ds-chip with a __DS_TAG__ placeholder) so no\n * client-side Liquid render is needed \u2014 this is why it ships. Progressive\n * enhancement: server-rendered tags submit and remove-markup exists without JS;\n * only adding via the keyboard is enhanced.\n *\n * Authoring rules: src/behaviors/README.md. No import/export.\n */\n\nvar TAGS = '[data-ds-tags-input]';\nvar TAGS_LIST = '[data-ds-tags-input-list]';\nvar TAGS_FIELD = '[data-ds-tags-input-field]';\nvar TAGS_TEMPLATE = '[data-ds-tags-input-template]';\nvar TAG_MARKER = '__DS_TAG__';\n\nfunction addTag(root, value) {\n  var list = root.querySelector(TAGS_LIST);\n  var template = root.querySelector(TAGS_TEMPLATE);\n  var field = root.querySelector(TAGS_FIELD);\n  if (!list || !template || !field) return;\n\n  var chip = template.content.querySelector('[data-ds-chip]');\n  if (!chip) return;\n  chip = chip.cloneNode(true);\n\n  // Swap the placeholder in the visible label text node.\n  var walker = document.createTreeWalker(chip, NodeFilter.SHOW_TEXT, null);\n  var node;\n  while ((node = walker.nextNode())) {\n    if (node.nodeValue.indexOf(TAG_MARKER) !== -1) {\n      node.nodeValue = node.nodeValue.replace(TAG_MARKER, value);\n    }\n  }\n  // Accessible name: \"<remove-copy>: <value>\", falling back to the value alone.\n  var removeCopy = root.getAttribute('data-ds-tags-input-remove');\n  chip.setAttribute('aria-label', removeCopy ? removeCopy + ': ' + value : value);\n\n  // Each tag is a hidden <input> immediately before its chip so the set submits.\n  var hidden = document.createElement('input');\n  hidden.type = 'hidden';\n  hidden.name = field.name || 'tags';\n  hidden.value = value;\n\n  list.insertBefore(hidden, field);\n  list.insertBefore(chip, field);\n}\n\nif (!document.__dsTagsInputBound) {\n  document.__dsTagsInputBound = true;\n\n  document.addEventListener('keydown', function (e) {\n    // Ignore the Enter that only commits an IME composition. Vietnamese/CJK IMEs\n    // fire a keydown (isComposing, keyCode 229) to end composition then a real\n    // Enter; compositionend re-inserts the text between them, so handling the\n    // first would add the tag twice.\n    if (e.key !== 'Enter' || e.isComposing || e.keyCode === 229) return;\n    var field = e.target;\n    if (!(field instanceof HTMLInputElement) || !field.matches(TAGS_FIELD)) return;\n    e.preventDefault();\n    var value = field.value.trim();\n    if (!value) return;\n    var root = field.closest(TAGS);\n    if (root) addTag(root, value);\n    field.value = '';\n  });\n\n  document.addEventListener('click', function (e) {\n    var remove = e.target.closest && e.target.closest('[data-ds-chip-remove]');\n    if (!remove || !remove.closest(TAGS)) return;\n    var chip = remove.closest('[data-ds-chip]');\n    // Drop the chip and the hidden input right before it, else the removed tag\n    // lingers in the submitted form set.\n    var hidden = chip && chip.previousElementSibling;\n    if (hidden instanceof HTMLInputElement && hidden.type === 'hidden') hidden.remove();\n    if (chip) chip.remove();\n  });\n}\n\n} catch (sfError) {\nif (typeof console !== 'undefined') console.error('[sf-core] behavior \"tags-input\" failed to initialise:', sfError);\n}\n})();\n\n/* toast.js */\n(function () {\ntry {\n/* toast \u2014 the queue behind ds-toast: one strip at a time, auto-dismissed,\n * announced politely.\n *\n * Any behaviour can raise one by dispatching ds:toast with\n * { message, highlight, image, action, detail }. Taking the action dispatches it\n * with that detail; letting the toast go instead dispatches ds:toast:dismissed\n * with the same detail, which is how cart-lines.js knows an undo window closed.\n *\n * The copy is cloned into the live region rather than swapped into one node:\n * aria-live announces insertions, so a reused node would go unread. Up to three\n * stand at once; anything past that queues.\n *\n * Authoring rules: src/behaviors/README.md. No import/export.\n */\n\nvar TOAST_REGION = '[data-ds-toast-region]';\nvar TOAST_TEMPLATE = '[data-ds-toast-template]';\nvar TOAST_LIFETIME = 8000;\n\nvar toastQueue = [];\nvar toastVisible = [];\n// Stacked, not serialised: a queued toast would leave its own removal without an\n// Undo affordance while the one before it counted down.\nvar TOAST_MAX_VISIBLE = 3;\n\nfunction toastRegion() {\n  return document.querySelector(TOAST_REGION);\n}\n\n// `taken` says the action was used. Otherwise the toast simply ran out, which\n// is what closes an undo window \u2014 cart-lines.js commits the removal on it.\nfunction toastClose(node, taken) {\n  if (!node) return;\n  if (node.__dsToastTimer) window.clearTimeout(node.__dsToastTimer);\n  var at = toastVisible.indexOf(node);\n  if (at > -1) toastVisible.splice(at, 1);\n  if (node.parentNode) node.parentNode.removeChild(node);\n  if (!taken && node.__dsToastDetail) {\n    document.dispatchEvent(new CustomEvent('ds:toast:dismissed', { detail: node.__dsToastDetail }));\n  }\n  toastPump();\n}\n\nfunction toastRender(next) {\n  var region = toastRegion();\n  var template = document.querySelector(TOAST_TEMPLATE);\n  if (!region || !template) return;\n\n  var node = template.content.firstElementChild.cloneNode(true);\n  var message = node.querySelector('[data-ds-toast-message]');\n  if (message) {\n    message.textContent = '';\n    if (next.highlight) {\n      var strong = document.createElement('strong');\n      strong.className = 'sf-font-semibold';\n      // SoT UndoToast: the name is quoted, then the status (\"was removed\")\n      // follows in the same sentence. Do not clamp the whole sentence \u2014 a long\n      // title fills both lines and the status disappears.\n      strong.textContent = '\\u201c' + next.highlight + '\\u201d';\n      message.appendChild(strong);\n      if (next.message) message.appendChild(document.createTextNode(' '));\n    }\n    if (next.message) message.appendChild(document.createTextNode(next.message));\n  }\n\n  var image = node.querySelector('[data-ds-toast-image]');\n  if (image && next.image) {\n    image.src = next.image;\n    image.alt = '';\n    image.removeAttribute('hidden');\n  } else if (image) {\n    // Drop the whole frame: removing only the <img> leaves ds-image's tinted box.\n    var frame = image.closest('[data-ds-image]') || image;\n    var slot = frame.parentNode && frame.parentNode.hasAttribute('data-ds-toast-image-slot')\n      ? frame.parentNode\n      : frame;\n    if (slot.parentNode) slot.parentNode.removeChild(slot);\n  }\n\n  var undo = node.querySelector('[data-ds-toast-undo]');\n  if (undo) {\n    if (next.action) {\n      undo.addEventListener('click', function () {\n        document.dispatchEvent(new CustomEvent(next.action, { detail: next.detail || null }));\n        toastClose(node, true);\n      });\n    } else if (undo.parentNode) {\n      undo.parentNode.removeChild(undo);\n    }\n  }\n\n  var close = node.querySelector('[data-ds-toast-close]');\n  if (close) close.addEventListener('click', function () { toastClose(node, false); });\n\n  // Dismissing without the action tells the raiser its window has closed.\n  if (next.action) node.__dsToastDetail = next.detail || null;\n  node.__dsToastTimer = window.setTimeout(function () { toastClose(node, false); }, next.lifetime || TOAST_LIFETIME);\n  region.appendChild(node);\n  toastVisible.push(node);\n}\n\nfunction toastPump() {\n  while (toastVisible.length < TOAST_MAX_VISIBLE && toastQueue.length) {\n    toastRender(toastQueue.shift());\n  }\n}\n\nfunction toastPush(detail) {\n  if (!detail || (!detail.message && !detail.highlight)) return;\n  toastQueue.push(detail);\n  toastPump();\n}\n\nif (!document.__dsToastBound) {\n  document.__dsToastBound = true;\n\n  document.addEventListener('ds:toast', function (event) {\n    toastPush(event.detail);\n  });\n\n  document.addEventListener('ds:cart:removed', function (event) {\n    var detail = event.detail || {};\n    var region = toastRegion();\n    if (!region) return;\n    toastPush({\n      highlight: detail.title || '',\n      message: region.getAttribute('data-ds-toast-removed') || 'was removed',\n      image: detail.image || '',\n      action: detail.pending ? 'ds:cart:undo' : '',\n      detail: detail.undo || null,\n    });\n  });\n}\n\n} catch (sfError) {\nif (typeof console !== 'undefined') console.error('[sf-core] behavior \"toast\" failed to initialise:', sfError);\n}\n})();\n\n/* upload.js */\n(function () {\ntry {\n/* upload \u2014 show the chosen file name(s) next to a file input.\n * Binds [data-ds-upload-input] with a [data-ds-upload-input-filename] label\n * (see snippets/ds-upload-input.liquid). The label's data-ds-upload-input-empty\n * holds the placeholder shown when nothing is selected. Progressive enhancement:\n * the native file input works without JS; only the label echo is enhanced.\n *\n * Authoring rules: src/behaviors/README.md. No import/export.\n */\n\nif (!document.__dsUploadBound) {\n  document.__dsUploadBound = true;\n  document.addEventListener('change', function (e) {\n    var field = e.target;\n    if (!(field instanceof HTMLInputElement) || field.type !== 'file') return;\n    var root = field.closest('[data-ds-upload-input]');\n    var label = root && root.querySelector('[data-ds-upload-input-filename]');\n    if (!label) return;\n    var names = field.files ? Array.from(field.files).map(function (f) { return f.name; }) : [];\n    label.textContent = names.length ? names.join(', ') : (label.dataset.dsUploadInputEmpty || '');\n  });\n}\n\n} catch (sfError) {\nif (typeof console !== 'undefined') console.error('[sf-core] behavior \"upload\" failed to initialise:', sfError);\n}\n})();\n\n/* variant-droplist.js */\n(function () {\ntry {\n/* variant-droplist \u2014 open/close for droplist-style option pickers.\n * The trigger (ds-variants layout:trigger) toggles a listbox of row values.\n * Selection itself is handled by product-variants.js (rows are variant links);\n * this module only manages open/close, the chevron, and keyboard/outside-click\n * dismissal. Progressive enhancement: without JS the rows are normal variant\n * links inside a list that is only visually collapsed.\n * Authoring rules: src/behaviors/README.md. No import/export.\n */\n\nfunction dsDroplistTrigger(dl) {\n  return dl.querySelector('[data-ds-variants-layout=\"trigger\"]');\n}\n\nfunction dsDroplistList(dl) {\n  return dl.querySelector('[data-ds-variant-droplist-list]');\n}\n\nfunction dsDroplistSetOpen(dl, open) {\n  dl.setAttribute('data-open', open ? 'true' : 'false');\n\n  var trigger = dsDroplistTrigger(dl);\n  if (trigger) {\n    trigger.setAttribute('aria-expanded', open ? 'true' : 'false');\n    var icon = trigger.querySelector('svg');\n    var chevron = icon ? icon.parentElement : null;\n    if (chevron) chevron.classList.toggle('sf-rotate-180', open);\n  }\n\n  var list = dsDroplistList(dl);\n  if (list) list.hidden = !open;\n}\n\nfunction dsDroplistCloseAll(except) {\n  document.querySelectorAll('[data-ds-variant-droplist][data-open=\"true\"]').forEach(function (dl) {\n    if (dl !== except) dsDroplistSetOpen(dl, false);\n  });\n}\n\nif (!document.__dsVariantDroplistBound) {\n  document.__dsVariantDroplistBound = true;\n\n  document.addEventListener('click', function (event) {\n    var target = event.target;\n    if (!target || !target.closest) return;\n\n    var trigger = target.closest('[data-ds-variant-droplist] [data-ds-variants-layout=\"trigger\"]');\n    if (trigger) {\n      event.preventDefault();\n      var dl = trigger.closest('[data-ds-variant-droplist]');\n      var isOpen = dl.getAttribute('data-open') === 'true';\n      dsDroplistCloseAll(dl);\n      dsDroplistSetOpen(dl, !isOpen);\n      return;\n    }\n\n    // Selecting a row closes the list; product-variants.js applies the variant.\n    var row = target.closest('[data-ds-variant-droplist-list] a[data-ds-variants]');\n    if (row) {\n      var owner = row.closest('[data-ds-variant-droplist]');\n      if (owner) dsDroplistSetOpen(owner, false);\n      return;\n    }\n\n    // Click outside any droplist closes them all.\n    if (!target.closest('[data-ds-variant-droplist]')) dsDroplistCloseAll(null);\n  });\n\n  document.addEventListener('keydown', function (event) {\n    if (event.key !== 'Escape') return;\n    var open = document.querySelector('[data-ds-variant-droplist][data-open=\"true\"]');\n    if (!open) return;\n    dsDroplistSetOpen(open, false);\n    var trigger = dsDroplistTrigger(open);\n    if (trigger) trigger.focus();\n  });\n}\n\n} catch (sfError) {\nif (typeof console !== 'undefined') console.error('[sf-core] behavior \"variant-droplist\" failed to initialise:', sfError);\n}\n})();\n"],
  "mappings": "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",
  "names": ["cleaned", "k", "bindAnnouncement", "track", "duration", "initAnnouncements", "cartAlertsPad", "cartAlertsFormat", "cartAlertsInitPriceUpdate", "cartAlertsInitExpiry", "cartAlertsInit", "cartAlertsInitAll", "cartCheckoutStickyInit", "bindSheet", "close", "cartCheckoutStickyInitAll", "cartItemCents", "cartItemQuantity", "cartItemMoney", "cartItemMainQuantity", "cartItemSync", "cartItemTotalBusy", "cartLinesUrl", "cartLinesScope", "cartLinesRendered", "cartLinesBusy", "cartLinesRestore", "cartLinesApply", "cartLinesShowEmpty", "cartLinesHideEmpty", "cartLinesReloadIfEmpty", "cartLinesPieceCount", "cartLinesTitleBase", "cartLinesTitleWrite", "cartLinesTitle", "cartLinesTitleDelta", "cartLinesUpdating", "cartLinesFinishUpdating", "cartLinesPublish", "cartLinesCleanupBundle", "cartLinesChange", "cartLinesScreenLoading", "cartLinesAnnounceRemoved", "cartLinesDetach", "cartLinesSyncAddons", "cartLinesRestoreNode", "cartLinesCommit", "cartLinesUncommit", "cartLinesLabel", "cartLinesDrop", "cartLinesRemove", "cartPopoverMoney", "cartPopoverIsAddon", "cartPopoverImage", "cartPopoverSet", "cartPopoverToggle", "cartPopoverFillImage", "cartPopoverFillText", "cartPopoverVariant", "cartPopoverAddonNode", "cartPopoverLineNode", "cartPopoverAddonsOf", "cartPopoverApply", "cartPopoverBusy", "cartSummaryMoney", "cartSummarySet", "cartSummaryCount", "cartSummaryToggle", "cartSummaryApply", "cartSummaryApplyStickyBar", "cartSummaryBusy", "applyIndeterminate", "checkoutLoadingIconSize", "checkoutLoadingIcon", "checkoutLoadingArmTimeout", "checkoutLoadingStop", "checkoutLoadingStart", "checkoutLoadingStopAll", "bindCollapsible", "initCollapsibles", "animateCollapsible", "restoreCollapsibleItemPosition", "confirmModalEl", "confirmModalFill", "confirmModalClose", "confirmModalOpen", "confirmModalTrap", "text", "parseReview", "parseItem", "setStars", "fillCard", "setup", "init", "dbFor", "dbOpenEl", "dbOpen", "dbClose", "dbTrap", "galleryRail", "centerRailTile", "galleryIndexFromScroll", "preloadGalleryNeighbors", "stopGalleryMedia", "playGalleryMedia", "activeGalleryVideo", "updateGalleryPlayback", "observeGallery", "syncGalleryUI", "updateGalleryButtons", "scrollGalleryTo", "setGalleryActive", "bindGalleryScroll", "mediaStickyGap", "mediaBottomCover", "clearProductMediaFit", "canvasNaturalHeight", "fitProductMedia", "bindProductMediaFit", "initGalleries", "watchHeaders", "bindHero", "initHeroSliders", "hideSpinner", "isPreview", "syncInputCounter", "numBound", "numCurrent", "numSync", "numStep", "numClamp", "desktopQuery", "bindRail", "initRails", "countryName", "countryFlagImage", "formatAverageRating", "applyAggregate", "applyRatingRows", "compactCount", "applyHubHeader", "absoluteDate", "fillReview", "img", "bandPageSize", "pairSortValues", "mount", "PER_PAGE", "pages", "show", "source", "badge", "allSort", "payloadTotal", "fetchReviewsJson", "countryCode", "parseAll", "jsonReview", "readPayload", "readAggregate", "createJudgeMe", "parseShopReview", "parseCarouselItem", "shopCards", "sfSort", "apiBase", "markDown", "isDown", "getJson", "toReview", "toProduct", "readList", "readStats", "createSf", "dsParseVariants", "dsInitialSelection", "dsColorName", "dsPickVariant", "dsValueAvailable", "dsSetLinkSelected", "dsSyncDroplistTriggers", "dsUnitCents", "dsFindAvailableVariant", "dsVariantMoney", "dsSyncUnitPrices", "dsUpdatePrice", "dsUpdateForms", "dsUpdateGallery", "dsApplyVariant", "visible", "sflKey", "sflRead", "sflWrite", "sflUrl", "sflVariantId", "sflText", "sflSnapshot", "sflAddEntry", "sflRemoveEntry", "sflFillCard", "sflRender", "sflSave", "sflMoveToCart", "dsScrollTopUpdate", "seasonalRange", "bindSeasonalFeature", "initSeasonalFeatures", "addTag", "toastRegion", "toastClose", "toastPump", "toastRender", "toastPush", "dsDroplistTrigger", "dsDroplistList", "dsDroplistSetOpen", "dsDroplistCloseAll"]
}
