{"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/./sections/product-detail/mbg-modal.ts","webpack://lange/./sections/product-detail/countdown-timer-override.ts","webpack://lange/./sections/product-detail/product-image-gallery.ts","webpack://lange/./lib/video.ts","webpack://lange/./sections/product-detail/subscriptions.ts","webpack://lange/./sections/product-detail/subscriptions-v2.ts","webpack://lange/./sections/product-detail/selling-plan-selector/index.ts","webpack://lange/./sections/product-detail/addons.ts","webpack://lange/./sections/product-detail/selling-points.ts","webpack://lange/./sections/product-detail/b2s-promo.ts"],"sourcesContent":["import type { CustomElement } from '@integrabeauty/custom-elements';\nimport html from './index.html';\nimport styles from './index.scss';\n\n/**\n * Wraps product carousel gallery to display it as full screen gallery. It also has the zoom in,\n * zoom out option. The gallery is a carousel-element inside this one, and we read the slide count,\n * the current slide, and which slides are videos off of it for the counter and the prev/next\n * arrows, which move it by setting its data-go. It requires product carousel to have high\n * resolution images so images do not look too blurry after zooming.\n */\nclass FullscreenGallery extends HTMLElement implements CustomElement {\n  declare readonly dataset: {\n    /**\n     * Present while someone drags a zoomed image. Reflected to CSS.\n     */\n    dragging?: 'true';\n\n    /**\n     * Present while the gallery is in fullscreen mode. Reflected to CSS.\n     */\n    expanded?: 'true';\n\n    /**\n     * Present while the active image is zoomed in. Reflected to CSS.\n     */\n    zoom?: 'true';\n  };\n\n  private containerMoved = false;\n  private dragStarted = false;\n  private declare posX: number;\n  private declare posY: number;\n  private scale: number = innerWidth > 430 ? 1.75 : 2.25;\n  private scaleStarted = false;\n  private videoIndexes: number[] = [];\n\n  /**\n   * Current index of the carousel slide (0-based to match the carousel)\n   */\n  private slidesIndex = 0;\n  private slidesTotal = 0;\n\n  private onGalleryOpenBound = this.galleryOpen.bind(this);\n  private onGalleryCloseBound = this.galleryClose.bind(this);\n  private onGalleryKeyboardEventBound = this.onGalleryKeyboardEvent.bind(this);\n  private onToggleZoomBound = this.toggleZoom.bind(this);\n  private onSlideVisibleBound = this.onSlideVisible.bind(this);\n  private onCarouselPrevBound = this.onCarouselPrev.bind(this);\n  private onCarouselNextBound = this.onCarouselNext.bind(this);\n  private onCarouselMountedBound = this.onCarouselMounted.bind(this);\n  private onViewportChangedBound = this.onViewportChanged.bind(this);\n\n  private onStartDragBound = this.onStartDrag.bind(this);\n  private onStopDragBound = this.onStopDrag.bind(this);\n  private onMoveDragBound = this.onMoveDrag.bind(this);\n\n  private onStartTouchBound = this.onStartTouch.bind(this);\n  private onStopTouchBound = this.onStopTouch.bind(this);\n  private onMoveTouchBound = this.onMoveTouch.bind(this);\n\n  public connectedCallback() {\n    this.initializeShadowRoot();\n\n    const closeButton = this.shadowRoot.querySelector('#close');\n    closeButton?.addEventListener('click', this.onGalleryCloseBound);\n    closeButton?.addEventListener('keydown', this.onGalleryCloseBound);\n    closeButton?.addEventListener('keyup', this.onGalleryCloseBound);\n\n    const zoomButton = this.shadowRoot.querySelector('#toggle-zoom');\n    zoomButton?.addEventListener('click', this.onToggleZoomBound);\n    zoomButton?.addEventListener('keydown', this.onToggleZoomBound);\n    zoomButton?.addEventListener('keyup', this.onToggleZoomBound);\n\n    const prevButton = this.shadowRoot.querySelector('.prev');\n    prevButton?.addEventListener('click', this.onCarouselPrevBound);\n    prevButton?.addEventListener('keydown', this.onCarouselPrevBound);\n    prevButton?.addEventListener('keyup', this.onCarouselPrevBound);\n\n    const nextButton = this.shadowRoot.querySelector('.next');\n    nextButton?.addEventListener('click', this.onCarouselNextBound);\n    nextButton?.addEventListener('keydown', this.onCarouselNextBound);\n    nextButton?.addEventListener('keyup', this.onCarouselNextBound);\n\n    const container = this.shadowRoot.querySelector('.inner');\n    container.addEventListener('click', this.onGalleryOpenBound);\n\n    if (typeof visualViewport !== 'undefined') {\n      visualViewport.addEventListener('resize', this.onViewportChangedBound);\n      visualViewport.addEventListener('scroll', this.onViewportChangedBound);\n    }\n\n    addEventListener('scrollend', this.onViewportChangedBound);\n\n    this.addEventListener('keyup', this.onGalleryKeyboardEventBound);\n\n    this.addEventListener('carousel-element-mounted', this.onCarouselMountedBound);\n    this.addEventListener('carousel-slide-visible', this.onSlideVisibleBound);\n\n    // The carousel may have mounted before we connected, in which case we missed the event\n    this.readCarousel();\n\n    this.render();\n  }\n\n  public disconnectedCallback() {\n    const closeButton = this.shadowRoot.querySelector('#close');\n    closeButton?.removeEventListener('click', this.onGalleryCloseBound);\n    closeButton?.removeEventListener('keydown', this.onGalleryCloseBound);\n    closeButton?.removeEventListener('keyup', this.onGalleryCloseBound);\n\n    const zoomButton = this.shadowRoot.querySelector('#toggle-zoom');\n    zoomButton?.removeEventListener('click', this.onToggleZoomBound);\n    zoomButton?.removeEventListener('keydown', this.onToggleZoomBound);\n    zoomButton?.removeEventListener('keyup', this.onToggleZoomBound);\n\n    const prevButton = this.shadowRoot.querySelector('.prev');\n    prevButton?.removeEventListener('click', this.onCarouselPrevBound);\n    prevButton?.removeEventListener('keydown', this.onCarouselPrevBound);\n    prevButton?.removeEventListener('keyup', this.onCarouselPrevBound);\n\n    const nextButton = this.shadowRoot.querySelector('.next');\n    nextButton?.removeEventListener('click', this.onCarouselNextBound);\n    nextButton?.removeEventListener('keydown', this.onCarouselNextBound);\n    nextButton?.removeEventListener('keyup', this.onCarouselNextBound);\n\n    if (typeof visualViewport !== 'undefined') {\n      visualViewport.removeEventListener('resize', this.onViewportChangedBound);\n      visualViewport.removeEventListener('scroll', this.onViewportChangedBound);\n    }\n\n    removeEventListener('scrollend', this.onViewportChangedBound);\n\n    const container = this.shadowRoot.querySelector('.inner');\n    container?.removeEventListener('click', this.onGalleryOpenBound);\n\n    this.removeEventListener('keyup', this.onGalleryKeyboardEventBound);\n\n    this.removeEventListener('carousel-element-mounted', this.onCarouselMountedBound);\n    this.removeEventListener('carousel-slide-visible', this.onSlideVisibleBound);\n  }\n\n  private initializeShadowRoot() {\n    if (!this.shadowRoot) {\n      this.attachShadow({ mode: 'open' });\n      this.shadowRoot.innerHTML = `<style>${styles}</style>${html}`;\n    }\n  }\n\n  private render() {\n    // The index is 0 based but visitors expect it to start at 1\n    const visualCurrentSlideNumber = this.slidesIndex + 1;\n\n    const currentSlide = this.shadowRoot.querySelector('.current-slide');\n    currentSlide.textContent = visualCurrentSlideNumber.toString();\n\n    const totalSlidesElement = this.shadowRoot.querySelector('.total-slides');\n    totalSlidesElement.textContent = this.slidesTotal.toString();\n\n    const counter = this.shadowRoot.querySelector('.counter');\n    counter?.setAttribute('aria-label', [\n      'Slide',\n      visualCurrentSlideNumber.toString(),\n      'of',\n      this.slidesTotal.toString()\n    ].join(' '));\n\n    // Decide whether to show or hide the arrows\n    const arrows = this.shadowRoot.querySelectorAll<HTMLElement>('.prev, .next');\n    if (this.slidesTotal <= 1) {\n      for (const arrow of arrows) {\n        arrow.hidden = true;\n      }\n    } else {\n      // The number of slides isn't expected to change, but the render method is always called the\n      // first time with this.slidesTotal === 0. After that, it gets updated with the actual value.\n      for (const arrow of arrows) {\n        arrow.hidden = false;\n      }\n    }\n  }\n\n  private onViewportChanged(_event: Event) {\n    this.updateViewportVars();\n  }\n\n  private updateViewportVars() {\n    const style = this.style;\n    let viewport = {\n      width: innerWidth,\n      height: innerHeight,\n      offsetTop: 0,\n      offsetLeft: 0,\n      scale: 1\n    };\n\n    if (typeof visualViewport !== 'undefined') {\n      viewport = visualViewport;\n    }\n\n    style.setProperty('--viewport-width', Math.ceil(viewport.width) + 'px');\n    style.setProperty('--viewport-height', Math.ceil(viewport.height) + 'px');\n    style.setProperty('--viewport-top', viewport.offsetTop + 'px');\n    style.setProperty('--viewport-left', viewport.offsetLeft + 'px');\n    style.setProperty('--viewport-scale', viewport.scale.toString());\n  }\n\n  private onCarouselMounted(_event: Event) {\n    this.readCarousel();\n    this.render();\n  }\n\n  /**\n   * Reads the slide count, the current slide, and the video slides off the carousel once it has\n   * mounted. Does nothing before then.\n   */\n  private readCarousel() {\n    const carousel = this.querySelector('carousel-element');\n    if (!carousel?.dataset.slideCount) {\n      return;\n    }\n\n    this.slidesTotal = parseInt(carousel.dataset.slideCount, 10);\n    this.slidesIndex = parseInt(carousel.dataset.index, 10);\n\n    this.videoIndexes = [];\n    const slides = carousel.querySelectorAll('[data-carousel-slide]');\n    for (const [index, slide] of slides.entries()) {\n      if (slide.classList.contains('product-single-media')) {\n        this.videoIndexes.push(index);\n      }\n    }\n  }\n\n  private onSlideVisible(event: GlobalEventHandlersEventMap['carousel-slide-visible']) {\n    this.slidesIndex = event.detail.index;\n    this.render();\n  }\n\n  /**\n   * Moves the carousel to the slide at the given index.\n   */\n  private goTo(index: number) {\n    const carousel = this.querySelector('carousel-element');\n    if (carousel) {\n      carousel.dataset.go = index.toString();\n    }\n  }\n\n  private onCarouselPrev(event?: Event) {\n    if (event instanceof KeyboardEvent) {\n      if (['Space', 'Enter'].includes(event.code)) {\n        event.preventDefault();\n      } else {\n        return;\n      }\n\n      if (event.type === 'keydown') {\n        return;\n      }\n    }\n\n    this.clearZoom();\n\n    // skip video slides as we do not want to display them in fullscreen mode\n    const total = this.slidesTotal;\n    let prevIndex = this.slidesIndex === 0 ? total - 1 : this.slidesIndex - 1;\n    while (this.videoIndexes.includes(prevIndex)) {\n      prevIndex = prevIndex === 0 ? total - 1 : prevIndex - 1;\n      // make sure we do not loop endlessly\n      if (prevIndex === this.slidesIndex) {\n        break;\n      }\n    }\n\n    this.goTo(prevIndex);\n  }\n\n  private onCarouselNext(event?: Event) {\n    if (event instanceof KeyboardEvent) {\n      if (['Space', 'Enter'].includes(event.code)) {\n        event.preventDefault();\n      } else {\n        return;\n      }\n\n      if (event.type === 'keydown') {\n        return;\n      }\n    }\n\n    this.clearZoom();\n\n    // skip video slides as we do not want to display them in fullscreen mode\n    const total = this.slidesTotal;\n    let nextIndex = this.slidesIndex + 1 === total ? 0 : this.slidesIndex + 1;\n    while (this.videoIndexes.includes(nextIndex)) {\n      nextIndex = nextIndex + 1 === total ? 0 : nextIndex + 1;\n      // make sure we do not loop endlessly\n      if (nextIndex === this.slidesIndex) {\n        break;\n      }\n    }\n\n    this.goTo(nextIndex);\n  }\n\n  private galleryOpen(event: Event) {\n    // prevent opening the video in full screen\n    if (this.videoIndexes.includes(this.slidesIndex)) {\n      return;\n    }\n\n    // The event handler is installed on an area that contains some clickable elements.\n    // Unfortunately the carousel does not use `stopPropagation()` so we receive the event even if\n    // it was a pagination click etc. Filter out such events for a better UX and accessibility.\n    const target = event.target;\n    if (target instanceof HTMLElement &&\n      (target.tagName === 'BUTTON' || target.parentElement?.tagName === 'BUTTON')) {\n      return;\n    }\n\n    // The carousel's own controls, like its drag bar, sit inside this element too, so only a tap on\n    // one of the slides opens the gallery\n    if (target instanceof Element && target.closest('carousel-element') &&\n      !target.closest('[data-carousel-slide]')) {\n      return;\n    }\n\n    const container = this.shadowRoot.querySelector<HTMLElement>('.inner');\n    container.addEventListener('mousedown', this.onStartDragBound);\n    container.addEventListener('mouseup', this.onStopDragBound);\n    container.addEventListener('touchstart', this.onStartTouchBound, { passive: false });\n    container.addEventListener('touchend', this.onStopTouchBound, { passive: false });\n\n    const scrollbarWidth = innerWidth - document.documentElement.clientWidth;\n    if (Number.isInteger(scrollbarWidth)) {\n      document.documentElement.style.setProperty('--scrollbar-width', scrollbarWidth + 'px');\n    }\n\n    document.documentElement.dataset.scrollLocked = 'true';\n\n    this.dataset.expanded = 'true';\n    this.updateViewportVars();\n\n    const carousel = this.querySelector('carousel-element');\n    if (carousel) {\n      carousel.dataset.controlsHidden = 'true';\n    }\n\n    const selector = '#toggle-zoom, #close, .prev>[role=\"button\"], .next>[role=\"button\"]';\n    const focusable = this.shadowRoot.querySelectorAll<HTMLElement>(selector);\n    for (const focusableEl of focusable) {\n      focusableEl.tabIndex = 0;\n    }\n\n    // focus on the first active element\n    const zoomButton = this.shadowRoot.querySelector<HTMLElement>('#toggle-zoom');\n    zoomButton.focus({ focusVisible: true });\n\n    const galleryEvent = new Event('fullscreen-gallery-opened', { bubbles: true, composed: true });\n    this.dispatchEvent(galleryEvent);\n  }\n\n  private onGalleryKeyboardEvent(event: KeyboardEvent) {\n    if (event.code === 'Escape') {\n      this.galleryClose();\n    }\n  }\n\n  private galleryClose(event?: Event) {\n    if (event instanceof KeyboardEvent) {\n      if (['Space', 'Enter'].includes(event.code)) {\n        event.preventDefault();\n      } else {\n        return;\n      }\n\n      if (event.type === 'keydown') {\n        return;\n      }\n    }\n\n    this.clearZoom();\n\n    const selector = '#toggle-zoom, #close, .prev>[role=\"button\"], .next>[role=\"button\"]';\n    const focusable = this.shadowRoot.querySelectorAll<HTMLElement>(selector);\n    for (const focusableEl of focusable) {\n      focusableEl.tabIndex = -1;\n    }\n\n    delete document.documentElement.dataset.scrollLocked;\n    delete this.dataset.expanded;\n\n    const carousel = this.querySelector('carousel-element');\n    if (carousel) {\n      delete carousel.dataset.controlsHidden;\n    }\n\n    const container = this.shadowRoot.querySelector<HTMLElement>('.inner');\n    container.removeEventListener('mousedown', this.onStartDragBound);\n    container.removeEventListener('mouseup', this.onStopDragBound);\n    container.removeEventListener('touchstart', this.onStartTouchBound);\n    container.removeEventListener('touchend', this.onStopTouchBound);\n\n    const galleryEvent = new Event('fullscreen-gallery-closed', { bubbles: true, composed: true });\n    this.dispatchEvent(galleryEvent);\n  }\n\n  private clearZoom() {\n    const carouselEl = this.querySelector('carousel-element');\n    if (!carouselEl) {\n      return;\n    }\n\n    const zoomEl = this.shadowRoot.querySelector('#toggle-zoom');\n    zoomEl?.setAttribute('aria-checked', 'false');\n\n    delete this.dataset.zoom;\n    carouselEl.style.transform = 'translate(0, 0) scale(1)';\n  }\n\n  private toggleZoom(event?: Event) {\n    if (event instanceof KeyboardEvent) {\n      if (['Space', 'Enter'].includes(event.code)) {\n        event.preventDefault();\n      } else {\n        return;\n      }\n\n      if (event.type === 'keydown') {\n        return;\n      }\n    }\n\n    const carouselEl = this.querySelector('carousel-element');\n    if (!carouselEl) {\n      return;\n    }\n\n    if (this.dataset.zoom === 'true') {\n      this.clearZoom();\n      return;\n    }\n\n    const zoomEl = this.shadowRoot.querySelector('#toggle-zoom');\n    zoomEl?.setAttribute('aria-checked', 'true');\n\n    carouselEl.style.transform = `translate(0px, 0px) scale(${this.scale})`;\n    this.dataset.zoom = 'true';\n  }\n\n  private onStartDrag(event: MouseEvent) {\n    const carouselEl = this.querySelector('carousel-element');\n    if (!carouselEl) {\n      return;\n    }\n\n    const matches = carouselEl.style.transform.match(/(-?\\d+)/g) || [];\n\n    this.posX = event.clientX - (matches[0] ? parseInt(matches[0], 10) : carouselEl.offsetLeft);\n    this.posY = event.clientY - (matches[1] ? parseInt(matches[1], 10) : carouselEl.offsetTop);\n\n    if (this.dataset.zoom === 'true') {\n      this.dataset.dragging = 'true';\n      this.dragStarted = true;\n      document.body.addEventListener('mousemove', this.onMoveDragBound);\n    }\n  }\n\n  private onStopDrag(_event: MouseEvent) {\n    const carouselEl = this.querySelector('carousel-element');\n    if (!carouselEl) {\n      return;\n    }\n\n    if (!this.containerMoved) {\n      this.toggleZoom();\n    }\n\n    this.containerMoved = false;\n    this.dragStarted = false;\n    delete this.dataset.dragging;\n    document.body.removeEventListener('mousemove', this.onMoveDragBound);\n  }\n\n  private onMoveDrag(event: MouseEvent) {\n    const carouselEl = this.querySelector('carousel-element');\n    if (!carouselEl) {\n      return;\n    }\n\n    this.containerMoved = true;\n    if (!this.dragStarted) {\n      return;\n    }\n\n    let offsetX = event.clientX - this.posX;\n    let offsetY = event.clientY - this.posY;\n\n    const container = this.shadowRoot.querySelector('.inner');\n    const { width, height } = carouselEl.getBoundingClientRect();\n\n    const constraintX = Math.floor((width - container.clientWidth) / 2);\n    const constraintY = Math.floor((height - container.clientHeight) / 2);\n\n    if (!(offsetX <= constraintX && offsetX >= constraintX * -1)) {\n      offsetX = offsetX > 0 ? constraintX : -1 * constraintX;\n    }\n\n    if (!(offsetY <= constraintY && offsetY >= constraintY * -1)) {\n      offsetY = offsetY > 0 ? constraintY : -1 * constraintY;\n    }\n\n    carouselEl.style.transform = `translate(${offsetX}px,${offsetY}px) scale(${this.scale})`;\n  }\n\n  private onStartTouch(event: TouchEvent) {\n    event.preventDefault();\n\n    const carouselEl = this.querySelector('carousel-element');\n    if (!carouselEl) {\n      return;\n    }\n\n    if (event.touches.length === 1) {\n      const matches = carouselEl.style.transform.match(/(-?\\d+(\\.\\d+)?)/g) || [];\n\n      const x = matches[0] ? parseInt(matches[0], 10) : carouselEl.offsetLeft;\n      const y = matches[1] ? parseInt(matches[1], 10) : carouselEl.offsetTop;\n\n      this.posX = event.touches[0].pageX - x;\n      this.posY = event.touches[0].pageY - y;\n    }\n\n    if (this.dataset.zoom === 'true') {\n      this.dataset.dragging = 'true';\n      this.dragStarted = true;\n      document.body.addEventListener('touchmove', this.onMoveTouchBound);\n    }\n  }\n\n  private onStopTouch(event: TouchEvent) {\n    event.preventDefault();\n\n    const carouselEl = this.querySelector('carousel-element');\n    if (!carouselEl) {\n      return;\n    }\n\n    if (event.touches.length < 1) {\n      if (!this.containerMoved) {\n        this.toggleZoom();\n      }\n\n      this.dragStarted = false;\n      this.containerMoved = false;\n      this.scaleStarted = false;\n\n      delete this.dataset.dragging;\n      document.body.removeEventListener('touchmove', this.onMoveTouchBound);\n    }\n  }\n\n  private onMoveTouch(event: TouchEvent) {\n    event.preventDefault();\n\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","import { ready } from '../../lib/dom-ready.js';\n\n// The modal-element around the money back guarantee handles the close button, the dismiss gestures,\n// and telling the modal manager when it opens and closes. We open it, and inert the product detail\n// section while it's open.\n\ntype ModalElement = HTMLElementTagNameMap['modal-element'];\n\nfunction onReady() {\n  const modal = document.querySelector('#pdp-mbg-modal');\n  if (!modal) {\n    return;\n  }\n\n  modal.addEventListener('modal-open-requested', onModalOpenRequested);\n  modal.addEventListener('modal-close-requested', onModalCloseRequested);\n\n  const mbgButtons = document.querySelectorAll<HTMLButtonElement>(\n    '.product-details .money-back-guarantee button');\n  for (const button of mbgButtons) {\n    button.addEventListener('click', onMBGButtonClicked);\n  }\n}\n\nfunction onMBGButtonClicked(event: MouseEvent) {\n  if (!(event.currentTarget instanceof HTMLElement)) {\n    return;\n  }\n\n  const focusEvent = new Event('focus-stack-push-requested', {\n    bubbles: true,\n    composed: true\n  });\n  event.currentTarget.dispatchEvent(focusEvent);\n\n  const modal = document.querySelector<ModalElement>('#pdp-mbg-modal');\n  modal.dataset.open = 'true';\n}\n\n/**\n * The modal manager inerts every other section on the page, but it can't inert ours, since the\n * modal lives in it too. That leaves the buy box and the gallery tabbable behind the open modal, so\n * we inert them ourselves. The modal renders as a sibling of the product detail page rather than\n * inside it, so this doesn't reach the modal.\n */\nfunction onModalOpenRequested(_event: HTMLElementEventMap['modal-open-requested']) {\n  const section = document.querySelector('#product-detail-section');\n  if (section instanceof HTMLElement) {\n    section.inert = true;\n  }\n}\n\n/**\n * Takes the page back out of inert. This listener is on the modal, so it runs before the manager's\n * listener on the window, which puts focus back on the trigger, and a focus call into an inert\n * subtree does nothing.\n */\nfunction onModalCloseRequested(_event: HTMLElementEventMap['modal-close-requested']) {\n  const section = document.querySelector('#product-detail-section');\n  if (section instanceof HTMLElement) {\n    section.inert = false;\n  }\n}\n\nready(onReady);\n","import { ready } from '../../lib/dom-ready.js';\n\nfunction onReady() {\n  // We must query the DOM to determine if a flash sale countdown timer override exists. If none is\n  // found we have no need to continue further and short circuit.\n  const overrideEl = document.querySelector<HTMLElement>('#product-detail-countdown-timer');\n  if (!overrideEl) {\n    return;\n  }\n\n  if (!overrideEl.dataset.startTime || !overrideEl.dataset.endTime) {\n    return;\n  }\n\n  const flashStartTime = parseInt(overrideEl.dataset.startTime, 10);\n  const flashEndTime = parseInt(overrideEl.dataset.endTime, 10);\n  const timestamp = Math.floor(Date.now() / 1000);\n\n  if (timestamp > flashStartTime && timestamp < flashEndTime) {\n    let appendTimer = false;\n    let timerEl = document.querySelector('product-detail-countdown-timer');\n\n    // Since the global countdown timer is uniquely shown based on the global timer we must check if\n    // it exists and create the element to be inserted into the DOM after all dataset attributes\n    // have been updated to ensure proper rendering.\n    if (!timerEl) {\n      timerEl = document.createElement('product-detail-countdown-timer');\n      appendTimer = true;\n    }\n\n    timerEl.dataset.startTime = overrideEl.dataset.startTime;\n    timerEl.dataset.endTime = overrideEl.dataset.endTime;\n\n    if (overrideEl.dataset.heading) {\n      timerEl.dataset.heading = overrideEl.dataset.heading;\n    }\n\n    if (appendTimer) {\n      overrideEl.appendChild(timerEl);\n    }\n  }\n}\n\nready(onReady);\n","import { ready } from '../../lib/dom-ready.js';\nimport { play } from '../../lib/video.js';\n\n// The gallery and its thumbnail strip are two separate carousel-element elements, and neither one\n// knows about the other. This module is what keeps them pointed at the same picture. A move on\n// either carousel moves the other one to the same index through data-go. The carousel ignores a\n// move to the slide it is already on, so the echo coming back from the other carousel stops there.\n\n// Keep track of the slide indices that have been viewed in each gallery so we can calculate the\n// percentage of the gallery that has been viewed. A sale page can carry more than one gallery.\nconst viewedSlidesByGallery = new WeakMap<Element, Set<number>>();\n\nfunction showVariantImage(variantId: number) {\n  const allVariantImages = document.querySelectorAll<HTMLElement>(\n    '.product-image-gallery [data-variant-image]');\n\n  let matched = false;\n  for (const el of allVariantImages) {\n    const isMatch = el.dataset.variantImage === variantId.toString();\n    el.classList.toggle('selected-variant', isMatch);\n    if (isMatch) {\n      matched = true;\n    }\n  }\n\n  if (!matched) {\n    // Variant has no image — show the default (first variant image). The main gallery and the nav\n    // strip each have their own default, so both need it back or the nav thumbnail stays hidden.\n    const defaultEls = document.querySelectorAll(\n      '.product-image-gallery [data-variant-image-default]');\n    for (const defaultEl of defaultEls) {\n      defaultEl.classList.add('selected-variant');\n    }\n  }\n}\n\nfunction goToVariantSlide(gallery: HTMLElement) {\n  if (gallery.querySelector('[data-variant-slide]')) {\n    gallery.dataset.go = '0';\n  }\n}\n\n/**\n * Finds the thumbnail strip that goes with a gallery, or the gallery that goes with a strip. Both\n * sit inside the same product image gallery container.\n */\nfunction findPartner(carousel: Element) {\n  let selector: string;\n  if (carousel.classList.contains('product-image-gallery-main')) {\n    selector = '.product-image-gallery-nav';\n  } else {\n    selector = '.product-image-gallery-main';\n  }\n\n  const container = carousel.closest('.product-image-gallery');\n  return container?.querySelector<HTMLElement>(selector);\n}\n\nfunction isGalleryCarousel(target: EventTarget): target is HTMLElement {\n  return target instanceof HTMLElement && (\n    target.classList.contains('product-image-gallery-main') ||\n    target.classList.contains('product-image-gallery-nav'));\n}\n\n/**\n * Marks the thumbnail button for the slide at the given index as the current one, the way the\n * carousel's navigation option used to mark the thumbnail slide itself.\n */\nfunction markCurrentThumbnail(nav: Element, index: number) {\n  const buttons = nav.querySelectorAll('[data-carousel-slide] > button');\n  for (const [buttonIndex, button] of buttons.entries()) {\n    if (buttonIndex === index) {\n      button.setAttribute('aria-current', 'true');\n    } else {\n      button.removeAttribute('aria-current');\n    }\n  }\n}\n\nfunction onSlideMoveStarted(event: WindowEventMap['carousel-slide-move-started']) {\n  if (!isGalleryCarousel(event.target)) {\n    return;\n  }\n\n  const partner = findPartner(event.target);\n  if (partner) {\n    partner.dataset.go = event.detail.index.toString();\n  }\n\n  if (event.target.classList.contains('product-image-gallery-nav')) {\n    markCurrentThumbnail(event.target, event.detail.index);\n  }\n}\n\n/**\n * A click or Enter on a thumbnail moves the strip to it, and the strip's move carries the gallery\n * along with it.\n */\nfunction onSlideClicked(event: WindowEventMap['carousel-slide-clicked']) {\n  if (isGalleryCarousel(event.target) &&\n    event.target.classList.contains('product-image-gallery-nav')) {\n    event.target.dataset.go = event.detail.index.toString();\n  }\n}\n\n/**\n * The two carousels mount on their own, so whichever one mounts second catches up to the other.\n * They both start on the first slide, so this only matters when the first one moved before the\n * second one was ready.\n */\nfunction onCarouselMounted(event: WindowEventMap['carousel-element-mounted']) {\n  const carousel = event.target;\n  if (!isGalleryCarousel(carousel)) {\n    return;\n  }\n\n  const partner = findPartner(carousel);\n  if (partner?.dataset.index && partner.dataset.index !== carousel.dataset.index) {\n    carousel.dataset.go = partner.dataset.index;\n  }\n\n  if (carousel.classList.contains('product-image-gallery-nav')) {\n    markCurrentThumbnail(carousel, parseInt(carousel.dataset.index, 10));\n  }\n}\n\nfunction onSlideHidden(event: WindowEventMap['carousel-slide-hidden']) {\n  if (!(event.target instanceof HTMLElement)) {\n    return;\n  }\n\n  if (event.target.classList.contains('product-image-gallery-main')) {\n    const video = event.detail.slide.querySelector('video');\n    if (video && !video.paused) {\n      video.pause();\n    }\n  }\n}\n\nfunction onSlideVisible(event: WindowEventMap['carousel-slide-visible']) {\n  if (!(event.target instanceof HTMLElement)) {\n    return;\n  }\n\n  if (event.target.classList.contains('product-image-gallery-main')) {\n    handleSlideVisible(event.target, event.detail.slide, event.detail.index);\n  }\n}\n\nfunction handleSlideVisible(gallery: HTMLElement, slide: HTMLElement, index: number) {\n  // This module expects that the Shopify global is correctly declared and initialized. We\n  // intentionally are not being defensive about its presence here.\n\n  const video = slide.querySelector('video');\n  if (video && !Shopify.designMode) {\n    // a11y requirement - 1.4.2 Audio Control – Level A\n    video.muted = true;\n    // we delay setting the loop attribute until the video is visible as a workaround for a11y\n    // checkers that incorrectly flag looping muted videos as violations\n    video.loop = true;\n\n    // A refusal here is not actionable, so keep a breadcrumb and never warn.\n    play(video).catch(console.debug);\n  }\n\n  let viewedSlides = viewedSlidesByGallery.get(gallery);\n  if (!viewedSlides) {\n    viewedSlides = new Set();\n    viewedSlidesByGallery.set(gallery, viewedSlides);\n  }\n\n  viewedSlides.add(index);\n\n  // The carousel reports the first slides in view during its mount, before it writes its slide\n  // count, so we count the slides ourselves\n  const totalSlides = gallery.querySelectorAll('[data-carousel-slide]').length;\n\n  type Detail = ProductCarouselViewedEvent['detail'];\n\n  const detail: Detail = {\n    index,\n    is_video: !!video,\n    percent_viewed: (viewedSlides.size / totalSlides) * 100,\n    product_id: parseInt(gallery.dataset.productId, 10)\n  };\n\n  const variantImageEl = slide.querySelector<HTMLElement>('.selected-variant[data-variant-image]');\n  if (variantImageEl?.dataset.variantImage) {\n    detail.variant_id = parseInt(variantImageEl.dataset.variantImage, 10);\n  }\n\n  const event = new CustomEvent<Detail>('product-carousel-viewed', {\n    bubbles: true,\n    composed: true,\n    detail\n  });\n\n  gallery.dispatchEvent(event);\n}\n\n/**\n * To facilitate product image gallery updates for bundles, we must query the bundle products\n * available variant children in the <add-to-cart> custom element. This involves mapping an array\n * with the updated selected option values and If found, return the data-id value.\n */\nfunction getBundleVariantIdFromOptions(event: WindowEventMap['product-variant-option-changed']) {\n  let variantId: number;\n\n  // update actively changed bundle option values in the <bundle-option> elements\n  for (const optionGroup of event.detail.option_groups) {\n    // We must query for the active bundle option element based on the bundle-option element name\n    // property as the provided option group position value is only relevant to the product whose\n    // options have been changed and not the bundle product abstraction created by the Shopify\n    // Bundle App. We bypass this limitation by populating the name attribute with the product and\n    // option group name in parentheses when rendering the <bundle-option> elements server side.\n    // This allows a bundle product to have multiple option groups represented by the bundle-option\n    // elements. Scope to the product detail root by an id the author defines, since the wrapper id\n    // from the dynamically rendered section varies per page.\n    const activeOption = document.querySelector<HTMLElement>('#product-detail-section ' +\n      `add-to-cart bundle-option[data-name=\"${event.detail.title} (${optionGroup.name})\"]`);\n\n    // If the active option element is found update the data-selected attribute value. We must\n    // perform this check as there are two active products created using the Shopify Bundle\n    // application which are using an unconventional method to bypass the app limits on variant\n    // option permutations. We do this by separating the Blush and Black color options for the\n    // \"Switch It Up\" into two separate product bundles. In these cases there will be no matching\n    // <bundle-option> element for the Color option group so we must check for existence before\n    // attempting to update the data-selected attribute. In these cases it is ok to simply bypass\n    // incorporating this option group into the selectedBundleOptions array as the color option hard\n    // coded to a default value for the respective product bundles.\n    if (activeOption) {\n      activeOption.dataset.selected = optionGroup.selected_option;\n    }\n  }\n\n  const bundleOptions = document.querySelectorAll<HTMLElement>('#product-detail-section ' +\n    'add-to-cart bundle-option');\n\n  // populate an array of selected bundle variant option values to query for active bundle variant\n  // ID value\n  const selectedBundleOptions: string[] = [];\n  for (const option of bundleOptions) {\n    selectedBundleOptions.push(option.dataset.selected);\n  }\n\n  // Query for the matching variant element based on all the selected bundle option values separated\n  // by \" / \" to find the active variant\n  const variantEl = document.querySelector<HTMLElement>('#product-detail-section add-to-cart' +\n    ` product-element[data-id=\"${event.detail.bundle_product_id}\"]` +\n    ` variant-element[data-title=\"${selectedBundleOptions.join(' / ')}\"]`);\n\n  if (variantEl) {\n    variantId = parseInt(variantEl.dataset.id, 10);\n  }\n\n  return variantId;\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    const variantId = getBundleVariantIdFromOptions(event);\n\n    // If a valid value is returned for variantId show the variant image in the gallery\n    if (variantId) {\n      showVariantImage(variantId);\n      goToVariantSlide(gallery);\n    }\n\n    return;\n  }\n\n  // Do to the fact sale pages could have more than one product detail section each with their own\n  // respective image galleries, we must incorporate a check to ensure the scoped gallery is for the\n  // same product which originated the variant changed event.\n\n  if (productId === event.detail.id) {\n    showVariantImage(event.detail.selected_variant_id);\n    goToVariantSlide(gallery);\n  }\n}\n\nfunction isProductVariantOptionChangedEvent(value: any):\n  value is WindowEventMap['product-variant-option-changed'] {\n  return value instanceof Event && value.type === 'product-variant-option-changed';\n}\n\nfunction onReady() {\n  const gallery = document.querySelector<HTMLElement>('.product-image-gallery-main');\n  if (!gallery) {\n    return;\n  }\n\n  // The gallery may have mounted before these listeners went on, in which case it already reported\n  // its first slide as visible and we never heard it. So when we have not seen any slide yet but\n  // the gallery is mounted, we handle the slide it is showing now.\n  if (!viewedSlidesByGallery.has(gallery) && gallery.dataset.index) {\n    const slides = gallery.querySelectorAll<HTMLElement>('[data-carousel-slide]');\n    const index = parseInt(gallery.dataset.index, 10);\n    const slide = slides[index];\n    if (slide) {\n      handleSlideVisible(gallery, slide, index);\n    }\n  }\n\n  // Same for the thumbnail strip, which marks its current thumbnail once it mounts\n  const nav = findPartner(gallery);\n  if (nav?.dataset.index) {\n    markCurrentThumbnail(nav, parseInt(nav.dataset.index, 10));\n  }\n\n  type ProductVariantOptionChangedEvent = WindowEventMap['product-variant-option-changed'];\n  const onProductVariantOptionChanged = (event: ProductVariantOptionChangedEvent) => {\n    updateVariantImage(gallery, event);\n  };\n\n  for (const event of product_image_gallery.queue) {\n    if (isProductVariantOptionChangedEvent(event)) {\n      try {\n        onProductVariantOptionChanged(event);\n      } catch (error) {\n        console.warn(error);\n      }\n    }\n  }\n\n  addEventListener('product-variant-option-changed', onProductVariantOptionChanged);\n}\n\ntype ProductCarouselViewedEvent = CustomEvent<{\n  index: number;\n  is_video: boolean;\n  percent_viewed: number;\n  product_id: number;\n  variant_id?: number;\n}>;\n\ndeclare global {\n  /**\n   * Holds the global state for the product image gallery. Created in the product image gallery\n   * snippet before this module loads so that events can be captured and replayed once the module\n   * initializes.\n   */\n  var product_image_gallery: {\n    /**\n     * A listener used by the snippet to capture events before this module loads.\n     *\n     * This is only declared for awareness purposes. Nothing should use this other than the snippet.\n     */\n    listener: (event: Event) => void;\n\n    /**\n     * Captures events that occurred before this module loaded.\n     */\n    queue: Event[];\n  };\n\n  interface WindowEventMap {\n    'product-carousel-viewed': ProductCarouselViewedEvent;\n  }\n}\n\n// These go on now rather than once the document is ready, because a carousel can mount and report\n// its first slides before a ready callback gets to run.\naddEventListener('carousel-element-mounted', onCarouselMounted);\naddEventListener('carousel-slide-move-started', onSlideMoveStarted);\naddEventListener('carousel-slide-clicked', onSlideClicked);\naddEventListener('carousel-slide-visible', onSlideVisible);\naddEventListener('carousel-slide-hidden', onSlideHidden);\n\nready(onReady);\n","/**\n * Try to play a video, and always report the outcome as a promise.\n *\n * Do not inline this function. Playing a video is inconsistent across browsers in several ways, and\n * wrapping the call in an async function is what smooths all of them over at once.\n *\n * Some browsers have no `play` function on the element at all, so the call throws a TypeError. Some\n * have one that returns undefined instead of a promise, which is how `play` worked before the spec\n * gained a return value. Some return a promise that rejects later, which is the modern behavior and\n * is how a browser reports that it refused to autoplay. And some throw synchronously, right out of\n * the call, rather than rejecting.\n *\n * An async function turns all four into the same thing. Whatever happens inside it, the caller gets\n * a promise back, so a synchronous throw arrives as a rejection and a missing or non-promise `play`\n * still produces a promise that resolves. That means one `catch` on the call site covers every\n * failure mode, and the caller never has to check whether it got a promise before attaching a\n * handler.\n *\n * Nothing here is actionable, because a refusal is the browser enforcing its own autoplay policy.\n * Restrictive in-app browsers and low power mode refuse even after a user gesture, and interrupted\n * or unsupported sources reject too. The video simply stays paused, so callers should keep a debug\n * breadcrumb and never warn.\n */\nexport async function play(video: HTMLVideoElement) {\n  return video.play();\n}\n","import { ready } from '../../lib/dom-ready.js';\n\nfunction onPurchaseOptionChanged(event: Event) {\n  if (!(event.target instanceof HTMLInputElement)) {\n    return;\n  }\n\n  const options = document.querySelector<HTMLElement>('.subscription-options');\n  if (!options) {\n    console.error('subscription-options not found');\n    return;\n  }\n\n  const plans = document.querySelector('.selling-plans');\n  if (!plans) {\n    console.error('selling-plans not found');\n    return;\n  }\n\n  if (event.target.value === 'subscription') {\n    options.inert = false;\n    options.classList.remove('collapsed');\n\n    plans.addEventListener('change', onSellingPlanChanged);\n    const selected =\n      document.querySelector<HTMLOptionElement>('select.selling-plans option:checked');\n\n    if (selected) {\n      updateSellingPlanIdOnAddToBag(selected?.value);\n    }\n  } else if (event.target.value === 'one-time') {\n    plans.removeEventListener('change', onSellingPlanChanged);\n    updateSellingPlanIdOnAddToBag('');\n    options.classList.add('collapsed');\n    options.inert = true;\n  }\n}\n\nfunction onSellingPlanChanged(event: Event) {\n  if (event.target instanceof HTMLSelectElement) {\n    const selected = event.target.options[event.target.selectedIndex];\n    const sellingPlanId = selected.value;\n\n    updateSellingPlanIdOnAddToBag(sellingPlanId);\n    updateProductDiscountPercentage(sellingPlanId);\n    updateProductSellingPlanPrices(sellingPlanId);\n  }\n}\n\nfunction updateProductDiscountPercentage(sellingPlanId: string) {\n  const sellingPlanEl = document.querySelector<HTMLElement>(\n    `subscription-plan[data-id=\"${sellingPlanId}\"]`);\n\n  if (!sellingPlanEl) {\n    console.warn('subscription-plan element not found for selling plan id: ', sellingPlanId);\n    return;\n  }\n\n  const priceEl = document.querySelector<HTMLElementTagNameMap['price-element']>(\n    '.subscription-wrapper price-element.subscription-price');\n  if (!priceEl) {\n    return;\n  }\n\n  const compareEl = document.querySelector<HTMLElementTagNameMap['price-element']>(\n    '.subscription-wrapper price-element.subscription-price + price-element.price-discount');\n  if (!compareEl) {\n    return;\n  }\n\n  const newPrice = parseInt(sellingPlanEl.dataset.subunits, 10);\n  const price = parseInt(priceEl.dataset.subunits, 10);\n\n  // If the subscription price is lower than the original price and the compare at price is null we\n  // must update the compare at price to be the original price so that the discount badge will show\n  // with the correct discount percentage.\n  if (!compareEl.dataset.subunits && newPrice < price) {\n    priceEl.dataset.subunits = newPrice.toString();\n    compareEl.dataset.subunits = price.toString();\n  }\n\n  // If new price matches the existing compare at price we zero out the compare at price subunit\n  // attributes to prevent showing a discount badge when there is no discount.\n  if (parseInt(compareEl.dataset.subunits, 10) === newPrice) {\n    compareEl.dataset.subunits = '';\n  }\n\n  // Extract final compare at price after potential adjustments above to ensure discount badge is\n  // calculated correctly based on the actual values that will be rendered in the price elements.\n  const comparePrice = compareEl.dataset.subunits ? parseInt(compareEl.dataset.subunits, 10) : 0;\n\n  let badgeEl = document.querySelector<HTMLElement>('span.subscription.discount-copy');\n  if (newPrice < comparePrice) {\n    if (!badgeEl) {\n      badgeEl = document.createElement('span');\n      badgeEl.classList.add('subscription');\n      badgeEl.classList.add('discount-copy');\n      badgeEl.style.display = 'inline';\n      compareEl.insertAdjacentElement('afterend', badgeEl);\n    }\n\n    const discountValue = comparePrice - newPrice;\n    const discountPercent = Math.round((discountValue / comparePrice) * 100);\n    badgeEl.innerHTML = `${discountPercent}% OFF`;\n  } else if (badgeEl) {\n    // If there is no discount but the badge exists, remove it.\n    badgeEl.remove();\n  }\n}\n\nfunction updateProductSellingPlanPrices(sellingPlanId: string) {\n  const sellingPlanEl = document.querySelector<HTMLElement>(\n    `subscription-plan[data-id=\"${sellingPlanId}\"]`);\n\n  if (!sellingPlanEl) {\n    console.warn('subscription-plan element not found for selling plan id: ', sellingPlanId);\n    return;\n  }\n\n  const priceEls = document.querySelectorAll<HTMLElementTagNameMap['price-element']>(\n    'price-element.subscription-price');\n  if (!priceEls) {\n    console.warn('price-element with class subscription-price not found');\n    return;\n  }\n\n  for (const priceEl of priceEls) {\n    priceEl.dataset.subunits = sellingPlanEl.dataset.subunits;\n  }\n}\n\nfunction updateSellingPlanIdOnAddToBag(sellingPlanId: string) {\n  const addToBag = document.querySelector<HTMLElementTagNameMap['add-to-cart']>(\n    '#product-detail-section add-to-cart');\n  if (!addToBag) {\n    console.warn('add-to-cart not found');\n    return;\n  }\n\n  addToBag.dataset.sellingPlanId = sellingPlanId;\n  addToBag.dataset.update = Date.now().toString();\n}\n\nfunction onPurchaseOptionClick(event: MouseEvent) {\n  // Ignore events from the selling plans dropdown\n  if (event.target instanceof Element && event.target?.tagName === 'SELECT') {\n    return;\n  }\n\n  if (event.currentTarget instanceof HTMLDivElement) {\n    const input = event.currentTarget.querySelector<HTMLInputElement>('input[type=\"radio\"]');\n    if (input && !input.checked) {\n      input.focus();\n      input.click();\n    }\n  }\n}\n\nfunction onReady() {\n  const optionWrappers = document.querySelectorAll<HTMLElement>(\n    '#product-detail-section .purchase-option');\n  for (const wrapper of optionWrappers) {\n    const radio = wrapper.querySelector<HTMLInputElement>('input[type=\"radio\"]');\n    if (radio) {\n      radio.addEventListener('change', onPurchaseOptionChanged);\n    }\n\n    // for better ux allow clicking anywhere in the wrapper to activate the radio button\n    wrapper.addEventListener('click', onPurchaseOptionClick);\n  }\n\n  // get active subscription plan id\n  const activeSellingPlan = document.querySelector<HTMLOptionElement>(\n    '#product-detail-section select.selling-plans option:checked');\n\n  // update pricing and discount percents in case selling plan has additional discounts\n  if (activeSellingPlan) {\n    updateProductDiscountPercentage(activeSellingPlan.value);\n    updateProductSellingPlanPrices(activeSellingPlan.value);\n  }\n}\n\ntype ProductSubscriptionOptionChangedEvent = CustomEvent<Partial<{\n  selling_plan_id?: string;\n}>>;\n\ndeclare global {\n  interface WindowEventMap {\n    'product-subscription-option-changed': ProductSubscriptionOptionChangedEvent;\n  }\n}\n\nready(onReady);\n","import { ready } from '../../lib/dom-ready.js';\n\nfunction onSellingPlanChanged(event: WindowEventMap['selling-plan-changed']) {\n  const addToBag = document.querySelector<HTMLElementTagNameMap['add-to-cart']>(\n    '#product-detail-section add-to-cart');\n  if (!addToBag) {\n    console.warn('add-to-cart not found');\n    return;\n  }\n\n  const sellingPlanId = event.detail.selling_plan_id;\n  if (sellingPlanId) {\n    addToBag.dataset.sellingPlanId = sellingPlanId;\n  } else {\n    addToBag.removeAttribute('data-selling-plan-id');\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\n  productDetail.addEventListener('selling-plan-changed', onSellingPlanChanged);\n  productDetail.addEventListener('product-variant-option-changed', onProductVariantOptionChanged);\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     * Subscription promo message\n     */\n    subscriptionPromoText: 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 subscriptionPromo = this.shadowRoot.querySelector('.subscription-promo');\n    subscriptionPromo.textContent = this.dataset.subscriptionPromoText;\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.setAttribute('data-content-id', item.dataset.id);\n    button.addEventListener('click', onSellingPointButtonClicked);\n  }\n}\n\nready(onReady);\n","import { ready } from '../../lib/dom-ready.js';\nimport '@integrabeauty/klaviyo-types';\n\n// B2S is the back to school promotion\n\nfunction onB2sCtaClicked(event: MouseEvent) {\n  const target = event.currentTarget;\n\n  if (!(target instanceof HTMLButtonElement)) {\n    return;\n  }\n\n  if (!window.klaviyo) {\n    return;\n  }\n\n  const openEvent = new Event('klaviyo-form-opened', { bubbles: true });\n  klaviyo.openForm('T4k3yf', () => target.dispatchEvent(openEvent));\n}\n\nfunction onReady() {\n  const params = new URLSearchParams(location.search);\n  const utmCampaign = params.get('utm_campaign');\n  const isB2sPromo = utmCampaign?.toLowerCase() === 'b2s';\n  const isB2sProduct = location.pathname.includes('axia-dryer') ||\n    location.pathname.includes('dorm-kit');\n\n  if (isB2sPromo && isB2sProduct) {\n    const b2sCta = document.querySelector<HTMLButtonElement>('.b2s-promo-cta');\n    if (!b2sCta) {\n      return;\n    }\n\n    b2sCta.style.display = 'block';\n    b2sCta.addEventListener('click', onB2sCtaClicked);\n  }\n}\n\nready(onReady);\n"],"names":["FullscreenGallery","HTMLElement","containerMoved","dragStarted","scale","innerWidth","scaleStarted","videoIndexes","slidesIndex","slidesTotal","onGalleryOpenBound","this","galleryOpen","bind","onGalleryCloseBound","galleryClose","onGalleryKeyboardEventBound","onGalleryKeyboardEvent","onToggleZoomBound","toggleZoom","onSlideVisibleBound","onSlideVisible","onCarouselPrevBound","onCarouselPrev","onCarouselNextBound","onCarouselNext","onCarouselMountedBound","onCarouselMounted","onViewportChangedBound","onViewportChanged","onStartDragBound","onStartDrag","onStopDragBound","onStopDrag","onMoveDragBound","onMoveDrag","onStartTouchBound","onStartTouch","onStopTouchBound","onStopTouch","onMoveTouchBound","onMoveTouch","connectedCallback","initializeShadowRoot","closeButton","shadowRoot","querySelector","addEventListener","zoomButton","prevButton","nextButton","visualViewport","readCarousel","render","disconnectedCallback","removeEventListener","container","attachShadow","mode","innerHTML","visualCurrentSlideNumber","textContent","toString","counter","setAttribute","join","arrows","querySelectorAll","arrow","hidden","_event","updateViewportVars","style","viewport","width","height","innerHeight","offsetTop","offsetLeft","setProperty","Math","ceil","carousel","dataset","slideCount","parseInt","index","slides","slide","entries","classList","contains","push","event","detail","goTo","go","KeyboardEvent","includes","code","preventDefault","type","clearZoom","total","prevIndex","nextIndex","_target$parentElement","target","tagName","parentElement","Element","closest","passive","scrollbarWidth","document","documentElement","clientWidth","Number","isInteger","scrollLocked","expanded","controlsHidden","focusable","focusableEl","tabIndex","focus","focusVisible","galleryEvent","Event","bubbles","composed","dispatchEvent","carouselEl","zoomEl","zoom","transform","matches","match","posX","clientX","posY","clientY","dragging","body","offsetX","offsetY","getBoundingClientRect","constraintX","floor","constraintY","clientHeight","touches","length","x","y","pageX","pageY","ready","callback","readyState","timer","setTimeout","clearTimeout","onMBGButtonClicked","currentTarget","focusEvent","open","onModalOpenRequested","section","inert","onModalCloseRequested","customElements","get","define","overrideEl","startTime","endTime","flashStartTime","flashEndTime","timestamp","Date","now","appendTimer","timerEl","createElement","heading","appendChild","modal","mbgButtons","button","viewedSlidesByGallery","WeakMap","showVariantImage","variantId","allVariantImages","matched","el","isMatch","variantImage","toggle","defaultEls","defaultEl","add","goToVariantSlide","gallery","findPartner","selector","isGalleryCarousel","markCurrentThumbnail","nav","buttons","buttonIndex","removeAttribute","handleSlideVisible","video","Shopify","designMode","muted","loop","async","play","catch","console","debug","viewedSlides","Set","set","totalSlides","is_video","percent_viewed","size","product_id","productId","variantImageEl","variant_id","CustomEvent","updateVariantImage","selected_variant_id","bundle_product_id","optionGroup","option_groups","activeOption","title","name","selected","selected_option","bundleOptions","selectedBundleOptions","option","variantEl","id","getBundleVariantIdFromOptions","isProductVariantOptionChangedEvent","value","onPurchaseOptionChanged","HTMLInputElement","options","error","plans","remove","onSellingPlanChanged","updateSellingPlanIdOnAddToBag","HTMLSelectElement","sellingPlanId","selectedIndex","updateProductDiscountPercentage","updateProductSellingPlanPrices","sellingPlanEl","warn","priceEl","compareEl","newPrice","subunits","price","comparePrice","badgeEl","display","insertAdjacentElement","discountValue","discountPercent","round","priceEls","addToBag","update","onPurchaseOptionClick","_event$target","HTMLDivElement","input","checked","click","subscriptions_v2_onSellingPlanChanged","selling_plan_id","onProductVariantOptionChanged","selectedVariantId","sellingPlanSelector","partner","paused","pause","has","product_image_gallery","queue","optionWrappers","wrapper","radio","activeSellingPlan","productDetail","SellingPlanSelector","observedAttributes","subscriptionSellingPlans","onPurchaseOptionChangedBound","onSellingPlanChangedBound","purchaseOptions","onPurchaseOptionClicked","gatherData","attributeChangedCallback","oldValue","newValue","isConnected","variants","variantElements","variantElement","_variantElement$datas","variant","available","compareAtPrice","isNaN","selectedVariant","undefined","sellingPlans","sellingPlanElements","sellingPlanElement","sellingPlan","trim","priceAdjustmentPercentage","parseFloat","recurringDeliveries","_this$selectedSelling","subscriptionCtaText","subscriptionBadgeText","subscriptionPromoText","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","productElement","imageSrc","mainImage","productImage","src","backorderMessage","backorder","cloneNode","isAddon","setCustomAttribute","backorderSellingPlanId","key","customAttribute","onSellingPointButtonClicked","contentItems","item","contentId","selling_points_onModalOpenRequested","selling_points_onModalCloseRequested","onB2sCtaClicked","HTMLButtonElement","window","klaviyo","openEvent","openForm","addons","checkbox","sellingPointButtons","utmCampaign","URLSearchParams","location","search","isB2sPromo","toLowerCase","isB2sProduct","pathname","b2sCta"],"sourceRoot":""}