{"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/./lib/video.ts","webpack://lange/./sections/product-detail/countdown-timer-override.ts","webpack://lange/./sections/product-detail/product-image-gallery.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 onModalDismissRequestedBound = this.onModalDismissRequested.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('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    // The listener is only added while the gallery is open, but if we are removed while open the\n    // window would hold onto us and keep sending us dismiss requests from a page we left.\n    removeEventListener('modal-dismiss-requested', this.onModalDismissRequestedBound);\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    // Nothing gets pushed onto the modal manager's focus stack here, since the gallery opens from a\n    // click on a slide rather than from a focusable opener. So when the gallery closes, the manager\n    // may move focus to an older opener still on the page, such as the header cart button.\n    const openEvent = new Event('modal-open-requested', { bubbles: true, composed: true });\n    this.dispatchEvent(openEvent);\n\n    addEventListener('modal-dismiss-requested', this.onModalDismissRequestedBound);\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  /**\n   * Closes the gallery on a dismiss request from the modal manager, which it sends for an escape\n   * key press or a backdrop click. This listener is only attached while the gallery is open.\n   */\n  private onModalDismissRequested(_event: WindowEventMap['modal-dismiss-requested']) {\n    this.galleryClose();\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 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    removeEventListener('modal-dismiss-requested', this.onModalDismissRequestedBound);\n\n    const closeEvent = new Event('modal-close-requested', { bubbles: true, composed: true });\n    this.dispatchEvent(closeEvent);\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, in the subscription selector, and in the gift with purchase section's one-time\n * purchase option.\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 in the gift with purchase section's one-time purchase option, and the product\n  // slot in the subscription selector.\n  let variantElement: VariantElement;\n  const priceBox = message.closest('.price-box');\n  const purchaseOption = message.closest('.purchase-option');\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 (purchaseOption) {\n    variantElement = purchaseOption.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","/**\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 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    return;\n  }\n\n  if (event.target.classList.contains('product-image-gallery-nav')) {\n    event.target.dataset.go = event.detail.index.toString();\n  } else {\n    toggleVideo(event.detail.slide);\n  }\n}\n\n/**\n * The gallery's videos play muted on a loop as part of the page, with no browser controls drawn\n * over them, so a tap on one is how a visitor pauses it and plays it again.\n */\nfunction toggleVideo(slide: HTMLElement) {\n  const video = slide.querySelector('video');\n  if (!video) {\n    return;\n  }\n\n  if (video.paused) {\n    // A refusal here is not actionable, so keep a breadcrumb and never warn.\n    play(video).catch(console.debug);\n  } else {\n    video.pause();\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","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","onModalDismissRequestedBound","onModalDismissRequested","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","openEvent","Event","bubbles","composed","dispatchEvent","expanded","controlsHidden","focusable","focusableEl","tabIndex","focus","focusVisible","galleryEvent","closeEvent","carouselEl","zoomEl","zoom","transform","matches","match","posX","clientX","posY","clientY","dragging","document","body","offsetX","offsetY","getBoundingClientRect","constraintX","floor","clientWidth","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","Number","isInteger","modals","modal","renderProductDiscountMessages","renderInstallmentMessage","renderInstallmentMessages","priceElement","priceBox","purchaseOption","sellingPlanSelector","percentage","parseFloat","discountPercentage","round","estimatedTotal","onMBGButtonClicked","focusEvent","open","onModalOpenRequested","section","inert","onModalCloseRequested","async","play","video","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","Shopify","designMode","muted","loop","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","toggleVideo","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","openForm","addons","checkbox","sellingPointButtons","utmCampaign","URLSearchParams","location","search","isB2sPromo","toLowerCase","isB2sProduct","pathname","b2sCta"],"sourceRoot":""}