{"version":3,"file":"product-detail-section.min.js","mappings":"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","sources":["webpack://lange/./sections/product-detail/fullscreen-gallery/index.ts","webpack://lange/./lib/dom-ready.ts","webpack://lange/./lib/bundle-variant.ts","webpack://lange/./sections/product-detail/variant-prices.ts","webpack://lange/./sections/product-detail/mbg-modal.ts","webpack://lange/./sections/product-detail/countdown-timer-override.ts","webpack://lange/./sections/product-detail/product-image-gallery.ts","webpack://lange/./lib/video.ts","webpack://lange/./sections/product-detail/subscriptions.ts","webpack://lange/./sections/product-detail/subscriptions-v2.ts","webpack://lange/./sections/product-detail/selling-plan-selector/index.ts","webpack://lange/./sections/product-detail/addons.ts","webpack://lange/./sections/product-detail/selling-points.ts","webpack://lange/./sections/product-detail/b2s-promo.ts"],"sourcesContent":["import type { CustomElement } from '@integrabeauty/custom-elements';\nimport html from './index.html';\nimport styles from './index.scss';\n\n/**\n * Wraps product carousel gallery to display it as full screen gallery. It also has the zoom in,\n * zoom out option. The gallery is a carousel-element inside this one, and we read the slide count,\n * the current slide, and which slides are videos off of it for the counter and the prev/next\n * arrows, which move it by setting its data-go. It requires product carousel to have high\n * resolution images so images do not look too blurry after zooming.\n */\nclass FullscreenGallery extends HTMLElement implements CustomElement {\n  declare readonly dataset: {\n    /**\n     * Present while someone drags a zoomed image. Reflected to CSS.\n     */\n    dragging?: 'true';\n\n    /**\n     * Present while the gallery is in fullscreen mode. Reflected to CSS.\n     */\n    expanded?: 'true';\n\n    /**\n     * Present while the active image is zoomed in. Reflected to CSS.\n     */\n    zoom?: 'true';\n  };\n\n  private containerMoved = false;\n  private dragStarted = false;\n  private declare posX: number;\n  private declare posY: number;\n  private scale: number = innerWidth > 430 ? 1.75 : 2.25;\n  private scaleStarted = false;\n  private videoIndexes: number[] = [];\n\n  /**\n   * Current index of the carousel slide (0-based to match the carousel)\n   */\n  private slidesIndex = 0;\n  private slidesTotal = 0;\n\n  private onGalleryOpenBound = this.galleryOpen.bind(this);\n  private onGalleryCloseBound = this.galleryClose.bind(this);\n  private onGalleryKeyboardEventBound = this.onGalleryKeyboardEvent.bind(this);\n  private onToggleZoomBound = this.toggleZoom.bind(this);\n  private onSlideVisibleBound = this.onSlideVisible.bind(this);\n  private onCarouselPrevBound = this.onCarouselPrev.bind(this);\n  private onCarouselNextBound = this.onCarouselNext.bind(this);\n  private onCarouselMountedBound = this.onCarouselMounted.bind(this);\n  private onViewportChangedBound = this.onViewportChanged.bind(this);\n\n  private onStartDragBound = this.onStartDrag.bind(this);\n  private onStopDragBound = this.onStopDrag.bind(this);\n  private onMoveDragBound = this.onMoveDrag.bind(this);\n\n  private onStartTouchBound = this.onStartTouch.bind(this);\n  private onStopTouchBound = this.onStopTouch.bind(this);\n  private onMoveTouchBound = this.onMoveTouch.bind(this);\n\n  public connectedCallback() {\n    this.initializeShadowRoot();\n\n    const closeButton = this.shadowRoot.querySelector('#close');\n    closeButton?.addEventListener('click', this.onGalleryCloseBound);\n    closeButton?.addEventListener('keydown', this.onGalleryCloseBound);\n    closeButton?.addEventListener('keyup', this.onGalleryCloseBound);\n\n    const zoomButton = this.shadowRoot.querySelector('#toggle-zoom');\n    zoomButton?.addEventListener('click', this.onToggleZoomBound);\n    zoomButton?.addEventListener('keydown', this.onToggleZoomBound);\n    zoomButton?.addEventListener('keyup', this.onToggleZoomBound);\n\n    const prevButton = this.shadowRoot.querySelector('.prev');\n    prevButton?.addEventListener('click', this.onCarouselPrevBound);\n    prevButton?.addEventListener('keydown', this.onCarouselPrevBound);\n    prevButton?.addEventListener('keyup', this.onCarouselPrevBound);\n\n    const nextButton = this.shadowRoot.querySelector('.next');\n    nextButton?.addEventListener('click', this.onCarouselNextBound);\n    nextButton?.addEventListener('keydown', this.onCarouselNextBound);\n    nextButton?.addEventListener('keyup', this.onCarouselNextBound);\n\n    const container = this.shadowRoot.querySelector('.inner');\n    container.addEventListener('click', this.onGalleryOpenBound);\n\n    if (typeof visualViewport !== 'undefined') {\n      visualViewport.addEventListener('resize', this.onViewportChangedBound);\n      visualViewport.addEventListener('scroll', this.onViewportChangedBound);\n    }\n\n    addEventListener('scrollend', this.onViewportChangedBound);\n\n    this.addEventListener('keyup', this.onGalleryKeyboardEventBound);\n\n    this.addEventListener('carousel-element-mounted', this.onCarouselMountedBound);\n    this.addEventListener('carousel-slide-visible', this.onSlideVisibleBound);\n\n    // The carousel may have mounted before we connected, in which case we missed the event\n    this.readCarousel();\n\n    this.render();\n  }\n\n  public disconnectedCallback() {\n    const closeButton = this.shadowRoot.querySelector('#close');\n    closeButton?.removeEventListener('click', this.onGalleryCloseBound);\n    closeButton?.removeEventListener('keydown', this.onGalleryCloseBound);\n    closeButton?.removeEventListener('keyup', this.onGalleryCloseBound);\n\n    const zoomButton = this.shadowRoot.querySelector('#toggle-zoom');\n    zoomButton?.removeEventListener('click', this.onToggleZoomBound);\n    zoomButton?.removeEventListener('keydown', this.onToggleZoomBound);\n    zoomButton?.removeEventListener('keyup', this.onToggleZoomBound);\n\n    const prevButton = this.shadowRoot.querySelector('.prev');\n    prevButton?.removeEventListener('click', this.onCarouselPrevBound);\n    prevButton?.removeEventListener('keydown', this.onCarouselPrevBound);\n    prevButton?.removeEventListener('keyup', this.onCarouselPrevBound);\n\n    const nextButton = this.shadowRoot.querySelector('.next');\n    nextButton?.removeEventListener('click', this.onCarouselNextBound);\n    nextButton?.removeEventListener('keydown', this.onCarouselNextBound);\n    nextButton?.removeEventListener('keyup', this.onCarouselNextBound);\n\n    if (typeof visualViewport !== 'undefined') {\n      visualViewport.removeEventListener('resize', this.onViewportChangedBound);\n      visualViewport.removeEventListener('scroll', this.onViewportChangedBound);\n    }\n\n    removeEventListener('scrollend', this.onViewportChangedBound);\n\n    const container = this.shadowRoot.querySelector('.inner');\n    container?.removeEventListener('click', this.onGalleryOpenBound);\n\n    this.removeEventListener('keyup', this.onGalleryKeyboardEventBound);\n\n    this.removeEventListener('carousel-element-mounted', this.onCarouselMountedBound);\n    this.removeEventListener('carousel-slide-visible', this.onSlideVisibleBound);\n  }\n\n  private initializeShadowRoot() {\n    if (!this.shadowRoot) {\n      this.attachShadow({ mode: 'open' });\n      this.shadowRoot.innerHTML = `<style>${styles}</style>${html}`;\n    }\n  }\n\n  private render() {\n    // The index is 0 based but visitors expect it to start at 1\n    const visualCurrentSlideNumber = this.slidesIndex + 1;\n\n    const currentSlide = this.shadowRoot.querySelector('.current-slide');\n    currentSlide.textContent = visualCurrentSlideNumber.toString();\n\n    const totalSlidesElement = this.shadowRoot.querySelector('.total-slides');\n    totalSlidesElement.textContent = this.slidesTotal.toString();\n\n    const counter = this.shadowRoot.querySelector('.counter');\n    counter?.setAttribute('aria-label', [\n      'Slide',\n      visualCurrentSlideNumber.toString(),\n      'of',\n      this.slidesTotal.toString()\n    ].join(' '));\n\n    // Decide whether to show or hide the arrows\n    const arrows = this.shadowRoot.querySelectorAll<HTMLElement>('.prev, .next');\n    if (this.slidesTotal <= 1) {\n      for (const arrow of arrows) {\n        arrow.hidden = true;\n      }\n    } else {\n      // The number of slides isn't expected to change, but the render method is always called the\n      // first time with this.slidesTotal === 0. After that, it gets updated with the actual value.\n      for (const arrow of arrows) {\n        arrow.hidden = false;\n      }\n    }\n  }\n\n  private onViewportChanged(_event: Event) {\n    this.updateViewportVars();\n  }\n\n  private updateViewportVars() {\n    const style = this.style;\n    let viewport = {\n      width: innerWidth,\n      height: innerHeight,\n      offsetTop: 0,\n      offsetLeft: 0,\n      scale: 1\n    };\n\n    if (typeof visualViewport !== 'undefined') {\n      viewport = visualViewport;\n    }\n\n    style.setProperty('--viewport-width', Math.ceil(viewport.width) + 'px');\n    style.setProperty('--viewport-height', Math.ceil(viewport.height) + 'px');\n    style.setProperty('--viewport-top', viewport.offsetTop + 'px');\n    style.setProperty('--viewport-left', viewport.offsetLeft + 'px');\n    style.setProperty('--viewport-scale', viewport.scale.toString());\n  }\n\n  private onCarouselMounted(_event: Event) {\n    this.readCarousel();\n    this.render();\n  }\n\n  /**\n   * Reads the slide count, the current slide, and the video slides off the carousel once it has\n   * mounted. Does nothing before then.\n   */\n  private readCarousel() {\n    const carousel = this.querySelector('carousel-element');\n    if (!carousel?.dataset.slideCount) {\n      return;\n    }\n\n    this.slidesTotal = parseInt(carousel.dataset.slideCount, 10);\n    this.slidesIndex = parseInt(carousel.dataset.index, 10);\n\n    this.videoIndexes = [];\n    const slides = carousel.querySelectorAll('[data-carousel-slide]');\n    for (const [index, slide] of slides.entries()) {\n      if (slide.classList.contains('product-single-media')) {\n        this.videoIndexes.push(index);\n      }\n    }\n  }\n\n  private onSlideVisible(event: GlobalEventHandlersEventMap['carousel-slide-visible']) {\n    this.slidesIndex = event.detail.index;\n    this.render();\n  }\n\n  /**\n   * Moves the carousel to the slide at the given index.\n   */\n  private goTo(index: number) {\n    const carousel = this.querySelector('carousel-element');\n    if (carousel) {\n      carousel.dataset.go = index.toString();\n    }\n  }\n\n  private onCarouselPrev(event?: Event) {\n    if (event instanceof KeyboardEvent) {\n      if (['Space', 'Enter'].includes(event.code)) {\n        event.preventDefault();\n      } else {\n        return;\n      }\n\n      if (event.type === 'keydown') {\n        return;\n      }\n    }\n\n    this.clearZoom();\n\n    // skip video slides as we do not want to display them in fullscreen mode\n    const total = this.slidesTotal;\n    let prevIndex = this.slidesIndex === 0 ? total - 1 : this.slidesIndex - 1;\n    while (this.videoIndexes.includes(prevIndex)) {\n      prevIndex = prevIndex === 0 ? total - 1 : prevIndex - 1;\n      // make sure we do not loop endlessly\n      if (prevIndex === this.slidesIndex) {\n        break;\n      }\n    }\n\n    this.goTo(prevIndex);\n  }\n\n  private onCarouselNext(event?: Event) {\n    if (event instanceof KeyboardEvent) {\n      if (['Space', 'Enter'].includes(event.code)) {\n        event.preventDefault();\n      } else {\n        return;\n      }\n\n      if (event.type === 'keydown') {\n        return;\n      }\n    }\n\n    this.clearZoom();\n\n    // skip video slides as we do not want to display them in fullscreen mode\n    const total = this.slidesTotal;\n    let nextIndex = this.slidesIndex + 1 === total ? 0 : this.slidesIndex + 1;\n    while (this.videoIndexes.includes(nextIndex)) {\n      nextIndex = nextIndex + 1 === total ? 0 : nextIndex + 1;\n      // make sure we do not loop endlessly\n      if (nextIndex === this.slidesIndex) {\n        break;\n      }\n    }\n\n    this.goTo(nextIndex);\n  }\n\n  private galleryOpen(event: Event) {\n    // prevent opening the video in full screen\n    if (this.videoIndexes.includes(this.slidesIndex)) {\n      return;\n    }\n\n    // The event handler is installed on an area that contains some clickable elements.\n    // Unfortunately the carousel does not use `stopPropagation()` so we receive the event even if\n    // it was a pagination click etc. Filter out such events for a better UX and accessibility.\n    const target = event.target;\n    if (target instanceof HTMLElement &&\n      (target.tagName === 'BUTTON' || target.parentElement?.tagName === 'BUTTON')) {\n      return;\n    }\n\n    // The carousel's own controls, like its drag bar, sit inside this element too, so only a tap on\n    // one of the slides opens the gallery\n    if (target instanceof Element && target.closest('carousel-element') &&\n      !target.closest('[data-carousel-slide]')) {\n      return;\n    }\n\n    const container = this.shadowRoot.querySelector<HTMLElement>('.inner');\n    container.addEventListener('mousedown', this.onStartDragBound);\n    container.addEventListener('mouseup', this.onStopDragBound);\n    container.addEventListener('touchstart', this.onStartTouchBound, { passive: false });\n    container.addEventListener('touchend', this.onStopTouchBound, { passive: false });\n\n    const scrollbarWidth = innerWidth - document.documentElement.clientWidth;\n    if (Number.isInteger(scrollbarWidth)) {\n      document.documentElement.style.setProperty('--scrollbar-width', scrollbarWidth + 'px');\n    }\n\n    document.documentElement.dataset.scrollLocked = 'true';\n\n    this.dataset.expanded = 'true';\n    this.updateViewportVars();\n\n    const carousel = this.querySelector('carousel-element');\n    if (carousel) {\n      carousel.dataset.controlsHidden = 'true';\n    }\n\n    const selector = '#toggle-zoom, #close, .prev>[role=\"button\"], .next>[role=\"button\"]';\n    const focusable = this.shadowRoot.querySelectorAll<HTMLElement>(selector);\n    for (const focusableEl of focusable) {\n      focusableEl.tabIndex = 0;\n    }\n\n    // focus on the first active element\n    const zoomButton = this.shadowRoot.querySelector<HTMLElement>('#toggle-zoom');\n    zoomButton.focus({ focusVisible: true });\n\n    const galleryEvent = new Event('fullscreen-gallery-opened', { bubbles: true, composed: true });\n    this.dispatchEvent(galleryEvent);\n  }\n\n  private onGalleryKeyboardEvent(event: KeyboardEvent) {\n    if (event.code === 'Escape') {\n      this.galleryClose();\n    }\n  }\n\n  private galleryClose(event?: Event) {\n    if (event instanceof KeyboardEvent) {\n      if (['Space', 'Enter'].includes(event.code)) {\n        event.preventDefault();\n      } else {\n        return;\n      }\n\n      if (event.type === 'keydown') {\n        return;\n      }\n    }\n\n    this.clearZoom();\n\n    const selector = '#toggle-zoom, #close, .prev>[role=\"button\"], .next>[role=\"button\"]';\n    const focusable = this.shadowRoot.querySelectorAll<HTMLElement>(selector);\n    for (const focusableEl of focusable) {\n      focusableEl.tabIndex = -1;\n    }\n\n    delete document.documentElement.dataset.scrollLocked;\n    delete this.dataset.expanded;\n\n    const carousel = this.querySelector('carousel-element');\n    if (carousel) {\n      delete carousel.dataset.controlsHidden;\n    }\n\n    const container = this.shadowRoot.querySelector<HTMLElement>('.inner');\n    container.removeEventListener('mousedown', this.onStartDragBound);\n    container.removeEventListener('mouseup', this.onStopDragBound);\n    container.removeEventListener('touchstart', this.onStartTouchBound);\n    container.removeEventListener('touchend', this.onStopTouchBound);\n\n    const galleryEvent = new Event('fullscreen-gallery-closed', { bubbles: true, composed: true });\n    this.dispatchEvent(galleryEvent);\n  }\n\n  private clearZoom() {\n    const carouselEl = this.querySelector('carousel-element');\n    if (!carouselEl) {\n      return;\n    }\n\n    const zoomEl = this.shadowRoot.querySelector('#toggle-zoom');\n    zoomEl?.setAttribute('aria-checked', 'false');\n\n    delete this.dataset.zoom;\n    carouselEl.style.transform = 'translate(0, 0) scale(1)';\n  }\n\n  private toggleZoom(event?: Event) {\n    if (event instanceof KeyboardEvent) {\n      if (['Space', 'Enter'].includes(event.code)) {\n        event.preventDefault();\n      } else {\n        return;\n      }\n\n      if (event.type === 'keydown') {\n        return;\n      }\n    }\n\n    const carouselEl = this.querySelector('carousel-element');\n    if (!carouselEl) {\n      return;\n    }\n\n    if (this.dataset.zoom === 'true') {\n      this.clearZoom();\n      return;\n    }\n\n    const zoomEl = this.shadowRoot.querySelector('#toggle-zoom');\n    zoomEl?.setAttribute('aria-checked', 'true');\n\n    carouselEl.style.transform = `translate(0px, 0px) scale(${this.scale})`;\n    this.dataset.zoom = 'true';\n  }\n\n  private onStartDrag(event: MouseEvent) {\n    const carouselEl = this.querySelector('carousel-element');\n    if (!carouselEl) {\n      return;\n    }\n\n    const matches = carouselEl.style.transform.match(/(-?\\d+)/g) || [];\n\n    this.posX = event.clientX - (matches[0] ? parseInt(matches[0], 10) : carouselEl.offsetLeft);\n    this.posY = event.clientY - (matches[1] ? parseInt(matches[1], 10) : carouselEl.offsetTop);\n\n    if (this.dataset.zoom === 'true') {\n      this.dataset.dragging = 'true';\n      this.dragStarted = true;\n      document.body.addEventListener('mousemove', this.onMoveDragBound);\n    }\n  }\n\n  private onStopDrag(_event: MouseEvent) {\n    const carouselEl = this.querySelector('carousel-element');\n    if (!carouselEl) {\n      return;\n    }\n\n    if (!this.containerMoved) {\n      this.toggleZoom();\n    }\n\n    this.containerMoved = false;\n    this.dragStarted = false;\n    delete this.dataset.dragging;\n    document.body.removeEventListener('mousemove', this.onMoveDragBound);\n  }\n\n  private onMoveDrag(event: MouseEvent) {\n    const carouselEl = this.querySelector('carousel-element');\n    if (!carouselEl) {\n      return;\n    }\n\n    this.containerMoved = true;\n    if (!this.dragStarted) {\n      return;\n    }\n\n    let offsetX = event.clientX - this.posX;\n    let offsetY = event.clientY - this.posY;\n\n    const container = this.shadowRoot.querySelector('.inner');\n    const { width, height } = carouselEl.getBoundingClientRect();\n\n    const constraintX = Math.floor((width - container.clientWidth) / 2);\n    const constraintY = Math.floor((height - container.clientHeight) / 2);\n\n    if (!(offsetX <= constraintX && offsetX >= constraintX * -1)) {\n      offsetX = offsetX > 0 ? constraintX : -1 * constraintX;\n    }\n\n    if (!(offsetY <= constraintY && offsetY >= constraintY * -1)) {\n      offsetY = offsetY > 0 ? constraintY : -1 * constraintY;\n    }\n\n    carouselEl.style.transform = `translate(${offsetX}px,${offsetY}px) scale(${this.scale})`;\n  }\n\n  private onStartTouch(event: TouchEvent) {\n    event.preventDefault();\n\n    const carouselEl = this.querySelector('carousel-element');\n    if (!carouselEl) {\n      return;\n    }\n\n    if (event.touches.length === 1) {\n      const matches = carouselEl.style.transform.match(/(-?\\d+(\\.\\d+)?)/g) || [];\n\n      const x = matches[0] ? parseInt(matches[0], 10) : carouselEl.offsetLeft;\n      const y = matches[1] ? parseInt(matches[1], 10) : carouselEl.offsetTop;\n\n      this.posX = event.touches[0].pageX - x;\n      this.posY = event.touches[0].pageY - y;\n    }\n\n    if (this.dataset.zoom === 'true') {\n      this.dataset.dragging = 'true';\n      this.dragStarted = true;\n      document.body.addEventListener('touchmove', this.onMoveTouchBound);\n    }\n  }\n\n  private onStopTouch(event: TouchEvent) {\n    event.preventDefault();\n\n    const carouselEl = this.querySelector('carousel-element');\n    if (!carouselEl) {\n      return;\n    }\n\n    if (event.touches.length < 1) {\n      if (!this.containerMoved) {\n        this.toggleZoom();\n      }\n\n      this.dragStarted = false;\n      this.containerMoved = false;\n      this.scaleStarted = false;\n\n      delete this.dataset.dragging;\n      document.body.removeEventListener('touchmove', this.onMoveTouchBound);\n    }\n  }\n\n  private onMoveTouch(event: TouchEvent) {\n    const carouselEl = this.querySelector('carousel-element');\n    if (!carouselEl) {\n      return;\n    }\n\n    this.containerMoved = true;\n    if (!this.dragStarted) {\n      return;\n    }\n\n    if (event.touches.length === 1) {\n      if (this.scaleStarted) {\n        return;\n      }\n\n      const container = this.shadowRoot.querySelector('.inner');\n      const { width, height } = carouselEl.getBoundingClientRect();\n      let offsetX = event.touches[0].pageX - this.posX;\n      let offsetY = event.touches[0].pageY - this.posY;\n\n      const constraintX = Math.floor((width - container.clientWidth) / 2);\n      const constraintY = Math.floor((height - container.clientHeight) / 2);\n\n      if (!(offsetX <= constraintX && offsetX >= constraintX * -1)) {\n        offsetX = offsetX > 0 ? constraintX : -1 * constraintX;\n      }\n\n      if (!(offsetY <= constraintY && offsetY >= constraintY * -1)) {\n        offsetY = offsetY > 0 ? constraintY : -1 * constraintY;\n      }\n\n      carouselEl.style.transform = `translate(${offsetX}px,${offsetY}px) scale(${this.scale})`;\n    }\n  }\n}\n\ndeclare global {\n  interface GlobalEventHandlersEventMap {\n    'fullscreen-gallery-closed': Event;\n    'fullscreen-gallery-opened': Event;\n  }\n\n  interface HTMLElementTagNameMap {\n    'fullscreen-gallery': FullscreenGallery;\n  }\n}\n\nif (!customElements.get('fullscreen-gallery')) {\n  customElements.define('fullscreen-gallery', FullscreenGallery);\n}\n","/**\n * Releases the handle returned from the ready() function.\n *\n * This is idempotent. Additional calls are pointless but harmless.\n */\nexport type ReleaseHandle = () => void;\n\n/**\n * A subset of events that pertain to document ready state.\n */\ntype LoadingEventMap = Pick<DocumentEventMap, 'DOMContentLoaded' | 'load'>;\n\n/**\n * Calls the provided callback when the document is ready. This works both when the document is\n * already ready and when it is not, so callers do not have to care. This exists because it ensures\n * that each caller consistently expresses its dependency on document readiness, and to save a bit\n * of boilerplate.\n *\n * A document is considered ready when it has been parsed. This corresponds to the ready state of\n * 'interactive'. When the document has been fully loaded, the ready state is 'complete'. The\n * callback runs either way, so callers do not have to check which one they are in.\n *\n * Ready means the html is parsed and the DOM is there to query. It does not mean the page's other\n * scripts have run. Deferred scripts and module scripts run after the ready state turns\n * 'interactive' and before DOMContentLoaded fires, so a callback here can run before them. When a\n * caller needs some other script to have run already, this is the wrong tool, and it should wait on\n * whatever that script produces instead.\n *\n * That remedy has a catch of its own when the calling bundle is loaded with async. An async bundle\n * can execute after DOMContentLoaded has already fired, so a plain listener for that event never\n * runs and the work silently never happens. Nothing here can detect that, because by the time this\n * file executes the event is long gone. Catching it needs something registered at parse time,\n * before any bundle loads.\n *\n * There is a second trap right next to that one, and it applies only when waiting for\n * DOMContentLoaded. The ready state stays 'interactive' after DOMContentLoaded has fired, all the\n * way until 'complete', so the ready state cannot tell you whether DOMContentLoaded is still coming\n * or has already gone. Waiting for load is not exposed to this, because the ready state turns\n * 'complete' and load fires in the same task, so seeing 'complete' means load has fired too.\n *\n * When the document is already ready, the callback is invoked via setTimeout. This way, if the\n * callback throws, the error surfaces to the global error handler in a way that is similar to what\n * happens when an event listener throws.\n *\n * We might one day want to move this to an NPM library.\n *\n * Beware of a quirk here. setTimeout yields, and the busier the frontend, the longer it takes to\n * fire. Page load is exactly when the frontend is busiest. The timeout is a floor and not a\n * guarantee, so there is no real bound on how long that yield lasts. What this means is that using\n * this library can introduce more delay than you might expect. Compare code that runs on async load\n * and checks ready state and invokes immediately to code that runs after setTimeout. The former\n * runs in the same tick, the latter runs in \"some later tick\". That later tick can be surprisingly\n * much later. That can cause initial paint issues.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Document/readyState\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Document/DOMContentLoaded_event\n * @see https://html.spec.whatwg.org/multipage/parsing.html#the-end\n */\nexport function ready(callback: (event?: Event) => void,\n  type: keyof LoadingEventMap = 'DOMContentLoaded') {\n  if (document.readyState === 'complete' || (type === 'DOMContentLoaded' &&\n    document.readyState === 'interactive')) {\n    const timer = setTimeout(callback, 0);\n    const handle: ReleaseHandle = () => clearTimeout(timer);\n    return handle;\n  } else {\n    addEventListener(type, callback);\n    const handle: ReleaseHandle = () => removeEventListener(type, callback);\n    return handle;\n  }\n}\n","type ProductElement = HTMLElementTagNameMap['product-element'];\ntype BundleOption = HTMLElementTagNameMap['bundle-option'];\ntype VariantElement = HTMLElementTagNameMap['variant-element'];\n\n/**\n * One of the inner product's option groups, cut down to the two fields we read. The option groups\n * the product variant option changed event carries already fit this shape, so a listener can pass\n * them straight in.\n */\ninterface SelectOption {\n  /**\n   * The name of the option\n   *\n   * @example \"Size\"\n   * @example \"Color\"\n   */\n  name: string;\n\n  /**\n   * The value the shopper chose for the option\n   *\n   * @example \"Black\"\n   */\n  selected_option: string;\n}\n\n/**\n * Records the chosen options of one product inside a bundle and returns the bundle variant that all\n * of the bundle's current choices add up to, or nothing when the bundle has no variant for that\n * combination.\n *\n * A bundle made with the Shopify Bundles app is its own product, and each of its variants stands\n * for one combination of the options of the products inside it. The page shows a picker for each\n * product inside the bundle rather than one for the bundle, so when a shopper changes one of those\n * pickers we call this to work out which bundle variant the pickers now add up to.\n *\n * Choices carry over from one call to the next, so the result matches the pickers on screen even\n * after a change that landed on a combination with no bundle variant. Recording the same choice\n * twice changes nothing, so every script reacting to the same picker change can call this and they\n * all get the same answer, whichever runs first.\n *\n * An option of the inner product that the bundle has no bundle option for is left out. Some bundles\n * leave an inner product's option out on purpose, such as the Switch It Up bundles, which come in a\n * bundle per color and so carry no color option.\n *\n * @param bundle The bundle's product element\n * @param productTitle The title of the inner product whose picker changed\n * @param options The inner product's option groups with their chosen values\n */\nexport function select(bundle: ProductElement, productTitle: string, options: SelectOption[]) {\n  // We keep the current choice on the bundle option elements rather than starting from the bundle's\n  // selected variant, so a change that lands on a combination with no bundle variant still counts\n  // when the next change builds on it. Each bundle option element is named by the inner product's\n  // title and the option's name.\n  for (const option of options) {\n    const bundleOption = bundle.querySelector<BundleOption>(\n      `bundle-option[data-name=\"${productTitle} (${option.name})\"]`);\n    if (bundleOption) {\n      bundleOption.dataset.selected = option.selected_option;\n    }\n  }\n\n  // The bundle variant titles join one value per bundle option, in the order of each bundle\n  // option's position, like \"Black / Oval 75mm\"\n  const values: string[] = [];\n  const bundleOptions = bundle.querySelectorAll('bundle-option');\n  for (const bundleOption of bundleOptions) {\n    // Positions count from 1, and the array counts from 0\n    const position = parseInt(bundleOption.dataset.position, 10);\n    const index = position - 1;\n    values[index] = bundleOption.dataset.selected;\n  }\n\n  const title = values.join(' / ');\n  return bundle.querySelector<VariantElement>(`variant-element[data-title=\"${title}\"]`);\n}\n","import { select as selectBundleVariant } from '../../lib/bundle-variant.js';\nimport { ready } from '../../lib/dom-ready.js';\n\n// The server renders the prices on the product page for the variant selected on page load, and\n// variants can have different prices, so when the shopper picks another variant we show that\n// variant's prices. We read them from the product price on the page, so every price matches the\n// regular price shown next to it.\n//\n// The discount messages show the price with the product's discount code. There are two kinds. One\n// is the snippet message in the price box and the subscription selector, and the other is the\n// product discount message element on the default product page, which can also render as the\n// discount modal.\n//\n// The installment message is the exception and reads the variant prices it carries itself. The\n// server works its amount out from the variant's own price, while an offer template can put the\n// offer's price in the product price, so reading the product price could change the amount on a\n// switch that keeps the same price.\n//\n// On a value set the picker belongs to a product inside the bundle, so its change carries that\n// product's variant rather than the bundle's. We work out which bundle variant the pickers add up\n// to, and then the bundle's regular price, its bundle total and its discount messages follow that\n// variant. The product price follows the shopper's own picker by itself, but not a bundle variant\n// worked out from another product's picker, so we point it at the bundle variant ourselves.\n\ntype ProductElement = HTMLElementTagNameMap['product-element'];\ntype VariantElement = HTMLElementTagNameMap['variant-element'];\n\nfunction onProductVariantOptionChanged(\n  event: GlobalEventHandlersEventMap['product-variant-option-changed']) {\n  const productDetail = event.currentTarget;\n  if (!(productDetail instanceof HTMLElement)) {\n    return;\n  }\n\n  let productId = event.detail.id;\n  let variantId = event.detail.selected_variant_id;\n\n  if (event.detail.bundle_product_id) {\n    const bundle = productDetail.querySelector<ProductElement>(\n      `add-to-cart > product-element[data-id=\"${event.detail.bundle_product_id}\"]`);\n    if (!bundle) {\n      return;\n    }\n\n    const variant = selectBundleVariant(bundle, event.detail.title, event.detail.option_groups);\n    if (!variant) {\n      return;\n    }\n\n    productId = event.detail.bundle_product_id;\n    variantId = parseInt(variant.dataset.id, 10);\n    renderBundlePrices(productDetail, productId, variantId);\n  }\n\n  renderSnippetMessages(productDetail, variantId);\n  renderProductDiscountMessages(productDetail, productId, variantId);\n  renderInstallmentMessages(productDetail, productId, variantId);\n}\n\n/**\n * Points the bundle's product prices at the given bundle variant, and shows that variant's prices\n * in the bundle total.\n */\nfunction renderBundlePrices(productDetail: HTMLElement, productId: number, variantId: number) {\n  const productElements = productDetail.querySelectorAll<ProductElement>(\n    `product-price > product-element[data-id=\"${productId}\"]`);\n  for (const productElement of productElements) {\n    productElement.dataset.selectedVariantId = variantId.toString();\n    const productPrice = productElement.closest('product-price');\n    productPrice.dataset.update = Date.now().toString();\n  }\n\n  const variantElement = productDetail.querySelector<VariantElement>(\n    `product-price > product-element[data-id=\"${productId}\"] >\n    variant-element[data-id=\"${variantId}\"]`);\n  type PriceElement = HTMLElementTagNameMap['price-element'];\n  const totalPrice = productDetail.querySelector<PriceElement>('.bundle-pricing #price');\n  const totalCompareAtPrice = productDetail.querySelector<PriceElement>(\n    '.bundle-pricing #compare-at-price');\n  if (variantElement && totalPrice && totalCompareAtPrice) {\n    totalPrice.dataset.subunits = variantElement.dataset.price;\n    totalCompareAtPrice.dataset.subunits = variantElement.dataset.compareAtPrice;\n  }\n}\n\n/**\n * Shows the price with the code for the given variant in the snippet messages, the ones in the\n * price box and in the subscription selector.\n */\nfunction renderSnippetMessages(productDetail: HTMLElement, variantId: number) {\n  const messages = productDetail.querySelectorAll<HTMLElement>('.discount-message');\n  for (const message of messages) {\n    renderSnippetMessage(message, variantId);\n  }\n}\n\nfunction renderSnippetMessage(message: HTMLElement, variantId: number) {\n  type PriceElement = HTMLElementTagNameMap['price-element'];\n  const priceElement = message.querySelector<PriceElement>('.price price-element');\n  if (!priceElement) {\n    return;\n  }\n\n  // We read the variant's data from the element holding the message. That is the product price in\n  // the price box, and the product slot in the subscription selector.\n  let variantElement: VariantElement;\n  const priceBox = message.closest('.price-box');\n  const sellingPlanSelector = message.closest('selling-plan-selector');\n  if (priceBox) {\n    variantElement = priceBox.querySelector<VariantElement>(\n      `product-price variant-element[data-id=\"${variantId}\"]`);\n  } else if (sellingPlanSelector) {\n    variantElement = sellingPlanSelector.querySelector<VariantElement>(\n      `[slot=\"product\"] > variant-element[data-id=\"${variantId}\"]`);\n  }\n\n  if (!variantElement) {\n    return;\n  }\n\n  const price = parseInt(variantElement.dataset.price, 10);\n  const percentage = parseFloat(message.dataset.discountPercentage);\n  priceElement.dataset.subunits = Math.round(price - price * percentage / 100).toString();\n}\n\n/**\n * Gives the product discount messages and discount modals of the given product the prices of the\n * given variant. Only that product's messages follow, so an add-on in the section keeps its own. A\n * message whose price an offer template overrides keeps that price.\n */\nfunction renderProductDiscountMessages(productDetail: HTMLElement, productId: number,\n  variantId: number) {\n  type MessageElement = HTMLElementTagNameMap['product-discount-message'];\n  type ModalElement = HTMLElementTagNameMap['product-discount-modal'];\n\n  const variantElement = productDetail.querySelector<VariantElement>(\n    `product-price product-element[data-id=\"${productId}\"] >\n    variant-element[data-id=\"${variantId}\"]`);\n  if (!variantElement) {\n    return;\n  }\n\n  const price = parseInt(variantElement.dataset.price, 10);\n  let compareAtPrice = parseInt(variantElement.dataset.compareAtPrice, 10);\n  if (!Number.isInteger(compareAtPrice) || compareAtPrice < price) {\n    compareAtPrice = price;\n  }\n\n  const messages = productDetail.querySelectorAll<MessageElement>(\n    `product-discount-message[data-product-id=\"${productId}\"]:not([data-offer-price-override])`);\n  for (const message of messages) {\n    message.dataset.price = price.toString();\n    message.dataset.compareAtPrice = compareAtPrice.toString();\n  }\n\n  const modals = productDetail.querySelectorAll<ModalElement>(\n    `product-discount-modal[data-product-id=\"${productId}\"]:not([data-offer-price-override])`);\n  for (const modal of modals) {\n    modal.dataset.price = price.toString();\n  }\n}\n\n/**\n * Gives the installment messages of the given product the estimated total for the given variant,\n * the variant's price with the message's sale price percentage taken off when it has one. The\n * message hides itself when that total is under the installment threshold.\n */\nfunction renderInstallmentMessages(productDetail: HTMLElement, productId: number,\n  variantId: number) {\n  type MessageElement = HTMLElementTagNameMap['installment-payment-message'];\n\n  const messages = productDetail.querySelectorAll<MessageElement>(\n    `installment-payment-message[data-product-id=\"${productId}\"]`);\n  for (const message of messages) {\n    renderInstallmentMessage(message, variantId);\n  }\n}\n\nfunction renderInstallmentMessage(message: HTMLElementTagNameMap['installment-payment-message'],\n  variantId: number) {\n  const variantElement = message.querySelector<VariantElement>(\n    `:scope > variant-element[data-id=\"${variantId}\"]`);\n  if (!variantElement) {\n    return;\n  }\n\n  // The server works the starting amount out the same way, so keep the two in step\n  const price = parseInt(variantElement.dataset.price, 10);\n  let estimatedTotal = price;\n  if (message.dataset.discountPercentage) {\n    const percentage = parseFloat(message.dataset.discountPercentage);\n    estimatedTotal = Math.round((100 - percentage) * price / 100);\n  }\n\n  message.dataset.estimatedTotal = estimatedTotal.toString();\n}\n\nfunction onReady() {\n  const productDetail = document.querySelector<HTMLElement>('#product-detail-section');\n  if (productDetail) {\n    productDetail.addEventListener('product-variant-option-changed', onProductVariantOptionChanged);\n  }\n}\n\nready(onReady);\n","import { ready } from '../../lib/dom-ready.js';\n\n// The modal-element around the money back guarantee handles the close button, the dismiss gestures,\n// and telling the modal manager when it opens and closes. We open it, and inert the product detail\n// section while it's open.\n\ntype ModalElement = HTMLElementTagNameMap['modal-element'];\n\nfunction onReady() {\n  const modal = document.querySelector('#pdp-mbg-modal');\n  if (!modal) {\n    return;\n  }\n\n  modal.addEventListener('modal-open-requested', onModalOpenRequested);\n  modal.addEventListener('modal-close-requested', onModalCloseRequested);\n\n  const mbgButtons = document.querySelectorAll<HTMLButtonElement>(\n    '.product-details .money-back-guarantee button');\n  for (const button of mbgButtons) {\n    button.addEventListener('click', onMBGButtonClicked);\n  }\n}\n\nfunction onMBGButtonClicked(event: MouseEvent) {\n  if (!(event.currentTarget instanceof HTMLElement)) {\n    return;\n  }\n\n  const focusEvent = new Event('focus-stack-push-requested', {\n    bubbles: true,\n    composed: true\n  });\n  event.currentTarget.dispatchEvent(focusEvent);\n\n  const modal = document.querySelector<ModalElement>('#pdp-mbg-modal');\n  modal.dataset.open = 'true';\n}\n\n/**\n * The modal manager inerts every other section on the page, but it can't inert ours, since the\n * modal lives in it too. That leaves the buy box and the gallery tabbable behind the open modal, so\n * we inert them ourselves. The modal renders as a sibling of the product detail page rather than\n * inside it, so this doesn't reach the modal.\n */\nfunction onModalOpenRequested(_event: HTMLElementEventMap['modal-open-requested']) {\n  const section = document.querySelector('#product-detail-section');\n  if (section instanceof HTMLElement) {\n    section.inert = true;\n  }\n}\n\n/**\n * Takes the page back out of inert. This listener is on the modal, so it runs before the manager's\n * listener on the window, which puts focus back on the trigger, and a focus call into an inert\n * subtree does nothing.\n */\nfunction onModalCloseRequested(_event: HTMLElementEventMap['modal-close-requested']) {\n  const section = document.querySelector('#product-detail-section');\n  if (section instanceof HTMLElement) {\n    section.inert = false;\n  }\n}\n\nready(onReady);\n","import { ready } from '../../lib/dom-ready.js';\n\nfunction onReady() {\n  // We must query the DOM to determine if a flash sale countdown timer override exists. If none is\n  // found we have no need to continue further and short circuit.\n  const overrideEl = document.querySelector<HTMLElement>('#product-detail-countdown-timer');\n  if (!overrideEl) {\n    return;\n  }\n\n  if (!overrideEl.dataset.startTime || !overrideEl.dataset.endTime) {\n    return;\n  }\n\n  const flashStartTime = parseInt(overrideEl.dataset.startTime, 10);\n  const flashEndTime = parseInt(overrideEl.dataset.endTime, 10);\n  const timestamp = Math.floor(Date.now() / 1000);\n\n  if (timestamp > flashStartTime && timestamp < flashEndTime) {\n    let appendTimer = false;\n    let timerEl = document.querySelector('product-detail-countdown-timer');\n\n    // Since the global countdown timer is uniquely shown based on the global timer we must check if\n    // it exists and create the element to be inserted into the DOM after all dataset attributes\n    // have been updated to ensure proper rendering.\n    if (!timerEl) {\n      timerEl = document.createElement('product-detail-countdown-timer');\n      appendTimer = true;\n    }\n\n    timerEl.dataset.startTime = overrideEl.dataset.startTime;\n    timerEl.dataset.endTime = overrideEl.dataset.endTime;\n\n    if (overrideEl.dataset.heading) {\n      timerEl.dataset.heading = overrideEl.dataset.heading;\n    }\n\n    if (appendTimer) {\n      overrideEl.appendChild(timerEl);\n    }\n  }\n}\n\nready(onReady);\n","import { select as selectBundleVariant } from '../../lib/bundle-variant.js';\nimport { ready } from '../../lib/dom-ready.js';\nimport { play } from '../../lib/video.js';\n\n// The gallery and its thumbnail strip are two separate carousel-element elements, and neither one\n// knows about the other. This module is what keeps them pointed at the same picture. A move on\n// either carousel moves the other one to the same index through data-go. The carousel ignores a\n// move to the slide it is already on, so the echo coming back from the other carousel stops there.\n\n// Keep track of the slide indices that have been viewed in each gallery so we can calculate the\n// percentage of the gallery that has been viewed. A sale page can carry more than one gallery.\nconst viewedSlidesByGallery = new WeakMap<Element, Set<number>>();\n\nfunction showVariantImage(variantId: number) {\n  const allVariantImages = document.querySelectorAll<HTMLElement>(\n    '.product-image-gallery [data-variant-image]');\n\n  let matched = false;\n  for (const el of allVariantImages) {\n    const isMatch = el.dataset.variantImage === variantId.toString();\n    el.classList.toggle('selected-variant', isMatch);\n    if (isMatch) {\n      matched = true;\n    }\n  }\n\n  if (!matched) {\n    // Variant has no image — show the default (first variant image). The main gallery and the nav\n    // strip each have their own default, so both need it back or the nav thumbnail stays hidden.\n    const defaultEls = document.querySelectorAll(\n      '.product-image-gallery [data-variant-image-default]');\n    for (const defaultEl of defaultEls) {\n      defaultEl.classList.add('selected-variant');\n    }\n  }\n}\n\nfunction goToVariantSlide(gallery: HTMLElement) {\n  if (gallery.querySelector('[data-variant-slide]')) {\n    gallery.dataset.go = '0';\n  }\n}\n\n/**\n * Finds the thumbnail strip that goes with a gallery, or the gallery that goes with a strip. Both\n * sit inside the same product image gallery container.\n */\nfunction findPartner(carousel: Element) {\n  let selector: string;\n  if (carousel.classList.contains('product-image-gallery-main')) {\n    selector = '.product-image-gallery-nav';\n  } else {\n    selector = '.product-image-gallery-main';\n  }\n\n  const container = carousel.closest('.product-image-gallery');\n  return container?.querySelector<HTMLElement>(selector);\n}\n\nfunction isGalleryCarousel(target: EventTarget): target is HTMLElement {\n  return target instanceof HTMLElement && (\n    target.classList.contains('product-image-gallery-main') ||\n    target.classList.contains('product-image-gallery-nav'));\n}\n\n/**\n * Marks the thumbnail button for the slide at the given index as the current one, the way the\n * carousel's navigation option used to mark the thumbnail slide itself.\n */\nfunction markCurrentThumbnail(nav: Element, index: number) {\n  const buttons = nav.querySelectorAll('[data-carousel-slide] > button');\n  for (const [buttonIndex, button] of buttons.entries()) {\n    if (buttonIndex === index) {\n      button.setAttribute('aria-current', 'true');\n    } else {\n      button.removeAttribute('aria-current');\n    }\n  }\n}\n\nfunction onSlideMoveStarted(event: WindowEventMap['carousel-slide-move-started']) {\n  if (!isGalleryCarousel(event.target)) {\n    return;\n  }\n\n  const partner = findPartner(event.target);\n  if (partner) {\n    partner.dataset.go = event.detail.index.toString();\n  }\n\n  if (event.target.classList.contains('product-image-gallery-nav')) {\n    markCurrentThumbnail(event.target, event.detail.index);\n  }\n}\n\n/**\n * A click or Enter on a thumbnail moves the strip to it, and the strip's move carries the gallery\n * along with it.\n */\nfunction onSlideClicked(event: WindowEventMap['carousel-slide-clicked']) {\n  if (isGalleryCarousel(event.target) &&\n    event.target.classList.contains('product-image-gallery-nav')) {\n    event.target.dataset.go = event.detail.index.toString();\n  }\n}\n\n/**\n * The two carousels mount on their own, so whichever one mounts second catches up to the other.\n * They both start on the first slide, so this only matters when the first one moved before the\n * second one was ready.\n */\nfunction onCarouselMounted(event: WindowEventMap['carousel-element-mounted']) {\n  const carousel = event.target;\n  if (!isGalleryCarousel(carousel)) {\n    return;\n  }\n\n  const partner = findPartner(carousel);\n  if (partner?.dataset.index && partner.dataset.index !== carousel.dataset.index) {\n    carousel.dataset.go = partner.dataset.index;\n  }\n\n  if (carousel.classList.contains('product-image-gallery-nav')) {\n    markCurrentThumbnail(carousel, parseInt(carousel.dataset.index, 10));\n  }\n}\n\nfunction onSlideHidden(event: WindowEventMap['carousel-slide-hidden']) {\n  if (!(event.target instanceof HTMLElement)) {\n    return;\n  }\n\n  if (event.target.classList.contains('product-image-gallery-main')) {\n    const video = event.detail.slide.querySelector('video');\n    if (video && !video.paused) {\n      video.pause();\n    }\n  }\n}\n\nfunction onSlideVisible(event: WindowEventMap['carousel-slide-visible']) {\n  if (!(event.target instanceof HTMLElement)) {\n    return;\n  }\n\n  if (event.target.classList.contains('product-image-gallery-main')) {\n    handleSlideVisible(event.target, event.detail.slide, event.detail.index);\n  }\n}\n\nfunction handleSlideVisible(gallery: HTMLElement, slide: HTMLElement, index: number) {\n  // This module expects that the Shopify global is correctly declared and initialized. We\n  // intentionally are not being defensive about its presence here.\n\n  const video = slide.querySelector('video');\n  if (video && !Shopify.designMode) {\n    // a11y requirement - 1.4.2 Audio Control – Level A\n    video.muted = true;\n    // we delay setting the loop attribute until the video is visible as a workaround for a11y\n    // checkers that incorrectly flag looping muted videos as violations\n    video.loop = true;\n\n    // A refusal here is not actionable, so keep a breadcrumb and never warn.\n    play(video).catch(console.debug);\n  }\n\n  let viewedSlides = viewedSlidesByGallery.get(gallery);\n  if (!viewedSlides) {\n    viewedSlides = new Set();\n    viewedSlidesByGallery.set(gallery, viewedSlides);\n  }\n\n  viewedSlides.add(index);\n\n  // The carousel reports the first slides in view during its mount, before it writes its slide\n  // count, so we count the slides ourselves\n  const totalSlides = gallery.querySelectorAll('[data-carousel-slide]').length;\n\n  type Detail = ProductCarouselViewedEvent['detail'];\n\n  const detail: Detail = {\n    index,\n    is_video: !!video,\n    percent_viewed: (viewedSlides.size / totalSlides) * 100,\n    product_id: parseInt(gallery.dataset.productId, 10)\n  };\n\n  const variantImageEl = slide.querySelector<HTMLElement>('.selected-variant[data-variant-image]');\n  if (variantImageEl?.dataset.variantImage) {\n    detail.variant_id = parseInt(variantImageEl.dataset.variantImage, 10);\n  }\n\n  const event = new CustomEvent<Detail>('product-carousel-viewed', {\n    bubbles: true,\n    composed: true,\n    detail\n  });\n\n  gallery.dispatchEvent(event);\n}\n\nfunction updateVariantImage(gallery: HTMLElement,\n  event: WindowEventMap['product-variant-option-changed']) {\n  if (!Number.isInteger(event.detail.selected_variant_id)) {\n    return;\n  }\n\n  const productId = parseInt(gallery.dataset.productId, 10);\n\n  // Because the Shopify Bundle App creates a new abstraction product, the emitted values from the\n  // event.detail.id property will not match the passed in productId for bundle product id\n  // configured using the app. We can test the passed in productId against\n  // event.detail.bundle_product_id to confirm this is a bundle.\n  if (productId === event.detail.bundle_product_id) {\n    type ProductElement = HTMLElementTagNameMap['product-element'];\n    const bundle = document.querySelector<ProductElement>(\n      `#product-detail-section add-to-cart > product-element[data-id=\"${productId}\"]`);\n    if (!bundle) {\n      return;\n    }\n\n    const variant = selectBundleVariant(bundle, event.detail.title, event.detail.option_groups);\n    if (variant) {\n      showVariantImage(parseInt(variant.dataset.id, 10));\n      goToVariantSlide(gallery);\n    }\n\n    return;\n  }\n\n  // Due to the fact sale pages could have more than one product detail section each with their own\n  // respective image galleries, we must incorporate a check to ensure the scoped gallery is for the\n  // same product which originated the variant changed event.\n\n  if (productId === event.detail.id) {\n    showVariantImage(event.detail.selected_variant_id);\n    goToVariantSlide(gallery);\n  }\n}\n\nfunction isProductVariantOptionChangedEvent(value: any):\n  value is WindowEventMap['product-variant-option-changed'] {\n  return value instanceof Event && value.type === 'product-variant-option-changed';\n}\n\nfunction onReady() {\n  const gallery = document.querySelector<HTMLElement>('.product-image-gallery-main');\n  if (!gallery) {\n    return;\n  }\n\n  // The gallery may have mounted before these listeners went on, in which case it already reported\n  // its first slide as visible and we never heard it. So when we have not seen any slide yet but\n  // the gallery is mounted, we handle the slide it is showing now.\n  if (!viewedSlidesByGallery.has(gallery) && gallery.dataset.index) {\n    const slides = gallery.querySelectorAll<HTMLElement>('[data-carousel-slide]');\n    const index = parseInt(gallery.dataset.index, 10);\n    const slide = slides[index];\n    if (slide) {\n      handleSlideVisible(gallery, slide, index);\n    }\n  }\n\n  // Same for the thumbnail strip, which marks its current thumbnail once it mounts\n  const nav = findPartner(gallery);\n  if (nav?.dataset.index) {\n    markCurrentThumbnail(nav, parseInt(nav.dataset.index, 10));\n  }\n\n  type ProductVariantOptionChangedEvent = WindowEventMap['product-variant-option-changed'];\n  const onProductVariantOptionChanged = (event: ProductVariantOptionChangedEvent) => {\n    updateVariantImage(gallery, event);\n  };\n\n  for (const event of product_image_gallery.queue) {\n    if (isProductVariantOptionChangedEvent(event)) {\n      try {\n        onProductVariantOptionChanged(event);\n      } catch (error) {\n        console.warn(error);\n      }\n    }\n  }\n\n  addEventListener('product-variant-option-changed', onProductVariantOptionChanged);\n}\n\ntype ProductCarouselViewedEvent = CustomEvent<{\n  index: number;\n  is_video: boolean;\n  percent_viewed: number;\n  product_id: number;\n  variant_id?: number;\n}>;\n\ndeclare global {\n  /**\n   * Holds the global state for the product image gallery. Created in the product image gallery\n   * snippet before this module loads so that events can be captured and replayed once the module\n   * initializes.\n   */\n  var product_image_gallery: {\n    /**\n     * A listener used by the snippet to capture events before this module loads.\n     *\n     * This is only declared for awareness purposes. Nothing should use this other than the snippet.\n     */\n    listener: (event: Event) => void;\n\n    /**\n     * Captures events that occurred before this module loaded.\n     */\n    queue: Event[];\n  };\n\n  interface WindowEventMap {\n    'product-carousel-viewed': ProductCarouselViewedEvent;\n  }\n}\n\n// These go on now rather than once the document is ready, because a carousel can mount and report\n// its first slides before a ready callback gets to run.\naddEventListener('carousel-element-mounted', onCarouselMounted);\naddEventListener('carousel-slide-move-started', onSlideMoveStarted);\naddEventListener('carousel-slide-clicked', onSlideClicked);\naddEventListener('carousel-slide-visible', onSlideVisible);\naddEventListener('carousel-slide-hidden', onSlideHidden);\n\nready(onReady);\n","/**\n * Try to play a video, and always report the outcome as a promise.\n *\n * Do not inline this function. Playing a video is inconsistent across browsers in several ways, and\n * wrapping the call in an async function is what smooths all of them over at once.\n *\n * Some browsers have no `play` function on the element at all, so the call throws a TypeError. Some\n * have one that returns undefined instead of a promise, which is how `play` worked before the spec\n * gained a return value. Some return a promise that rejects later, which is the modern behavior and\n * is how a browser reports that it refused to autoplay. And some throw synchronously, right out of\n * the call, rather than rejecting.\n *\n * An async function turns all four into the same thing. Whatever happens inside it, the caller gets\n * a promise back, so a synchronous throw arrives as a rejection and a missing or non-promise `play`\n * still produces a promise that resolves. That means one `catch` on the call site covers every\n * failure mode, and the caller never has to check whether it got a promise before attaching a\n * handler.\n *\n * Nothing here is actionable, because a refusal is the browser enforcing its own autoplay policy.\n * Restrictive in-app browsers and low power mode refuse even after a user gesture, and interrupted\n * or unsupported sources reject too. The video simply stays paused, so callers should keep a debug\n * breadcrumb and never warn.\n */\nexport async function play(video: HTMLVideoElement) {\n  return video.play();\n}\n","import { ready } from '../../lib/dom-ready.js';\n\nfunction onPurchaseOptionChanged(event: Event) {\n  if (!(event.target instanceof HTMLInputElement)) {\n    return;\n  }\n\n  const options = document.querySelector<HTMLElement>('.subscription-options');\n  if (!options) {\n    console.error('subscription-options not found');\n    return;\n  }\n\n  const plans = document.querySelector('.selling-plans');\n  if (!plans) {\n    console.error('selling-plans not found');\n    return;\n  }\n\n  if (event.target.value === 'subscription') {\n    options.inert = false;\n    options.classList.remove('collapsed');\n\n    plans.addEventListener('change', onSellingPlanChanged);\n    const selected =\n      document.querySelector<HTMLOptionElement>('select.selling-plans option:checked');\n\n    if (selected) {\n      updateSellingPlanIdOnAddToBag(selected?.value);\n    }\n  } else if (event.target.value === 'one-time') {\n    plans.removeEventListener('change', onSellingPlanChanged);\n    updateSellingPlanIdOnAddToBag('');\n    options.classList.add('collapsed');\n    options.inert = true;\n  }\n}\n\nfunction onSellingPlanChanged(event: Event) {\n  if (event.target instanceof HTMLSelectElement) {\n    const selected = event.target.options[event.target.selectedIndex];\n    const sellingPlanId = selected.value;\n\n    updateSellingPlanIdOnAddToBag(sellingPlanId);\n    updateProductDiscountPercentage(sellingPlanId);\n    updateProductSellingPlanPrices(sellingPlanId);\n  }\n}\n\nfunction updateProductDiscountPercentage(sellingPlanId: string) {\n  const sellingPlanEl = document.querySelector<HTMLElement>(\n    `subscription-plan[data-id=\"${sellingPlanId}\"]`);\n\n  if (!sellingPlanEl) {\n    console.warn('subscription-plan element not found for selling plan id: ', sellingPlanId);\n    return;\n  }\n\n  const priceEl = document.querySelector<HTMLElementTagNameMap['price-element']>(\n    '.subscription-wrapper price-element.subscription-price');\n  if (!priceEl) {\n    return;\n  }\n\n  const compareEl = document.querySelector<HTMLElementTagNameMap['price-element']>(\n    '.subscription-wrapper price-element.subscription-price + price-element.price-discount');\n  if (!compareEl) {\n    return;\n  }\n\n  const newPrice = parseInt(sellingPlanEl.dataset.subunits, 10);\n  const price = parseInt(priceEl.dataset.subunits, 10);\n\n  // If the subscription price is lower than the original price and the compare at price is null we\n  // must update the compare at price to be the original price so that the discount badge will show\n  // with the correct discount percentage.\n  if (!compareEl.dataset.subunits && newPrice < price) {\n    priceEl.dataset.subunits = newPrice.toString();\n    compareEl.dataset.subunits = price.toString();\n  }\n\n  // If new price matches the existing compare at price we zero out the compare at price subunit\n  // attributes to prevent showing a discount badge when there is no discount.\n  if (parseInt(compareEl.dataset.subunits, 10) === newPrice) {\n    compareEl.dataset.subunits = '';\n  }\n\n  // Extract final compare at price after potential adjustments above to ensure discount badge is\n  // calculated correctly based on the actual values that will be rendered in the price elements.\n  const comparePrice = compareEl.dataset.subunits ? parseInt(compareEl.dataset.subunits, 10) : 0;\n\n  let badgeEl = document.querySelector<HTMLElement>('span.subscription.discount-copy');\n  if (newPrice < comparePrice) {\n    if (!badgeEl) {\n      badgeEl = document.createElement('span');\n      badgeEl.classList.add('subscription');\n      badgeEl.classList.add('discount-copy');\n      badgeEl.style.display = 'inline';\n      compareEl.insertAdjacentElement('afterend', badgeEl);\n    }\n\n    const discountValue = comparePrice - newPrice;\n    const discountPercent = Math.round((discountValue / comparePrice) * 100);\n    badgeEl.innerHTML = `${discountPercent}% OFF`;\n  } else if (badgeEl) {\n    // If there is no discount but the badge exists, remove it.\n    badgeEl.remove();\n  }\n}\n\nfunction updateProductSellingPlanPrices(sellingPlanId: string) {\n  const sellingPlanEl = document.querySelector<HTMLElement>(\n    `subscription-plan[data-id=\"${sellingPlanId}\"]`);\n\n  if (!sellingPlanEl) {\n    console.warn('subscription-plan element not found for selling plan id: ', sellingPlanId);\n    return;\n  }\n\n  const priceEls = document.querySelectorAll<HTMLElementTagNameMap['price-element']>(\n    'price-element.subscription-price');\n  if (!priceEls) {\n    console.warn('price-element with class subscription-price not found');\n    return;\n  }\n\n  for (const priceEl of priceEls) {\n    priceEl.dataset.subunits = sellingPlanEl.dataset.subunits;\n  }\n}\n\nfunction updateSellingPlanIdOnAddToBag(sellingPlanId: string) {\n  const addToBag = document.querySelector<HTMLElementTagNameMap['add-to-cart']>(\n    '#product-detail-section add-to-cart');\n  if (!addToBag) {\n    console.warn('add-to-cart not found');\n    return;\n  }\n\n  addToBag.dataset.sellingPlanId = sellingPlanId;\n  addToBag.dataset.update = Date.now().toString();\n}\n\nfunction onPurchaseOptionClick(event: MouseEvent) {\n  // Ignore events from the selling plans dropdown\n  if (event.target instanceof Element && event.target?.tagName === 'SELECT') {\n    return;\n  }\n\n  if (event.currentTarget instanceof HTMLDivElement) {\n    const input = event.currentTarget.querySelector<HTMLInputElement>('input[type=\"radio\"]');\n    if (input && !input.checked) {\n      input.focus();\n      input.click();\n    }\n  }\n}\n\nfunction onReady() {\n  const optionWrappers = document.querySelectorAll<HTMLElement>(\n    '#product-detail-section .purchase-option');\n  for (const wrapper of optionWrappers) {\n    const radio = wrapper.querySelector<HTMLInputElement>('input[type=\"radio\"]');\n    if (radio) {\n      radio.addEventListener('change', onPurchaseOptionChanged);\n    }\n\n    // for better ux allow clicking anywhere in the wrapper to activate the radio button\n    wrapper.addEventListener('click', onPurchaseOptionClick);\n  }\n\n  // get active subscription plan id\n  const activeSellingPlan = document.querySelector<HTMLOptionElement>(\n    '#product-detail-section select.selling-plans option:checked');\n\n  // update pricing and discount percents in case selling plan has additional discounts\n  if (activeSellingPlan) {\n    updateProductDiscountPercentage(activeSellingPlan.value);\n    updateProductSellingPlanPrices(activeSellingPlan.value);\n  }\n}\n\ntype ProductSubscriptionOptionChangedEvent = CustomEvent<Partial<{\n  selling_plan_id?: string;\n}>>;\n\ndeclare global {\n  interface WindowEventMap {\n    'product-subscription-option-changed': ProductSubscriptionOptionChangedEvent;\n  }\n}\n\nready(onReady);\n","import { ready } from '../../lib/dom-ready.js';\n\nfunction onSellingPlanChanged(event: WindowEventMap['selling-plan-changed']) {\n  const addToBag = document.querySelector<HTMLElementTagNameMap['add-to-cart']>(\n    '#product-detail-section add-to-cart');\n  if (!addToBag) {\n    console.warn('add-to-cart not found');\n    return;\n  }\n\n  const sellingPlanId = event.detail.selling_plan_id;\n  if (sellingPlanId) {\n    addToBag.dataset.sellingPlanId = sellingPlanId;\n  } else {\n    delete addToBag.dataset.sellingPlanId;\n  }\n\n  addToBag.dataset.update = Date.now().toString();\n}\n\nfunction onProductVariantOptionChanged(event: WindowEventMap['product-variant-option-changed']) {\n  const selectedVariantId = event.detail.selected_variant_id;\n\n  const sellingPlanSelector = document.querySelector('selling-plan-selector');\n  if (!sellingPlanSelector) {\n    // not a subscription product, or using v1 of the subscription UI\n    return;\n  }\n\n  sellingPlanSelector.dataset.selectedVariantId = selectedVariantId.toString();\n}\n\nfunction onReady() {\n  const productDetail = document.querySelector<HTMLElement>('#product-detail-section');\n  if (productDetail) {\n    productDetail.addEventListener('selling-plan-changed', onSellingPlanChanged);\n    productDetail.addEventListener('product-variant-option-changed', onProductVariantOptionChanged);\n  }\n}\n\nready(onReady);\n","import type { CustomElement } from '@integrabeauty/custom-elements';\nimport html from './index.html';\nimport styles from './index.scss';\n\ntype VariantElement = HTMLElementTagNameMap['variant-element'];\n\n/**\n * Product variants, e.g. different colors or sizes\n **/\ninterface ProductVariant {\n  /**\n   * Whether the variant is available or out of stock\n   */\n  available: boolean;\n\n  /**\n   * Variant price in cents\n   **/\n  compareAtPrice?: number;\n\n  /**\n   * The ID of the variant\n   */\n  id: string;\n\n  /**\n   * Variant price in cents\n   **/\n  price: number;\n}\n\ninterface SubscriptionSellingPlan {\n  id: string;\n  name: string;\n\n  /**\n   * The price adjustment percentage of the selling plan, e.g. 10 for a 10% discount or 0 when there\n   * is no discount\n   */\n  priceAdjustmentPercentage: number;\n\n  /**\n   * Whether the selling plan is a subscription plan\n   */\n  recurringDeliveries: boolean;\n}\n\n/**\n * The selling plan selector component is responsible for displaying the available selling plans\n * (e.g. one-time purchase and subscription options) for a product, allowing the user to select a\n * selling plan, and communicating the selected selling plan to the rest of the app via a custom\n * event. It handles multiple variants even when the variants have different prices.\n */\nclass SellingPlanSelector extends HTMLElement implements CustomElement {\n  public static get observedAttributes() {\n    return [\n      'data-selected-variant-id'\n    ];\n  }\n\n  declare readonly dataset: {\n    /**\n     * Id of the currently selected variant. This is used to determine which selling plans to show.\n     * This may be updated by the product form when the user selects a different variant - we react\n     * to those changes and update the selling plans accordingly.\n     */\n    selectedVariantId: string;\n\n    /**\n     * Text on the subscription badge\n     */\n    subscriptionBadgeText: string;\n\n    /**\n     * Text on the subscription CTA button\n     */\n    subscriptionCtaText: string;\n  };\n\n  private declare selectedVariant?: ProductVariant;\n  private subscriptionSellingPlans: SubscriptionSellingPlan[] = [];\n  private declare selectedSellingPlan?: SubscriptionSellingPlan;\n  private onPurchaseOptionChangedBound = this.onPurchaseOptionChanged.bind(this);\n  private onSellingPlanChangedBound = this.onSellingPlanChanged.bind(this);\n\n  public connectedCallback() {\n    this.initializeShadowRoot();\n\n    const purchaseOptions = this.shadowRoot.querySelectorAll<HTMLElement>('.purchase-option');\n    for (const option of purchaseOptions) {\n      const radio = option.querySelector<HTMLInputElement>('input[type=\"radio\"]');\n      if (radio) {\n        radio.addEventListener('change', this.onPurchaseOptionChangedBound);\n      }\n\n      // for better UX the entire div wrapper is clickable to activate the radio button\n      option.addEventListener('click', onPurchaseOptionClicked);\n    }\n\n    const sellingPlansSelect = this.shadowRoot.querySelector<HTMLSelectElement>(\n      'select.selling-plans');\n    sellingPlansSelect.addEventListener('change', this.onSellingPlanChangedBound);\n\n    this.gatherData();\n    this.render();\n  }\n\n  public disconnectedCallback() {\n    const purchaseOptions = this.shadowRoot.querySelectorAll<HTMLElement>('.purchase-option');\n    for (const option of purchaseOptions) {\n      const radio = option.querySelector<HTMLInputElement>('input[type=\"radio\"]');\n      if (radio) {\n        radio.removeEventListener('change', this.onPurchaseOptionChangedBound);\n      }\n\n      option.removeEventListener('click', onPurchaseOptionClicked);\n    }\n\n    const sellingPlansSelect = this.shadowRoot.querySelector<HTMLSelectElement>(\n      'select.selling-plans');\n    sellingPlansSelect.removeEventListener('change', this.onSellingPlanChangedBound);\n  }\n\n  public attributeChangedCallback(name: string, oldValue: string, newValue: string) {\n    this.initializeShadowRoot();\n\n    if (oldValue === newValue) {\n      return;\n    }\n\n    if (this.isConnected) {\n      this.gatherData();\n      this.render();\n    }\n  }\n\n  private initializeShadowRoot() {\n    if (!this.shadowRoot) {\n      this.attachShadow({ mode: 'open' });\n      this.shadowRoot.innerHTML = `<style>${styles}</style>${html}`;\n    }\n  }\n\n  private gatherData() {\n    const variants = [];\n    const variantElements = this.querySelectorAll<VariantElement>(\n      '[slot=\"product\"] > variant-element');\n\n    for (const variantElement of variantElements) {\n      const variant: ProductVariant = {\n        available: variantElement.dataset.available === 'true',\n        id: variantElement.dataset.id,\n        price: parseInt(variantElement.dataset.price ?? '0', 10)\n      };\n\n      if (variantElement.dataset.compareAtPrice) {\n        const compareAtPrice = parseInt(variantElement.dataset.compareAtPrice, 10);\n        if (!isNaN(compareAtPrice)) {\n          variant.compareAtPrice = compareAtPrice;\n        }\n      }\n\n      variants.push(variant);\n    }\n\n    const selectedVariantId = this.dataset.selectedVariantId;\n    this.selectedVariant = undefined;\n    for (const variant of variants) {\n      if (variant.id === selectedVariantId) {\n        this.selectedVariant = variant;\n        break;\n      }\n    }\n\n    type SubscriptionSellingPlanElement = HTMLElementTagNameMap['subscription-selling-plan'];\n\n    const sellingPlans: SubscriptionSellingPlan[] = [];\n    const sellingPlanElements = this.querySelectorAll<SubscriptionSellingPlanElement>(\n      'subscription-selling-plan[slot=\"plans\"]');\n\n    for (const sellingPlanElement of sellingPlanElements) {\n      const sellingPlan: SubscriptionSellingPlan = {\n        id: sellingPlanElement.dataset.id,\n        name: sellingPlanElement.textContent.trim(),\n        priceAdjustmentPercentage: parseFloat(sellingPlanElement.dataset.priceAdjustmentPercentage),\n        recurringDeliveries: sellingPlanElement.dataset.recurringDeliveries === 'true'\n      };\n\n      if (isNaN(sellingPlan.priceAdjustmentPercentage)) {\n        sellingPlan.priceAdjustmentPercentage = 0;\n      }\n\n      sellingPlans.push(sellingPlan);\n    }\n\n    this.subscriptionSellingPlans = sellingPlans;\n  }\n\n  private render() {\n    const subscriptionCta = this.shadowRoot.querySelector('.subscription-cta');\n    subscriptionCta.textContent = this.dataset.subscriptionCtaText;\n\n    const subscriptionBadge = this.shadowRoot.querySelector('.subscription-badge');\n    subscriptionBadge.textContent = this.dataset.subscriptionBadgeText;\n\n    const sellingPlansSelect = this.shadowRoot.querySelector<HTMLSelectElement>(\n      'select.selling-plans');\n    sellingPlansSelect.innerHTML = '';\n\n    let activeSellingPlanId = this.selectedSellingPlan?.id;\n    // if no plan has been selected yet, default to the first one\n    if (!activeSellingPlanId && this.subscriptionSellingPlans.length > 0) {\n      activeSellingPlanId = this.subscriptionSellingPlans[0].id;\n    }\n\n    for (const plan of this.subscriptionSellingPlans) {\n      const option = document.createElement('option');\n      option.value = plan.id;\n      option.textContent = plan.name;\n      if (plan.id === activeSellingPlanId) {\n        option.selected = true;\n      }\n\n      sellingPlansSelect.appendChild(option);\n    }\n\n    this.updatePrices();\n  }\n\n  private updatePrices() {\n    if (!this.selectedVariant) {\n      return;\n    }\n\n    type PriceElement = HTMLElementTagNameMap['price-element'];\n    const oneTimePriceElement = this.querySelector<PriceElement>('[slot=\"one-time-price\"] .price');\n    const oneTimeCompareAtPriceElement = this.querySelector<PriceElement>(\n      '[slot=\"one-time-price\"] .compare-at-price');\n\n    const oneTimePrice = this.selectedVariant.price;\n    const oneTimeCompareAtPrice = this.selectedVariant.compareAtPrice;\n\n    oneTimePriceElement.dataset.subunits = oneTimePrice.toString();\n    if (oneTimeCompareAtPrice) {\n      oneTimeCompareAtPriceElement.dataset.subunits = oneTimeCompareAtPrice.toString();\n      oneTimeCompareAtPriceElement.dataset.hide = 'false';\n    } else {\n      oneTimeCompareAtPriceElement.dataset.hide = 'true';\n    }\n\n    const subscriptionPriceElement = this.querySelector<PriceElement>(\n      '[slot=\"subscription-price\"] .price');\n    const subscriptionCompareAtPriceElement = this.querySelector<PriceElement>(\n      '[slot=\"subscription-price\"] .compare-at-price');\n\n    let priceAdjustmentPercentage;\n    if (this.selectedSellingPlan) {\n      // if the plan is selected - use its price adjustment percentage\n      priceAdjustmentPercentage = this.selectedSellingPlan.priceAdjustmentPercentage;\n    } else if (this.subscriptionSellingPlans.length > 0) {\n      // if a one-time plan is currently selected but there are subscription plans available,\n      // default to the first subscription plan's price adjustment percentage for price calculation\n      priceAdjustmentPercentage = this.subscriptionSellingPlans[0].priceAdjustmentPercentage;\n    }\n\n    if (priceAdjustmentPercentage > 0) {\n      subscriptionPriceElement.dataset.subunits = calculatePrice(this.selectedVariant.price,\n        priceAdjustmentPercentage).toString();\n      subscriptionCompareAtPriceElement.dataset.subunits = this.selectedVariant.price.toString();\n      subscriptionCompareAtPriceElement.dataset.hide = 'false';\n    } else {\n      subscriptionPriceElement.dataset.subunits = this.selectedVariant.price.toString();\n      subscriptionCompareAtPriceElement.dataset.hide = 'true';\n    }\n  }\n\n  private onSellingPlanChanged(_event: Event) {\n    this.updateCurrentSellingPlan();\n  }\n\n  private onPurchaseOptionChanged(event: Event) {\n    if (!(event.currentTarget instanceof HTMLInputElement)) {\n      return;\n    }\n\n    const options = this.shadowRoot.querySelector<HTMLElement>('.subscription-options');\n\n    const oneTimeLabel = this.shadowRoot.querySelector('label .one-time');\n    const subscriptionLabel =\n      this.shadowRoot.querySelector('label .subscription-cta');\n\n    if (event.currentTarget.value === 'subscription') {\n      options.inert = false;\n      options.classList.remove('collapsed');\n      oneTimeLabel.classList.remove('selected');\n      subscriptionLabel.classList.add('selected');\n    } else {\n      options.classList.add('collapsed');\n      options.inert = true;\n      oneTimeLabel.classList.add('selected');\n      subscriptionLabel.classList.remove('selected');\n    }\n\n    this.updateCurrentSellingPlan();\n  }\n\n  private updateCurrentSellingPlan() {\n    const radio = this.shadowRoot.querySelector<HTMLInputElement>(\n      'input[name=\"purchase-option\"][value=\"subscription\"]');\n\n    let subscriptionPlan: SubscriptionSellingPlan;\n    // if the subscription option is selected, the selling plan is determined by the dropdown;\n    // otherwise the selling plan is the \"one-time purchase\" option, which doesn't have a selling\n    // plan id and is represented by undefined\n    if (radio && radio.checked) {\n      // get active subscription plan id\n      const activeSellingPlan = this.shadowRoot.querySelector<HTMLOptionElement>(\n        'select.selling-plans option:checked');\n      if (activeSellingPlan) {\n        const sellingPlanId = activeSellingPlan.value;\n        for (const plan of this.subscriptionSellingPlans) {\n          if (plan.id === sellingPlanId) {\n            subscriptionPlan = plan;\n            break;\n          }\n        }\n      }\n    }\n\n    // dispatch the event only if the selling plan has changed\n    if (this.selectedSellingPlan !== subscriptionPlan) {\n      this.selectedSellingPlan = subscriptionPlan;\n\n      const event = new CustomEvent('selling-plan-changed', {\n        bubbles: true,\n        composed: true,\n        detail: {\n          selling_plan_id: this.selectedSellingPlan?.id\n        }\n      });\n\n      this.dispatchEvent(event);\n    }\n\n    this.updatePrices();\n  }\n}\n\n/**\n * Calculate the price after applying the discount percentage. For example, if the original price is\n * $24.00 (2400 cents) and the discount percentage is 10, the final price will be $21.60 (2160\n * cents). We round the result to avoid fractional cents, e.g. when the original price is $19.99 and\n * the discount percentage is 15, the final price will be $16.99 instead of $16.9915.\n */\nfunction calculatePrice(price: number, adjustmentPercentage?: number) {\n  const discountPercent = (adjustmentPercentage ?? 0) / 100;\n  return Math.round(price * (1 - discountPercent));\n}\n\n/**\n * For better UX we allow clicking anywhere in the purchase option wrapper to activate the radio\n * button, so we need to forward those click events to the radio button. However, we don't want to\n * trigger that when the user is interacting with the selling plan dropdown, so we ignore click\n * events coming from there.\n */\nfunction onPurchaseOptionClicked(event: MouseEvent) {\n  if (event.target instanceof Element && event.target.tagName === 'SELECT') {\n    return;\n  }\n\n  if (event.currentTarget instanceof HTMLDivElement) {\n    const input = event.currentTarget.querySelector<HTMLInputElement>('input[type=\"radio\"]');\n    if (input && !input.checked) {\n      input.focus();\n      input.click();\n    }\n  }\n}\n\ndeclare global {\n  interface HTMLElementTagNameMap {\n    'selling-plan-selector': SellingPlanSelector;\n  }\n}\n\nif (!customElements.get('selling-plan-selector')) {\n  customElements.define('selling-plan-selector', SellingPlanSelector);\n}\n","import { ready } from '../../lib/dom-ready.js';\n\ntype AddToCartElement = HTMLElementTagNameMap['add-to-cart'];\ntype VariantElement = HTMLElementTagNameMap['variant-element'];\n\nfunction onCheckboxChanged(event: Event) {\n  if (event.currentTarget instanceof HTMLInputElement) {\n    const addon = event.currentTarget.closest<HTMLElement>('.addon');\n    if (addon) {\n      onVariantChanged(addon);\n    }\n  }\n}\n\nfunction onSelectChanged(event: Event) {\n  if (event.currentTarget instanceof HTMLSelectElement) {\n    const addon = event.currentTarget.closest<HTMLElement>('.addon');\n    if (addon) {\n      onVariantChanged(addon);\n    }\n  }\n}\n\n/**\n * Refresh the addon card contents after any of the possible state changes (checkbox, variant\n * select, etc)\n */\nfunction onVariantChanged(addon: HTMLElement) {\n  // Find the main add to cart button\n  const addToCart = document.querySelector<AddToCartElement>('add-to-cart[data-main=\"true\"]');\n  if (!addToCart) {\n    console.warn('Could not find main add to cart element to add addon variant data to');\n    return;\n  }\n\n  const addonContainer = addon.querySelector<HTMLElement>('.addon-container');\n\n  const select = addon.querySelector<HTMLSelectElement>('select.variant-selector');\n  const selectedVariantId = select?.value;\n\n  let addonVariantData = addonContainer?.querySelector<VariantElement>(\n    'product-element > variant-element[data-available=\"true\"]');\n\n  if (selectedVariantId) {\n    addonVariantData = addonContainer?.querySelector<VariantElement>(\n      'product-element > variant-element[data-id=\"' + selectedVariantId + '\"]');\n  }\n\n  if (!addonVariantData) {\n    console.warn('Could not find variant data for addon');\n    return;\n  }\n\n  const productElement = addonVariantData.closest('product-element');\n  let imageSrc = addonVariantData.dataset.imageSrc;\n  if (!imageSrc) {\n    if (productElement) {\n      imageSrc = productElement.dataset.mainImage;\n    }\n  }\n\n  const productImage = addon.querySelector('img');\n  if (productImage && imageSrc) {\n    productImage.src = imageSrc;\n  }\n\n  const backorderMessage = addon.querySelector<HTMLElement>('.backorder');\n  if (backorderMessage) {\n    const backorder = addonVariantData.dataset.backorder;\n    backorderMessage.textContent = backorder || '';\n    backorderMessage.hidden = !backorder;\n  }\n\n  const productId = productElement.dataset.id;\n\n  // remove any existing addon data from the add to cart element (any variants)\n  const selector = `variant-element[data-is-addon=\"true\"][data-product-id=\"${productId}\"]`;\n  const variants = addToCart.querySelectorAll(selector);\n  for (const variant of variants) {\n    variant.remove();\n  }\n\n  // if the checkbox is checked - add the current variant to the add to cart element\n  const checkbox = addonContainer.querySelector<HTMLInputElement>('input[type=\"checkbox\"]');\n  if (checkbox.checked) {\n    // Add the addon variant data to the add to cart element\n    const variant = <VariantElement>addonVariantData.cloneNode(true);\n    variant.dataset.isAddon = 'true';\n    variant.dataset.productId = productId;\n\n    if (variant.dataset.backorder) {\n      setCustomAttribute(variant, '_backorder_message', variant.dataset.backorder);\n    }\n\n    if (variant.dataset.backorderSellingPlanId) {\n      setCustomAttribute(variant, 'selling_plan', variant.dataset.backorderSellingPlanId);\n    }\n\n    addToCart.appendChild(variant);\n  }\n\n  addToCart.dataset.update = Date.now().toString();\n}\n\n/**\n * Sets a custom attribute on a variant element\n */\nfunction setCustomAttribute(variant: VariantElement, key: string, value: string) {\n  type CustomAttribute = HTMLElementTagNameMap['custom-attribute'];\n  let customAttribute = variant.querySelector<CustomAttribute>(\n    `custom-attribute[data-key=\"${key}\"]`);\n  if (!customAttribute) {\n    customAttribute = document.createElement('custom-attribute');\n    customAttribute.dataset.key = key;\n    variant.appendChild(customAttribute);\n  }\n\n  customAttribute.dataset.value = value;\n}\n\nfunction onReady() {\n  const addons = document.querySelectorAll<HTMLElement>('#product-detail-section .addon');\n  for (const addon of addons) {\n    const checkbox = addon.querySelector<HTMLInputElement>('input[type=\"checkbox\"]');\n    if (checkbox) {\n      checkbox.addEventListener('change', onCheckboxChanged);\n    }\n\n    const select = addon.querySelector<HTMLSelectElement>('select.variant-selector');\n    if (select) {\n      select.addEventListener('change', onSelectChanged);\n    }\n\n    onVariantChanged(addon);\n  }\n}\n\nready(onReady);\n","import { ready } from '../../lib/dom-ready.js';\n\n// The modal-element around the selling point disclaimers handles the close button, the dismiss\n// gestures, and telling the modal manager when it opens and closes. We pick which disclaimer shows,\n// open it, and inert the product detail section while it's open.\n\ntype ModalElement = HTMLElementTagNameMap['modal-element'];\n\nfunction onSellingPointButtonClicked(event: MouseEvent) {\n  if (!(event.currentTarget instanceof HTMLElement)) {\n    return;\n  }\n\n  const focusEvent = new Event('focus-stack-push-requested', {\n    bubbles: true,\n    composed: true\n  });\n  event.currentTarget.dispatchEvent(focusEvent);\n\n  const modal = document.querySelector<ModalElement>('#selling-point-disclaimer-modal');\n  if (!modal) {\n    console.warn('Selling point disclaimer modal not found');\n    return;\n  }\n\n  const contentItems = modal.querySelectorAll<HTMLElement>('.content p');\n  for (const item of contentItems) {\n    if (item.dataset.id === event.currentTarget.dataset.contentId) {\n      item.hidden = false;\n    } else {\n      item.hidden = true;\n    }\n  }\n\n  modal.dataset.open = 'true';\n}\n\n/**\n * The modal manager inerts every other section on the page, but it can't inert ours, since the\n * modal lives in it too. That leaves the buy box and the gallery tabbable behind the open modal, so\n * we inert them ourselves. The modal renders as a sibling of the product detail page rather than\n * inside it, so this doesn't reach the modal.\n */\nfunction onModalOpenRequested(_event: HTMLElementEventMap['modal-open-requested']) {\n  const section = document.querySelector('#product-detail-section');\n  if (section instanceof HTMLElement) {\n    section.inert = true;\n  }\n}\n\n/**\n * Takes the page back out of inert. This listener is on the modal, so it runs before the manager's\n * listener on the window, which puts focus back on the trigger, and a focus call into an inert\n * subtree does nothing.\n */\nfunction onModalCloseRequested(_event: HTMLElementEventMap['modal-close-requested']) {\n  const section = document.querySelector('#product-detail-section');\n  if (section instanceof HTMLElement) {\n    section.inert = false;\n  }\n}\n\nfunction onReady() {\n  const modal = document.querySelector('#selling-point-disclaimer-modal');\n  if (!modal) {\n    return;\n  }\n\n  modal.addEventListener('modal-open-requested', onModalOpenRequested);\n  modal.addEventListener('modal-close-requested', onModalCloseRequested);\n\n  const sellingPointButtons = document.querySelectorAll<HTMLButtonElement>(\n    '#product-detail-section .why-love-it .body button');\n  for (const button of sellingPointButtons) {\n    const item = button.closest<HTMLElement>('li[data-id]');\n    if (!item) {\n      button.inert = true;\n      continue;\n    }\n\n    button.setAttribute('aria-label', 'Open a window with more information');\n    button.dataset.contentId = item.dataset.id;\n    button.addEventListener('click', onSellingPointButtonClicked);\n  }\n}\n\nready(onReady);\n","import { ready } from '../../lib/dom-ready.js';\nimport '@integrabeauty/klaviyo-types';\n\n// B2S is the back to school promotion\n\nfunction onB2sCtaClicked(event: MouseEvent) {\n  const target = event.currentTarget;\n\n  if (!(target instanceof HTMLButtonElement)) {\n    return;\n  }\n\n  if (!window.klaviyo) {\n    return;\n  }\n\n  const openEvent = new Event('klaviyo-form-opened', { bubbles: true });\n  klaviyo.openForm('T4k3yf', () => target.dispatchEvent(openEvent));\n}\n\nfunction onReady() {\n  const params = new URLSearchParams(location.search);\n  const utmCampaign = params.get('utm_campaign');\n  const isB2sPromo = utmCampaign?.toLowerCase() === 'b2s';\n  const isB2sProduct = location.pathname.includes('axia-dryer') ||\n    location.pathname.includes('dorm-kit');\n\n  if (isB2sPromo && isB2sProduct) {\n    const b2sCta = document.querySelector<HTMLButtonElement>('.b2s-promo-cta');\n    if (!b2sCta) {\n      return;\n    }\n\n    b2sCta.style.display = 'block';\n    b2sCta.addEventListener('click', onB2sCtaClicked);\n  }\n}\n\nready(onReady);\n"],"names":["FullscreenGallery","HTMLElement","containerMoved","dragStarted","scale","innerWidth","scaleStarted","videoIndexes","slidesIndex","slidesTotal","onGalleryOpenBound","this","galleryOpen","bind","onGalleryCloseBound","galleryClose","onGalleryKeyboardEventBound","onGalleryKeyboardEvent","onToggleZoomBound","toggleZoom","onSlideVisibleBound","onSlideVisible","onCarouselPrevBound","onCarouselPrev","onCarouselNextBound","onCarouselNext","onCarouselMountedBound","onCarouselMounted","onViewportChangedBound","onViewportChanged","onStartDragBound","onStartDrag","onStopDragBound","onStopDrag","onMoveDragBound","onMoveDrag","onStartTouchBound","onStartTouch","onStopTouchBound","onStopTouch","onMoveTouchBound","onMoveTouch","connectedCallback","initializeShadowRoot","closeButton","shadowRoot","querySelector","addEventListener","zoomButton","prevButton","nextButton","visualViewport","readCarousel","render","disconnectedCallback","removeEventListener","container","attachShadow","mode","innerHTML","visualCurrentSlideNumber","textContent","toString","counter","setAttribute","join","arrows","querySelectorAll","arrow","hidden","_event","updateViewportVars","style","viewport","width","height","innerHeight","offsetTop","offsetLeft","setProperty","Math","ceil","carousel","dataset","slideCount","parseInt","index","slides","slide","entries","classList","contains","push","event","detail","goTo","go","KeyboardEvent","includes","code","preventDefault","type","clearZoom","total","prevIndex","nextIndex","_target$parentElement","target","tagName","parentElement","Element","closest","passive","scrollbarWidth","document","documentElement","clientWidth","Number","isInteger","scrollLocked","expanded","controlsHidden","focusable","focusableEl","tabIndex","focus","focusVisible","galleryEvent","Event","bubbles","composed","dispatchEvent","carouselEl","zoomEl","zoom","transform","matches","match","posX","clientX","posY","clientY","dragging","body","offsetX","offsetY","getBoundingClientRect","constraintX","floor","constraintY","clientHeight","touches","length","x","y","pageX","pageY","ready","callback","readyState","timer","setTimeout","clearTimeout","bundle_variant_select","bundle","productTitle","options","option","bundleOption","name","selected","selected_option","values","bundleOptions","position","title","onProductVariantOptionChanged","productDetail","currentTarget","productId","id","variantId","selected_variant_id","bundle_product_id","variant","option_groups","productElements","productElement","selectedVariantId","update","Date","now","variantElement","totalPrice","totalCompareAtPrice","subunits","price","compareAtPrice","renderBundlePrices","messages","message","renderSnippetMessage","renderSnippetMessages","modals","modal","renderProductDiscountMessages","renderInstallmentMessage","renderInstallmentMessages","priceElement","priceBox","sellingPlanSelector","percentage","parseFloat","discountPercentage","round","estimatedTotal","onMBGButtonClicked","focusEvent","open","onModalOpenRequested","section","inert","onModalCloseRequested","customElements","get","define","overrideEl","startTime","endTime","flashStartTime","flashEndTime","timestamp","appendTimer","timerEl","createElement","heading","appendChild","mbgButtons","button","viewedSlidesByGallery","WeakMap","showVariantImage","allVariantImages","matched","el","isMatch","variantImage","toggle","defaultEls","defaultEl","add","goToVariantSlide","gallery","findPartner","selector","isGalleryCarousel","markCurrentThumbnail","nav","buttons","buttonIndex","removeAttribute","handleSlideVisible","video","Shopify","designMode","muted","loop","async","play","catch","console","debug","viewedSlides","Set","set","totalSlides","is_video","percent_viewed","size","product_id","variantImageEl","variant_id","CustomEvent","isProductVariantOptionChangedEvent","value","onPurchaseOptionChanged","HTMLInputElement","error","plans","remove","onSellingPlanChanged","updateSellingPlanIdOnAddToBag","HTMLSelectElement","sellingPlanId","selectedIndex","updateProductDiscountPercentage","updateProductSellingPlanPrices","sellingPlanEl","warn","priceEl","compareEl","newPrice","comparePrice","badgeEl","display","insertAdjacentElement","discountValue","discountPercent","priceEls","addToBag","onPurchaseOptionClick","_event$target","HTMLDivElement","input","checked","click","subscriptions_v2_onSellingPlanChanged","selling_plan_id","subscriptions_v2_onProductVariantOptionChanged","partner","paused","pause","has","updateVariantImage","product_image_gallery","queue","optionWrappers","wrapper","radio","activeSellingPlan","SellingPlanSelector","observedAttributes","subscriptionSellingPlans","onPurchaseOptionChangedBound","onSellingPlanChangedBound","purchaseOptions","onPurchaseOptionClicked","gatherData","attributeChangedCallback","oldValue","newValue","isConnected","variants","variantElements","_variantElement$datas","available","isNaN","selectedVariant","undefined","sellingPlans","sellingPlanElements","sellingPlanElement","sellingPlan","trim","priceAdjustmentPercentage","recurringDeliveries","_this$selectedSelling","subscriptionCtaText","subscriptionBadgeText","sellingPlansSelect","activeSellingPlanId","selectedSellingPlan","plan","updatePrices","oneTimePriceElement","oneTimeCompareAtPriceElement","oneTimePrice","oneTimeCompareAtPrice","hide","subscriptionPriceElement","subscriptionCompareAtPriceElement","adjustmentPercentage","calculatePrice","updateCurrentSellingPlan","oneTimeLabel","subscriptionLabel","subscriptionPlan","_this$selectedSelling2","onCheckboxChanged","addon","onVariantChanged","onSelectChanged","addToCart","addonContainer","select","addonVariantData","imageSrc","mainImage","productImage","src","backorderMessage","backorder","cloneNode","isAddon","setCustomAttribute","backorderSellingPlanId","key","customAttribute","onSellingPointButtonClicked","contentItems","item","contentId","selling_points_onModalOpenRequested","selling_points_onModalCloseRequested","onB2sCtaClicked","HTMLButtonElement","window","klaviyo","openEvent","openForm","addons","checkbox","sellingPointButtons","utmCampaign","URLSearchParams","location","search","isB2sPromo","toLowerCase","isB2sProduct","pathname","b2sCta"],"sourceRoot":""}