{
  "version": 3,
  "sources": ["/cdn/shop/t/350/assets/js--new-cart-drawer.js?cb=1793992879152"],
  "sourcesContent": ["/* =========================================================\n   Cart Drawer \u2014 Cart 2027.\n\n   Structure expected (sections/new-cart-drawer.liquid):\n   - .ncd-header            tabs + close\n   - #cart-drawer-content   section-rendered: rewards + #cart-drawer-items\n   - #cart-sfl-items        client-rendered Save for Later\n   - [data-gift-sheet] / [data-gifting-sheet] / [data-sheet-scrim]\n\n   One rule runs through the whole file: a write to the cart answers with\n   the whole cart, so nothing here asks for it a second time. Markup is\n   fetched only when a row has to exist that does not yet.\n   ========================================================= */\n\nlet __cartUpdatedTimer = null;\n/* Carries the cart when the caller has one. Everything listening for this\n   used to answer it by reading /cart.js again, which is how a single\n   quantity change still ended in two or three requests. */\nfunction emitCartUpdatedBatched(cart = null) {\n  clearTimeout(__cartUpdatedTimer);\n  __cartUpdatedTimer = setTimeout(() => {\n    document.dispatchEvent(new CustomEvent('cart:updated', { detail: { cart } }));\n  }, 50);\n}\n\n/* \"One Size\" is the absence of a size rather than a size, and a variant\n   title carries it as a part like any other -- so a row built from the title\n   read \"18k Gold Plated / One Size\" about a piece that comes one way. */\nfunction stripOneSize(title) {\n  return String(title || '')\n    .split(' / ')\n    .filter((part) => {\n      const p = part.trim().toLowerCase();\n      return p && p !== 'one size' && p !== 'default title';\n    })\n    .join(' / ');\n}\n\n/* Money, in the shop's own format.\n\n   Both price helpers here used to pass a hard-coded \"${{amount}}\" to\n   Shopify.formatMoney and fall back to a dollar sign, so every price this\n   file wrote came out in dollars whatever market the shopper was in -- a\n   rupee cart showed $78500.00 the moment a quantity changed. The format\n   comes off the drawer now, straight from shop.money_format.\n\n   Trailing zeros are dropped to match money_without_trailing_zeros, which is\n   the filter the markup is rendered with; without that a line would change\n   from Rs.15,700 to Rs.15,700.00 just because someone pressed plus. */\nfunction formatMoneyLikeLiquid(cents, format) {\n  const amount = Number(cents || 0) / 100;\n  const fmt = format || '${{amount}}';\n\n  const withDelims = (value, decimals, thousands = ',', decimal = '.') => {\n    const fixed = Math.abs(value).toFixed(decimals);\n    const [whole, frac] = fixed.split('.');\n    const grouped = whole.replace(/\\B(?=(\\d{3})+(?!\\d))/g, thousands);\n    return frac ? grouped + decimal + frac : grouped;\n  };\n\n  const rendered = fmt.replace(/\\{\\{\\s*(\\w+)\\s*\\}\\}/g, (_, token) => {\n    switch (token) {\n      case 'amount': return withDelims(amount, 2);\n      case 'amount_no_decimals': return withDelims(amount, 0);\n      case 'amount_with_comma_separator': return withDelims(amount, 2, '.', ',');\n      case 'amount_no_decimals_with_comma_separator': return withDelims(amount, 0, '.', ',');\n      case 'amount_with_space_separator': return withDelims(amount, 2, ' ', ',');\n      case 'amount_no_decimals_with_space_separator': return withDelims(amount, 0, ' ', ',');\n      default: return withDelims(amount, 2);\n    }\n  });\n\n  return rendered.replace(/([.,])00(?!\\d)/, '');\n}\n\n/* Adds the drawer makes itself -- the gift, a gift box, moving a saved\n   piece back into the bag, undoing a move. new-cart-drawer-wiring.js\n   watches every POST to /cart/add so that an add from anywhere on the page\n   opens the drawer; these are already in it and refresh themselves, so the\n   flag tells the wiring to stand back rather than fetch the section a\n   second time. Cleared on a timer, not immediately, because the wiring\n   reacts on the next tick. */\nasync function ncdInternalAdd(body) {\n  window.__ncdInternalAdd = (window.__ncdInternalAdd || 0) + 1;\n  try {\n    return await fetch('/cart/add.js', {\n      method: 'POST',\n      headers: { 'Content-Type': 'application/json', Accept: 'application/json' },\n      body: JSON.stringify(body),\n    });\n  } finally {\n    setTimeout(() => { window.__ncdInternalAdd = Math.max(0, (window.__ncdInternalAdd || 1) - 1); }, 60);\n  }\n}\n\nfunction escapeHtml(value) {\n  return String(value == null ? '' : value)\n    .replace(/&/g, '&amp;')\n    .replace(/</g, '&lt;')\n    .replace(/>/g, '&gt;')\n    .replace(/\"/g, '&quot;')\n    .replace(/'/g, '&#39;');\n}\n\n/* ---------- motion ----------\n\n   The drawer's one-off motion, matched to the Bag Motion reference: rows\n   opening and closing, numbers rolling, the save-for-later card, sheets.\n   Hover, press and the always-on loops live in the stylesheet.\n\n   Four curves and a short list of durations, as the reference names them.\n   Everything goes through ncdAnimate, which reduces every animation to a\n   single frame for a shopper who has asked for less motion, so nothing\n   below has to remember to check. */\nconst NCD_EASE = {\n  out: 'cubic-bezier(.22, 1, .36, 1)',     // anything arriving\n  in: 'cubic-bezier(.45, 0, .55, 1)',      // anything leaving\n  io: 'cubic-bezier(.65, 0, .35, 1)',      // anything moving in place\n  spring: 'cubic-bezier(.34, 1.4, .5, 1)', // icons and counts\n};\nconst NCD_DUR = { xs: 160, sm: 220, md: 360, lg: 500, bar: 800, pulse: 700, stag: 50 };\n\nfunction ncdReducedMotion() {\n  return !!(window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches);\n}\n\n/* Resolves with the Animation once it has finished or been cancelled, so a\n   caller can always await it. */\nfunction ncdAnimate(el, keyframes, opts = {}) {\n  if (!el || typeof el.animate !== 'function') return Promise.resolve(null);\n  const reduce = ncdReducedMotion();\n  const o = Object.assign({ fill: 'none' }, opts, {\n    duration: reduce ? 1 : (opts.duration || 200),\n    delay: reduce ? 0 : (opts.delay || 0),\n  });\n  let a;\n  try { a = el.animate(keyframes, o); } catch { return Promise.resolve(null); }\n  return a.finished.then(() => a, () => a);\n}\n\n/* On screen: in the document and laid out. Motion is only played on what\n   can be seen; anything else just changes. */\nfunction ncdShown(el) {\n  return !!(el && el.isConnected && el.getClientRects().length);\n}\n\n/* Cancels what was played from here, leaving the stylesheet's own\n   transitions and loops alone. */\nfunction ncdClearAnims(el) {\n  if (!el || typeof el.getAnimations !== 'function') return;\n  el.getAnimations().forEach((a) => {\n    if (window.CSSAnimation && a instanceof CSSAnimation) return;\n    if (window.CSSTransition && a instanceof CSSTransition) return;\n    a.cancel();\n  });\n}\n\nfunction ncdResetBox(el) {\n  if (!el) return;\n  el.style.overflow = '';\n  el.style.boxSizing = '';\n}\n\n/* Something closing: fades in 220ms, and from 80ms in its height, padding,\n   margins and top rule ease to nothing over 360ms, so the fade leads and\n   what is below slides up. Holds at nothing until the caller removes it.\n\n   Overflow is clipped before measuring so the box owns its children's\n   margins rather than letting them collapse through it -- the total space\n   is the same either way, and this way all of it closes. */\nfunction ncdExit(el) {\n  if (!ncdShown(el)) return Promise.resolve();\n  el.style.boxSizing = 'border-box';\n  el.style.overflow = 'hidden';\n  const h = el.offsetHeight;\n  const cs = getComputedStyle(el);\n  return Promise.all([\n    ncdAnimate(el, [{ opacity: 1 }, { opacity: 0 }], { duration: NCD_DUR.sm, easing: NCD_EASE.out, fill: 'forwards' }),\n    ncdAnimate(el, [\n      { height: h + 'px', paddingTop: cs.paddingTop, paddingBottom: cs.paddingBottom, marginTop: cs.marginTop, marginBottom: cs.marginBottom, borderTopWidth: cs.borderTopWidth },\n      { height: '0px', paddingTop: '0px', paddingBottom: '0px', marginTop: '0px', marginBottom: '0px', borderTopWidth: '0px' },\n    ], { duration: NCD_DUR.md, delay: 80, easing: NCD_EASE.io, fill: 'forwards' }),\n  ]).then(() => {});\n}\n\n/* Something opening: its height eases open over 360ms and its content\n   rises 8px out of a slight blur. `highlight` is the accepted gift's flash,\n   which helps the eye find the row. `rise: false` opens the box alone. */\nfunction ncdEnter(el, { highlight = false, rise = true } = {}) {\n  if (!ncdShown(el)) return;\n  el.style.boxSizing = 'border-box';\n  el.style.overflow = 'hidden';\n  const h = el.offsetHeight;\n  const cs = getComputedStyle(el);\n  ncdAnimate(el, [\n    { height: '0px', paddingTop: '0px', paddingBottom: '0px', marginTop: '0px', marginBottom: '0px' },\n    { height: h + 'px', paddingTop: cs.paddingTop, paddingBottom: cs.paddingBottom, marginTop: cs.marginTop, marginBottom: cs.marginBottom },\n  ], { duration: NCD_DUR.md, easing: NCD_EASE.io }).then(() => ncdResetBox(el));\n\n  if (rise) {\n    ncdAnimate(el.firstElementChild || el, [\n      { opacity: 0, transform: 'translateY(8px)', filter: 'blur(3px)' },\n      { opacity: 1, transform: 'none', filter: 'blur(0)' },\n    ], { duration: NCD_DUR.md + 80, delay: 100, easing: NCD_EASE.out, fill: 'backwards' });\n  }\n\n  if (highlight) {\n    ncdAnimate(el, [\n      { backgroundColor: '#f7f6f6' },\n      { backgroundColor: '#f7f6f6', offset: 0.3 },\n      { backgroundColor: 'rgba(247, 246, 246, 0)' },\n    ], { duration: 1200, delay: NCD_DUR.md, easing: 'linear' });\n  }\n}\n\n/* Collapses something and then hides it. Shown again before it finishes,\n   it simply stays. */\nfunction ncdCollapseThenHide(el) {\n  if (!el || el.hidden) return;\n  if (el._ncdLeaving) return;\n  if (!ncdShown(el)) { el.hidden = true; return; }\n  const token = {};\n  el._ncdLeaving = token;\n  ncdExit(el).then(() => {\n    if (el._ncdLeaving !== token) return;\n    el._ncdLeaving = null;\n    el.hidden = true;\n    ncdClearAnims(el);\n    ncdResetBox(el);\n  });\n}\n\nfunction ncdCancelCollapse(el) {\n  if (!el || !el._ncdLeaving) return;\n  el._ncdLeaving = null;\n  ncdClearAnims(el);\n  ncdResetBox(el);\n}\n\n/* \"Saving $X\" and the discount chip rise 4px into place. */\n/* Something that was not there a moment ago fades in where it belongs. It\n   used to rise 4px as it came, which on a line's saving row read as the\n   figure arriving from underneath -- the same complaint as the subtotal,\n   on a smaller number. Nothing moves now. */\nfunction ncdFadeIn(el, delay = 120) {\n  return ncdAnimate(el, [\n    { opacity: 0 },\n    { opacity: 1 },\n  ], { duration: NCD_DUR.sm, delay, easing: NCD_EASE.out, fill: 'backwards' });\n}\n\n/* A sale, BOGO or gift tag wipes in left to right. */\nfunction ncdPop(el, delay = 260) {\n  return ncdAnimate(el, [\n    { clipPath: 'inset(0 100% 0 0)' },\n    { clipPath: 'inset(0)' },\n  ], { duration: 600, delay, easing: NCD_EASE.io, fill: 'backwards' });\n}\n\n/* A struck price fades in and its line draws itself left to right. The\n   line is the stylesheet's .ncd-strike-draw, worn only while it plays. */\nfunction ncdStrike(el) {\n  if (!el) return;\n  ncdAnimate(el, [{ opacity: 0 }, { opacity: 1 }], { duration: NCD_DUR.sm, easing: NCD_EASE.out, fill: 'backwards' });\n  if (ncdReducedMotion()) return;\n  el.classList.remove('ncd-strike-draw');\n  void el.offsetWidth;\n  el.classList.add('ncd-strike-draw');\n  clearTimeout(el._ncdStrikeT);\n  el._ncdStrikeT = setTimeout(() => el.classList.remove('ncd-strike-draw'), 900);\n}\n\n/* A number changing: the new one rolls in from 8px below -- above, for a\n   quantity going down -- out of a slight blur. `clip` keeps it inside its\n   own box, as the subtotal and savings lines do. Nothing changes if the\n   text has not. */\n/* A figure that has changed settles into its new value. It does not\n   perform.\n\n   This used to empty the element, put the new value in a span beneath it\n   and slide that up through a blur -- so a subtotal went blank, then\n   arrived from below. On the one number a shopper is actually watching\n   that reads as the cart losing its place rather than answering.\n\n   The text is swapped in the same frame, so the figure is never missing\n   and nothing moves; only the opacity lifts, and only from most of the way\n   there. Short enough to feel immediate, long enough to notice something\n   changed. */\nfunction ncdSetValue(el, text) {\n  if (!el) return;\n  const next = String(text);\n  if (el.textContent.trim() === next.trim()) return;\n\n  el.textContent = next;\n  if (!ncdShown(el) || ncdReducedMotion()) return;\n\n  ncdAnimate(el, [{ opacity: 0.55 }, { opacity: 1 }], {\n    duration: NCD_DUR.xs,\n    easing: NCD_EASE.out,\n  });\n}\n\n/* The tab counts bump the way the PDP's bag icon does. */\nfunction ncdBump(el) {\n  return ncdAnimate(el, [\n    { transform: 'scale(1) rotate(0)' },\n    { transform: 'scale(1.28) rotate(-6deg)', offset: 0.3 },\n    { transform: 'scale(.95)', offset: 0.6 },\n    { transform: 'none' },\n  ], { duration: 600, easing: NCD_EASE.spring });\n}\n\n/* ---------- Save for Later: Wishlist Plus ----------\n\n   Saving a piece puts it in the shopper's Wishlist Plus (Swym) list and\n   takes it out of the bag. Swym owns what is saved -- it is the same list\n   the wishlist page and the PDP heart read, it follows a signed-in\n   shopper between devices, and the store can see it -- so nothing here\n   keeps a second copy of that.\n\n   swymSnippet.liquid loads the tracker and pushes it through\n   window.SwymCallbacks. It arrives on its own schedule, so everything\n   below waits on it rather than assuming it is there, and the drawer\n   carries on working if it never turns up. */\nlet __swymPromise = null;\nfunction swymReady(timeout = 8000) {\n  if (window._swat && typeof window._swat.addToWishList === 'function') {\n    return Promise.resolve(window._swat);\n  }\n  if (__swymPromise) return __swymPromise;\n\n  __swymPromise = new Promise((resolve, reject) => {\n    const timer = setTimeout(() => reject(new Error('Wishlist Plus did not load')), timeout);\n    window.SwymCallbacks = window.SwymCallbacks || [];\n    window.SwymCallbacks.push((swat) => { clearTimeout(timer); resolve(swat); });\n  });\n  /* A rejection nobody is listening to yet is still a rejection; this\n     keeps it from surfacing as an unhandled one before a caller attaches. */\n  __swymPromise.catch(() => {});\n  return __swymPromise;\n}\n\n/* Swym's own product shape, as swymSnippet.liquid builds it for the PDP.\n   Prices are in the shop's major unit there and in cents here. */\nfunction swymProductFromRow(row) {\n  /* When the row came back from the wishlist, remove it as the wishlist\n     describes it. Swym rewrites du on the way in -- the set goes in as\n     /products/open-circle-set and comes back as\n     /products/open-collection-complete-bundle -- and matches on what it\n     stored, so a removal built from the cart's own URL quietly does\n     nothing. */\n  if (row && row.swym) {\n    return { epi: row.swym.epi, empi: row.swym.empi, du: row.swym.du };\n  }\n\n  const product = {\n    empi: Number(row.product_id) || undefined,\n    epi: Number(row.id),\n    du: row.url && row.url.indexOf('http') === 0 ? row.url : (window.location.origin + (row.url || '')),\n    dt: row.title || '',\n    iu: row.image || '',\n    pr: Number(row.price || 0) / 100,\n  };\n  if (row.variant) product.variants = [{ [row.variant]: Number(row.id) }];\n  if (row.compare_at_price) product.op = Number(row.compare_at_price) / 100;\n  return product;\n}\n\n/* What a wishlist entry is good for, and what it is not.\n\n   swat.fetch answers with a flat array of entries, each carrying epi (the\n   variant), empi (the product), du, iu and vi. It also carries pr -- and\n   that one is not usable here: it is the catalogue price in the shop's\n   base currency, so a PKR cart reads back 120 for a piece it is charging\n   Rs.32,560 for. There is no compare-at either.\n\n   So the wishlist says *which* pieces are saved and Shopify says what\n   they cost, in the market the shopper is actually in. */\nfunction rowFromSwymItem(item) {\n  return {\n    /* Kept verbatim so the entry can be removed the way it was stored. */\n    swym: { epi: Number(item.epi), empi: Number(item.empi) || undefined, du: item.du || '' },\n    id: Number(item.epi),\n    product_id: Number(item.empi) || 0,\n    url: item.du || '',\n    image: item.iu || '',\n    variant: typeof item.vi === 'string' && item.vi !== 'Default Title' ? item.vi.split('/').join(' / ') : '',\n    title: '',\n    price: 0,\n    compare_at_price: 0,\n  };\n}\n\nasync function wishlistFetch() {\n  const swat = await swymReady();\n  const items = await new Promise((resolve) => {\n    try { swat.fetch((d) => resolve(Array.isArray(d) ? d : []), () => resolve([])); }\n    catch { resolve([]); }\n  });\n  return items.filter(i => i && i.epi).map(rowFromSwymItem);\n}\n\nasync function wishlistAdd(row) {\n  const swat = await swymReady();\n  return new Promise((resolve, reject) => {\n    swat.addToWishList(swymProductFromRow(row), resolve, reject);\n  });\n}\n\nasync function wishlistRemove(row) {\n  const swat = await swymReady();\n  return new Promise((resolve, reject) => {\n    /* removeFromWishList, not deleteFromWishList: the latter is in Swym's\n       docs but is not on the tracker this shop loads. */\n    swat.removeFromWishList(swymProductFromRow(row), resolve, reject);\n  });\n}\n\n/* Prices for a saved piece, from Shopify rather than from the wishlist,\n   so they are the active market's. One request per distinct product, held\n   for the life of the page -- a saved list is usually a handful of\n   pieces, and reopening the tab asks for none of them again. */\nconst __ncdProductCache = new Map();\nfunction productJson(url) {\n  if (!url) return Promise.resolve(null);\n  const path = String(url || '').replace(/^https?:\\/\\/[^/]+/, '').split('?')[0];\n  if (!/^\\/products\\/[^/]+$/.test(path)) return Promise.resolve(null);\n  if (__ncdProductCache.has(path)) return __ncdProductCache.get(path);\n\n  const pending = fetch(`${path}.js`, { credentials: 'same-origin' })\n    .then(r => (r.ok ? r.json() : null))\n    .catch(() => null);\n  __ncdProductCache.set(path, pending);\n  return pending;\n}\n\n/* What Swym does not carry.\n\n   A wishlist entry knows a piece's price and image; it does not know the\n   pre-order date on the product metafield or the name of the discount\n   that was running when it was saved, and states 07c and 07d draw both.\n   They are kept here, keyed by variant, purely as decoration: Swym still\n   decides what is in the list, and a row with nothing cached renders\n   without those lines rather than wrongly. */\nconst SFL_DECOR_KEY = 'ncd_sfl_decor';\n\nfunction decorGet() {\n  try { return JSON.parse(localStorage.getItem(SFL_DECOR_KEY) || '{}') || {}; }\n  catch { return {}; }\n}\n\nfunction decorSet(id, fields) {\n  try {\n    /* Empty fields are dropped rather than stored: these are spread over\n       the live product data, and a blank here would blank that out. */\n    const kept = {};\n    Object.keys(fields || {}).forEach((k) => { if (fields[k]) kept[k] = fields[k]; });\n    if (!Object.keys(kept).length) return;\n\n    const all = decorGet();\n    all[String(id)] = kept;\n    localStorage.setItem(SFL_DECOR_KEY, JSON.stringify(all));\n  } catch {}\n}\n\nfunction decorDrop(id) {\n  try {\n    const all = decorGet();\n    delete all[String(id)];\n    localStorage.setItem(SFL_DECOR_KEY, JSON.stringify(all));\n  } catch {}\n}\n\n/* ---------- mystery gift: skipped, in this browser ----------\n   Skipping is a choice about an offer, not a change to the cart, so it has\n   nowhere to live on the server. It is cleared when the gift locks again,\n   so coming back over the threshold offers it afresh. */\nconst GIFT_SKIP_KEY = 'ncd_gift_skipped';\nconst giftSkip = {\n  get() { try { return localStorage.getItem(GIFT_SKIP_KEY) === '1'; } catch { return false; } },\n  set(on) { try { on ? localStorage.setItem(GIFT_SKIP_KEY, '1') : localStorage.removeItem(GIFT_SKIP_KEY); } catch {} },\n};\n\n/* ---------------------------------------------------------\n   Drawer\n   --------------------------------------------------------- */\nclass NewCartDrawer {\n  constructor({\n    drawerSel     = '#cart-drawer',\n    overlaySel    = '#cart-drawer-overlay',\n    closeSel      = '#cart-drawer-close',\n    openSel       = '[data-open-cart]',\n    contentSel    = '#cart-drawer-content',\n    itemsSel      = '#cart-drawer-content #cart-drawer-items',\n    sflSel        = '#cart-sfl-items',\n    /* .new_cart_drawer_header was the pre-redesign header and has not been\n       in the markup since the Core Bag was built, so the tabs and both\n       counts were binding to nothing. */\n    tabHeaderSel  = '.ncd-header'\n  } = {}) {\n    this.drawer   = document.querySelector(drawerSel);\n    this.overlay  = document.querySelector(overlaySel);\n    this.closeBtn = document.querySelector(closeSel);\n    this.openBtn  = document.querySelector(openSel);\n    this.content  = document.querySelector(contentSel);\n    this.itemsWrap= document.querySelector(itemsSel);\n    this.sflWrap  = document.querySelector(sflSel);\n    this.tabHead  = document.querySelector(tabHeaderSel);\n    this.sectionId= this.drawer?.getAttribute('data-section-id') || window.CART_DRAWER_SECTION_ID;\n\n    this.tabCartBtn = this.tabHead?.querySelector('[data-tab=\"cart\"]');\n    this.tabSflBtn  = this.tabHead?.querySelector('[data-tab=\"sfl\"]');\n    this.countCart  = this.tabHead?.querySelector('[data-count-cart]');\n    this.countSfl   = this.tabHead?.querySelector('[data-count-sfl]');\n    this.activeTab  = 'cart';\n\n    this.onOpenClick    = this.open.bind(this);\n    this.onCloseClick   = this.close.bind(this);\n    this.onOverlayClick = this.close.bind(this);\n    this.onEsc          = (e) => {\n      if (e.key !== 'Escape') return;\n      /* Escape closes the sheet first when one is up, the drawer after. */\n      if (this.openSheet) this.closeSheets({ dismissed: true });\n      else this.close();\n    };\n    this.onClick        = this.handleClicks.bind(this);\n    this.onSubmit       = this.handleSubmits.bind(this);\n    this.onInput        = this.handleInput.bind(this);\n\n    this.previousActiveEl = null;\n    this.focusHandler = null;\n    this.renderNonce = 0;\n    this.openSheet = null;\n    /* What Wishlist Plus last told us is saved, so the tab count and the\n       empty bag's saved line can be drawn without asking it again. */\n    this.savedItems = [];\n\n    this.bind();\n  }\n\n  bind() {\n    this.openBtn?.addEventListener('click', this.onOpenClick);\n    this.closeBtn?.addEventListener('click', this.onCloseClick);\n    this.overlay?.addEventListener('click', this.onOverlayClick);\n    document.addEventListener('keydown', this.onEsc);\n\n    this.drawer?.addEventListener('click', this.onClick);\n    this.drawer?.addEventListener('submit', this.onSubmit);\n    this.drawer?.addEventListener('input', this.onInput);\n\n    /* The bars collapsing, or the phone turning, changes what is on\n       screen. Only ever while the drawer is up. */\n    this.onViewportChange = () => {\n      if (this.drawer?.classList.contains('active')) this.pinHeight();\n    };\n    window.visualViewport?.addEventListener('resize', this.onViewportChange);\n    window.addEventListener('orientationchange', this.onViewportChange);\n    /* visualViewport's resize does not fire for every way the window can\n       change size, and the one that does not reach us is the one that\n       leaves the drawer the wrong height. */\n    window.addEventListener('resize', this.onViewportChange);\n\n    /* Delegated, because the tab buttons also appear in the empty bag and\n       in the saved-for-later empty state, which are rendered elsewhere. */\n    this.drawer?.addEventListener('click', (e) => {\n      const tabBtn = e.target.closest('[data-tab]');\n      if (!tabBtn || !this.drawer.contains(tabBtn)) return;\n      this.switchTab(tabBtn.getAttribute('data-tab'));\n    });\n\n    /* Dark buttons: a soft gold ripple spreads from wherever they are\n       pressed. Checkout and Shop Best Sellers also give a little on click. */\n    this.drawer?.addEventListener('pointerdown', (e) => {\n      const btn = e.target.closest('.ncd-foot__cta');\n      if (!btn || btn.disabled || ncdReducedMotion()) return;\n      const skin = btn.querySelector('.ncd-skin');\n      if (!skin) return;\n      const r = skin.getBoundingClientRect();\n      const ripple = document.createElement('span');\n      ripple.className = 'ncd-ripple';\n      ripple.style.left = (e.clientX - r.left) + 'px';\n      ripple.style.top = (e.clientY - r.top) + 'px';\n      skin.appendChild(ripple);\n      setTimeout(() => ripple.remove(), 1000);\n    });\n    this.drawer?.addEventListener('click', (e) => {\n      const btn = e.target.closest('button[name=\"checkout\"], .ncd-empty__cta');\n      if (!btn) return;\n      ncdAnimate(btn, [{ transform: 'scale(.98)' }, { transform: 'none' }], { duration: NCD_DUR.xs });\n    });\n  }\n\n  /* Motion is only played in a drawer that is open. */\n  motionOk() {\n    return !!(this.drawer && this.drawer.classList.contains('active') && !this.drawer.hidden);\n  }\n\n  /* A last resort behind the stylesheet, and only ever in one\n     direction.\n\n     The frame is held between the top and bottom of the viewport in CSS,\n     which needs no measuring and is right almost everywhere. This is here\n     for the case where the browser paints that box shorter than the\n     screen it is showing -- iOS, with the bars collapsed and a locked\n     page behind the drawer, where the figures the browser reports for its\n     own viewport do not all agree.\n\n     Taller only. A frame a little too tall loses its last line behind the\n     browser's bar; a frame too short shows a strip of the page under the\n     bag, which is the fault being chased. This is the only code that can\n     put an inline height on the drawer, so it is the only thing that\n     could cause that -- and it is not allowed to.\n\n     Phones and tablets. The keyboard shrinks the viewport too, and a\n     drawer that resized while someone typed a gift note would be worse\n     than the thing being fixed, so nothing is measured while a field has\n     focus. */\n  pinHeight() {\n    const d = this.drawer;\n    if (!d) return;\n\n    const vv = window.visualViewport;\n    if (!vv || window.innerWidth > 989) { d.style.height = ''; return; }\n\n    const el = document.activeElement;\n    if (el && /^(INPUT|TEXTAREA|SELECT)$/.test(el.tagName)) return;\n\n    /* Cleared before it is read, every time. An inline height the\n       stylesheet cannot override is a liability -- one resize that does\n       not reach us and the drawer keeps a number that was true a moment\n       ago. Starting from nothing means a stale value cannot survive a\n       single call, and it is also how the stylesheet gets asked what it\n       would do. No paint happens in between: this is one task. */\n    d.style.height = '';\n\n    /* Clear only. Math.max(vv.height, innerHeight) put an inline height on\n       whenever iOS reported two different figures, and an inline height\n       overrides the stylesheet's `bottom: 0`: the frame then stopped at a\n       number -- often the one that counts the strip behind Safari's bar --\n       instead of at the bottom edge. Pinned top/bottom is the screen. */\n  }\n\n  /* The opening stagger lasts a little over a second. Content that arrives\n     inside that window joins it rather than playing motion of its own. */\n  inOpeningWindow() {\n    return !!(this._openedAt && performance.now() - this._openedAt < 1400);\n  }\n\n  /* a11y focus trap */\n  getFocusableEls() {\n    if (!this.drawer) return [];\n    return Array.from(this.drawer.querySelectorAll(\n      'a[href], button:not([disabled]), textarea, input, select, details,[tabindex]:not([tabindex=\"-1\"])'\n    )).filter(el => el.offsetParent !== null);\n  }\n\n  trapFocus = (e) => {\n    if (e.key !== 'Tab') return;\n    const f = this.getFocusableEls();\n    if (!f.length) return;\n    const first = f[0], last = f[f.length - 1];\n    if (e.shiftKey && document.activeElement === first) { e.preventDefault(); last.focus(); }\n    else if (!e.shiftKey && document.activeElement === last) { e.preventDefault(); first.focus(); }\n  };\n\n  /* open/close */\n  async open() {\n    this.previousActiveEl = document.activeElement;\n    const wasOpen = !!this.drawer?.classList.contains('active');\n\n    /* A close still running would hide the drawer a moment from now. */\n    clearTimeout(this._hideTimer);\n\n    this.drawer?.removeAttribute('hidden');\n\n    /* The off-screen position has to be painted before the class that slides\n       it in, or there is nothing to transition from and the drawer simply\n       appears. Removing `hidden` and adding `active` in one go is what made\n       it pop rather than slide. */\n    if (this.drawer) void this.drawer.offsetWidth;\n\n    this.overlay?.classList.add('active');\n    this.drawer?.classList.add('active');\n    this.lockPage();\n    this.pinHeight();\n\n    /* Opening an open drawer is a refresh, not an entrance.\n\n       QA: the content used to rise into place piece by piece once the\n       frame had slid in, which read as the bag loading after it had\n       opened. It is simply there now -- the slide is the entrance. The\n       timestamp stays: it is what the rest of the file asks when it wants\n       to know whether something is appearing because the drawer just\n       opened, and hold its own motion if so. */\n    if (!wasOpen) this._openedAt = performance.now();\n\n    this.drawer?.setAttribute('aria-hidden', 'false');\n    this.content?.setAttribute('tabindex', '-1');\n    setTimeout(() => this.content?.focus(), 0);\n\n    this.focusHandler = this.trapFocus.bind(this);\n    document.addEventListener('keydown', this.focusHandler);\n\n    /* The one place markup is fetched: a row may have been added from\n       elsewhere on the page and this is the first time the drawer has seen\n       it. Everything after this is applied from cart JSON in place. */\n    await this.rerenderItems();\n    await this.renderSFL();\n    this.switchTab(this.activeTab || 'cart');\n\n    if (window.EttikaPreorderCard) window.EttikaPreorderCard.decorate(this.drawer);\n    this.watchPreorderNote();\n\n    emitCartUpdatedBatched();\n  }\n\n  /* Page lock, the way the Nothing theme's cart drawer does it.\n\n     `overflow: hidden` on <body> alone does not stop iOS Safari: the page\n     still scrolls under the drawer, Safari's toolbar collapses and expands\n     with it, and the page behind shows through at the bottom of the screen.\n     Pinning <body> with position: fixed (offset by the scroll it was at)\n     leaves the document nothing to scroll, so the toolbar stays put and\n     nothing moves behind the bag. The scroll is put back on close.\n\n     Locked once per open: a refresh of an open drawer calls open() again,\n     and reading pageYOffset off a pinned body would give 0. */\n  lockPage() {\n    if (this._pageLocked) return;\n    this._pageLocked = true;\n    const b = document.body.style;\n    this._lockedScrollY = window.pageYOffset || document.documentElement.scrollTop || 0;\n    const scrollbar = window.innerWidth - document.documentElement.clientWidth;\n    b.position = 'fixed';\n    b.top = `-${this._lockedScrollY}px`;\n    b.left = '0';\n    b.right = '0';\n    b.width = '100%';\n    b.overflow = 'hidden';\n    if (scrollbar > 0) b.paddingRight = `${scrollbar}px`;\n  }\n\n  unlockPage() {\n    if (!this._pageLocked) { document.body.style.overflow = ''; return; }\n    this._pageLocked = false;\n    const b = document.body.style;\n    b.position = ''; b.top = ''; b.left = ''; b.right = '';\n    b.width = ''; b.overflow = ''; b.paddingRight = '';\n    /* 'instant' so a theme-wide scroll-behavior: smooth does not play a\n       scroll back from the top of the page. */\n    window.scrollTo({ top: this._lockedScrollY || 0, left: 0, behavior: 'instant' });\n  }\n\n  close() {\n    this._openedAt = 0;\n    if (this.drawer) this.drawer.style.height = '';\n    this.closeSheets();\n    this.drawer?.classList.remove('active');\n    this.overlay?.classList.remove('active');\n    this.unlockPage();\n\n    /* `hidden` goes on after it has slid out, not with the class that starts\n       it -- hiding it immediately cut the animation off at the first frame.\n       Guarded, so a reopen part-way through leaves it alone. */\n    clearTimeout(this._hideTimer);\n    this._hideTimer = setTimeout(() => {\n      if (!this.drawer?.classList.contains('active')) this.drawer?.setAttribute('hidden', '');\n    }, 380);\n\n    this.drawer?.setAttribute('aria-hidden', 'true');\n    this.content?.removeAttribute('tabindex');\n    if (this.focusHandler) { document.removeEventListener('keydown', this.focusHandler); this.focusHandler = null; }\n    if (this.previousActiveEl?.focus) this.previousActiveEl.focus();\n  }\n\n  /* tabs: toggle whole containers */\n  switchTab(tab) {\n    if (!tab) return;\n    const prev = this.activeTab;\n    this.activeTab = tab;\n    this.tabCartBtn?.classList.toggle('is-active', tab === 'cart');\n    this.tabSflBtn?.classList.toggle('is-active', tab === 'sfl');\n    if (this.content) this.content.hidden = tab !== 'cart';\n    if (this.sflWrap) this.sflWrap.hidden = tab !== 'sfl';\n\n    /* The panel slides 12px in the direction of travel and fades in. */\n    if (prev && prev !== tab && this.motionOk()) {\n      ncdAnimate(tab === 'cart' ? this.content : this.sflWrap, [\n        { opacity: 0, transform: `translateX(${tab === 'sfl' ? 12 : -12}px)`, filter: 'blur(2px)' },\n        { opacity: 1, transform: 'none', filter: 'blur(0)' },\n      ], { duration: 320, easing: NCD_EASE.out });\n    }\n\n    /* The tab that just became visible may be the empty one, and its\n       Trending Now block only loads once it can be seen. */\n    this.syncRecs();\n  }\n\n  /* ---------- sheets ----------\n     Both sheets are in the markup from the start, so opening one is a\n     class and a scrim rather than a render. */\n  showSheet(name) {\n    const sheet = this.drawer?.querySelector(\n      name === 'gift' ? '[data-gift-sheet]' : '[data-gifting-sheet]'\n    );\n    if (!sheet) return;\n\n    const scrim = this.drawer.querySelector('[data-sheet-scrim]');\n    if (scrim) {\n      /* Fades in over 360ms -- also when it was part-way out. */\n      const fading = !scrim.hidden && scrim.getAnimations?.().length;\n      const wasHidden = scrim.hidden;\n      ncdClearAnims(scrim);\n      scrim.hidden = false;\n      if (wasHidden || fading) {\n        ncdAnimate(scrim, [{ opacity: 0 }, { opacity: 1 }], { duration: NCD_DUR.md, easing: NCD_EASE.out });\n      }\n    }\n\n    clearTimeout(sheet._ncdHide);\n    const wasHidden = sheet.hidden;\n    sheet.hidden = false;\n    /* A frame between being in the flow and being animated, or the\n       transform has nothing to transition from. */\n    void sheet.offsetWidth;\n    requestAnimationFrame(() => sheet.classList.add('is-open'));\n    this.openSheet = name;\n\n    /* The sheet arrives with its contents. Each child used to be held at\n       opacity 0 and then raised 8px through a blur, 140ms in and another\n       50ms apart -- so on a sheet with six children the last one did not\n       begin until 390ms and was still settling at 870ms, long after the\n       sheet itself had finished sliding up. The panel came up empty and\n       filled in afterwards.\n\n       There is one movement here now, and it is the sheet's. */\n\n    /* The claim bar and the gift sheet are two ways of saying the same\n       thing, so only ever one of them is up. */\n    window.cartThresholds?.refreshGiftBar?.();\n\n    sheet.querySelector('button, textarea, input')?.focus({ preventScroll: true });\n  }\n\n  /* `dismissed` means the shopper closed it rather than it closing because\n     the thing it was asking about is settled. Dismissing the gift sheet\n     is as much an answer as skipping it: the gift is not being taken now,\n     so the claim bar takes its place and the sheet stops re-offering\n     itself on later visits. */\n  closeSheets({ dismissed = false } = {}) {\n    const wasGift = this.openSheet === 'gift';\n\n    /* The sheet eases down over 360ms and the scrim fades over 320ms; each\n       is hidden once it has gone, unless it was opened again meanwhile. */\n    const scrim = this.drawer?.querySelector('[data-sheet-scrim]');\n    if (scrim && !scrim.hidden) {\n      ncdClearAnims(scrim);\n      ncdAnimate(scrim, [{ opacity: 1 }, { opacity: 0 }], { duration: 320, easing: NCD_EASE.in, fill: 'forwards' }).then((a) => {\n        if (!this.openSheet) scrim.hidden = true;\n        if (a && a.playState !== 'idle') a.cancel();\n      });\n    }\n\n    this.drawer?.querySelectorAll('.ncd-sheet').forEach((sheet) => {\n      sheet.classList.remove('is-open');\n      if (sheet.hidden) return;\n      clearTimeout(sheet._ncdHide);\n      sheet._ncdHide = setTimeout(() => {\n        if (!sheet.classList.contains('is-open')) sheet.hidden = true;\n      }, ncdReducedMotion() ? 0 : NCD_DUR.md);\n    });\n    this.openSheet = null;\n\n    if (dismissed && wasGift) giftSkip.set(true);\n    window.cartThresholds?.refreshGiftBar?.();\n  }\n\n  /* server helpers */\n  async fetchCartJson() {\n    const res = await fetch(`/cart.js?_=${Date.now()}`, { credentials: 'same-origin', cache: 'no-store' });\n    return res.json();\n  }\n\n  /* price fmt */\n  priceMoney(cents) {\n    return formatMoneyLikeLiquid(cents, this.drawer?.getAttribute('data-money-format'));\n  }\n\n  /* ---------- Save for Later ----------\n     Figma 1726:537 (pre-order) and 1726:622 (sale). The row is the cart\n     row with the stepper swapped for Move to Bag and the save control\n     gone -- it is already saved. */\n  /* Cart JSON hands back the image at its original size -- a 1920px file\n     for a box 96 wide, which is what the saved list was asking the CDN\n     for. Shopify's CDN sizes on the URL, so this asks for the size the\n     row actually draws, as image_url does in the Liquid. */\n  sizedImage(url, width = 288) {\n    if (!url) return '';\n    if (!/cdn\\.shopify\\.com|\\/cdn\\/shop\\//.test(url)) return url;\n    if (/[?&]width=/.test(url)) return url;\n    return url + (url.indexOf('?') === -1 ? '?' : '&') + 'width=' + width;\n  }\n\n  sflRowHtml(it) {\n    const onSale = it.compare_at_price > it.price && it.price > 0;\n    const pctOff = onSale ? Math.round(((it.compare_at_price - it.price) / it.compare_at_price) * 100) : 0;\n    /* Only a markdown the product carries itself. The badge the cart row\n       wore -- BOGO, or a percentage off a cart discount -- belonged to a\n       line in the bag, and a saved piece is not in the bag: it earns no\n       BOGO and is allocated no discount, so wearing the label was a promise\n       nothing was keeping. Figma 1674:87 shows a saved row plain. */\n    const tag = pctOff >= 1 ? `${pctOff}% Off` : '';\n    const saved = onSale ? it.compare_at_price - it.price : 0;\n\n    return `\n      <div class=\"cart-drawer-item ncd-item\" data-sfl-variant-id=\"${it.id}\">\n        <div class=\"ncd-item__media\">\n          ${it.image ? `<img class=\"ncd-item__img\" src=\"${escapeHtml(this.sizedImage(it.image))}\" alt=\"${escapeHtml(it.title)}\" width=\"96\" height=\"128\" loading=\"eager\" decoding=\"sync\">` : ''}\n          ${tag ? `<span class=\"ncd-item__tag\">${escapeHtml(tag)}</span>` : ''}\n        </div>\n\n        <div class=\"ncd-item__body\">\n          <div class=\"ncd-item__head\">\n            <h3 class=\"ncd-item__title\">${it.url ? `<a href=\"${escapeHtml(it.url)}\">${escapeHtml(it.title)}</a>` : escapeHtml(it.title)}</h3>\n            <div class=\"ncd-item__prices\">\n              ${onSale ? `<span class=\"ncd-item__was\">${this.priceMoney(it.compare_at_price)}</span>` : ''}\n              ${it.price > 0 ? `<span class=\"ncd-item__now\">${this.priceMoney(it.price)}</span>` : ''}\n            </div>\n          </div>\n\n          ${(it.variant || saved > 0) ? `\n          <div class=\"ncd-item__meta\">\n            <div class=\"ncd-item__meta-main\">\n              ${it.variant ? `<ul class=\"ncd-item__options\"><li>${escapeHtml(it.variant)}</li></ul>` : ''}\n            </div>\n            <div class=\"ncd-item__meta-side\">\n              ${saved > 0 ? `<p class=\"ncd-item__saving\">Saving ${this.priceMoney(saved)}</p>` : ''}\n            </div>\n          </div>` : ''}\n          ${it.ship_date ? `<p class=\"ncd-item__ships\"><span class=\"ncd-item__dot\" aria-hidden=\"true\"></span><span>Est. Ships ${escapeHtml(it.ship_date)}</span></p>` : ''}\n\n          <div class=\"ncd-item__foot\">\n            <button class=\"ncd-btn-outline ncd-sfl__move\" data-sfl-move data-variant-id=\"${it.id}\" type=\"button\"><span class=\"ncd-ob__t\"><span>Move to Bag</span><span>Moved</span></span></button>\n            <div class=\"ncd-item__actions\">\n              <button class=\"ncd-item__action\" data-sfl-remove data-variant-id=\"${it.id}\" type=\"button\" aria-label=\"Remove ${escapeHtml(it.title)} from saved\">\n                ${this.binIcon()}\n              </button>\n            </div>\n          </div>\n        </div>\n      </div>`;\n  }\n\n\n  binIcon() {\n    /* Same drawing as snippets/ncd-icon-bin.liquid: the single path at rest,\n       and the split copy whose lid lifts on hover. */\n    return `<svg class=\"ncd-icon ncd-icon--bin\" width=\"15\" height=\"16\" viewBox=\"0 0 15 16\" fill=\"none\" aria-hidden=\"true\"><path class=\"ncd-bin__whole\" d=\"M1 3H2.75M2.75 3H6M2.75 3L3.75 15.5H12.25L13.25 3M6 3H10M6 3V0.5H10V3M10 3H13.25M13.25 3H15M6.5 6V12.5M9.5 6V12.5\" stroke=\"currentColor\"/><g class=\"ncd-bin__parts\"><g class=\"ncd-bin__lid\"><path d=\"M1 3H15M6 3V0.5H10V3\" stroke=\"currentColor\"/></g><path d=\"M2.75 3L3.75 15.5H12.25L13.25 3M6.5 6V12.5M9.5 6V12.5\" stroke=\"currentColor\"/></g></svg>`;\n  }\n\n  /* Out of the wishlist, and so out of the saved list. Move to Bag, the\n     delete control and Undo all end here, because all three mean the same\n     thing to Wishlist Plus. */\n  /* `before` is motion to let finish -- a saved row closing -- before the\n     list is redrawn without it. */\n  async forgetSaved(id, before = null) {\n    const row = (this.savedItems || []).find(it => it.id === Number(id)) || { id: Number(id) };\n    let removed = true;\n    try {\n      await wishlistRemove(row);\n    } catch (err) {\n      removed = false;\n      console.error('[cart drawer] could not remove from Wishlist Plus:', err);\n    }\n    /* Only forget the decoration for an entry that actually left the\n       list; otherwise it comes back on the next render with nothing to\n       draw itself with. */\n    if (removed) decorDrop(id);\n    await this.renderSFL(before);\n  }\n\n  async renderSFL(before = null) {\n    if (!this.sflWrap) return;\n\n    const list  = this.sflWrap.querySelector('[data-sfl-list]');\n    const empty = this.sflWrap.querySelector('[data-sfl-empty]');\n    const intro = this.sflWrap.querySelector('[data-sfl-intro]');\n    if (!list) return;\n\n    let items = [];\n    try { items = await wishlistFetch(); }\n    catch (err) {\n      /* Wishlist Plus is the list. If it is not there the saved tab has\n         nothing to show, and saying so beats inventing a second list that\n         would then disagree with the shopper's real wishlist. */\n      console.warn('[cart drawer] Wishlist Plus unavailable:', err.message);\n      items = [];\n    }\n\n    /* The wishlist gives the pieces; Shopify gives what they cost here,\n       and the cache gives the two things neither carries. */\n    const decor = decorGet();\n    this.savedItems = await Promise.all(items.map(async (it) => {\n      const cached = decor[String(it.id)] || {};\n      const product = (await productJson(cached.url)) || (await productJson(it.url));\n      const variant = product?.variants?.find(v => Number(v.id) === it.id) || null;\n\n      return {\n        ...it,\n        title: product?.title || it.title,\n        image: variant?.featured_image?.src || product?.featured_image || it.image || '',\n        price: variant ? Number(variant.price) : 0,\n        compare_at_price: variant && variant.compare_at_price ? Number(variant.compare_at_price) : 0,\n        variant: (variant && variant.title && variant.title !== 'Default Title' ? stripOneSize(variant.title) : '') || it.variant,\n        ...cached,\n      };\n    }));\n\n    if (before) await before;\n\n    const any = this.savedItems.length > 0;\n    const emptyWasHidden = !!empty?.hidden;\n    list.innerHTML = any ? this.savedItems.map(it => this.sflRowHtml(it)).join('') : '';\n    if (intro) intro.hidden = !any;\n    if (empty) empty.hidden = any;\n\n    /* The saved list emptying slides its empty state in, as a panel does. */\n    if (empty && emptyWasHidden && !empty.hidden && this.activeTab === 'sfl' && this.motionOk() && !this.inOpeningWindow()) {\n      ncdAnimate(empty, [\n        { opacity: 0, transform: 'translateX(12px)', filter: 'blur(2px)' },\n        { opacity: 1, transform: 'none', filter: 'blur(0)' },\n      ], { duration: 320, easing: NCD_EASE.out });\n    }\n    this.updateSavedSummary();\n    this.updateSavedCount();\n    this.syncRecs();\n  }\n\n  /* The saved count appears twice outside the saved tab -- on the tab\n     itself and on the empty bag's \"N pieces saved for later\" row -- and\n     both are read from the same list. */\n  updateSavedSummary() {\n    const saved = (this.savedItems || []).length;\n    const row = this.drawer?.querySelector('[data-empty-saved]');\n    const count = this.drawer?.querySelector('[data-empty-saved-count]');\n    if (row) row.hidden = saved === 0;\n    if (count) count.textContent = `${saved} ${saved === 1 ? 'piece' : 'pieces'} saved for later`;\n  }\n\n  /* What a cart row knows about itself, for the saved list to redraw later\n     without going back to the server for any of it. */\n  /* Everything the wishlist needs is on the row the shopper clicked:\n     Liquid puts the variant, the product, the clean product URL and the\n     prices in cents there. Reading them here rather than fetching\n     /cart.js first takes a round trip out of the slowest thing in the\n     drawer -- saving a piece. */\n  readRowForSfl(card) {\n    const text = (sel) => card.querySelector(sel)?.textContent?.trim() || '';\n    const attr = (name) => card.getAttribute(name) || '';\n\n    return {\n      id: Number(attr('data-variant-id')),\n      product_id: Number(attr('data-product-id')) || 0,\n      title: text('.ncd-item__title'),\n      url: attr('data-product-url') || (card.querySelector('.ncd-item__title a')?.getAttribute('href') || '').split('?')[0],\n      image: card.querySelector('.ncd-item__img')?.getAttribute('src') || '',\n      price: Number(attr('data-price')) || 0,\n      compare_at_price: Number(attr('data-compare')) || 0,\n      variant: text('.ncd-item__options li'),\n      ship_date: text('.ncd-item__ships span:last-child').replace(/^Est\\.\\s*Ships\\s*/i, ''),\n    };\n  }\n\n  /* Takes the cart when the caller already has one -- which, after a write,\n     it always does. Only reads /cart.js when called with nothing, which is\n     the first paint. */\n  updateCounts = async (cart = null) => {\n    try {\n      const c = Number((cart || (await this.fetchCartJson())).item_count || 0);\n      /* Kept for syncRecs: what is in the bag is a question about the cart,\n         not about how many rows happen to be on screen. */\n      this.cartCount = c;\n      /* Square brackets, and the number even at nought: Figma 1654:1371 and\n         1726:474 both show \"BAG [0]\" rather than a tab with nothing after\n         it, so the two tabs keep the same shape however full the bag is. */\n      this.setCount(this.countCart, c);\n    } catch {}\n    this.updateSavedCount();\n  };\n\n  /* The number sits in its own box inside the brackets so it can bump on\n     its own when it changes. */\n  setCount(el, n) {\n    if (!el) return;\n    const num = el.querySelector('.ncd-tab__num');\n    if (!num) {\n      el.innerHTML = `[<span class=\"ncd-tab__num\">${Number(n) || 0}</span>]`;\n      return;\n    }\n    const next = String(n);\n    if (num.textContent.trim() === next) return;\n    num.textContent = next;\n    if (this.motionOk()) ncdBump(num);\n  }\n\n  /* The saved count on its own. Redrawing the saved list says nothing new\n     about the bag, and reading /cart.js to find that out was costing a\n     round trip on every save, move and remove. */\n  updateSavedCount() {\n    const n = (this.savedItems || []).length;\n    this.setCount(this.countSfl, n);\n  }\n\n  /* Drops rows for lines that left the cart between the section being\n     rendered and now. Keys are matched loosely for the reason reseatLine\n     explains -- a row whose variant is still in the cart is still a row,\n     whatever its key says. */\n  sanitizeItemsFragment(freshItemsFragment, cart) {\n    if (!freshItemsFragment || !cart) return freshItemsFragment;\n\n    const keepKeys = new Set((cart.items || []).map(i => i.key));\n    const keepVariants = new Set((cart.items || []).map(i => Number(i.variant_id)));\n\n    freshItemsFragment.querySelectorAll('.cart-drawer-item').forEach(node => {\n      const key = node.getAttribute('data-line-item-key');\n      const variantId = Number(node.getAttribute('data-variant-id')) || 0;\n      if (keepKeys.has(key) || keepVariants.has(variantId)) return;\n      (node.closest('.cart-drawer-item-wrapper') || node).remove();\n    });\n    return freshItemsFragment;\n  }\n\n  async handleClicks(e) {\n    /* ---- sheets ---- */\n    if (e.target.closest('[data-sheet-close]') || e.target.closest('[data-sheet-scrim]')) {\n      e.preventDefault();\n      this.closeSheets({ dismissed: true });\n      return;\n    }\n\n    if (e.target.closest('[data-open-gifting]')) {\n      e.preventDefault();\n      this.showSheet('gifting');\n      return;\n    }\n\n    if (e.target.closest('[data-gift-claim-open]')) {\n      e.preventDefault();\n      /* Claiming is only un-skipping: the sheet is where the choice is\n         actually made, so it is the sheet that comes back. */\n      giftSkip.set(false);\n      this.showSheet('gift');\n      return;\n    }\n\n    if (e.target.closest('[data-gift-skip]')) {\n      e.preventDefault();\n      this.closeSheets({ dismissed: true });\n      return;\n    }\n\n    /* ---- gift box ---- */\n    const boxBtn = e.target.closest('[data-add-giftbox]');\n    if (boxBtn) {\n      e.preventDefault();\n      const id = Number(boxBtn.getAttribute('data-variant-id'));\n      if (!id || boxBtn.disabled) return;\n\n      /* The circle spins in the label's place while the line is written,\n         the same answer the recommendation cards give. On the way out the\n         button says Added and stays that way: one box to a bag. It is only\n         put back to Add if the box leaves the cart, which syncGiftBox\n         below notices. */\n      boxBtn.disabled = true;\n      boxBtn.classList.add('is-loading');\n      try {\n        await ncdInternalAdd({ items: [{ id, quantity: 1, properties: { source: 'cart_drawer', attribute: 'gift_box' } }] });\n        await this.rerenderItems();\n        this.syncGiftBox();\n      } catch (err) {\n        console.error(err);\n        boxBtn.disabled = false;\n      } finally {\n        boxBtn.classList.remove('is-loading');\n      }\n      return;\n    }\n\n    /* ---- gift note ---- */\n    const saveNote = e.target.closest('[data-save-gift-note]');\n    if (saveNote) {\n      e.preventDefault();\n      const input = this.drawer?.querySelector('[data-gift-note-input]');\n      const note = (input?.value || '').slice(0, 500);\n      saveNote.disabled = true;\n      try {\n        await fetch('/cart/update.js', {\n          method: 'POST',\n          headers: { 'Content-Type': 'application/json', Accept: 'application/json' },\n          body: JSON.stringify({ attributes: { 'Gift Note': note } })\n        });\n        this.applyGiftNote(note);\n        this.closeSheets();\n      } catch (err) { console.error(err); }\n      finally { saveNote.disabled = false; }\n      return;\n    }\n\n    /* ---- set: included pieces ---- */\n    const setToggle = e.target.closest('[data-set-toggle]');\n    if (setToggle) {\n      e.preventDefault();\n      if (setToggle._ncdBusy) return;\n      const panel = document.getElementById(setToggle.getAttribute('aria-controls'));\n      const open = setToggle.getAttribute('aria-expanded') === 'true';\n      setToggle.setAttribute('aria-expanded', String(!open));\n      const label = setToggle.querySelector('[data-set-toggle-label]');\n      if (label) label.textContent = open ? 'View included pieces' : 'Hide included pieces';\n      if (!panel) return;\n\n      if (open) {\n        /* Hide: the pieces fade, then the panel closes. */\n        setToggle._ncdBusy = true;\n        await ncdExit(panel);\n        panel.hidden = true;\n        ncdClearAnims(panel);\n        ncdResetBox(panel);\n        setToggle._ncdBusy = false;\n      } else {\n        /* View: the panel opens and the pieces rise 6px in turn. */\n        panel.hidden = false;\n        ncdEnter(panel, { rise: false });\n        panel.querySelectorAll('.ncd-set__piece').forEach((piece, i) => {\n          ncdAnimate(piece, [\n            { opacity: 0, transform: 'translateY(6px)' },\n            { opacity: 1, transform: 'none' },\n          ], { duration: NCD_DUR.md, delay: 120 + i * NCD_DUR.stag, easing: NCD_EASE.out, fill: 'backwards' });\n        });\n      }\n      return;\n    }\n\n    /* ---- save for later (cart line) ---- */\n    const saveBtn = e.target.closest('.cart_item_save_for_later');\n    if (saveBtn) {\n      e.preventDefault();\n      await this.moveToSaved(saveBtn);\n      return;\n    }\n\n    /* ---- undo a move ---- */\n    const undoBtn = e.target.closest('[data-sfl-undo]');\n    if (undoBtn) {\n      e.preventDefault();\n      const wrapper = undoBtn.closest('.cart-drawer-item-wrapper');\n      const id = Number(wrapper?.getAttribute('data-undo-variant') || 0);\n      if (!id) return;\n      /* Taken up before it ran out, so it must not fire mid-undo. */\n      this.clearMovedTimer(wrapper);\n      undoBtn.disabled = true;\n\n      /* The card's content fades while the bag updates; the restored row\n         then grows from the card's height to its own (rerenderItems). */\n      const hold = wrapper.querySelector('.ncd-moved-hold');\n      let cardHeight = 0;\n      if (this.motionOk() && ncdShown(wrapper)) {\n        wrapper.style.boxSizing = 'border-box';\n        wrapper.style.overflow = 'hidden';\n        cardHeight = wrapper.offsetHeight;\n        ncdResetBox(wrapper);\n        ncdAnimate(hold, [\n          { opacity: 1, filter: 'blur(0)' },\n          { opacity: 0, filter: 'blur(2px)' },\n        ], { duration: NCD_DUR.sm, easing: NCD_EASE.in, fill: 'forwards' });\n      }\n\n      try {\n        await ncdInternalAdd({ items: [{ id, quantity: 1 }] });\n        await this.forgetSaved(id);\n        this._undoMorph = cardHeight ? { variant: id, height: cardHeight } : null;\n        await this.rerenderItems();\n      } finally {\n        this._undoMorph = null;\n        undoBtn.disabled = false;\n        /* Still here means the undo did not land; show the card again. */\n        if (wrapper.isConnected) ncdClearAnims(hold);\n      }\n      return;\n    }\n\n    /* ---- SFL: move to bag ---- */\n    const moveBtn = e.target.closest('[data-sfl-move]');\n    if (moveBtn) {\n      e.preventDefault();\n      const id = Number(moveBtn.getAttribute('data-variant-id'));\n      if (!id) return;\n      moveBtn.disabled = true;\n      /* The fill rises and holds, and the label slides up to \"Moved\". */\n      moveBtn.classList.add('is-on', 'is-done');\n      const row = moveBtn.closest('.ncd-item');\n      try {\n        /* An add brings a line that has no row yet, so this is one of the\n           two places markup genuinely has to be fetched. */\n        /* No floor under this. It used to be raced against a 450ms wait so\n           the label had time to say \"Moved\", which meant the row could\n           never close sooner than that however quickly the server\n           answered. The label still runs; it just no longer holds the\n           shopper up. */\n        await ncdInternalAdd({ items: [{ id, quantity: 1 }] });\n        /* The saved row closes while Wishlist Plus is told. */\n        const exit = this.motionOk() ? ncdExit(row) : null;\n        await this.forgetSaved(id, exit);\n        await this.rerenderItems();\n        this.switchTab('cart');\n      } finally {\n        moveBtn.disabled = false;\n        moveBtn.classList.remove('is-on', 'is-done');\n      }\n      return;\n    }\n\n    /* ---- SFL: remove ---- */\n    const removeSflBtn = e.target.closest('[data-sfl-remove]');\n    if (removeSflBtn) {\n      e.preventDefault();\n      const id = Number(removeSflBtn.getAttribute('data-variant-id'));\n      if (!id) return;\n      removeSflBtn.disabled = true;\n      /* The row closes while Wishlist Plus is told. */\n      const row = removeSflBtn.closest('.ncd-item');\n      const exit = this.motionOk() ? ncdExit(row) : null;\n      try { await this.forgetSaved(id, exit); }\n      finally { removeSflBtn.disabled = false; }\n      return;\n    }\n\n    /* ---- qty +/- on cart lines ---- */\n    const minus = e.target.closest('.cart-drawer-quantity-selector-minus');\n    const plus  = e.target.closest('.cart-drawer-quantity-selector-plus');\n    if (minus || plus) {\n      e.preventDefault();\n      const wrap  = e.target.closest('[data-qty]');\n      const key   = wrap?.getAttribute('data-key');\n      /* .cart-drawer-qty-input was the pre-redesign class and is not in the\n         markup, so this used to find nothing and return -- the steppers\n         did nothing at all. */\n      const input = wrap?.querySelector('.ncd-qty__value, input[name=\"quantity\"]');\n      const card  = wrap?.closest('.cart-drawer-item');\n      if (!key || !input || !card) return;\n\n      const qty = parseInt(input.value || '1', 10);\n      const nextQty = minus ? Math.max(0, qty - 1) : qty + 1;\n\n      /* The number moves now, and the server is told once the shopper has\n         stopped clicking. Tapping + four times used to be four writes and\n         four re-renders, each one disabling the buttons being clicked. */\n      input.value = nextQty;\n      /* The number rolls: up for plus, down for minus. */\n      if (this.motionOk() && nextQty !== qty) this.settleQty(input);\n      const minusBtn = wrap.querySelector('.cart-drawer-quantity-selector-minus');\n      if (minusBtn) minusBtn.disabled = nextQty < 2;\n      if (card.hasAttribute('data-bogo-eligible')) card.setAttribute('data-bogo-eligible', String(nextQty));\n      this.updateBogoPrompt();\n\n      this.queueQuantity(key, nextQty, card);\n      return;\n    }\n\n    /* ---- remove button on cart line ---- */\n    const removeBtn = e.target.closest('.cart-drawer-item-delete');\n    if (removeBtn) {\n      e.preventDefault();\n      const card = removeBtn.closest('.cart-drawer-item');\n      const key  = card?.getAttribute('data-line-item-key');\n      if (!key || !card) return;\n\n      /* The whole row -- the line, a set's pieces, a BOGO prompt under it --\n         fades and closes up while the cart is told, and the rows below\n         slide up into its place. */\n      const wrapper = card.closest('.cart-drawer-item-wrapper') || card;\n      card.classList.add('is-removing');\n      const exit = ncdExit(wrapper);\n\n      try {\n        const [cart] = await Promise.all([this.changeLineByKey(key, 0), exit]);\n        /* The row has closed; applyCart takes it out and brings the totals\n           down with it. One request, no re-render. */\n        this.applyCart(cart);\n      } catch (err) {\n        console.error(err);\n        /* Put it back rather than leave a row mid-collapse. */\n        card.classList.remove('is-removing');\n        ncdClearAnims(wrapper);\n        ncdResetBox(wrapper);\n      }\n    }\n  }\n\n  handleInput(e) {\n    const note = e.target.closest('[data-gift-note-input]');\n    if (!note) return;\n    const count = this.drawer?.querySelector('[data-gift-note-count]');\n    if (count) count.textContent = String(note.value.length);\n  }\n\n  /* Moving a piece out of the bag, Figma 1726:402. The row is replaced by\n     a confirmation that can be undone, and the line leaves the cart at\n     once so the totals and the rewards are honest about what is in it. */\n  async moveToSaved(saveBtn) {\n    const card    = saveBtn.closest('.cart-drawer-item');\n    const wrapper = card?.closest('.cart-drawer-item-wrapper') || card;\n    const key     = card?.getAttribute('data-line-item-key');\n    if (!key || !wrapper) return;\n\n    const payload = this.readRowForSfl(card);\n    if (!payload.id) return;\n\n    /* The bookmark fills with ink and swells to 1.12 and back. Inline, so\n       it holds over the hover tint. */\n    const icon = saveBtn.querySelector('svg');\n    const iconPath = icon?.querySelector('path');\n    if (iconPath) iconPath.style.fill = 'currentColor';\n    const swell = ncdAnimate(icon, [\n      { transform: 'scale(1)' },\n      { transform: 'scale(1.12)', offset: 0.45 },\n      { transform: 'scale(1)' },\n    ], { duration: 600, easing: NCD_EASE.out });\n\n    /* Into the wishlist first. If that fails the piece stays in the bag,\n       rather than leaving the shopper with neither. */\n    saveBtn.disabled = true;\n    try {\n      await wishlistAdd(payload);\n    } catch (err) {\n      console.error('[cart drawer] could not save to Wishlist Plus:', err);\n      saveBtn.disabled = false;\n      if (iconPath) iconPath.style.fill = '';\n      return;\n    }\n    saveBtn.disabled = false;\n\n    /* The variant label goes in too: a set reads \"2 Pieces\" in the bag,\n       where its raw options would read \"18k Gold Plated / 18k Gold\n       Plated\". */\n    decorSet(payload.id, {\n      ship_date: payload.ship_date,\n      variant: payload.variant,\n      /* The cart's own product URL. Swym rewrites du to whatever handle\n         its catalogue has, which on this shop is a stale one -- the set\n         comes back as /products/open-collection-complete-bundle, which\n         404s, where the cart says /products/open-circle-set. */\n      url: payload.url,\n    });\n\n    /* Out of the bag's reckoning from here on, while the row turns into\n       its confirmation (show_sfl takes over once it has). */\n    wrapper.setAttribute('data-ncd-moving', '');\n\n    /* The saved list and the cart are brought up to date at once, while\n       the row is moving -- the motion never holds up the cart. */\n    const server = (async () => {\n      await this.renderSFL();\n\n      try {\n        const after = await this.changeLineByKey(key, 0);\n        /* The rows that are left have to be redrawn, not just the totals: a\n           BOGO earned by the piece that just went to Save for Later stops\n           applying, and the line that stayed was still showing FREE, its\n           saving and its discount chip until the page was reloaded.\n\n           applyCart keeps the row under its confirmation, so it can do the\n           whole job here. */\n        this.applyCart(after);\n      } catch (err) { console.error(err); }\n    })();\n\n    await swell;\n    await this.morphToMoved(wrapper, card, payload.id);\n    await server;\n  }\n\n  /* The row becomes its confirmation rather than closing and reopening:\n     its content fades out of a 3px blur, the row's height eases to the\n     card's, and the card's content arrives in turn. */\n  async morphToMoved(wrapper, card, id) {\n    const bar = wrapper.querySelector('[data-moved-bar]');\n    const parts = [card, wrapper.querySelector('.ncd-set'), wrapper.querySelector('.ncd-bogo:not([hidden])')].filter(Boolean);\n    const motion = this.motionOk() && ncdShown(wrapper);\n    let from = 0;\n\n    if (motion) {\n      await Promise.all(parts.map(p => ncdAnimate(p, [\n        { opacity: 1, filter: 'blur(0)' },\n        { opacity: 0, filter: 'blur(3px)' },\n      ], { duration: NCD_DUR.sm, easing: NCD_EASE.in, fill: 'forwards' })));\n      wrapper.style.boxSizing = 'border-box';\n      wrapper.style.overflow = 'hidden';\n      from = wrapper.offsetHeight;\n      wrapper.style.height = from + 'px';\n    }\n\n    wrapper.setAttribute('data-undo-variant', String(id));\n    wrapper.classList.add('show_sfl');\n    wrapper.removeAttribute('data-ncd-moving');\n    parts.forEach(ncdClearAnims);\n    if (bar) bar.hidden = false;\n\n    if (motion && bar && wrapper.isConnected) {\n      bar.style.visibility = 'hidden';\n      wrapper.style.height = 'auto';\n      const to = wrapper.offsetHeight;\n      wrapper.style.height = from + 'px';\n      await ncdAnimate(wrapper, [{ height: from + 'px' }, { height: to + 'px' }], { duration: NCD_DUR.md, easing: NCD_EASE.io });\n      bar.style.visibility = '';\n    }\n\n    wrapper.style.height = '';\n    ncdResetBox(wrapper);\n    if (bar) this.toastIn(bar);\n    this.startMovedTimer(wrapper);\n  }\n\n  /* The confirmation's content in sequence: the bookmark pops in, the line\n     slides in from the left, Undo fades up. A hairline along its foot\n     counts down the time it has left. */\n  toastIn(bar) {\n    const hold = bar?.querySelector('.ncd-moved-hold');\n    if (!hold) return;\n\n    hold.querySelector('.ncd-moved__line')?.remove();\n    const line = document.createElement('i');\n    line.className = 'ncd-moved__line';\n    line.setAttribute('aria-hidden', 'true');\n    hold.appendChild(line);\n\n    if (!this.motionOk()) return;\n    ncdAnimate(hold.querySelector('svg'), [\n      { transform: 'scale(.6)', opacity: 0 },\n      { transform: 'none', opacity: 1 },\n    ], { duration: NCD_DUR.md, delay: 60, easing: NCD_EASE.spring, fill: 'backwards' });\n    ncdAnimate(hold.querySelector('.ncd-moved__text'), [\n      { transform: 'translateX(-8px)', opacity: 0, filter: 'blur(2px)' },\n      { transform: 'none', opacity: 1, filter: 'blur(0)' },\n    ], { duration: NCD_DUR.md + 80, delay: 100, easing: NCD_EASE.out, fill: 'backwards' });\n    ncdAnimate(hold.querySelector('[data-sfl-undo]'), [\n      { opacity: 0 },\n      { opacity: 1 },\n    ], { duration: NCD_DUR.md, delay: 200, easing: NCD_EASE.out, fill: 'backwards' });\n  }\n\n  /* The quantity rolls inside its clip (.ncd-qty__roll). */\n  /* The quantity is already the new number by the time this runs -- it is\n     set the moment the shopper presses. This is only the acknowledgement,\n     and it stays where it is: the figure used to slide up from 8px below\n     through a blur, so the count a shopper had just chosen left its box\n     and came back. Same settle as every other figure in the drawer. */\n  settleQty(input) {\n    ncdAnimate(input, [{ opacity: 0.55 }, { opacity: 1 }], {\n      duration: NCD_DUR.xs,\n      easing: NCD_EASE.out,\n    });\n  }\n\n  /* The confirmation is an offer with a shelf life, not a row: the piece has\n     already left the bag and what is standing there is the chance to put it\n     back. Five seconds, then it takes itself down.\n\n     Keyed by the wrapper rather than the variant, because a re-render hands\n     back a different element for the same piece and the old timer should\n     find nothing to remove rather than the new row. */\n  startMovedTimer(wrapper) {\n    if (!wrapper) return;\n    this._movedTimers = this._movedTimers || new Map();\n    this.clearMovedTimer(wrapper);\n\n    this._movedTimers.set(wrapper, setTimeout(async () => {\n      this._movedTimers.delete(wrapper);\n      /* Undo may have taken it already, or a re-render replaced it. */\n      if (!wrapper.isConnected || !wrapper.classList.contains('show_sfl')) return;\n\n      /* Its content fades out of a 2px blur, then the card closes and the\n         rows below slide up. */\n      await ncdAnimate(wrapper.querySelector('.ncd-moved-hold'), [\n        { opacity: 1, filter: 'blur(0)' },\n        { opacity: 0, filter: 'blur(2px)' },\n      ], { duration: NCD_DUR.sm, easing: NCD_EASE.in, fill: 'forwards' });\n      if (!wrapper.isConnected || !wrapper.classList.contains('show_sfl')) return;\n\n      wrapper.style.boxSizing = 'border-box';\n      wrapper.style.overflow = 'hidden';\n      const h = wrapper.offsetHeight;\n      await ncdAnimate(wrapper, [{ height: h + 'px' }, { height: '0px' }], { duration: NCD_DUR.md, easing: NCD_EASE.io, fill: 'forwards' });\n      if (wrapper.isConnected) wrapper.remove();\n    }, 5000));\n  }\n\n  clearMovedTimer(wrapper) {\n    const t = this._movedTimers?.get(wrapper);\n    if (t) {\n      clearTimeout(t);\n      this._movedTimers.delete(wrapper);\n    }\n  }\n\n  /* One write per line, after the clicking stops. The latest number wins:\n     a shopper who goes 1 -> 4 -> 2 should end at 2, having written once. */\n  queueQuantity(key, quantity, card) {\n    this.pendingQty = this.pendingQty || new Map();\n\n    const pending = this.pendingQty.get(key);\n    if (pending) clearTimeout(pending.timer);\n\n    card?.classList.add('is-pending');\n\n    const timer = setTimeout(async () => {\n      this.pendingQty.delete(key);\n      const wanted = this.pendingQty.get(key)?.quantity ?? quantity;\n\n      try {\n        const cart = await this.changeLineByKey(key, wanted);\n        this.applyCart(cart);\n      } catch (err) {\n        console.error(err);\n        /* Fall back to the server's view rather than leave the drawer\n           showing a number the cart does not agree with. */\n        await this.rerenderItems();\n      } finally {\n        card?.classList.remove('is-pending');\n      }\n    }, 280);\n\n    this.pendingQty.set(key, { quantity, timer });\n  }\n\n  async handleSubmits(e) {\n    const form = e.target;\n    if (!form.matches('form[action=\"/cart/change\"]')) return;\n    e.preventDefault();\n    const card     = form.closest('.cart-drawer-item');\n    const key      = card?.getAttribute('data-line-item-key');\n    const qtyInput = form.querySelector('input[name=\"quantity\"]');\n    const nextQty  = qtyInput ? parseInt(qtyInput.value || '0', 10) : 0;\n    if (!key || !card) return;\n\n    if (nextQty === 0) {\n      const h = card.getBoundingClientRect().height;\n      card.style.height = h + 'px';\n      requestAnimationFrame(() => { card.classList.add('is-removing'); card.style.height = '0px'; card.style.opacity = '0'; });\n    } else {\n      card.classList.add('is-pending');\n    }\n\n    let swapped = false;\n    const fallback = setTimeout(() => {\n      if (!swapped && document.body.contains(card)) {\n        card.style.height = '0px'; card.style.opacity = '0'; card.classList.add('is-removing');\n      }\n    }, 5000);\n\n    try {\n      const cart = await this.changeLineByKey(key, nextQty);\n      swapped = true;\n      clearTimeout(fallback);\n      this.applyCart(cart);\n    } catch (err) {\n      clearTimeout(fallback);\n      console.error(err);\n      await this.rerenderItems();\n    } finally { card.classList.remove('is-pending'); }\n  }\n\n  /* server calls */\n  /* Returns the cart. /cart/change.js answers with the whole updated cart,\n     so there is never a reason to ask for it again afterwards -- which is\n     what the polling this replaced was doing. */\n  async changeLineByKey(key, quantity) {\n    const res = await fetch('/cart/change.js', {\n      method: 'POST',\n      headers: { 'Content-Type': 'application/json', Accept: 'application/json' },\n      body: JSON.stringify({ id: key, quantity })\n    });\n    if (!res.ok) {\n      console.error('Cart change failed', res.status, await res.text().catch(() => ''));\n      return null;\n    }\n    return res.json();\n  }\n\n  /* ---------- applying a cart to the drawer, without asking the server ----\n\n     A quantity or a removal changes numbers and, at most, drops a row. None\n     of that needs new markup, so none of it needs the section fetched again:\n     the row is already on the page and the arithmetic is in the cart JSON\n     that came back from the write.\n\n     Markup is only re-fetched when a row has to exist that does not yet --\n     an add from elsewhere on the page -- and that happens as the drawer\n     opens, where a request is expected anyway. */\n\n  lineNodes() {\n    return Array.from(this.drawer?.querySelectorAll('#cart-drawer-items .cart-drawer-item') || []);\n  }\n\n  /* Re-points a row at the line it is now, Shopify having given that line\n     a new key. The key is the variant plus a hash of the line's\n     properties, and on this shop something rewrites those properties on a\n     quantity update, so a row's key stops matching the cart the moment\n     the shopper presses plus. Matching on the key alone made applyCart\n     treat every row as a line that had been removed and empty the bag.\n\n     Everything that carries the key has to be moved with it, or the next\n     change would be written against a line that no longer exists. */\n  reseatLine(node, line) {\n    node.setAttribute('data-line-item-key', line.key);\n    node.querySelector('[data-qty]')?.setAttribute('data-key', line.key);\n    node.querySelector('.cart-drawer-item-delete')?.setAttribute('data-line-item-key', line.key);\n  }\n\n  applyCart(cart) {\n    if (!cart) return;\n\n    const lines = cart.items || [];\n    const byKey = new Map(lines.map(i => [i.key, i]));\n\n    /* A row under its \"Moved to Save for Later\" confirmation is out of this\n       altogether: it must not be redrawn, and it must not be allowed to\n       claim a line by variant either, since BOGO splits one add into two\n       lines of the same variant and the row just moved out would happily\n       reseat itself onto the one that stayed. */\n    const nodes = this.lineNodes().filter((node) => {\n      const w = node.closest('.cart-drawer-item-wrapper');\n      return !(w && (w.classList.contains('show_sfl') || w.hasAttribute('data-ncd-moving')));\n    });\n\n    /* A row already collapsing under the remove button is on its way out, so\n       it does not get to claim a line either -- with two rows of one variant\n       it would otherwise take the surviving line from the row that is\n       staying, and both would disappear. */\n    const claimable = nodes.filter(node => !node.classList.contains('is-removing'));\n\n    /* Who gets what is settled before anything is touched, so a line with no\n       row at all can be spotted while the rows still say what they said. */\n    const claimed = new Set();\n    const plan = new Map();\n\n    /* Rows whose key still matches get their line first, so a second row\n       of the same variant cannot steal it. */\n    claimable.forEach((node) => {\n      const key = node.getAttribute('data-line-item-key');\n      if (byKey.has(key) && !claimed.has(key)) {\n        claimed.add(key);\n        plan.set(node, byKey.get(key));\n      }\n    });\n\n    claimable.forEach((node) => {\n      if (plan.has(node)) return;\n      const variantId = Number(node.getAttribute('data-variant-id')) || 0;\n      const line = lines.find(l => Number(l.variant_id) === variantId && !claimed.has(l.key));\n      if (line) {\n        claimed.add(line.key);\n        plan.set(node, line);\n      }\n    });\n\n    /* A line with nowhere to land means the bag grew a row this cannot draw,\n       and patching in place can only ever update rows that already exist.\n       A BOGO does exactly that to a quantity change: asked for a second of\n       something, Shopify answers with two lines, one paid and one free, so\n       the free one had no row and the bag went on showing a single line\n       until the page was reloaded. The section renders both. */\n    /* A row Liquid drew at full price has no struck price, no saving line,\n       no badge and no discount chip -- there was nothing to draw them for.\n       applyLinePrices hides what it finds but never invents markup, by\n       design, so a line that has since become discounted could only change\n       the one number it has: the row read FREE with the amount it saved\n       nowhere on it. The section draws those rows properly. */\n    const needsDiscountMarkup = Array.from(plan.entries()).some(([node, line]) => {\n      const discounted = Number(line.original_price || 0) > Number(line.final_price || 0);\n      if (!discounted) return false;\n      return !node.querySelector('.ncd-item__was') || !node.querySelector('.cart_price_save');\n    });\n\n    if (lines.some(l => !claimed.has(l.key)) || needsDiscountMarkup) {\n      this.rerenderItems(cart);\n      return;\n    }\n\n    nodes.forEach((node) => {\n      const line = plan.get(node) || null;\n\n      /* Genuinely gone from the cart: drop the row rather than redraw. */\n      if (!line) {\n        const wrapper = node.closest('.cart-drawer-item-wrapper') || node;\n        wrapper.remove();\n        return;\n      }\n\n      if (node.getAttribute('data-line-item-key') !== line.key) this.reseatLine(node, line);\n\n      const input = node.querySelector('.ncd-qty__value, input[name=\"quantity\"]');\n      if (input && String(line.quantity) !== input.value) {\n        const was = parseInt(input.value || '0', 10) || 0;\n        input.value = line.quantity;\n        if (this.motionOk() && ncdShown(input)) this.settleQty(input);\n      }\n\n      const minusBtn = node.querySelector('.cart-drawer-quantity-selector-minus');\n      if (minusBtn) minusBtn.disabled = line.quantity < 2;\n\n      if (node.hasAttribute('data-bogo-eligible')) {\n        node.setAttribute('data-bogo-eligible', String(line.quantity));\n      }\n\n      this.applyLinePrices(node, line);\n    });\n\n    this.applyTotals(cart);\n    this.updateCounts(cart);\n    this.updateBogoPrompt();\n\n    if ((cart.item_count || 0) === 0) {\n      this.renderEmptyState();\n    } else {\n      this.hideEmptyState();\n      /* The other direction: a bag that has just stopped being empty wants\n         its widget back out of the park. */\n      this.syncRecs();\n    }\n\n    /* Hands the same cart to the reward tiers and anything else listening,\n       so the write is still the only request. */\n    emitCartUpdatedBatched(cart);\n  }\n\n  applyLinePrices(node, line) {\n    const now  = node.querySelector('.cart_item_price');\n    /* .cart_price_slash was the pre-redesign class; the struck price is\n       .ncd-item__was, so this used to hide nothing. */\n    const was  = node.querySelector('.ncd-item__was');\n    const save = node.querySelector('.cart_price_save_amount');\n\n    /* The gift is drawn as Figma 1725:561 draws it -- its own price struck\n       through, FREE beside it, and no saving line of its own, which the\n       design keeps for pieces being paid for. Only that missing saving row\n       is special now: the Mystery Gift function zeroes the line in the cart,\n       so the prices below come from the cart like every other row's. */\n    const isGift = node.classList.contains('ncd-item--gift');\n\n    /* Per piece, not per line, matching what Liquid draws: the row names what\n       one of these costs however many are in the bag, and the footer is where\n       quantity is accounted for. final_price and original_price are the\n       cart's own unit figures, with every automatic discount already applied\n       -- the _line_ variants beside them are the totals, and using those here\n       made the row jump to three times the price when the stepper went to 3. */\n    const final = Number(line.final_price ?? line.price ?? 0);\n    const strike = Math.max(Number(line.original_price ?? 0), final);\n    const saved = isGift ? 0 : Math.max(strike - final, 0);\n\n    /* Only what changed moves: prices roll, a struck price that appears\n       draws its line, and a saving, chip or tag that appears rises or\n       wipes into place. */\n    const motion = this.motionOk() && ncdShown(node);\n\n    if (now) {\n      const text = final <= 0 ? 'FREE' : this.priceMoney(final);\n      if (motion) ncdSetValue(now, text);\n      else now.textContent = text;\n    }\n    if (was) {\n      const wasHidden = was.hidden;\n      was.hidden = !(strike > final);\n      if (strike > final) was.textContent = this.priceMoney(strike);\n      if (motion && wasHidden && !was.hidden) ncdStrike(was);\n    }\n\n    /* The saving row is rendered by Liquid only when there is one, so it\n       may not be here to update -- and must not be invented in JS, or the\n       two renderers would disagree about what a line looks like. Hidden\n       instead, and the next open draws it properly. */\n    if (save) {\n      const row = save.closest('.cart_price_save') || save;\n      const rowWasHidden = row.hidden;\n      row.hidden = saved <= 0;\n      if (saved > 0) {\n        const isSet = row.classList.contains('ncd-item__saving--set');\n        const text = `${isSet ? 'Set Savings' : 'Saving'} ${this.priceMoney(saved)}`;\n        if (motion && !rowWasHidden) ncdSetValue(save, text);\n        else save.textContent = text;\n      }\n      if (motion && rowWasHidden && !row.hidden) ncdFadeIn(row);\n    }\n\n    /* The discount's own name and the badge, which Liquid reads off the\n       line's allocations. Without these a BOGO that stopped applying left\n       its \"HOLIDAYBOGO\" chip and its BOGO badge sitting on a line now\n       charged in full -- the prices above had corrected themselves and the\n       labels still said otherwise.\n\n       Same rule as the saving row: what Liquid did not render is not\n       invented here, only hidden. */\n    /* Only an allocation that actually took money off this line -- a BOGO\n       attaches itself to every line it entitles, at amount 0 on the ones\n       being paid for in full. Matches the Liquid, which asks the same. */\n    const allocs = line.line_level_discount_allocations || [];\n    const promoLabel = (allocs.find(a => Number(a.amount) > 0)?.discount_application?.title) || '';\n\n    const promo = node.querySelector('.ncd-item__promo');\n    if (promo) {\n      const promoWasHidden = promo.hidden;\n      promo.hidden = !promoLabel;\n      const text = promo.querySelector('span');\n      let renamed = false;\n      if (promoLabel && text && text.textContent !== promoLabel) { text.textContent = promoLabel; renamed = true; }\n      if (motion && !promo.hidden && (promoWasHidden || renamed)) ncdFadeIn(promo);\n    }\n\n    /* A gift's badge says Gift whatever the discounts are doing. */\n    const tag = node.querySelector('.ncd-item__tag');\n    const tagWasHidden = !!tag?.hidden;\n    const tagWas = tag?.textContent || '';\n    if (tag && !isGift) {\n      const isBogo = /BOGO|Buy One/i.test(promoLabel);\n      const pctOff = strike > 0 && strike > final\n        ? Math.round(((strike - final) / strike) * 100)\n        : 0;\n\n      if (isBogo) {\n        tag.hidden = false;\n        if (tag.textContent !== 'BOGO') tag.textContent = 'BOGO';\n      } else if (pctOff >= 1) {\n        const label = `${pctOff}% Off`;\n        tag.hidden = false;\n        if (tag.textContent !== label) tag.textContent = label;\n      } else {\n        tag.hidden = true;\n      }\n      if (motion && !tag.hidden && (tagWasHidden || tag.textContent !== tagWas)) ncdPop(tag);\n    }\n  }\n\n  applyTotals(cart) {\n    const root = this.drawer;\n    if (!root || !cart) return;\n\n    /* Same split the Liquid foot makes, Figma 1725:561: what the shop is\n       charging for the gift comes off the subtotal, and what the gift is\n       worth goes on the Total Savings line. A bag of $125 + $65 plus the\n       gift reads $190 over a $50 saving. */\n    const items = cart.items || [];\n    const isGift = (i) => {\n      const p = i?.properties || {};\n      const attr = String(p.attribute || '').toLowerCase();\n      if (attr === 'gift_product' && String(p.source || '').toLowerCase() === 'cart_drawer') return true;\n      const legacy = String(p.gift_product || '').toLowerCase();\n      return legacy === '1' || legacy === 'true' || legacy === 'yes';\n    };\n\n    let giftCharge = 0;\n    let giftValue = 0;\n    let before = 0;\n\n    items.forEach((i) => {\n      if (isGift(i)) {\n        giftCharge += Number(i.final_line_price || 0);\n        giftValue += Number(i.original_line_price || 0);\n        return;\n      }\n      before += Math.max(Number(i.original_line_price || 0), Number(i.final_line_price || 0));\n    });\n\n    const total = Math.max(Number(cart.total_price || 0) - giftCharge, 0);\n\n    /* Two numbers, as in the Liquid: what came off pieces being paid for\n       decides whether a struck subtotal is shown at all, and the gift's\n       worth is added on top for the savings line. */\n    const itemSavings = Math.max(before - total, 0);\n    const savings = itemSavings + giftValue;\n\n    /* The totals roll inside their own boxes as they change. */\n    const motion = this.motionOk();\n    const set = (sel, value) => {\n      const el = root.querySelector(sel);\n      if (!el) return;\n      if (motion && !el.hidden) ncdSetValue(el, value);\n      else el.textContent = value;\n    };\n\n    set('.cart-summary__row:not(.cart-summary__row--savings) .cart-summary__value', this.priceMoney(before));\n    set('.cart-summary__row--savings .cart-summary__value', this.priceMoney(savings));\n    set('.cart-summary__total-value', this.priceMoney(total));\n\n    /* Both only exist when something was saved, and the markup renders them\n       hidden -- so they have to be un-hidden, and hidden again, as the\n       number crosses zero. The struck subtotal draws its line as it\n       appears; the savings row opens and closes. */\n    const struck = root.querySelector('.ncd-foot__row--subtotal .cart-summary__value');\n    if (struck) {\n      const wasHidden = struck.hidden;\n      struck.hidden = itemSavings <= 0;\n      if (motion && wasHidden && !struck.hidden) ncdStrike(struck);\n    }\n    const savingsRow = root.querySelector('.ncd-foot__row--savings');\n    if (savingsRow) {\n      const show = savings > 0;\n      if (show) {\n        const wasHidden = savingsRow.hidden || !!savingsRow._ncdLeaving;\n        ncdCancelCollapse(savingsRow);\n        const reopening = savingsRow.hidden;\n        savingsRow.hidden = false;\n        if (motion && wasHidden && reopening) ncdEnter(savingsRow);\n      } else if (motion) {\n        ncdCollapseThenHide(savingsRow);\n      } else {\n        savingsRow.hidden = true;\n      }\n    }\n  }\n\n  /* The Buy-One-Get-One prompt follows the pieces it applies to: it belongs\n     under the last eligible row, and only while the count of those pieces\n     is odd -- an even count is already paired up. Figma 1725:44.\n\n     Every eligible row carries a copy of the prompt, so this only decides\n     which one is visible; no markup is built here. */\n  updateBogoPrompt() {\n    const notes = Array.from(this.drawer?.querySelectorAll('#cart-drawer-items [data-bogo-note]') || []);\n    if (!notes.length) return;\n\n    let units = 0;\n    let lastWrapper = null;\n\n    this.lineNodes().forEach((node) => {\n      if (!node.hasAttribute('data-bogo-eligible')) return;\n      const n = parseInt(node.getAttribute('data-bogo-eligible'), 10) || 0;\n      if (n <= 0) return;\n      units += n;\n      lastWrapper = node.closest('.cart-drawer-item-wrapper');\n    });\n\n    const show = units > 0 && units % 2 === 1;\n    const target = show && lastWrapper ? lastWrapper.querySelector('[data-bogo-note]') : null;\n\n    /* The prompt opens under its row and closes when the pair is complete.\n       Only a prompt that was shown from here closes with motion; one the\n       section has just drawn the other way is simply corrected. */\n    const variant = target ? (lastWrapper.querySelector('.cart-drawer-item')?.getAttribute('data-variant-id') || '') : '';\n    const prevVariant = this._bogoVariant;\n    const motion = this.motionOk() && prevVariant !== undefined && !this.inOpeningWindow();\n\n    notes.forEach((note) => {\n      if (note === target) return;\n      if (motion && note._ncdShown && !note.hidden) ncdCollapseThenHide(note);\n      else if (!note._ncdLeaving) note.hidden = true;\n      note._ncdShown = false;\n    });\n\n    if (target) {\n      const wasHidden = target.hidden || !!target._ncdLeaving;\n      ncdCancelCollapse(target);\n      target.hidden = false;\n      target._ncdShown = true;\n      if (motion && (wasHidden || variant !== prevVariant)) ncdEnter(target);\n    }\n    this._bogoVariant = variant;\n  }\n\n  /* Whether anything in the bag is on pre-order is Globo's answer, and it\n     arrives whenever its catalogue finishes loading -- after this renders,\n     and again when a line is added or removed. So rather than ask, the\n     drawer watches: the note follows the rows the shared script un-hides.\n\n     Attribute changes only, and the work is one query and at most one\n     attribute write, so it settles immediately. */\n  watchPreorderNote() {\n    const drawer = this.drawer;\n    if (!drawer) return;\n\n    const apply = () => {\n      const note = drawer.querySelector('[data-preorder-note]');\n      if (!note) return;\n      const any = !!drawer.querySelector('#cart-drawer-items [data-preorder-slot]:not([hidden])');\n      if (note.hidden !== !any) note.hidden = !any;\n    };\n\n    apply();\n\n    if (this._preorderWatcher) this._preorderWatcher.disconnect();\n    this._preorderWatcher = new MutationObserver(apply);\n    /* childList as well as the attribute: a re-render swaps the rows out\n       wholesale, and markup that arrives already in its final state changes\n       no attribute for the observer to catch -- which left the note hidden\n       under a row that was plainly showing its date, and would equally have\n       left it standing after the last pre-order line was replaced away. */\n    this._preorderWatcher.observe(drawer, {\n      subtree: true,\n      childList: true,\n      attributes: true,\n      attributeFilter: ['hidden'],\n    });\n  }\n\n  /* The footer carries either the gifting prompt or the note that was\n     written, never both. Figma 1726:277. */\n  applyGiftNote(note) {\n    const row = this.drawer?.querySelector('[data-gifting-row]');\n    const preview = this.drawer?.querySelector('[data-gift-note-row]');\n    const text = this.drawer?.querySelector('[data-gift-note-text]');\n    const has = !!(note && note.trim());\n    const hadNote = !!(preview && !preview.hidden);\n\n    if (row) row.hidden = has;\n    if (preview) preview.hidden = !has;\n    if (text) text.textContent = note || '';\n\n    /* A note saved opens its preview row; one cleared fades the gifting\n       prompt back in. */\n    if (this.motionOk() && has !== hadNote) {\n      if (has) ncdEnter(preview);\n      else if (row) ncdAnimate(row, [{ opacity: 0 }, { opacity: 1 }], { duration: NCD_DUR.sm, easing: NCD_EASE.out });\n    }\n  }\n\n  async fetchSectionHTML() {\n    if (!this.sectionId) throw new Error('Missing cart drawer section id');\n    const root = (window.Shopify?.routes?.root) || '/';\n    const url  = `${root}?sections=${encodeURIComponent(this.sectionId)}&_=${Date.now()}`;\n    const res  = await fetch(url, { credentials: 'same-origin', cache: 'no-store' });\n    if (!res.ok) throw new Error(`Section fetch failed: ${res.status}`);\n    let data; try { data = await res.json(); } catch { throw new Error('Invalid sections JSON'); }\n    return data[this.sectionId] || '';\n  }\n\n  async rerenderItems(expectedCart = null) {\n    const nonce = ++this.renderNonce;\n\n    /* Both requests at once. They ask different servers' worth of work --\n       the markup comes from the section, the numbers from /cart.js -- and\n       neither needs the other, but they used to run one after the other:\n       593ms then 637ms, measured, before anything could be drawn. Started\n       together they cost whichever is slower, not the sum.\n\n       The cart's rejection is caught here as well as awaited below, because\n       the early returns between the two would otherwise leave it\n       unhandled. */\n    const htmlJob = this.fetchSectionHTML();\n    const cartJob = expectedCart ? Promise.resolve(expectedCart) : this.fetchCartJson();\n    cartJob.catch(() => {});\n\n    let html = '';\n    try { html = await htmlJob; }\n    catch (e) { console.error(e); return; }\n    if (!html) return;\n    if (nonce !== this.renderNonce) return;\n\n    const cart = await cartJob;\n\n    const tpl = document.createElement('template');\n    tpl.innerHTML = html.trim();\n\n    const freshContent = tpl.content.querySelector('#cart-drawer-content');\n    const freshItems   = tpl.content.querySelector('#cart-drawer-items');\n\n    if (freshItems) this.sanitizeItemsFragment(freshItems, cart);\n\n    if (freshContent && this.content?.parentNode) {\n      /* The widget may be sitting inside the fragment about to be thrown\n         away. Rebuy mounts it once per page, so losing it means losing it\n         for good -- park it on the drawer and let syncRecs() put it back. */\n      const rebuy = this.drawer?.querySelector('[data-ncd-rebuy]');\n      if (rebuy && this.content.contains(rebuy)) this.drawer.appendChild(rebuy);\n\n      /* What was on screen, so only what actually changed moves. */\n      const before = this.snapshotBag();\n\n      this.content.parentNode.replaceChild(freshContent, this.content);\n      this.content = document.querySelector('#cart-drawer-content');\n      this.itemsWrap = document.querySelector('#cart-drawer-content #cart-drawer-items');\n      /* The tab state lives on the containers, and this is a new one. */\n      this.content.hidden = this.activeTab !== 'cart';\n\n      /* Nothing to play while the drawer is opening: the content is\n         already in place and the row-level motion would be the only thing\n         moving. */\n      if (!this.inOpeningWindow()) this.playBagChanges(before);\n    }\n\n    await this.updateCounts(cart);\n    this.updateBogoPrompt();\n\n    /* The shared script re-runs itself whenever nodes appear, but asking\n       directly fills the rows in this frame instead of the next one. */\n    if (window.EttikaPreorderCard) window.EttikaPreorderCard.decorate(this.drawer);\n    this.watchPreorderNote();\n\n    if (window.cartThresholds instanceof CartThresholds) {\n      await window.cartThresholds.update(cart || null);\n    }\n\n    this.syncRecs();\n\n    emitCartUpdatedBatched(cart || null);\n\n    /* Rebuy's cart used to be told about every change here -- reload() and\n       Cart.update() -- which it answered by fetching /cart.json. That cart\n       is stood down now, so the only thing those calls still did was cost a\n       request per change. */\n  }\n\n  /* What the bag looked like before a re-render: each row's tag, struck\n     price, saving, chip and prices, and the foot's totals. Rows already on\n     their way out -- under a confirmation, mid-move, collapsing -- are left\n     out, since they are not part of the bag any more. */\n  snapshotBag() {\n    const c = this.content;\n    if (!c || !this.motionOk() || c.hidden) return null;\n    const visible = (el) => !!el && !el.hidden;\n    const text = (el) => (el ? el.textContent.trim() : '');\n\n    const rows = [];\n    c.querySelectorAll('#cart-drawer-items .cart-drawer-item-wrapper').forEach((w) => {\n      if (w.hidden || w.classList.contains('show_sfl') || w.hasAttribute('data-ncd-moving')) return;\n      const card = w.querySelector('.cart-drawer-item');\n      if (!card || card.classList.contains('is-removing')) return;\n      const tag = card.querySelector('.ncd-item__tag');\n      const saveRow = card.querySelector('.cart_price_save');\n      const promo = card.querySelector('.ncd-item__promo');\n      rows.push({\n        key: card.getAttribute('data-line-item-key'),\n        variant: card.getAttribute('data-variant-id'),\n        tag: visible(tag) ? text(tag) : '',\n        was: visible(card.querySelector('.ncd-item__was')),\n        save: visible(saveRow),\n        saving: visible(saveRow) ? text(saveRow.querySelector('.cart_price_save_amount')) : '',\n        promo: visible(promo) ? text(promo) : '',\n        price: text(card.querySelector('.cart_item_price')),\n      });\n    });\n\n    const struck = c.querySelector('.ncd-foot__row--subtotal .cart-summary__value');\n    const savingsRow = c.querySelector('.ncd-foot__row--savings');\n    return {\n      rows,\n      total: text(c.querySelector('.cart-summary__total-value')),\n      struck: visible(struck) ? text(struck) : null,\n      savings: visible(savingsRow) ? text(savingsRow.querySelector('.cart-summary__value')) : null,\n      empty: visible(c.querySelector('[data-cart-empty]')),\n    };\n  }\n\n  /* After a re-render, plays what changed against the snapshot: a new row\n     opens (the accepted gift flashes as it does), an undone row grows back\n     from its confirmation, tags wipe in, struck prices draw, savings and\n     chips rise, numbers roll, the savings line opens. Rows that left are\n     already gone by now -- removals close themselves before the cart is\n     told. */\n  playBagChanges(before) {\n    const c = this.content;\n    if (!before || !c || c.hidden || !this.motionOk()) return;\n\n    const pool = before.rows.slice();\n    const claim = (key, variant) => {\n      let i = pool.findIndex(r => r.key === key);\n      if (i === -1) i = pool.findIndex(r => r.variant === variant);\n      return i === -1 ? null : pool.splice(i, 1)[0];\n    };\n    /* The fresh markup already carries the new figure; this puts the old\n       one back for an instant so the change is the thing that is seen,\n       rather than a number that was simply always there. */\n    const settleFrom = (el, oldText) => {\n      if (!el || !oldText) return;\n      const now = el.textContent.trim();\n      if (now === oldText) return;\n      el.textContent = oldText;\n      ncdSetValue(el, now);\n    };\n\n    c.querySelectorAll('#cart-drawer-items .cart-drawer-item-wrapper').forEach((w) => {\n      if (w.hidden) return;\n      const card = w.querySelector('.cart-drawer-item');\n      if (!card) return;\n      const key = card.getAttribute('data-line-item-key');\n      const variant = card.getAttribute('data-variant-id');\n      const old = claim(key, variant);\n\n      if (!old) {\n        const morph = this._undoMorph;\n        if (morph && String(morph.variant) === variant) {\n          /* Undo: the card grows back to the row's height and the row\n             settles in from a 3px blur and a 6px rise. */\n          this._undoMorph = null;\n          w.style.boxSizing = 'border-box';\n          w.style.overflow = 'hidden';\n          const h = w.offsetHeight;\n          ncdAnimate(w, [{ height: morph.height + 'px' }, { height: h + 'px' }], { duration: NCD_DUR.md, easing: NCD_EASE.io })\n            .then(() => ncdResetBox(w));\n          ncdAnimate(card, [\n            { opacity: 0, filter: 'blur(3px)', transform: 'translateY(6px)' },\n            { opacity: 1, filter: 'blur(0)', transform: 'none' },\n          ], { duration: NCD_DUR.md + 80, delay: 120, easing: NCD_EASE.out, fill: 'backwards' });\n        } else {\n          ncdEnter(w, { highlight: card.classList.contains('ncd-item--gift') });\n        }\n        return;\n      }\n\n      const tag = card.querySelector('.ncd-item__tag');\n      if (tag && !tag.hidden && tag.textContent.trim() !== old.tag) ncdPop(tag);\n\n      const was = card.querySelector('.ncd-item__was');\n      if (was && !was.hidden && !old.was) ncdStrike(was);\n\n      const saveRow = card.querySelector('.cart_price_save');\n      if (saveRow && !saveRow.hidden) {\n        if (!old.save) ncdFadeIn(saveRow);\n        else settleFrom(saveRow.querySelector('.cart_price_save_amount'), old.saving);\n      }\n\n      const promo = card.querySelector('.ncd-item__promo');\n      if (promo && !promo.hidden && promo.textContent.trim() !== old.promo) ncdFadeIn(promo);\n\n      settleFrom(card.querySelector('.cart_item_price'), old.price);\n    });\n\n    settleFrom(c.querySelector('.cart-summary__total-value'), before.total);\n\n    const struck = c.querySelector('.ncd-foot__row--subtotal .cart-summary__value');\n    if (struck && !struck.hidden) {\n      if (before.struck == null) ncdStrike(struck);\n      else settleFrom(struck, before.struck);\n    }\n\n    const savingsRow = c.querySelector('.ncd-foot__row--savings');\n    if (savingsRow && !savingsRow.hidden) {\n      if (before.savings == null) ncdEnter(savingsRow);\n      else settleFrom(savingsRow.querySelector('.cart-summary__value'), before.savings);\n    }\n\n    const empty = c.querySelector('[data-cart-empty]');\n    if (empty && !empty.hidden && !before.empty) ncdFadeIn(empty);\n  }\n\n  /* ---- recommendations ------------------------------------------------\n\n     Two widgets share one spot in each tab: Rebuy while the tab has\n     something in it, Kimonix when it is empty. Which one is wanted is a\n     question about counts, so it is answered in one place and both tabs are\n     settled together.\n  */\n\n  /* Rebuy scans the page once, on its own load. #cart-drawer-content is\n     replaced wholesale by every rerenderItems(), so the widget it left\n     behind goes with it and the markup that lands in its place is one it has\n     never seen -- which is why the Must Haves strip came back blank after\n     the first thing was added to an empty bag and stayed blank until the\n     page was reloaded. reinit() is Rebuy's own rescan.\n\n     Guarded on an empty widget: reinit() walks every widget on the page, so\n     it should answer markup that genuinely has none, not every cart write. */\n  reinitRebuy() {\n    const R = window.Rebuy;\n    if (!R || typeof R.reinit !== 'function') return;\n\n    /* Deliberately not offsetParent: base.css carries div:empty{display:none},\n       and a Rebuy placeholder waiting to be filled is empty by definition --\n       so the one widget this is here to catch is exactly the one offsetParent\n       calls invisible. Ask about the drawer and the tab instead, which is\n       what \"nobody is looking at this\" actually means.\n\n       reinit() walks every widget on the page, so it stays behind both that\n       and an empty widget rather than running on each cart write. */\n    if (this.drawer.hasAttribute('hidden')) return;\n\n    const onShownTab = (el) => {\n      const host = el.closest('#cart-sfl-items') || el.closest('#cart-drawer-content');\n      return host ? !host.hidden : true;\n    };\n\n    /* One attempt per node. A widget Rebuy has nothing to say about stays\n       empty however often it is asked, and the flag goes away by itself when\n       a re-render brings a new node with it. */\n    const stale = Array.from(this.drawer.querySelectorAll('[data-ncd-rebuy]'))\n      .filter(el => !el.hidden && onShownTab(el) && el.innerHTML.trim() === '' && !el.dataset.ncdReinit);\n    if (!stale.length) return;\n\n    stale.forEach(el => { el.dataset.ncdReinit = '1'; });\n    R.reinit();\n  }\n\n  /* The Kimonix block is driven by js-pdp-kimonix.js, which the PDP already\n     uses -- the markup is that file's contract on purpose, so it is reused\n     rather than copied. It boots on load, so a block that arrives later\n     needs the file run again; re-running it is safe because its boot() skips\n     any section already carrying data-kmnx-loaded. */\n  loadKimonix() {\n    const root = this.drawer;\n    if (!root) return;\n\n    /* Only once a block is genuinely on screen -- shown, on the tab the\n       shopper is looking at, in a drawer that is open. Three stylesheets and\n       a script should not be fetched for a saved tab nobody has opened. */\n    const pending = Array.from(root.querySelectorAll('[data-pdp-kimonix]'))\n      .some(el => el.offsetParent !== null && !el.dataset.kmnxLoaded);\n    if (!pending) return;\n\n    const once = (href) => {\n      if (!href || document.querySelector(`link[href=\"${href}\"]`)) return;\n      const link = document.createElement('link');\n      link.rel = 'stylesheet';\n      link.href = href;\n      document.head.appendChild(link);\n    };\n    once(root.dataset.kmnxCardCss);\n    once(root.dataset.kmnxCss);\n\n    const src = root.dataset.kmnxJs;\n    if (!src) return;\n    const tag = document.createElement('script');\n    tag.src = src;\n    document.head.appendChild(tag);\n  }\n\n  /* cartCount and savedCount are read from the drawer itself rather than\n     passed in, so this can be called from anywhere either side changes. */\n  syncRecs() {\n    const root = this.drawer;\n    if (!root) return;\n\n    /* The empty block is the question, asked the way the shopper asks it:\n       if \"Your bag is empty\" is on screen, Must Haves has no business being\n       under it. Tying the two together means the one code path that reveals\n       the empty state is the same one that parks the widget, so the two\n       cannot end up disagreeing.\n\n       Counting rows is not equivalent -- more than one flow leaves a row\n       standing on purpose, moving the last piece to Save for Later keeps it\n       in place under its confirmation -- and a remembered count can go stale\n       on a page that re-initialises under it. Both stay as fallbacks for the\n       first call, before the section has rendered. */\n    const emptyEl = root.querySelector('[data-cart-empty]');\n    const hasCart = emptyEl\n      ? emptyEl.hidden\n      : (typeof this.cartCount === 'number'\n          ? this.cartCount > 0\n          : !!root.querySelector('#cart-drawer-items .cart-drawer-item-wrapper:not([hidden])'));\n    const hasSaved = (this.savedItems?.length || 0) > 0;\n\n    const show = (el, on) => { if (el && el.hidden === on) el.hidden = !on; };\n\n    /* One widget, moved to whichever tab has something to recommend\n       alongside. Where it goes is an anchor in the markup, so this does not\n       need to know what either tab is built out of. */\n    const rebuy = root.querySelector('[data-ncd-rebuy]');\n    if (rebuy) {\n      const wanted = this.activeTab === 'sfl'\n        ? (hasSaved ? 'sfl' : null)\n        : (hasCart ? 'cart' : null);\n      const anchor = wanted && root.querySelector(`[data-rebuy-anchor=\"${wanted}\"]`);\n\n      if (anchor) {\n        /* Moving the node keeps what Rebuy rendered into it; re-rendering it\n           would mean asking Rebuy for the markup again on every tab switch. */\n        if (rebuy.parentNode !== anchor.parentNode || rebuy.previousElementSibling !== anchor) {\n          anchor.after(rebuy);\n        }\n        show(rebuy, true);\n      } else {\n        /* Parked back outside both tabs, where no re-render can reach it. */\n        if (rebuy.parentNode !== root) root.appendChild(rebuy);\n        show(rebuy, false);\n      }\n    }\n\n    show(root.querySelector('[data-ncd-recs=\"cart\"]'), !hasCart);\n    show(root.querySelector('[data-ncd-recs=\"sfl\"]'), !hasSaved);\n\n    this.loadKimonix();\n    this.reinitRebuy();\n    this.syncGiftBox();\n  }\n\n  /* One gift box to a bag. Read off the rows rather than a remembered\n     flag, so adding the box, removing it, or a re-render that brings the\n     cart back from the server all leave the button saying the right thing.\n     Called from syncRecs because that already runs everywhere the cart\n     changes. */\n  syncGiftBox() {\n    const btn = this.drawer?.querySelector('[data-add-giftbox]');\n    if (!btn) return;\n\n    const id = Number(btn.getAttribute('data-variant-id'));\n    if (!id) return;\n\n    const inCart = this.lineNodes().some(\n      (node) => Number(node.getAttribute('data-variant-id')) === id\n    );\n\n    const label = btn.querySelector('[data-giftbox-label]');\n    if (label) label.textContent = inCart ? 'Added' : 'Add';\n    btn.disabled = inCart;\n    btn.classList.toggle('is-added', inCart);\n  }\n\n  /* Shows the empty block the section already rendered, rather than writing\n     markup from here. There is one copy of it, in the Liquid, so the two\n     cannot drift. */\n  async renderEmptyState() {\n    const empty = this.drawer?.querySelector('[data-cart-empty]');\n    const items = this.itemsWrap;\n\n    /* Whatever is still on screen, this is the empty bag. */\n    this.cartCount = 0;\n\n    if (items) {\n      /* The empty block lives inside the items wrapper, so the rows and the\n         footer are hidden rather than the wrapper itself. */\n      items.querySelectorAll('.cart-drawer-item-wrapper, .new-cart-drawer-bottom').forEach(el => { el.hidden = true; });\n    }\n\n    if (empty) {\n      const wasHidden = empty.hidden;\n      empty.hidden = false;\n      this.updateSavedSummary();\n      /* The last row has closed; the empty state rises into its place. */\n      if (wasHidden && this.motionOk()) ncdFadeIn(empty);\n    }\n\n    this.syncRecs();\n\n    emitCartUpdatedBatched();\n  }\n\n  /* The other way round, for when a cart stops being empty. */\n  hideEmptyState() {\n    const empty = this.drawer?.querySelector('[data-cart-empty]');\n    if (empty) empty.hidden = true;\n    if (this.itemsWrap) {\n      this.itemsWrap.hidden = false;\n      this.itemsWrap.querySelectorAll('.cart-drawer-item-wrapper, .new-cart-drawer-bottom').forEach(el => { el.hidden = false; });\n    }\n  }\n}\n\n/* ------------------ Rewards tiers and the mystery gift ------------------ */\nclass CartThresholds {\n  constructor(rootSel = '[data-threshold-root]') {\n    this.rootSel = rootSel;\n    this.rebindDom();\n\n    this.HEADLINE_UNLOCKED = this.root?.getAttribute('data-headline-unlocked') || \"You\u2019ve unlocked all perks.\";\n    this.onDomReady    = this.update.bind(this);\n    /* Uses the cart the event carries when there is one, instead of asking\n       for it again a moment after the write that produced it. */\n    this.onCartUpdated = (e) => this.update(e?.detail?.cart || null);\n    document.addEventListener('DOMContentLoaded', this.onDomReady);\n    document.addEventListener('cart:updated', this.onCartUpdated);\n\n    document.addEventListener('click', async (e) => {\n      const btn = e.target.closest('[data-add-gift]');\n      if (!btn) return;\n      if (this._isMysteryGiftUnlockedCached !== true) { btn.blur(); return; }\n      btn.disabled = true;\n\n      /* Held up for the length of this, because between the sheet closing\n         and the re-render telling us the gift has landed there is a moment\n         where the bar's test reads: unlocked, no gift in the cart, no sheet\n         open -- and it shows. Then the rows come back, the gift is there,\n         and it hides again. That flash is the claim button appearing and\n         vanishing as the gift is added. */\n      this._giftAdding = true;\n      btn.classList.add('is-loading');\n      try {\n        const variantId = Number(btn.getAttribute('data-variant-id'));\n        if (!variantId) return;\n        await this.addGiftToCart(variantId);\n        giftSkip.set(false);\n        window.cartDrawer?.closeSheets?.();\n        await window.cartDrawer?.rerenderItems?.();\n      } catch (err) { console.error(err); }\n      finally {\n        btn.disabled = false;\n        btn.classList.remove('is-loading');\n        this._giftAdding = false;\n        /* Asked once more now the cart is settled, so whatever the answer\n           is it is reached without passing through the wrong one. */\n        this.refreshGiftBar();\n      }\n    });\n\n    /* Picking a material re-points the add button and redraws the sheet's\n       image and price, without touching the cart. */\n    document.addEventListener('change', (e) => {\n      const input = e.target.closest('input.js-gift-material');\n      if (!input) return;\n      const sheet = input.closest('[data-gift-sheet]');\n      if (!sheet) return;\n\n      const addBtn = sheet.querySelector('[data-add-gift]');\n      const newVariantId = Number(input.getAttribute('data-variant-id'));\n      if (addBtn && newVariantId) addBtn.setAttribute('data-variant-id', String(newVariantId));\n\n      const img = sheet.querySelector('[data-gift-image]');\n      const newImg = input.getAttribute('data-image');\n      if (img && newImg) img.src = newImg;\n\n      const was = sheet.querySelector('[data-gift-was]');\n      const price = input.getAttribute('data-price');\n      if (was && price) was.textContent = price;\n\n      /* The finish name fades out and back in with the new one. */\n      const label = sheet.querySelector('[data-gift-variant-label] li');\n      const labelHost = label?.closest('[data-gift-variant-label]');\n      if (label && labelHost && !ncdReducedMotion()) {\n        labelHost.classList.add('is-swapping');\n        clearTimeout(labelHost._ncdSwap);\n        labelHost._ncdSwap = setTimeout(() => {\n          label.textContent = input.value;\n          labelHost.classList.remove('is-swapping');\n        }, 250);\n      } else if (label) {\n        label.textContent = input.value;\n      }\n\n      /* The selected underline slides across from the swatch it left. The\n         one it left is cleared at once rather than shrinking, so there is\n         only ever one line. */\n      const inputs = Array.from(sheet.querySelectorAll('input.js-gift-material'));\n      const prevInput = sheet._ncdSwatch || inputs.filter(i => i.defaultChecked).pop() || null;\n      sheet._ncdSwatch = input;\n      if (prevInput && prevInput !== input) {\n        const fromBg = prevInput.parentElement?.querySelector('.ncd-swatch__bg');\n        const toBg = input.parentElement?.querySelector('.ncd-swatch__bg');\n        if (fromBg) {\n          fromBg.classList.add('is-leaving');\n          requestAnimationFrame(() => requestAnimationFrame(() => fromBg.classList.remove('is-leaving')));\n        }\n        const fromBox = prevInput.closest('.ncd-swatch')?.getBoundingClientRect();\n        const toBox = input.closest('.ncd-swatch')?.getBoundingClientRect();\n        if (toBg && fromBox && toBox) {\n          ncdAnimate(toBg, [\n            { transform: `translateX(${fromBox.left - toBox.left}px)` },\n            { transform: 'none' },\n          ], { duration: 700, easing: NCD_EASE.io, pseudoElement: '::after' });\n        }\n      }\n    });\n\n    this._giftAutoRemoving = false;\n    this._giftOffered = false;\n  }\n\n  rebindDom() {\n    this.root         = document.querySelector(this.rootSel);\n    this.pills        = this.root ? Array.from(this.root.querySelectorAll('[data-cth-pill]')) : [];\n    this.headlineNode = this.root?.querySelector('[data-cth-headline]') || null;\n    this.drawer       = document.getElementById('cart-drawer');\n    this.giftSheet    = this.drawer?.querySelector('[data-gift-sheet]') || null;\n    this.claimBar     = this.drawer?.querySelector('[data-gift-claim]') || null;\n  }\n\n  async fetchCart() {\n    const res = await fetch(`/cart.js?_=${Date.now()}`, { credentials: 'same-origin', cache: 'no-store' });\n    return res.json();\n  }\n\n  isGiftLine(item) {\n    const props = item?.properties || {};\n    const attr = (props.attribute || '').toString().toLowerCase();\n    const src  = (props.source || '').toString().toLowerCase();\n    if (attr === 'gift_product' && src === 'cart_drawer') return true;\n    const legacy = (props.gift_product || '').toString().toLowerCase();\n    return legacy === '1' || legacy === 'true' || legacy === 'yes';\n  }\n\n  findGiftLine(cart) {\n    if (!cart?.items) return null;\n    for (let i = 0; i < cart.items.length; i++) {\n      if (this.isGiftLine(cart.items[i])) return { line: i + 1, key: cart.items[i].key };\n    }\n    return null;\n  }\n\n  computeDiscountedSubtotal(cart) {\n    if (!cart?.items) return 0;\n    let sum = 0;\n    for (const item of cart.items) {\n      if (this.isGiftLine(item)) continue;\n      sum += Number(item.final_line_price ?? item.line_price ?? (item.price * item.quantity) ?? 0);\n    }\n    return sum;\n  }\n\n  /* Thresholds are written into the markup in the shop's own currency,\n     because that is the only currency Liquid knows: no conversion rate\n     reaches it, so its money filter re-letters $160 as \"Rs.160\" and the\n     comparison that followed was 4,130,000 against 16,000 -- every bag in a\n     converted market unlocked everything at once.\n\n     The storefront does hand a rate to JavaScript, and it is the same one\n     the gift's discount function converts with, so the two agree about when\n     a bag qualifies. */\n  presentmentRate() {\n    const rate = Number(window.Shopify?.currency?.rate);\n    return rate > 0 ? rate : 1;\n  }\n\n  /* Rounded up to a whole unit of the local currency, so the figure on the\n     pill is one a shopper can actually reach rather than Rs.44,814.70, and\n     so the number shown is the number compared against. Up rather than down\n     on purpose: the drawer should never promise a gift at a total the\n     discount would then turn down. */\n  localThreshold(shopMinorUnits) {\n    const converted = Number(shopMinorUnits || 0) * this.presentmentRate();\n    return Math.ceil(converted / 100) * 100;\n  }\n\n  getTiers() {\n    return this.pills.map(el => ({\n      el,\n      type: el.dataset.perk,\n      label: el.dataset.label || el.dataset.perk,\n      threshold: this.localThreshold(el.dataset.threshold),\n    })).sort((a, b) => a.threshold - b.threshold);\n  }\n\n  /* The pill still carries the shop-currency figure Liquid wrote, so it is\n     restated here in what the shopper is actually being charged in. */\n  localiseTierLabels() {\n    const format = this.drawer?.getAttribute('data-money-format');\n    this.pills.forEach((el) => {\n      const slot = el.querySelector('[data-cth-amount]');\n      if (!slot || !el.dataset.threshold) return;\n      const text = formatMoneyLikeLiquid(this.localThreshold(el.dataset.threshold), format);\n      if (slot.textContent !== text) slot.textContent = text;\n    });\n  }\n\n  summarize(total, tiers) {\n    const next = tiers.find(t => total < t.threshold) || null;\n    return { next, remaining: next ? (next.threshold - total) : 0 };\n  }\n\n  async addGiftToCart(variantId) {\n    await ncdInternalAdd({ items: [{ id: variantId, quantity: 1, properties: { source: 'cart_drawer', attribute: 'gift_product' } }] });\n  }\n\n  async removeGiftFromCart() {\n    const cart = await this.fetchCart();\n    const gift = this.findGiftLine(cart);\n    if (!gift) return;\n    await fetch('/cart/change.js', {\n      method: 'POST', headers: { 'Content-Type': 'application/json' },\n      body: JSON.stringify({ line: gift.line, quantity: 0 })\n    });\n  }\n\n  /* The claim bar, Figma 1725:808.\n\n     There is an unclaimed gift whenever the threshold is crossed and the\n     gift is not in the cart, and the bar is simply how that is said when\n     the sheet is not on screen. It does not depend on *how* the sheet\n     went away -- skipping it, closing it with the cross, clicking the\n     scrim and pressing Escape all leave the same situation, and all of\n     them used to leave the gift with nothing on screen to claim it from\n     except the cross.\n\n     Re-read on every cart change and every time a sheet opens or closes,\n     so it cannot be left showing beside the gift it is offering. */\n  refreshGiftBar() {\n    this.rebindDom();\n    if (!this.claimBar) return;\n\n    const unlocked  = this._isMysteryGiftUnlockedCached === true;\n    const hasGift   = this._hasGiftLineCached === true;\n    /* A sheet about to open counts as open: the bar should not flash up\n       for the second the sheet waits for the bar to fill. A gift on its way\n       into the cart counts too, for the same reason in reverse -- the cart\n       does not know about it yet, and the bar would offer what the shopper\n       has just taken. */\n    const busy = window.cartDrawer?.openSheet === 'gift'\n      || this._giftSheetPending === true\n      || this._giftAdding === true;\n\n    const show = unlocked && !hasGift && !busy && !!this.giftSheet;\n    const wasHidden = this.claimBar.hidden;\n    this.claimBar.hidden = !show;\n\n    /* Arriving, the bar opens above the footer. A re-render redraws a bar\n       that was already up, so that is not an arrival.\n\n       The Claim button used to pulse a ring three times as the bar landed;\n       QA asked for it to go, and the ring element went with it. */\n    if (show && wasHidden && !this._claimShown && window.cartDrawer?.motionOk?.()) {\n      ncdEnter(this.claimBar);\n    }\n    this._claimShown = show;\n  }\n\n  /* The ring round a track as its tier unlocks -- twice, from the moment\n     the fill lands -- and the label's small pop. */\n  pulseTier(track) {\n    if (!track || ncdReducedMotion() || !window.cartDrawer?.motionOk?.()) return;\n    ncdAnimate(track, [\n      { boxShadow: '0 0 0 0 rgba(124, 17, 0, .45)' },\n      { boxShadow: '0 0 0 6px rgba(124, 17, 0, 0)' },\n    ], { duration: NCD_DUR.pulse, delay: NCD_DUR.bar, iterations: 2, easing: NCD_EASE.out, pseudoElement: '::before' });\n    ncdAnimate(track.parentElement?.querySelector('.ncd-rewards__label'), [\n      { transform: 'scale(1)' },\n      { transform: 'scale(1.06)', fontWeight: 600 },\n      { transform: 'scale(1)' },\n    ], { duration: 360, delay: NCD_DUR.bar, easing: NCD_EASE.spring });\n  }\n\n  /* Three states, Figma 1725:412 / 1725:561 / 1725:704:\n\n     - unlocked, not in the cart -> the sheet, offered once; the claim bar\n                                    whenever that sheet is not up\n     - unlocked, in the cart     -> nothing; the line itself says it\n     - locked                    -> nothing, and the offer resets\n\n     The sheet is offered once per unlock rather than on every cart change,\n     or it would reopen itself over a shopper trying to edit quantities. */\n  applyGiftState(giftUnlocked, hasGiftLine) {\n    const drawer = window.cartDrawer;\n    this._hasGiftLineCached = hasGiftLine;\n\n    if (!giftUnlocked) {\n      this._giftOffered = false;\n      this._giftSheetPending = false;\n      clearTimeout(this._giftSheetTimer);\n      giftSkip.set(false);\n      if (drawer?.openSheet === 'gift') drawer.closeSheets();\n      this.refreshGiftBar();\n      return;\n    }\n\n    if (hasGiftLine) {\n      this._giftOffered = true;\n      this._giftSheetPending = false;\n      clearTimeout(this._giftSheetTimer);\n      if (drawer?.openSheet === 'gift') drawer.closeSheets();\n      this.refreshGiftBar();\n      return;\n    }\n\n    const drawerOpen = this.drawer?.classList.contains('active');\n    if (!this._giftOffered && !giftSkip.get() && this.giftSheet && drawerOpen) {\n      this._giftOffered = true;\n      if (this._giftJustCrossed && !ncdReducedMotion()) {\n        /* Crossed just now: the sheet waits for the fill and its pulse to\n           land (1s) before it rises. */\n        this._giftSheetPending = true;\n        clearTimeout(this._giftSheetTimer);\n        this._giftSheetTimer = setTimeout(() => {\n          this._giftSheetPending = false;\n          const still = this._isMysteryGiftUnlockedCached === true && this._hasGiftLineCached !== true && !giftSkip.get();\n          if (still && this.drawer?.classList.contains('active') && !window.cartDrawer?.openSheet) {\n            window.cartDrawer?.showSheet('gift');\n          } else if (still) {\n            /* Never seen, so still on offer next time. */\n            this._giftOffered = false;\n          }\n          this.refreshGiftBar();\n        }, NCD_DUR.bar + 200);\n      } else {\n        drawer?.showSheet('gift');\n      }\n    }\n\n    this.refreshGiftBar();\n  }\n\n  /* Moves one reward track to where it should be, smoothly.\n\n     Two things got in the way of that. The bar was animating its width,\n     which lays the track out again on every frame of the animation --\n     and it does this while the drawer is re-rendering rows and the\n     recommendation widget is loading images, so it stuttered. It is a\n     transform now, which the compositor handles on its own.\n\n     The other is that rerenderItems replaces the rewards band wholesale,\n     so after every add and remove the bar is a brand new element sitting\n     at zero. Told its new value it would snap there from nothing. It is\n     put back where the old one was first -- transition off, so that part\n     is instant and invisible -- and only then animated to the new value,\n     so a removal recedes from where it was rather than refilling from\n     empty.\n\n     The start state is committed with a forced reflow rather than deferred\n     to the next animation frame. A frame is not a reliable place to put\n     this: the browser throttles frames on a hidden tab, and a second\n     re-render can replace the bar before the frame arrives, leaving the\n     value set on an element no longer in the document and the one on\n     screen stuck at nothing. Two reflows on a two-tier bar costs nothing\n     and always happens. */\n  setFill(type, fill, ratio) {\n    this._fill = this._fill || {};\n    const target = `scaleX(${ratio})`;\n    /* The track carries the fill's ratio too, for the wave that runs\n       through its unfilled part. */\n    const track = fill.closest('.ncd-rewards__track');\n\n    /* This bar is already on its way there. A cart change raises\n       cart:updated more than once, and running the sequence below again\n       would cancel the transition in flight and snap the bar to the end\n       -- the jump this is all here to remove. Compared against the\n       element's own inline value, not the remembered one, so a bar that\n       has just been re-rendered still gets drawn even when the number\n       has not moved. */\n    if (fill.style.transform === target) {\n      this._fill[type] = ratio;\n      if (track) track.style.setProperty('--ncd-fill', String(ratio));\n      return;\n    }\n\n    const known = this._fill[type] != null;\n    const from = known ? this._fill[type] : 0;\n    this._fill[type] = ratio;\n\n    fill.style.transition = 'none';\n    fill.style.transform = `scaleX(${from})`;\n    if (track) track.style.setProperty('--ncd-fill', String(from));\n    void fill.offsetWidth;\n    if (track) getComputedStyle(track, '::after').left;\n    fill.style.transition = '';\n    fill.style.transform = target;\n    if (track) track.style.setProperty('--ncd-fill', String(ratio));\n\n    if (known && from < 1 && ratio >= 1) this.pulseTier(track);\n  }\n\n  async update(known = null) {\n    this.rebindDom();\n    if (!this.root) return;\n    const cart = known || await this.fetchCart();\n    const discountedSubtotal = this.computeDiscountedSubtotal(cart);\n    this.localiseTierLabels();\n\n    const tiers = this.getTiers();\n\n    if (!tiers.length) {\n      if (this.headlineNode) this.headlineNode.textContent = '';\n      this.applyGiftState(false, false);\n      return;\n    }\n\n    this.pills.forEach(p => p.classList.remove('is-active'));\n    tiers.forEach(t => { if (discountedSubtotal >= t.threshold) t.el.classList.add('is-active'); });\n\n    const { next, remaining } = this.summarize(discountedSubtotal, tiers);\n    if (this.headlineNode) {\n      /* The design's wording, and the shop's own currency rather than a\n         hard-coded dollar amount. */\n      let headline;\n      if (!next) headline = this.HEADLINE_UNLOCKED;\n      else {\n        const away = formatMoneyLikeLiquid(remaining, document.getElementById('cart-drawer')?.getAttribute('data-money-format'));\n        headline = `You\u2019re ${away} away from ${next.label ? next.label.toLowerCase() : 'your next perk'}`;\n      }\n      this.headlineNode.textContent = headline;\n\n      /* A new message rises 4px into place. Compared with the last one\n         written rather than the node, which a re-render replaces. */\n      if (this._lastHeadline != null && this._lastHeadline !== headline && window.cartDrawer?.motionOk?.()) {\n        ncdAnimate(this.headlineNode, [\n          { opacity: 0, transform: 'translateY(4px)', filter: 'blur(2px)' },\n          { opacity: 1, transform: 'none', filter: 'blur(0)' },\n        ], { duration: NCD_DUR.md, delay: 60, easing: NCD_EASE.out, fill: 'backwards' });\n      }\n      this._lastHeadline = headline;\n    }\n\n    /* Which loop the bar runs: the invite while the bag is empty, the\n       light sweep once everything is unlocked, and otherwise the beckon\n       wave on the one tier still being worked towards. */\n    const isEmpty = Number(cart?.item_count || 0) === 0;\n    const lastTier = tiers[tiers.length - 1];\n    this.root.classList.toggle('is-empty', isEmpty);\n    this.root.classList.toggle('is-full', !isEmpty && discountedSubtotal >= lastTier.threshold);\n    const beckon = isEmpty ? null : tiers.find(t => discountedSubtotal < t.threshold);\n    tiers.forEach(t => t.el.classList.toggle('is-beckon', t === beckon));\n\n    const prevGiftFill = this._fill ? this._fill.mystery_gift : undefined;\n\n    /* A track per tier, each filling against its own threshold and the one\n       before it, rather than one bar measured against the last tier. That\n       is what the design draws: the free-shipping track fills to 100% as\n       the mystery-gift track is still at nothing. */\n    let floor = 0;\n    tiers.forEach((t) => {\n      const fill = t.el.querySelector('[data-cth-progress]');\n      if (fill) {\n        const span = Math.max(t.threshold - floor, 1);\n        const done = Math.max(0, Math.min(1, (discountedSubtotal - floor) / span));\n        this.setFill(t.type, fill, done);\n      }\n      floor = t.threshold;\n    });\n\n    const giftTier = tiers.find(t => t.type === 'mystery_gift');\n    const giftUnlocked = !!(giftTier && discountedSubtotal >= giftTier.threshold);\n    this._isMysteryGiftUnlockedCached = giftUnlocked;\n    this._giftJustCrossed = giftUnlocked && prevGiftFill != null && prevGiftFill < 1;\n\n    const giftLine = this.findGiftLine(cart);\n\n    if (!giftUnlocked && giftLine && !this._giftAutoRemoving) {\n      this._giftAutoRemoving = true;\n      try {\n        await this.removeGiftFromCart();\n        emitCartUpdatedBatched();\n        window.cartDrawer?.rerenderItems?.();\n      } catch (e) { console.error('Auto-remove gift failed:', e); }\n      finally { this._giftAutoRemoving = false; }\n    }\n\n    this.applyGiftState(giftUnlocked, !!giftLine);\n  }\n}\n\n/* Boot */\n(function initCartUI() {\n  window.cartDrawer     = new NewCartDrawer();\n  window.cartThresholds = new CartThresholds();\n\n  /* A Kimonix card writes to the cart itself and says so. Without this the\n     shopper would add the first thing from an empty bag and watch the empty\n     bag stay empty. */\n  document.addEventListener('kmnx:added', () => {\n    window.cartDrawer?.rerenderItems?.();\n  });\n})();"],
  "mappings": "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",
  "names": ["wasHidden"]
}
