{
  "version": 3,
  "sources": ["/cdn/shop/t/12/assets/product.js?bust=1791313657903"],
  "sourcesContent": ["/**\n * <product-info>: product page behaviour.\n *  - Variant selection: radio inputs carry option value ids. On change the\n *    section is re-rendered server-side with `option_values` (Section\n *    Rendering API) and every [data-product-region] is swapped, so price,\n *    availability, picker states and gallery always come from Liquid.\n *  - Add to bag: submits [data-product-form] through Theme.cart.\n *  - Mobile gallery: keeps the pill indicators in sync with scroll.\n *  - \"Write a review\" opens the review app form (idempotent).\n */\n\nconst { cart, config, announce } = window.Theme;\n\nclass ProductInfo extends HTMLElement {\n  connectedCallback() {\n    this.sectionId = this.dataset.sectionId;\n    this.addEventListener('change', (event) => this.onOptionChange(event));\n    this.addEventListener('submit', (event) => this.onSubmit(event));\n    this.addEventListener('click', (event) => this.onDotClick(event));\n    this.bindGallery();\n    this.bindStickyColumn();\n    this.querySelectorAll('[data-product-form]').forEach((form) => this.guardBuyButton(form));\n  }\n\n  disconnectedCallback() {\n    this.stickyObserver?.disconnect();\n    this.buttonObservers?.forEach((observer) => observer.disconnect());\n  }\n\n  /* ---------------- Sticky info column ---------------- */\n\n  /** Only pin the info column when it fits the viewport; otherwise its bottom would be unreachable. */\n  bindStickyColumn() {\n    const column = this.querySelector('[data-sticky-column]');\n    if (!column) return;\n    const update = () => {\n      const header = parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--header-height')) || 0;\n      const available = window.innerHeight - header * 16 - 48;\n      column.toggleAttribute('data-fits', column.offsetHeight <= available);\n    };\n    this.stickyObserver = new ResizeObserver(update);\n    this.stickyObserver.observe(column);\n    window.addEventListener('resize', update, { passive: true });\n    update();\n  }\n\n  /* ---------------- Variants ---------------- */\n\n  onOptionChange(event) {\n    if (!event.target.matches('[data-option-value-id]')) return;\n    const ids = [...this.querySelectorAll('[data-option-value-id]:checked')].map((input) => input.value);\n    this.renderVariant(ids, event.target.id);\n  }\n\n  async renderVariant(optionValueIds, focusId) {\n    this.controller?.abort();\n    this.controller = new AbortController();\n    this.setAttribute('aria-busy', 'true');\n\n    const url = new URL(this.dataset.url, window.location.origin);\n    url.searchParams.set('option_values', optionValueIds.join(','));\n    url.searchParams.set('section_id', this.sectionId);\n\n    try {\n      const response = await fetch(url, { signal: this.controller.signal });\n      if (!response.ok) throw new Error(`Variant render failed: ${response.status}`);\n      const doc = new DOMParser().parseFromString(await response.text(), 'text/html');\n\n      this.querySelectorAll('[data-product-region]').forEach((region) => {\n        const next = doc.querySelector(`[data-product-region=\"${region.dataset.productRegion}\"]`);\n        if (!next) return;\n        if (region.querySelector('[data-product-form]')) this.updateBuyForm(region, next);\n        else region.replaceWith(next);\n      });\n      this.bindGallery();\n      if (focusId) document.getElementById(focusId)?.focus({ preventScroll: true });\n\n      const variantId = this.querySelector('[data-variant-id]')?.value;\n      if (variantId) {\n        const pageUrl = new URL(window.location.href);\n        pageUrl.searchParams.set('variant', variantId);\n        window.history.replaceState({}, '', pageUrl);\n      }\n    } catch (error) {\n      if (error.name !== 'AbortError') console.error(error);\n    } finally {\n      this.removeAttribute('aria-busy');\n    }\n  }\n\n  /**\n   * Update the add-to-bag form in place rather than replacing it: apps such as\n   * Timesact (pre-orders) keep references to the form and its submit button and\n   * re-apply their changes when the ?variant= URL changes.\n   */\n  updateBuyForm(region, next) {\n    const form = region.querySelector('[data-product-form]');\n    const nextForm = next.querySelector('[data-product-form]');\n    if (!nextForm) return region.replaceWith(next);\n\n    const id = form.querySelector('[data-variant-id]');\n    const nextId = nextForm.querySelector('[data-variant-id]');\n    if (id && nextId) {\n      id.value = nextId.value;\n      id.disabled = nextId.disabled;\n    }\n\n    const button = form.querySelector('[name=\"add\"]');\n    const nextButton = nextForm.querySelector('[name=\"add\"]');\n    if (button && nextButton) {\n      button.buyState = { html: nextButton.innerHTML, disabled: nextButton.disabled };\n      button.innerHTML = nextButton.innerHTML;\n      button.disabled = nextButton.disabled;\n    }\n\n    const installments = form.querySelector('[data-installments]');\n    const nextInstallments = nextForm.querySelector('[data-installments]');\n    if (installments && nextInstallments) installments.replaceWith(nextInstallments);\n  }\n\n  /**\n   * Apps rewrite the submit button (Timesact resets it to its own \"Add to cart\"\n   * text, back-in-stock widgets re-enable sold-out buttons). Keep the theme's\n   * label and state, unless an app has put the variant on a selling plan\n   * (pre-order), in which case the app owns the button.\n   */\n  guardBuyButton(form) {\n    const button = form.querySelector('[name=\"add\"]');\n    if (!button) return;\n    button.buyState = { html: button.innerHTML, disabled: button.disabled };\n\n    const observer = new MutationObserver(() => {\n      const state = button.buyState;\n      if (form.querySelector('[name=\"selling_plan\"]')?.value) return;\n      if (button.innerHTML !== state.html) button.innerHTML = state.html;\n      if (button.disabled !== state.disabled && !button.hasAttribute('aria-busy')) button.disabled = state.disabled;\n    });\n    observer.observe(button, { attributes: true, attributeFilter: ['disabled'], childList: true, characterData: true, subtree: true });\n    (this.buttonObservers ??= []).push(observer);\n  }\n\n  /* ---------------- Add to bag ---------------- */\n\n  async onSubmit(event) {\n    const form = event.target.closest('[data-product-form]');\n    if (!form) return;\n    event.preventDefault();\n\n    const button = form.querySelector('[type=\"submit\"]');\n    const error = form.querySelector('[data-product-error]');\n    button.setAttribute('aria-busy', 'true');\n    button.disabled = true;\n    if (error) error.textContent = '';\n\n    try {\n      await cart.add(new FormData(form));\n      if (config.cartType === 'page') {\n        window.location.href = config.routes.cart;\n        return;\n      }\n    } catch (err) {\n      if (error) error.textContent = err.message || config.strings.cartError;\n      announce(err.message || config.strings.cartError);\n    } finally {\n      button.removeAttribute('aria-busy');\n      button.disabled = false;\n    }\n  }\n\n  /* ---------------- Mobile gallery ---------------- */\n\n  bindGallery() {\n    this.track = this.querySelector('[data-gallery-track]');\n    this.dots = [...this.querySelectorAll('[data-gallery-dot]')];\n    if (!this.track || !this.dots.length) return;\n\n    this.track.addEventListener(\n      'scroll',\n      () => {\n        cancelAnimationFrame(this.frame);\n        this.frame = requestAnimationFrame(() => {\n          const slide = this.track.firstElementChild;\n          if (!slide) return;\n          const index = Math.round(this.track.scrollLeft / (slide.offsetWidth + 4));\n          this.dots.forEach((dot, i) => dot.setAttribute('aria-current', String(i === index)));\n        });\n      },\n      { passive: true },\n    );\n  }\n\n  onDotClick(event) {\n    const dot = event.target.closest('[data-gallery-dot]');\n    if (!dot || !this.track) return;\n    const slide = this.track.children[Number(dot.dataset.galleryDot)];\n    slide?.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'start' });\n  }\n}\n\nif (!customElements.get('product-info')) customElements.define('product-info', ProductInfo);\n\n/* \"Write a review\": open the reviews widget and trigger the review app's form. */\ndocument.addEventListener('click', (event) => {\n  const trigger = event.target.closest('[data-write-review]');\n  if (!trigger) return;\n  const widget = document.getElementById('ReviewsWidget');\n  if (!widget) return;\n  event.preventDefault();\n  widget.open = true;\n  widget.scrollIntoView({ behavior: 'smooth', block: 'start' });\n  // Judge.me initializes its widget asynchronously; wait for the write link.\n  let tries = 0;\n  const openForm = () => {\n    const link = widget.querySelector('.jdgm-write-rev-link');\n    const ready = link && getComputedStyle(link).display !== 'none';\n    if (ready) {\n      // Judge.me's link toggles the form; only click when it's closed.\n      const form = widget.querySelector('.jdgm-form-wrapper');\n      if (!form || getComputedStyle(form).display === 'none') link.click();\n      (form || widget).scrollIntoView({ behavior: 'smooth', block: 'center' });\n      return;\n    }\n    if (++tries < 40) setTimeout(openForm, 150);\n  };\n  openForm();\n});\n"],
  "mappings": "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",
  "names": []
}
