{"version":3,"sources":["jsdelivr-header.js","/gh/wonderup-agency/Mendaera@cee2cf8/tech.js"],"names":[],"mappings":"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","file":"/gh/wonderup-agency/Mendaera@cee2cf8/tech.js","sourceRoot":"","sourcesContent":["/**\n * Minified by jsDelivr using Terser v5.48.0.\n * Original file: /gh/wonderup-agency/Mendaera@cee2cf8/tech.js\n *\n * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files\n */\n","gsap.registerPlugin(ScrollTrigger)\nconst mm = gsap.matchMedia()\n\n// ------------------------\n// Diagnostics for the product-overview component\n// ------------------------\n// Off unless asked for, so nothing reaches a normal visitor's console. Turn it\n// on with ?mddebug=1 on the URL, or once with localStorage.mdDebug = '1'.\n// Built for a phone inspected remotely, where breakpoints are impractical.\nconst MD_DEBUG = (function () {\n  try {\n    return /[?&]mddebug=1/.test(location.search) ||\n      location.hash === '#mddebug' ||\n      localStorage.getItem('mdDebug') === '1'\n  } catch (e) { return false }\n})()\nconst MD_T0 = typeof performance !== 'undefined' ? performance.now() : Date.now()\nfunction mdNow() {\n  return ((typeof performance !== 'undefined' ? performance.now() : Date.now()) - MD_T0) / 1000\n}\nfunction mdlog() {\n  if (!MD_DEBUG) return\n  const a = Array.prototype.slice.call(arguments)\n  a.unshift('[MD ' + mdNow().toFixed(2) + 's]')\n  console.log.apply(console, a)\n}\n// Short label for a video: its Vimeo id and rendition\nfunction mdVid(v) {\n  if (!v) return 'null'\n  const src = (v.querySelector && v.querySelector('source') && v.querySelector('source').src) || v.currentSrc || ''\n  const m = src.match(/playback\\/(\\d+)\\/rendition\\/(\\w+)/)\n  return m ? m[1] + '@' + m[2] : '?'\n}\nfunction mdVidState(v) {\n  if (!v) return {}\n  let buffered = 0\n  try { buffered = v.buffered.length ? +v.buffered.end(v.buffered.length - 1).toFixed(2) : 0 } catch (e) {}\n  return {\n    rs: v.readyState, net: v.networkState, preload: v.preload, paused: v.paused,\n    t: +v.currentTime.toFixed(2), dur: Number.isFinite(v.duration) ? +v.duration.toFixed(2) : null,\n    buffered, z: v.style.zIndex, display: v.style.display, err: v.error ? v.error.code : null,\n  }\n}\nif (MD_DEBUG) {\n  mdlog('debug ON', {\n    viewport: innerWidth + 'x' + innerHeight,\n    dpr: devicePixelRatio,\n    connection: (navigator.connection && navigator.connection.effectiveType) || 'n/a',\n    downlinkMbps: (navigator.connection && navigator.connection.downlink) || 'n/a',\n    readyState: document.readyState,\n  })\n  addEventListener('load', function () { mdlog('window load') })\n}\nwindow.addEventListener('load', () => {\n  ScrollTrigger.refresh()\n  setTimeout(() => ScrollTrigger.refresh(), 1500)\n})\n\n// Both refreshes above run long before the lazy Lottie in section_target\n// renders. Its container measures 0 until the SVG lands and 758 px after, so\n// the section grows 540 px and every start below it -- the pinned video's\n// included -- is short by that much: the pin fires half a viewport early, the\n// title scrolls across the fixed video, and the video lands a second time when\n// the pin releases. Nothing in ScrollTrigger watches for content growing on its\n// own, so re-measure when the page height changes with the viewport unchanged.\n// A viewport change is ScrollTrigger's own resize path (and the address bar on\n// mobile), so those are left alone.\n;(function refreshOnLateLayoutShift() {\n  if (!window.ResizeObserver) return\n  const target = document.querySelector('.main-wrapper') || document.body\n  if (!target) return\n\n  let height = document.documentElement.scrollHeight\n  let vw = window.innerWidth\n  let vh = window.innerHeight\n  let timer = null\n\n  function check() {\n    timer = null\n    if (window.innerWidth !== vw || window.innerHeight !== vh) {\n      vw = window.innerWidth\n      vh = window.innerHeight\n      height = document.documentElement.scrollHeight\n      return\n    }\n    if (document.documentElement.scrollHeight === height) return\n    mdlog('late layout shift', { from: height, to: document.documentElement.scrollHeight })\n    ScrollTrigger.refresh()\n    // Read after the refresh so the pin spacers it re-measures are not seen as\n    // the next shift, which would loop.\n    height = document.documentElement.scrollHeight\n  }\n\n  new ResizeObserver(() => {\n    clearTimeout(timer)\n    timer = setTimeout(check, 200)\n  }).observe(target)\n})()\n\ndocument.querySelectorAll('[data-component=\"home-video\"]').forEach((videoComponent) => {\n  const wrapper = videoComponent.querySelector('[data-home-video=\"wrapper\"]')\n  const video = videoComponent.querySelector('[data-home-video=\"video\"]')\n  const playButton = videoComponent.querySelector('[data-home-video=\"play-button\"]')\n\n  const autoplayOnly = videoComponent.dataset.autoplayOnly === 'true'\n  const showControls = videoComponent.dataset.controls === 'true'\n  const playOnView = videoComponent.dataset.playOnView === 'true'\n  const noAnimation = videoComponent.dataset.noAnimation === 'true'\n  video.loop = true\n  video.muted = true\n\n  if (playOnView) {\n    playButton.style.display = 'none'\n    const observer = new IntersectionObserver(\n      (entries) => {\n        entries.forEach((entry) => {\n          if (entry.isIntersecting) {\n            video.play()\n          } else {\n            video.pause()\n          }\n        })\n      },\n      { threshold: 0.5 }\n    )\n    observer.observe(video)\n  } else if (showControls) {\n    playButton.style.display = 'flex'\n  } else {\n    playButton.style.display = 'none'\n    video.play()\n  }\n\n  function handlePlay() {\n    if (showControls) {\n      video.muted = false\n      video.controls = true\n    }\n    video.play()\n    playButton.style.display = 'none'\n  }\n\n  if (!autoplayOnly) {\n    const overlayWrapper = videoComponent.querySelector('.vid_overlay-wrapper')\n    if (overlayWrapper) overlayWrapper.style.pointerEvents = 'auto'\n\n    wrapper.addEventListener('click', (e) => {\n      if (e.target.closest('[data-home-video=\"play-button\"]')) return\n      if (video.paused) {\n        handlePlay()\n      } else {\n        video.pause()\n        playButton.style.display = 'flex'\n      }\n    })\n\n    playButton.addEventListener('click', () => {\n      handlePlay()\n    })\n  }\n\n  if (!noAnimation) {\n    mm.add('(min-width: 992px)', () => {\n      const tl = gsap.timeline({\n        scrollTrigger: {\n          trigger: videoComponent,\n          start: 'top top',\n          end: '+=100%',\n          scrub: true,\n          pin: true,\n          // recalcula los valores del tween (rem / %) en cada refresh -> resize\n          invalidateOnRefresh: true,\n        },\n      })\n      tl.to(wrapper, {\n        maxWidth: '87rem',\n        height: '95%',\n        borderRadius: '0.75rem',\n      })\n    })\n  }\n})\n\nconst MendaeraCharts = (() => {\n  const COLORS = {\n    primary: '#26ca99',       // --base-color--teal\n    secondary: '#D4D9D8',\n    text: '#1c1c1c',          // var(--base-color--soft-black)\n    textLight: '#828282',     // var(--base-color--mid-grey)\n    grid: '#f1f2f3',          // var(--base-color--super-soft-grey)\n    border: '#d4d4d4',        // var(--base-color--light-grey)\n    white: '#ffffff',\n  }\n\n  const altFamily = getComputedStyle(document.documentElement)\n    .getPropertyValue('--_typography---family--alternative').trim()\n  const FONT = {\n    family: altFamily || \"Suisseintl, Arial, sans-serif\",\n    weight: '500',\n  }\n\n  // Every size ramps with the chart width between the two designed ends\n  // (350px phone -> 1100px desktop). It used to switch at 400px, so any window\n  // in between got the full desktop scale -- 40px values and 18px labels in a\n  // 420px canvas, which run into each other and into the neighbouring bars.\n  function ramp(chartWidth, min, max) {\n    const t = Math.min(Math.max((chartWidth - 350) / 750, 0), 1)\n    return min + (max - min) * t\n  }\n\n  // Gaps: desktop 3.125rem (50px) between groups, 0.5rem (8px) internal\n  //        mobile  1rem (16px) between groups, 0.25rem (4px) internal\n  function getSizes(chartWidth, chartHeight, numCategories, numDatasets) {\n    // A short chart can't carry the full type scale either: the wrapper is\n    // 20rem from the tablet breakpoint down while the width stays wide.\n    const shortFactor = Math.min(Math.max((chartHeight || 460) / 460, 0.85), 1)\n    const size = (min, max) => Math.round(ramp(chartWidth, min, max) * shortFactor)\n\n    const groupGap = ramp(chartWidth, 16, 50)\n    const barGap = ramp(chartWidth, 4, 8)\n\n    // Calculate percentages from pixel gaps\n    const catWidth = chartWidth / (numCategories || 4)\n    const categoryPercentage = Math.min(Math.max(1 - groupGap / catWidth, 0.4), 0.95)\n    const barSlot = (catWidth * categoryPercentage) / (numDatasets || 2)\n    const barPercentage = Math.min(Math.max(1 - barGap / barSlot, 0.5), 0.98)\n\n    return {\n      labelTop: size(13, 18),\n      labelTopSub: size(8, 11),\n      value: size(24, 40),\n      // Space reserved above the plot area for the category labels.\n      // Must fit: label ascender + topGap + a little breathing room.\n      topPadding: size(34, 44),\n      topGap: size(8, 10),\n      valueGap: size(8, 14),\n      barRadius: chartWidth < 400 ? 3 : 4,\n      barPercentage,\n      categoryPercentage,\n    }\n  }\n\n  const instances = {}\n\n  function comparison(canvasId, config) {\n    if (instances[canvasId]) {\n      if (instances[canvasId]._sizeObserver) instances[canvasId]._sizeObserver.disconnect()\n      instances[canvasId].destroy()\n      delete instances[canvasId]\n    }\n\n    const canvas = document.getElementById(canvasId)\n    if (!canvas) return\n    if (typeof Chart === 'undefined') return\n\n    const {\n      labels = [],\n      datasets = [],\n      showPercent = false,\n      title = '',\n      colors = [COLORS.secondary, COLORS.primary],\n      maxValue = null,\n    } = config\n\n    Chart.defaults.font.family = FONT.family\n\n    // Get initial sizes\n    const container = canvas.parentElement\n    const numCat = labels.length\n    const numDs = datasets.length\n    let s = getSizes(container?.offsetWidth || 600, container?.offsetHeight || 400, numCat, numDs)\n\n    // Metallic green gradient — cached, recreated only on resize\n    let _gradCache = null\n    let _gradH = 0\n    function getPrimaryGradient(ctx, chartArea) {\n      const h = chartArea.bottom - chartArea.top\n      if (_gradCache && _gradH === h) return _gradCache\n      _gradH = h\n      const grad = ctx.createLinearGradient(0, chartArea.bottom, 0, chartArea.top)\n      // Bright at bottom → dark at top (clean base)\n      grad.addColorStop(0, '#a0f0d8')\n      grad.addColorStop(0.2, '#4dddb2')\n      grad.addColorStop(0.4, '#26ca99')\n      grad.addColorStop(0.6, '#1aad80')\n      grad.addColorStop(0.8, '#128c65')\n      grad.addColorStop(1, '#0a6b4a')\n      return _gradCache = grad\n    }\n\n    const primaryIdx = datasets.findIndex((ds, i) => !ds.color && colors[i % colors.length] === COLORS.primary)\n\n    const chartDatasets = datasets.map((ds, i) => ({\n      label: ds.label || `Dataset ${i + 1}`,\n      data: ds.data,\n      backgroundColor: ds.color || colors[i % colors.length],\n      borderRadius: s.barRadius,\n      borderSkipped: false,\n      barPercentage: s.barPercentage,\n      categoryPercentage: s.categoryPercentage,\n    }))\n\n    let gradientApplied = false\n\n    // Store real data; only zero out the primary (green) dataset for animation\n    const realData = chartDatasets.map((ds) => [...ds.data])\n    if (primaryIdx !== -1) {\n      chartDatasets[primaryIdx].data = chartDatasets[primaryIdx].data.map(() => 0)\n    }\n\n    instances[canvasId] = new Chart(canvas, {\n      type: 'bar',\n      data: { labels, datasets: chartDatasets },\n      options: {\n        layout: {\n          padding: { top: s.topPadding, right: 0, left: 0 },\n        },\n        animation: false,\n        // The canvas is sized by applySize() below, not by Chart.js.\n        responsive: false,\n        maintainAspectRatio: false,\n        // Disable all hover/pointer interactions on the bars\n        events: [],\n        onResize(chart, size) {\n          s = getSizes(size.width, size.height, numCat, numDs)\n          chart.options.layout.padding.top = s.topPadding\n          chart.data.datasets.forEach((ds) => {\n            ds.borderRadius = s.barRadius\n            ds.barPercentage = s.barPercentage\n            ds.categoryPercentage = s.categoryPercentage\n          })\n        },\n        scales: {\n          x: {\n            grid: { display: false },\n            border: { display: false },\n            ticks: { display: false },\n          },\n          y: {\n            display: false,\n            beginAtZero: true,\n            max: maxValue || 115,\n          },\n        },\n        plugins: {\n          legend: { display: false },\n          title: title\n            ? {\n                display: true,\n                text: title,\n                align: 'start',\n                font: {\n                  family: FONT.family,\n                  size: 40,\n                  weight: FONT.weight,\n                },\n                color: COLORS.text,\n                padding: { bottom: 32 },\n              }\n            : { display: false },\n          tooltip: { enabled: false },\n        },\n      },\n      plugins: [\n        // Plugin: category labels at the top of each bar group\n        {\n          id: 'topLabels',\n          // Keep the reserved top padding in sync with the CANVAS size.\n          // (`s` is measured from the container, which can differ and leave\n          //  too little room → labels drawn above y=0 and clipped.)\n          beforeLayout(chart) {\n            if (!chart.width || !chart.height) return\n            const sz = getSizes(chart.width, chart.height, chart.data.labels.length, chart.data.datasets.length)\n            if (chart.options.layout.padding.top !== sz.topPadding) {\n              chart.options.layout.padding.top = sz.topPadding\n            }\n          },\n          afterDraw(chart) {\n            const { ctx: c, scales: { x }, width, height } = chart\n            const sz = getSizes(width, height, chart.data.labels.length, chart.data.datasets.length)\n            const topY = chart.chartArea.top\n\n            chart.data.labels.forEach((label, i) => {\n              if (!label) return\n              c.save()\n              c.textAlign = 'left'\n              c.textBaseline = 'bottom'\n              c.letterSpacing = '0px'\n\n              const match = label.match(/^(.+?)(\\s*\\(.+\\))$/)\n              const name = (match ? match[1].trim() : label).toUpperCase()\n              const num = match ? match[2].trim().toUpperCase() : ''\n\n              // Measure first so we can keep the label inside the canvas\n              c.font = `${FONT.weight} ${sz.labelTop}px ${FONT.family}`\n              const nameMetrics = c.measureText(name)\n              const nameWidth = nameMetrics.width\n              const ascent = nameMetrics.actualBoundingBoxAscent || sz.labelTop * 0.8\n\n              let numWidth = 0\n              const numOffset = sz.labelTopSub * 0.4\n              if (num) {\n                c.font = `${FONT.weight} ${sz.labelTopSub}px ${FONT.family}`\n                numWidth = numOffset + c.measureText(num).width\n              }\n\n              // Never draw above the top edge of the canvas\n              const baselineY = Math.max(topY - sz.topGap, Math.ceil(ascent) + 1)\n              // Never overflow the right edge (labels are left-aligned on the group center)\n              const xPos = Math.max(0, Math.min(x.getPixelForValue(i), width - nameWidth - numWidth))\n\n              // Name (large, dark)\n              c.fillStyle = COLORS.text\n              c.font = `${FONT.weight} ${sz.labelTop}px ${FONT.family}`\n              c.fillText(name, xPos, baselineY)\n\n              // Number (small, light) on the same line, right after the name\n              if (num) {\n                c.fillStyle = COLORS.textLight\n                c.font = `${FONT.weight} ${sz.labelTopSub}px ${FONT.family}`\n                c.fillText(num, xPos + nameWidth + numOffset, baselineY)\n              }\n              c.restore()\n            })\n          },\n        },\n        // Plugin: mantiene el gradiente metalico en sync con la altura del chart.\n        // Sin esto, en resize las barras verdes conservan el gradiente creado con\n        // las coordenadas viejas (getPrimaryGradient cachea por altura).\n        {\n          id: 'primaryGradientSync',\n          afterLayout(chart) {\n            if (!gradientApplied || primaryIdx === -1 || !chart.chartArea) return\n            chart.data.datasets[primaryIdx].backgroundColor = getPrimaryGradient(chart.ctx, chart.chartArea)\n          },\n        },\n        // Plugin: draw % labels based on current data values\n        ...(showPercent\n          ? [\n              {\n                id: 'barValueLabels',\n                afterDatasetsDraw(chart) {\n                  const { ctx: c, width, height } = chart\n                  const sz = getSizes(width, height, chart.data.labels.length, chart.data.datasets.length)\n\n                  chart.data.datasets.forEach((dataset, di) => {\n                    const meta = chart.getDatasetMeta(di)\n                    meta.data.forEach((bar, index) => {\n                      const val = Math.round(dataset.data[index])\n                      if (val <= 0) return\n\n                      c.save()\n                      c.fillStyle = COLORS.text\n                      c.font = `400 ${sz.value}px Suisseintl, Arial, sans-serif`\n                      c.letterSpacing = '0px'\n                      c.textAlign = 'center'\n                      c.textBaseline = 'bottom'\n                      // Clamp: the value never rises past the plot area top,\n                      // so it can't overlap the category labels or get clipped\n                      const minY = chart.chartArea.top + sz.value * 0.85\n                      c.fillText(`${val}%`, bar.x, Math.max(bar.y - sz.valueGap, minY))\n                      c.restore()\n                    })\n                  })\n                },\n              },\n            ]\n          : []),\n      ],\n    })\n\n    const chart = instances[canvasId]\n\n    // Sizing is ours, not Chart.js's. Chart.js takes the height from the parent,\n    // but the parent (.chart_item, height auto) takes it from the canvas: with\n    // nothing definite in the chain the canvas ends up measuring itself and\n    // gains a few px on every resize (633 -> 664 -> ...) until it spills over\n    // the section below. Its min-height compounds it -- the wrapper is 320px\n    // from the tablet breakpoint down while the floor stays at 480.\n    // So: drop the floor, and measure the box with the canvas out of the\n    // layout, which is a height the canvas cannot feed back into.\n    const box = canvas.closest('.chart_container') || container\n    if (container) container.style.minHeight = '0px'\n    canvas.style.maxWidth = '100%'\n\n    function availableHeight() {\n      const display = canvas.style.display\n      canvas.style.display = 'none'\n      const h = box.clientHeight\n      canvas.style.display = display\n      return h\n    }\n\n    function applySize() {\n      const width = (container || box).clientWidth\n      if (!width) return\n      // Fallback for the widths where nothing up the chain sets a height.\n      const height = availableHeight() || Math.round(Math.min(Math.max(width * 0.6, 280), 480))\n      if (Math.round(chart.width) === width && Math.round(chart.height) === height) return\n      chart.resize(width, height)\n    }\n\n    applySize()\n\n    if (window.ResizeObserver) {\n      let sizeTimer = null\n      const observer = new ResizeObserver(() => {\n        clearTimeout(sizeTimer)\n        sizeTimer = setTimeout(applySize, 80)\n      })\n      observer.observe(box)\n      chart._sizeObserver = observer\n    }\n\n    // Animate datasets sequentially: grey bars first, then green bars\n    const animDuration = 1200\n    const easeOutQuart = (t) => 1 - Math.pow(1 - t, 4)\n\n    function animateDataset(dsIndex) {\n      return new Promise((resolve) => {\n        const target = realData[dsIndex]\n        const startTime = performance.now()\n        function tick(now) {\n          const elapsed = now - startTime\n          const progress = Math.min(elapsed / animDuration, 1)\n          const eased = easeOutQuart(progress)\n          chart.data.datasets[dsIndex].data = target.map((v) => v * eased)\n          chart.update('none')\n          if (progress < 1) requestAnimationFrame(tick)\n          else {\n            // Apply gradient after green bars finish\n            if (dsIndex === primaryIdx && primaryIdx !== -1 && !gradientApplied && chart.chartArea) {\n              gradientApplied = true\n              chart.data.datasets[primaryIdx].backgroundColor = getPrimaryGradient(chart.ctx, chart.chartArea)\n              chart.update('none')\n            }\n            resolve()\n          }\n        }\n        requestAnimationFrame(tick)\n      })\n    }\n\n    // Animate only the primary (green) dataset\n    function playAnimation() {\n      if (primaryIdx !== -1) {\n        animateDataset(primaryIdx)\n      }\n    }\n\n    if (!config.delayAnimation) {\n      playAnimation()\n    }\n\n    chart.playAnimation = playAnimation\n    return chart\n  }\n\n  function onScroll(canvasId, config) {\n    const container = document.getElementById(canvasId)?.closest('.chart_container')\n    if (!container) return\n\n    const observer = new IntersectionObserver(\n      (entries) => {\n        entries.forEach((entry) => {\n          if (entry.isIntersecting) {\n            comparison(canvasId, config)\n            observer.unobserve(container)\n          }\n        })\n      },\n      { threshold: 0.3 }\n    )\n\n    observer.observe(container)\n  }\n\n  return { comparison, onScroll }\n})()\n\n  const wrapper = document.getElementById('chart-wrapper')\n\n  if (wrapper) {\n    // Create chart immediately with grey bars visible, green bars at zero\n    wrapper.classList.add('is-visible')\n    const chart = MendaeraCharts.comparison('comparison-1', {\n      labels: ['NOVICE (200)', ' Mid-Tier (80)', 'EXPERT (80)'],\n      datasets: [\n        { label: 'FREEHAND (180)', data: [14, 27, 40] },\n        { label: 'ROBOT (180)',    data: [74, 80, 90] },\n      ],\n      showPercent: true,\n      delayAnimation: true,\n    })\n\n    // Animate green bars on scroll\n    let chartTriggered = false\n    ScrollTrigger.create({\n      trigger: wrapper,\n      start: 'top center',\n      onEnter: () => {\n        if (chartTriggered) return\n        chartTriggered = true\n        if (chart && chart.playAnimation) chart.playAnimation()\n      },\n    })\n  }\n  // ------------------------\n// Product Modal\n// ------------------------\nmm.add('(min-width: 992px)', () => {\nconst _ac = new AbortController()\nconst _signal = _ac.signal\nconst modal = document.querySelector('[data-modal=\"product-features\"]')\nif (modal) {\n  const modalComponent = document.querySelector('[data-component=\"modal-product\"]')\n  const cards = modalComponent ? modalComponent.querySelectorAll('[data-modal-tab]') : []\n  const tabLinks = modal.querySelectorAll('[data-tab-link]')\n\n  const tabsLinksWrapper = modal.querySelector('.product_modal-tabs-links-wrapper')\n  if (tabsLinksWrapper) tabsLinksWrapper.style.position = 'relative'\n\n  const SCROLL_OFFSET = 0\n  const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches\n\n  const tabPanes = modal.querySelectorAll('.product_modal-tabs-pane')\n  const paneWrapper = modal.querySelector('.product_modal-tabs-pane-wrapper')\n\n  // ---- Item switching (text + image/video) within active pane ----\n  function setActiveItem(pane, index) {\n    const textItems = pane.querySelectorAll('.product_modal-tab-text-item')\n    const imageItems = pane.querySelectorAll('.product_modal-tab-image-wrapper')\n    textItems.forEach((item, i) => item.classList.toggle('is-active', i === index))\n\n    // Ensure visual wrapper is stacking context with overflow hidden\n    const visualWrapper = pane.querySelector('.product_modal-tab-visual-wrapper')\n    if (visualWrapper) {\n      visualWrapper.style.position = 'relative'\n      visualWrapper.style.overflow = 'hidden'\n    }\n\n    // First pass: immediately hide all non-target items to prevent flash\n    imageItems.forEach((item, i) => {\n      if (i !== index && !item.classList.contains('is-active')) {\n        item.style.display = 'none'\n      }\n    })\n\n    imageItems.forEach((item, i) => {\n      const isActive = i === index\n      const wasActive = item.classList.contains('is-active')\n      const video = item.querySelector('[data-modal-video=\"video\"]')\n\n      gsap.killTweensOf(item)\n\n      if (isActive && !wasActive) {\n        // --- New item reveals on top like a curtain descending ---\n        item.classList.add('is-active')\n        item.style.cssText = 'display:flex;position:absolute;top:0;left:0;width:100%;height:100%;z-index:2;'\n        gsap.fromTo(item,\n          { clipPath: 'inset(0 0 100% 0)' },\n          {\n            clipPath: 'inset(0 0 0% 0)',\n            duration: 0.7,\n            ease: 'power3.inOut',\n            onComplete: () => {\n              // Once revealed, become the base layer\n              item.style.cssText = 'display:flex;position:relative;z-index:1;'\n              gsap.set(item, { clipPath: '' })\n              // Now hide the old item underneath\n              imageItems.forEach((other, j) => {\n                if (j !== i) {\n                  other.classList.remove('is-active')\n                  other.style.display = 'none'\n                  other.style.position = ''\n                  other.style.zIndex = ''\n                  gsap.set(other, { clipPath: '' })\n                  const v = other.querySelector('[data-modal-video=\"video\"]')\n                  if (v) { v.pause(); v.style.display = 'none' }\n                }\n              })\n            },\n          }\n        )\n        if (video) {\n          video.style.cssText = 'width:100%;height:100%;object-fit:cover;display:block;'\n          video.currentTime = 0\n          video.play().catch(() => {})\n        }\n      } else if (isActive && wasActive) {\n        // --- Already active (first load / tab switch) ---\n        // Don't clear clipPath — may be pre-set by setActiveTab for reveal\n        item.classList.add('is-active')\n        item.style.display = 'flex'\n        item.style.position = 'relative'\n        item.style.zIndex = '1'\n        if (video) {\n          video.style.cssText = 'width:100%;height:100%;object-fit:cover;display:block;'\n          video.currentTime = 0\n          video.play().catch(() => {})\n        }\n      } else if (!isActive && !wasActive) {\n        // --- Already hidden, keep hidden ---\n        item.classList.remove('is-active')\n        item.style.display = 'none'\n        item.style.position = ''\n        item.style.zIndex = ''\n        if (video) { video.pause(); video.style.display = 'none' }\n      }\n      // wasActive + !isActive: old item stays visible underneath, hidden in onComplete above\n    })\n  }\n\n  // ---- Auto-advance system (progress bar + GSAP tween) ----\n  const IMAGE_SLIDE_DURATION = 5 // seconds for image-only slides\n  let _autoAdvanceState = null\n  // { pane, barTween, video, onEnded, onMouseEnter, onMouseLeave, hoverTarget }\n\n  function ensureProgressBars(pane) {\n    pane.querySelectorAll('.product_modal-tab-text-item').forEach((item) => {\n      if (item.querySelector('.tab-progress-bar')) return\n      const bar = document.createElement('div')\n      bar.className = 'tab-progress-bar'\n      const fill = document.createElement('div')\n      fill.className = 'tab-progress-bar__fill'\n      bar.appendChild(fill)\n      item.appendChild(bar)\n    })\n  }\n\n  function resetAllBars(pane) {\n    pane.querySelectorAll('.tab-progress-bar').forEach((bar) => {\n      bar.classList.remove('is-active')\n      const fill = bar.querySelector('.tab-progress-bar__fill')\n      if (fill) gsap.set(fill, { width: '0%' })\n    })\n  }\n\n  function cleanupAutoAdvanceListeners() {\n    if (!_autoAdvanceState) return\n    const { video, onEnded, onMouseEnter, onMouseLeave, hoverTarget, barTween } = _autoAdvanceState\n    if (barTween) barTween.kill()\n    if (video && onEnded) video.removeEventListener('ended', onEnded)\n    if (hoverTarget) {\n      if (onMouseEnter) hoverTarget.removeEventListener('mouseenter', onMouseEnter)\n      if (onMouseLeave) hoverTarget.removeEventListener('mouseleave', onMouseLeave)\n    }\n  }\n\n  function stopAutoAdvance() {\n    if (!_autoAdvanceState) return\n    cleanupAutoAdvanceListeners()\n    if (_autoAdvanceState.pane) resetAllBars(_autoAdvanceState.pane)\n    _autoAdvanceState = null\n  }\n\n  // Advance to the next inner item, or next main tab if inner tabs exhausted\n  function advanceToNext(pane, currentInnerIndex, textItems) {\n    const nextInner = currentInnerIndex + 1\n    if (nextInner < textItems.length) {\n      // More inner tabs in this pane\n      setActiveItem(pane, nextInner)\n      startAutoAdvance(pane)\n    } else {\n      // All inner tabs done — advance to next main tab\n      const nextTab = (activeTabIndex + 1) % tabPanes.length\n      setActiveTab(nextTab, true)\n    }\n  }\n\n  function startAutoAdvance(pane) {\n    // Clean previous listeners but don't reset bars yet (resetAllBars called inside)\n    cleanupAutoAdvanceListeners()\n    _autoAdvanceState = null\n\n    ensureProgressBars(pane)\n\n    const textItems = pane.querySelectorAll('.product_modal-tab-text-item')\n    const imageItems = pane.querySelectorAll('.product_modal-tab-image-wrapper')\n    // Find active index\n    let activeIdx = 0\n    textItems.forEach((item, i) => { if (item.classList.contains('is-active')) activeIdx = i })\n\n    const wrapper = imageItems[activeIdx]\n    if (!wrapper) return\n\n    // Reset all fills and activate the current one\n    resetAllBars(pane)\n    const bar = textItems[activeIdx]?.querySelector('.tab-progress-bar')\n    if (bar) bar.classList.add('is-active')\n    const fill = bar?.querySelector('.tab-progress-bar__fill')\n\n    const video = wrapper.querySelector('[data-modal-video=\"video\"]')\n    const hasVideo = !!video\n\n    // --- Hover pause (on the visual wrapper area) ---\n    const visualWrapper = pane.querySelector('.product_modal-tab-visual-wrapper') || wrapper\n    function onMouseEnter() {\n      if (hasVideo && video) video.pause()\n      if (_autoAdvanceState?.barTween) _autoAdvanceState.barTween.pause()\n    }\n\n    function onMouseLeave() {\n      if (hasVideo && video) video.play().catch(() => {})\n      if (_autoAdvanceState?.barTween) _autoAdvanceState.barTween.resume()\n    }\n\n    visualWrapper.addEventListener('mouseenter', onMouseEnter)\n    visualWrapper.addEventListener('mouseleave', onMouseLeave)\n\n    if (hasVideo) {\n      // --- Video slide ---\n      video.loop = false\n\n      function startBarTween() {\n        if (!fill || !video.duration) return null\n        gsap.set(fill, { width: '0%' })\n        return gsap.to(fill, {\n          width: '100%',\n          duration: video.duration,\n          ease: 'none',\n        })\n      }\n\n      function onEnded() {\n        advanceToNext(pane, activeIdx, textItems)\n      }\n\n      let barTween = null\n      if (video.readyState >= 1 && video.duration) {\n        barTween = startBarTween()\n      } else {\n        video.addEventListener('loadedmetadata', () => {\n          if (_autoAdvanceState && _autoAdvanceState.video === video) {\n            _autoAdvanceState.barTween = startBarTween()\n          }\n        }, { once: true })\n      }\n\n      video.addEventListener('ended', onEnded)\n      _autoAdvanceState = {\n        pane, index: activeIdx, video, onEnded, barTween,\n        onMouseEnter, onMouseLeave, hoverTarget: visualWrapper,\n      }\n    } else {\n      // --- Image slide (no video) — use fixed timer ---\n      gsap.set(fill, { width: '0%' })\n      const barTween = gsap.to(fill, {\n        width: '100%',\n        duration: IMAGE_SLIDE_DURATION,\n        ease: 'none',\n        onComplete: () => {\n          advanceToNext(pane, activeIdx, textItems)\n        },\n      })\n\n      _autoAdvanceState = {\n        pane, index: activeIdx, video: null, onEnded: null, barTween,\n        onMouseEnter, onMouseLeave, hoverTarget: visualWrapper,\n      }\n    }\n  }\n\n  // Event delegation — click on text items (works across all panes)\n  paneWrapper.addEventListener('click', (e) => {\n    const textItem = e.target.closest('.product_modal-tab-text-item')\n    if (!textItem) return\n    const pane = textItem.closest('.product_modal-tabs-pane')\n    const textItems = [...pane.querySelectorAll('.product_modal-tab-text-item')]\n    const index = textItems.indexOf(textItem)\n    if (index !== -1) {\n      setActiveItem(pane, index)\n      startAutoAdvance(pane)\n    }\n  }, { signal: _signal })\n\n  let activeTabIndex = -1\n  let tabTransition = null\n\n  // ---- Sliding pill indicator ----\n  const tabMenu = tabLinks[0] && tabLinks[0].parentElement\n  var pill = null\n  var pillResizeObserver = null\n  var _pillRaf = null\n  if (tabMenu) {\n    tabMenu.style.position = 'relative'\n    // Si quedo un pill de un ciclo anterior de matchMedia (resize cruzando 992px), se descarta\n    const stalePill = tabMenu.querySelector('.tab-pill')\n    if (stalePill) stalePill.remove()\n    pill = document.createElement('div')\n    pill.className = 'tab-pill'\n    pill.style.cssText =\n      'position:absolute;top:50%;transform:translateY(-50%);left:0;height:calc(100% - 0.5rem);border-radius:0.5rem;' +\n      'background:#000;z-index:0;pointer-events:none;'\n    tabMenu.insertBefore(pill, tabMenu.firstChild)\n    tabLinks.forEach(function (link) {\n      link.style.position = 'relative'\n      link.style.zIndex = '1'\n      // Strip is-active styles — pill handles the visual indicator\n      link.classList.remove('is-active')\n    })\n  }\n\n  function movePill(index, animate) {\n    if (!pill || !tabLinks[index]) return\n    var target = tabLinks[index]\n    var parentRect = tabMenu.getBoundingClientRect()\n    var targetRect = target.getBoundingClientRect()\n    var props = {\n      left: targetRect.left - parentRect.left,\n      width: targetRect.width,\n    }\n    // Update text colors\n    tabLinks.forEach(function (link, i) {\n      gsap.to(link, {\n        color: i === index ? '#fff' : '#000',\n        duration: animate ? 0.3 : 0,\n      })\n    })\n    if (animate) {\n      gsap.to(pill, {\n        ...props,\n        duration: 0.6,\n        ease: 'power3.inOut',\n      })\n    } else {\n      gsap.set(pill, props)\n    }\n  }\n\n  // ---- Resize: el pill vive en px (left/width medidos), hay que recalcularlo ----\n  function repositionPill() {\n    if (!pill || activeTabIndex < 0) return\n    gsap.killTweensOf(pill)\n    movePill(activeTabIndex, false)\n  }\n\n  function schedulePillReposition() {\n    if (_pillRaf) cancelAnimationFrame(_pillRaf)\n    _pillRaf = requestAnimationFrame(() => {\n      _pillRaf = null\n      repositionPill()\n    })\n  }\n\n  if (tabMenu && typeof ResizeObserver !== 'undefined') {\n    // cubre resize del window, cambios de layout del menu y carga de fuentes\n    pillResizeObserver = new ResizeObserver(schedulePillReposition)\n    pillResizeObserver.observe(tabMenu)\n    tabLinks.forEach((link) => pillResizeObserver.observe(link))\n  }\n  window.addEventListener('resize', schedulePillReposition, { signal: _signal })\n  window.addEventListener('orientationchange', schedulePillReposition, { signal: _signal })\n  if (document.fonts && document.fonts.ready) {\n    document.fonts.ready.then(() => {\n      if (pill && pill.isConnected) schedulePillReposition()\n    })\n  }\n\n  // ---- Pause all videos inside a pane ----\n  function pausePaneVideos(pane) {\n    pane.querySelectorAll('[data-modal-video=\"video\"]').forEach((v) => v.pause())\n  }\n\n  // ---- Tab switching ----\n  function setActiveTab(index, animate = false) {\n    if (index === activeTabIndex) return\n    stopAutoAdvance()\n    const prevIndex = activeTabIndex\n    activeTabIndex = index\n\n    // Move pill\n    movePill(index, animate && prevIndex >= 0)\n\n    // Tab links — no is-active class, pill handles the indicator\n    tabLinks.forEach((link) => {\n      link.classList.remove('is-active')\n    })\n\n    const newPane = tabPanes[index]\n\n    // If no previous pane or not animating, just show instantly\n    if (!animate || prevIndex < 0) {\n      tabPanes.forEach((pane, i) => {\n        if (i !== index) pausePaneVideos(pane)\n        pane.classList.toggle('is-active', i === index)\n        pane.style.display = i === index ? 'flex' : 'none'\n        gsap.set(pane, { opacity: i === index ? 1 : 0 })\n      })\n      if (newPane) {\n        // Reset image wrappers and show first item's video\n        const imgs = newPane.querySelectorAll('.product_modal-tab-image-wrapper')\n        imgs.forEach((item, i) => {\n          gsap.killTweensOf(item)\n          item.classList.toggle('is-active', i === 0)\n          item.style.cssText = i === 0 ? 'display:flex;position:relative;z-index:1;' : 'display:none;'\n          gsap.set(item, { clipPath: '' })\n          const v = item.querySelector('[data-modal-video=\"video\"]')\n          if (v && i === 0) {\n            v.style.cssText = 'width:100%;height:100%;object-fit:cover;display:block;'\n            v.currentTime = 0\n            v.play().catch(() => {})\n          } else if (v) {\n            v.pause()\n            v.style.display = 'none'\n          }\n        })\n        newPane.querySelectorAll('.product_modal-tab-text-item').forEach((t, i) => {\n          t.classList.toggle('is-active', i === 0)\n        })\n        startAutoAdvance(newPane)\n      }\n      return\n    }\n\n    // Kill any running tab transition\n    if (tabTransition) tabTransition.kill()\n\n    // Hide ALL panes immediately\n    tabPanes.forEach((pane) => {\n      pausePaneVideos(pane)\n      gsap.killTweensOf(pane)\n      pane.classList.remove('is-active')\n      pane.style.display = 'none'\n      gsap.set(pane, { opacity: 0 })\n    })\n\n    // Reset all image wrappers in the new pane before showing it\n    const newImageItems = newPane.querySelectorAll('.product_modal-tab-image-wrapper')\n    newImageItems.forEach((item, i) => {\n      gsap.killTweensOf(item)\n      item.classList.toggle('is-active', i === 0)\n      item.style.cssText = i === 0 ? 'display:flex;position:relative;z-index:1;' : 'display:none;'\n      gsap.set(item, { clipPath: '' })\n      const v = item.querySelector('[data-modal-video=\"video\"]')\n      if (v) { v.pause(); v.style.display = 'none' }\n    })\n\n    // Reset text items too\n    newPane.querySelectorAll('.product_modal-tab-text-item').forEach((t, i) => {\n      t.classList.toggle('is-active', i === 0)\n    })\n\n    // Show new pane with fade\n    newPane.classList.add('is-active')\n    newPane.style.display = 'flex'\n\n    // Pre-hide first image wrapper for clip-path reveal (CTA pattern)\n    const activeImg = newImageItems[0] || null\n    if (activeImg) gsap.set(activeImg, { clipPath: 'inset(0 0 100% 0)' })\n\n    tabTransition = gsap.to(newPane, {\n      opacity: 1,\n      duration: 0.5,\n      delay: 0.15,\n      ease: 'power2.out',\n      onComplete: () => {\n        // Start video of first item now\n        if (activeImg) {\n          const firstVideo = activeImg.querySelector('[data-modal-video=\"video\"]')\n          if (firstVideo) {\n            firstVideo.style.cssText = 'width:100%;height:100%;object-fit:cover;display:block;'\n            firstVideo.currentTime = 0\n            firstVideo.play().catch(() => {})\n          }\n          // Reveal image/video with clip-path\n          gsap.fromTo(activeImg,\n            { clipPath: 'inset(0 0 100% 0)' },\n            { clipPath: 'inset(0 0 0% 0)', duration: 0.7, ease: 'power3.inOut',\n              onComplete: () => {\n                gsap.set(activeImg, { clipPath: '' })\n                startAutoAdvance(newPane)\n              },\n            }\n          )\n        } else {\n          startAutoAdvance(newPane)\n        }\n      },\n    })\n  }\n\n  // ---- Lenis — capture the running instance via prototype patch ----\n  // Webflow doesn't expose the Lenis instance globally.\n  // Since window.Lenis (the class) exists and raf() runs every frame,\n  // we intercept it once to grab `this` (the instance).\n  let _lenisInstance = null\n\n  if (window.Lenis && !window.lenis) {\n    const origRaf = window.Lenis.prototype.raf\n    window.Lenis.prototype.raf = function (time) {\n      if (!_lenisInstance) {\n        _lenisInstance = this\n        window.Lenis.prototype.raf = origRaf\n      }\n      return origRaf.call(this, time)\n    }\n  }\n\n  function getLenis() {\n    if (_lenisInstance) return _lenisInstance\n    if (window.lenis) return window.lenis\n    if (window.__lenis) return window.__lenis\n    if (document.documentElement.lenis) return document.documentElement.lenis\n    return null\n  }\n\n  // ---- Smooth scroll to inline section (uses Lenis when available) ----\n  function scrollToModal() {\n    const lenis = getLenis()\n    if (lenis && !prefersReducedMotion) {\n      lenis.scrollTo(modal, { offset: SCROLL_OFFSET, duration: 1.2 })\n    } else {\n      modal.scrollIntoView({ behavior: prefersReducedMotion ? 'auto' : 'smooth', block: 'start' })\n    }\n  }\n\n  // ---- Event listeners ----\n\n  // Cards → scroll to inline section + activate matching tab\n  // Match by [data-modal-tab] value against [data-tab-link]; fallback to card index\n  cards.forEach((card, index) => {\n    card.addEventListener('click', () => {\n      const value = card.getAttribute('data-modal-tab')\n      let targetIndex = -1\n      if (value) {\n        tabLinks.forEach((link, i) => {\n          if (link.getAttribute('data-tab-link') === value) targetIndex = i\n        })\n      }\n      if (targetIndex === -1 && index < tabLinks.length) targetIndex = index\n      scrollToModal()\n      if (targetIndex !== -1 && targetIndex !== activeTabIndex) {\n        setActiveTab(targetIndex, !prefersReducedMotion)\n      }\n    }, { signal: _signal })\n  })\n\n  tabLinks.forEach((link, index) => {\n    link.addEventListener('click', () => setActiveTab(index, !prefersReducedMotion), { signal: _signal })\n  })\n\n  // Initialize first tab active on load (no animation) — content is inline\n  setActiveTab(0, false)\n}\n\nreturn () => {\n  _ac.abort()\n  if (typeof stopAutoAdvance === 'function') stopAutoAdvance()\n  // declarados con `var` dentro del if (modal) -> visibles aca por hoisting\n  if (typeof _pillRaf !== 'undefined' && _pillRaf) cancelAnimationFrame(_pillRaf)\n  if (typeof pillResizeObserver !== 'undefined' && pillResizeObserver) pillResizeObserver.disconnect()\n  if (typeof pill !== 'undefined' && pill) pill.remove()\n}\n})\n\n// ------------------------\n// Teal active state on the mobile accordion items\n// ------------------------\n// Mobile has no progress bar (that one is desktop only), so the item itself\n// is the only cue that its video is the one currently playing.\n// Injected as a stylesheet rather than inline styles so it never fights GSAP.\n;(function injectActiveItemStyle() {\n  if (document.getElementById('md-tab-active-style')) return\n  const style = document.createElement('style')\n  style.id = 'md-tab-active-style'\n  style.textContent = [\n    '@media (max-width: 991px) {',\n    '  .product-overview_card-tablet .product_modal-tab-text-item {',\n    '    transition: background-color 0.3s ease;',\n    '  }',\n    '  .product-overview_card-tablet .product_modal-tab-text-item.is-active {',\n    '    background-color: #2de7b0;',\n    '  }',\n    '}',\n  ].join('\\n')\n  document.head.appendChild(style)\n})()\n\nmm.add('(max-width: 991px)', () => {\n  const _ac = new AbortController()\n  const _signal = _ac.signal\n\n  // ---- Section fade-up: disabled on mobile ----\n  // The site-wide footer animates [data-animate], leaving the content at\n  // opacity 0 until its ScrollTrigger fires. On mobile this section is exactly\n  // what people scroll down for, and it read as empty for too long. That\n  // script runs before this file (inline #30 vs tech.js #31), so the triggers\n  // already exist by now. The divider lines are left alone: no data-animate.\n  // Note: killing them is not reverted when resizing up to desktop. Content\n  // stays visible, only the entrance animation is lost for that session.\n  function killOverviewFade(when) {\n    const overviewSection = document.querySelector('.section_product-overview')\n    if (!overviewSection || typeof ScrollTrigger === 'undefined') {\n      mdlog('fade-kill (' + when + '): nothing to do. section?', !!overviewSection,\n        '| ScrollTrigger?', typeof ScrollTrigger)\n      return\n    }\n    let killed = 0\n    overviewSection.querySelectorAll('[data-animate]').forEach((el) => {\n      ScrollTrigger.getAll().forEach((t) => { if (t.trigger === el) { t.kill(); killed++ } })\n      // fade-up animates the element itself; stagger animates its children\n      const stagger = el.getAttribute('data-animate') === 'stagger'\n      const targets = stagger ? [...el.children] : el\n      const probe = stagger && el.children[0] ? el.children[0] : el\n      const before = getComputedStyle(probe).opacity\n      gsap.killTweensOf(targets)\n      // Written out rather than cleared: clearProps would fall back to\n      // whatever start state the footer left behind\n      gsap.set(targets, { opacity: 1, y: 0 })\n      mdlog('fade-kill (' + when + '):', el.getAttribute('data-animate'),\n        String(el.className).slice(0, 38), '| triggers killed:', killed,\n        '| opacity', before, '->', getComputedStyle(probe).opacity)\n    })\n    mdlog('fade-kill (' + when + '): total killed', killed, '| still alive in the section:',\n      ScrollTrigger.getAll().filter((t) => t.trigger && overviewSection.contains(t.trigger))\n        .map((t) => String(t.trigger.className).slice(0, 28)))\n  }\n\n  // The first call is the one that does the work: the site-wide footer block\n  // that creates these triggers runs before this file, so they already exist\n  // (measured: 3 killed, opacity 0 -> 1). The repeats are insurance for the day\n  // Webflow's code ordering changes -- if that block ever ended up below this\n  // file, a single early call would kill nothing and the section would read as\n  // empty until deep scroll. Measured on a 390x844 viewport with the kill\n  // missing: the cards only reached opacity 1 once the section top was 84 px\n  // from the top of the viewport. Every call is idempotent, so repeating is free.\n  killOverviewFade('immediately')\n  if (document.readyState === 'loading') {\n    document.addEventListener('DOMContentLoaded', () => killOverviewFade('DOMContentLoaded'),\n      { once: true, signal: _signal })\n  }\n  addEventListener('load', () => killOverviewFade('load'), { once: true, signal: _signal })\n\n  // Tablet / Mobile: accordion dropdown on product cards\n  const allCards = document.querySelectorAll('[data-modal-tab]')\n  const modal = document.querySelector('[data-modal=\"product-features\"]')\n  const modalPanes = modal ? modal.querySelectorAll('.product_modal-tabs-pane') : []\n  let openCard = null\n\n  // Build a video data map from the modal panes (source of truth)\n  // videoDataMap[cardIndex] = [{ src, poster }, ...] per text item (index 0 = first item)\n  const videoDataMap = {}\n  modalPanes.forEach((pane, paneIndex) => {\n    const wrappers = pane.querySelectorAll('.product_modal-tab-image-wrapper')\n    const items = []\n    wrappers.forEach((wrapper) => {\n      const video = wrapper.querySelector('[data-modal-video=\"video\"]')\n      if (video) {\n        const source = video.querySelector('source')\n        items.push({\n          src: source ? source.getAttribute('src') : null,\n          poster: video.getAttribute('poster') || '',\n        })\n      } else {\n        items.push(null)\n      }\n    })\n    videoDataMap[paneIndex] = items\n  })\n  mdlog('discovery: cards', allCards.length, '| modal?', !!modal, '| panes', modalPanes.length,\n    '| videos per pane:', Object.keys(videoDataMap).map((k) =>\n      k + ':[' + videoDataMap[k].map((it) => {\n        if (!it || !it.src) return 'NO-SRC'\n        const m = it.src.match(/playback\\/(\\d+)\\/rendition\\/(\\w+)/)\n        return m ? m[1] + '@' + m[2] : 'unrecognised'\n      }).join(' ') + ']').join('  '))\n\n  // Track dynamically created videos per card so we can clean them up\n  const cardVideos = new Map()\n  // cardIndex -> last requested item, to discard reveals that arrive late\n  const currentItem = new Map()\n\n  // Initial state: dropdowns hidden, content clipped, images stacked for crossfade\n  allCards.forEach((card) => {\n    const dropdown = card.querySelector('.product-overview_card-tablet')\n    if (!dropdown) return\n    gsap.set(dropdown, { height: 0, overflow: 'hidden' })\n\n    // Pre-clip all text items and CTA so they never flash unclipped\n    const textItems = dropdown.querySelectorAll('.product_modal-tab-text-item')\n    textItems.forEach((t) => gsap.set(t, { clipPath: 'inset(0 100% 0 0)' }))\n    const cta = card.querySelector('.product-overview_card-tablet .product_modal-cta')\n    if (cta) gsap.set(cta, { clipPath: 'inset(0 0 100% 0)' })\n\n    // Stack images: wrapper = relative, non-main = absolute overlay\n    const imageWrapper = card.querySelector('.product-overview_card-image-wrapper')\n    if (imageWrapper) imageWrapper.style.position = 'relative'\n\n    const images = card.querySelectorAll('.product-overview_card-image')\n    images.forEach((img) => {\n      if (!img.classList.contains('is-main')) {\n        gsap.set(img, {\n          display: 'block',\n          opacity: 0,\n          position: 'absolute',\n          top: 0,\n          left: 0,\n          width: '100%',\n          height: '100%',\n          objectFit: 'cover',\n        })\n      }\n    })\n  })\n\n  // Create or retrieve a video element for a card at a given text-item index\n  function getOrCreateVideo(card, cardIndex, itemIndex) {\n    const key = `${cardIndex}-${itemIndex}`\n    let existing = cardVideos.get(key)\n    if (existing) return existing\n\n    const videoData = videoDataMap[cardIndex]\n    if (!videoData || !videoData[itemIndex] || !videoData[itemIndex].src) return null\n\n    const { src, poster } = videoData[itemIndex]\n    const video = document.createElement('video')\n    video.setAttribute('playsinline', '')\n    video.muted = true\n    // iOS Safari looks at the attribute, not just the property, before it lets\n    // a dynamically created video play inline without a gesture\n    video.setAttribute('muted', '')\n    video.style.cssText = 'position:absolute;top:0;left:0;width:100%;height:100%;object-fit:cover;display:none;z-index:2;'\n    const source = document.createElement('source')\n    source.src = src\n    source.type = 'video/mp4'\n    video.appendChild(source)\n    // The element paints its poster attribute only while readyState is below\n    // HAVE_CURRENT_DATA, and play() clears the show-poster flag straight away.\n    // On an empty buffer that leaves the box transparent, and since every\n    // ancestor is transparent too, the card image underneath shows through\n    // until the first frame decodes. Painting a placeholder as the element's\n    // own background closes that gap for good, at the same crop as\n    // object-fit: cover. Same fix the Home video cards use.\n    //\n    // The placeholder is the card's own cover image, not the modal poster: they\n    // are two different photos (`handheld-robot.avif` vs `thumb.avif`), so\n    // handing over to the poster put a third picture on screen and the sequence\n    // read as cover image, flash, video. With the cover image as the\n    // placeholder the hand-off is invisible and the only visible change is the\n    // video actually starting.\n    const cardImage = card.querySelector('.product-overview_card-image')\n    const paintPlaceholder = (url) => {\n      video.style.backgroundImage = `url(\"${url}\")`\n      video.style.backgroundSize = 'cover'\n      video.style.backgroundPosition = 'center'\n      video.style.backgroundRepeat = 'no-repeat'\n    }\n    // currentSrc is the exact file already in the HTTP cache, so this costs no\n    // request. It is empty until the image loads, hence the fallback and the\n    // one-shot listener below.\n    const placeholder = (cardImage && cardImage.currentSrc) || poster\n    if (placeholder) {\n      video.poster = placeholder\n      paintPlaceholder(placeholder)\n    }\n    if (cardImage && !cardImage.currentSrc) {\n      cardImage.addEventListener('load', () => {\n        if (video.isConnected && cardImage.currentSrc) paintPlaceholder(cardImage.currentSrc)\n      }, { once: true })\n    }\n\n    // Append inside the image wrapper of this card\n    const imageWrapper = card.querySelector('.product-overview_card-image-wrapper')\n    if (imageWrapper) imageWrapper.appendChild(video)\n\n    cardVideos.set(key, video)\n    mdlog('video created', key, mdVid(video), '| poster?', !!poster)\n    if (MD_DEBUG) {\n      const label = key + ' ' + mdVid(video)\n      ;['loadedmetadata', 'loadeddata', 'canplay', 'canplaythrough', 'playing', 'pause',\n        'waiting', 'stalled', 'suspend', 'ended', 'error'].forEach((ev) => {\n        video.addEventListener(ev, () => mdlog('  video ' + label + ' -> ' + ev, mdVidState(video)))\n      })\n    }\n    return video\n  }\n\n  // Destroy all dynamic videos for a card\n  function destroyCardVideos(cardIndex) {\n    for (const [key, video] of cardVideos) {\n      if (key.startsWith(`${cardIndex}-`)) {\n        video.pause()\n        video.remove()\n        cardVideos.delete(key)\n        warmed.delete(key)\n        if (warming === key) warming = null\n      }\n    }\n  }\n\n  // Videos already told to fetch. Keeping the playing one in here matters:\n  // load() on it would restart playback from zero.\n  const warmed = new Set()\n\n  // Warm the open card's videos, one at a time. This used to hang off the\n  // playing video's 'canplaythrough' and only ever warmed the *next* item, so\n  // item 3 did not start downloading until item 2 could play through. Tap item\n  // 3 directly and it began from zero, with the previous frame frozen on\n  // screen for seconds. Serialized on purpose: two files at once starve the\n  // one that is playing.\n  const WARM_BUFFER_AHEAD = 4 // seconds of headroom before prefetching a sibling\n\n  // Seconds already buffered past the playhead, 0 if the playhead sits in no\n  // buffered range\n  function bufferAhead(v) {\n    try {\n      for (let i = 0; i < v.buffered.length; i++) {\n        if (v.buffered.start(i) <= v.currentTime && v.currentTime <= v.buffered.end(i)) {\n          return v.buffered.end(i) - v.currentTime\n        }\n      }\n    } catch (e) {}\n    return 0\n  }\n\n  // Key of the sibling currently downloading, or null. Without it the chain\n  // was not actually serial: warmNextVideo hangs off both 'playing' and\n  // 'canplaythrough' of the visible video, so two events warmed two siblings at\n  // once and the one on screen was starved. Traced on card 2 at 1.6 Mbps: both\n  // siblings reached readyState 3 by t=6 s while the playing video sat at\n  // currentTime 1.9 s for five seconds straight, playing but frozen.\n  let warming = null\n\n  function warmNextVideo(card, cardIndex) {\n    if (openCard !== card || !isCardVisible(card)) return\n    if (warming) return\n\n    // Only prefetch with real headroom. readyState 3 just means \"enough to keep\n    // going for now\", and gating on that still let a sibling's download starve\n    // the video on screen: traced at 1.6 Mbps, currentTime sat at 1.9 s for\n    // five seconds while playing. Seconds buffered ahead is self-tuning -- on a\n    // fast link the buffer builds and prefetching starts, on a link no quicker\n    // than the video's own bitrate it never does, which is right either way.\n    const showing = cardVideos.get(`${cardIndex}-${currentItem.get(cardIndex)}`)\n    if (showing && showing.readyState < 4 && bufferAhead(showing) < WARM_BUFFER_AHEAD) {\n      mdlog('warm: holding off, only', bufferAhead(showing).toFixed(1) + 's buffered ahead',\n        mdVidState(showing))\n      return\n    }\n\n    const count = (videoDataMap[cardIndex] || []).length\n    for (let i = 0; i < count; i++) {\n      const key = `${cardIndex}-${i}`\n      if (warmed.has(key)) continue\n      warmed.add(key)\n      const v = getOrCreateVideo(card, cardIndex, i)\n      if (!v) continue\n      warming = key\n      const done = () => {\n        v.removeEventListener('canplaythrough', done)\n        v.removeEventListener('error', done)\n        if (warming === key) warming = null\n        warmNextVideo(card, cardIndex)\n      }\n      // The head snippet's observer sets preload=\"none\" in a microtask after\n      // insertion; this rAF runs after it, so it wins\n      requestAnimationFrame(() => {\n        if (!v.isConnected) return (warming === key) && (warming = null)\n        v.preload = 'auto'\n        try { v.load() } catch (e) {}\n        mdlog('warming', key, mdVid(v))\n      })\n      v.addEventListener('canplaythrough', done)\n      v.addEventListener('error', done)\n      return\n    }\n  }\n\n  // ---- Auto-advance: the open card's items play back to back, on a loop ----\n  // Mobile counterpart of desktop's startAutoAdvance(): when the current\n  // item's video ends it moves to the next, and wraps back to the first.\n  //\n  // Watch out for the perf snippet in the <head>: it forces preload=\"none\" on\n  // every <video> entering the DOM, these JS-created ones included. That is\n  // why the chain only runs while the card is on screen — otherwise the first\n  // video starts its multi-MB download during page load and drags it 4 s down.\n  const ITEM_FALLBACK_DURATION = 5 // seconds, for items with no video\n  const METADATA_TIMEOUT = 8 // seconds: advance anyway if duration never lands\n  let _cardAuto = null\n\n  // Cards currently on screen. The chain pauses when the card leaves the\n  // viewport: without this the loop keeps pulling MB down unseen, forever.\n  const visibleCards = new Set()\n  const cardObserver = typeof IntersectionObserver !== 'undefined'\n    ? new IntersectionObserver((entries) => {\n        entries.forEach((e) => {\n          if (e.isIntersecting) visibleCards.add(e.target)\n          else visibleCards.delete(e.target)\n          mdlog('visibility: card', [...allCards].indexOf(e.target) + 1,\n            e.isIntersecting ? 'ON screen' : 'OFF screen', '| open one?', openCard === e.target)\n          if (openCard !== e.target) return\n          if (e.isIntersecting) resumeCardAutoAdvance()\n          else pauseCardAutoAdvance()\n        })\n      }, { threshold: 0.01 })\n    : null\n  if (cardObserver) allCards.forEach((c) => cardObserver.observe(c))\n\n  // With no IntersectionObserver, fall back to the old behavior (just play)\n  function isCardVisible(card) {\n    return !cardObserver || visibleCards.has(card)\n  }\n\n  // Tapping a card used to leave you looking at the wrong place: the card that\n  // was open collapses, everything below it jumps up (measured 262-277 px on a\n  // 390x844 viewport) and the visual area of the card you just opened lands\n  // above the fold. Playback is gated on visibility, so that also meant the\n  // chain never started and the card sat there dead. Desktop already scrolls\n  // to the section on click; this is the mobile counterpart.\n  const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches\n  const CARD_SCROLL_OFFSET = 16 // px of air above the card header\n\n  function scrollCardIntoView(card) {\n    const top = Math.max(0, card.getBoundingClientRect().top + window.scrollY - CARD_SCROLL_OFFSET)\n    const lenis = window.lenis || window.__lenis || document.documentElement.lenis\n    if (lenis && !prefersReducedMotion) lenis.scrollTo(top, { duration: 0.8 })\n    else window.scrollTo({ top, behavior: prefersReducedMotion ? 'auto' : 'smooth' })\n  }\n\n  function stopCardAutoAdvance() {\n    if (!_cardAuto) return\n    const { video, listeners, timer } = _cardAuto\n    if (timer) clearTimeout(timer)\n    if (video && listeners) listeners.forEach(([ev, fn]) => video.removeEventListener(ev, fn))\n    _cardAuto = null\n  }\n\n  // Card off screen: stop the video and the timer, but keep the chain around\n  // so it can resume on the same item when the card comes back\n  function pauseCardAutoAdvance() {\n    if (!_cardAuto) return\n    if (_cardAuto.timer) { clearTimeout(_cardAuto.timer); _cardAuto.timer = null }\n    if (_cardAuto.video) _cardAuto.video.pause()\n  }\n\n  function resumeCardAutoAdvance() {\n    if (!_cardAuto) return\n    if (_cardAuto.video) {\n      _cardAuto.video.preload = 'auto'\n      _cardAuto.video.play().catch(() => {})\n    }\n    if (_cardAuto.arm) _cardAuto.arm()\n  }\n\n  function startCardAutoAdvance(card, cardIndex, index) {\n    stopCardAutoAdvance()\n\n    const dropdown = card.querySelector('.product-overview_card-tablet')\n    if (!dropdown) return\n    // Desktop (videoDataMap) drives the chain length, not the text items: add\n    // a 4th video to the modal and mobile plays it too, untouched\n    const videoCount = (videoDataMap[cardIndex] || []).length\n    const total = Math.max(dropdown.querySelectorAll('.product_modal-tab-text-item').length, videoCount)\n    if (total < 2) return\n\n    // Loop: after the last item it wraps back to 0\n    const nextIndex = (index + 1) % total\n    function advance() {\n      // The card may have closed (or another opened) while the timer ran\n      if (openCard !== card) return stopCardAutoAdvance()\n      mdlog('advance: card', cardIndex + 1, 'item', index + 1, '->', nextIndex + 1)\n      setCardItem(card, cardIndex, nextIndex)\n    }\n\n    // Warming starts once the current video can play through, so it never\n    // competes for bandwidth with the one on screen\n    function preloadNext() {\n      warmNextVideo(card, cardIndex)\n    }\n\n    const video = cardVideos.get(`${cardIndex}-${index}`)\n    if (!video) {\n      // Item with no video (image fallback): fixed timer, same as desktop\n      const state = { card, index, video: null, timer: null }\n      state.arm = () => {\n        if (state.timer) { clearTimeout(state.timer); state.timer = null }\n        // Off screen the observer re-arms this on the way back in\n        if (!isCardVisible(card)) return\n        state.timer = setTimeout(advance, ITEM_FALLBACK_DURATION * 1000)\n      }\n      _cardAuto = state\n      if (isCardVisible(card)) state.arm()\n      return\n    }\n\n    video.loop = false // the loop belongs to the chain, not to one video\n\n    const state = { card, index, video, timer: null }\n    // Safety net in case 'ended' never fires (stall, codec, backgrounded tab):\n    // armed only once the real duration is known, so it never cuts one short.\n    state.arm = function armSafetyTimer() {\n      if (state.timer) { clearTimeout(state.timer); state.timer = null }\n      // Off screen this stays disarmed: 'loadedmetadata' lands while the card\n      // is away (the video keeps buffering) and used to re-arm the timer, so\n      // the chain walked to the next item unseen, pulling one file after\n      // another. The observer arms it again when the card comes back.\n      if (!isCardVisible(card)) return\n      const d = Number.isFinite(video.duration) && video.duration > 0 ? video.duration : null\n      // No metadata yet: retry after METADATA_TIMEOUT\n      const secs = d ? d - video.currentTime + 2 : METADATA_TIMEOUT\n      const startedAt = video.currentTime\n      state.timer = setTimeout(() => {\n        // A file whose bitrate is above the visitor's connection cannot play in\n        // real time, so 'ended' never arrives and this timer used to skip the\n        // item while it was still buffering -- nobody ever saw it. Measured on\n        // a 1.6 Mbps link: the 1080p items sit at 4.2 and 3.8 Mbps and got\n        // dropped around t=0.9 s of 6.1 s. While playback is still creeping\n        // forward, give it another window instead of moving on.\n        const creeping = !video.paused && video.currentTime > startedAt + 0.1\n        if (creeping && (state.waits = (state.waits || 0) + 1) <= 3) {\n          mdlog('safety timer: still buffering, extension', state.waits, 'of 3',\n            mdVid(video), mdVidState(video))\n          return state.arm()\n        }\n        mdlog('safety timer fired -> advancing away from item', index + 1,\n          mdVid(video), mdVidState(video))\n        advance()\n      }, secs * 1000)\n    }\n\n    const onMeta = () => { if (_cardAuto === state) state.arm() }\n    state.listeners = [\n      ['ended', advance],\n      ['error', advance],\n      ['loadedmetadata', onMeta],\n      // 'playing' is the moment the viewer sees motion: from here on the\n      // bandwidth is free to warm the siblings. 'canplaythrough' stays as the\n      // backstop for a video that was already buffered and never fires\n      // 'playing' again.\n      ['playing', preloadNext],\n      ['canplaythrough', preloadNext],\n    ]\n    state.listeners.forEach(([ev, fn]) => video.addEventListener(ev, fn))\n\n    _cardAuto = state\n    // Nothing is armed off screen: the observer does it when the card enters\n    if (isCardVisible(card)) state.arm()\n  }\n\n  // Activate an item: active text + visual + restart the chain from there\n  function setCardItem(card, cardIndex, index) {\n    mdlog('setCardItem: card', cardIndex + 1, 'item', index + 1)\n    const dropdown = card.querySelector('.product-overview_card-tablet')\n    const textItems = dropdown ? dropdown.querySelectorAll('.product_modal-tab-text-item') : []\n    // With more videos than text items, the highlight stays on the last one\n    const activeText = Math.min(index, textItems.length - 1)\n    textItems.forEach((t, i) => t.classList.toggle('is-active', i === activeText))\n    switchCardImage(card, cardIndex, index)\n    startCardAutoAdvance(card, cardIndex, index)\n  }\n\n  // `animate: false` collapses in place, no tweens. Used when another card is\n  // being opened: the layout has to settle in this same frame so the scroll\n  // target below is the final one, otherwise the card drifts 270 px while the\n  // collapse animates and you end up somewhere else again.\n  function closeCard(card, animate = true) {\n    const dropdown = card.querySelector('.product-overview_card-tablet')\n    const icon = card.querySelector('.icon_plus')\n    if (!dropdown) return\n\n    // Fast toggles: the open tweens have to die before these start. Both\n    // animate `height` on the same dropdown and GSAP defaults to\n    // overwrite: false, so whichever finishes last used to win — reopening\n    // mid-close left the card collapsed, showing the image and no video.\n    killCardTweens(card, dropdown)\n\n    // Stop the chain before destroying the videos it listens to\n    if (_cardAuto && _cardAuto.card === card) stopCardAutoAdvance()\n\n    // Pause and destroy dynamic videos\n    const cardIndex = [...allCards].indexOf(card)\n    // Invalidate any pending reveal before the videos are destroyed\n    currentItem.delete(cardIndex)\n    destroyCardVideos(cardIndex)\n\n    // Also pause any static videos that might exist in the HTML\n    card.querySelectorAll('[data-modal-video=\"video\"]').forEach((v) => {\n      v.pause()\n      v.style.display = 'none'\n    })\n\n    const cta = card.querySelector('.product-overview_card-tablet .product_modal-cta')\n    const textItems = dropdown.querySelectorAll('.product_modal-tab-text-item')\n\n    if (!animate) {\n      gsap.set(dropdown, { height: 0, overflow: 'hidden' })\n      gsap.set(textItems, { clipPath: 'inset(0 100% 0 0)' })\n      if (cta) gsap.set(cta, { clipPath: 'inset(0 0 100% 0)' })\n      if (icon) gsap.set(icon, { rotation: 0 })\n      textItems.forEach((t, i) => t.classList.toggle('is-active', i === 0))\n      card.querySelectorAll('.product-overview_card-image').forEach((img) => {\n        gsap.killTweensOf(img)\n        gsap.set(img, { opacity: img.classList.contains('is-main') ? 1 : 0 })\n      })\n      return\n    }\n\n    // 1. Text items clip out right → left (reverse stagger)\n    gsap.to(textItems, {\n      clipPath: 'inset(0 100% 0 0)',\n      duration: 0.4,\n      stagger: { each: 0.06, from: 'end' },\n      ease: 'power3.inOut',\n    })\n\n    const itemsDelay = textItems.length * 0.06\n\n    // 2. CTA clips back down (bottom edge rises)\n    if (cta) {\n      gsap.to(cta, {\n        clipPath: 'inset(0 0 100% 0)',\n        duration: 0.5,\n        ease: 'power3.inOut',\n        delay: itemsDelay * 0.5,\n      })\n    }\n\n    // 3. Dropdown collapses after content is hidden\n    gsap.to(dropdown, {\n      height: 0,\n      duration: 0.6,\n      ease: 'power3.inOut',\n      delay: itemsDelay + 0.15,\n      onComplete: () => {\n        gsap.set(dropdown, { overflow: 'hidden' })\n      },\n    })\n    if (icon) gsap.to(icon, { rotation: 0, duration: 0.4, ease: 'power3.inOut' })\n\n    // Reset to first item + main image. killTweensOf first: with a fade to 0\n    // still running, the two tweens fought over opacity and the image could\n    // end up stuck dark after closing.\n    textItems.forEach((t, i) => t.classList.toggle('is-active', i === 0))\n    const images = card.querySelectorAll('.product-overview_card-image')\n    images.forEach((img) => {\n      gsap.killTweensOf(img)\n      gsap.to(img, { opacity: img.classList.contains('is-main') ? 1 : 0, duration: 0.4 })\n    })\n  }\n\n  // Every target both open and close animate. Killing them on each toggle is\n  // what keeps a fast tap from leaving two tweens fighting over the same prop.\n  function killCardTweens(card, dropdown) {\n    gsap.killTweensOf(dropdown)\n    gsap.killTweensOf(dropdown.querySelectorAll('.product_modal-tab-text-item'))\n    const cta = dropdown.querySelector('.product_modal-cta')\n    if (cta) gsap.killTweensOf(cta)\n    const icon = card.querySelector('.icon_plus')\n    if (icon) gsap.killTweensOf(icon)\n  }\n\n  function openCardDropdown(card, cardIndex, animate = true) {\n    const dropdown = card.querySelector('.product-overview_card-tablet')\n    const icon = card.querySelector('.icon_plus')\n    if (!dropdown) return\n\n    killCardTweens(card, dropdown)\n\n    // Set first item active, prepare stagger entrance\n    const textItems = dropdown.querySelectorAll('.product_modal-tab-text-item')\n    textItems.forEach((t, i) => {\n      t.classList.toggle('is-active', i === 0)\n      gsap.set(t, { clipPath: 'inset(0 100% 0 0)' })\n    })\n    const cta = card.querySelector('.product-overview_card-tablet .product_modal-cta')\n    if (cta) gsap.set(cta, { clipPath: 'inset(0 0 100% 0)' })\n\n    // Instant open (initial state on load): no animation, no scroll jump\n    if (!animate) {\n      gsap.set(dropdown, { height: 'auto', overflow: 'visible', visibility: 'visible' })\n      gsap.set(textItems, { clipPath: 'inset(0 0% 0 0)' })\n      if (cta) gsap.set(cta, { clipPath: 'inset(0% 0 0 0)' })\n      if (icon) gsap.set(icon, { rotation: 45 })\n      setCardItem(card, cardIndex, 0)\n      return\n    }\n\n    // Measure height without flashing content\n    gsap.set(dropdown, { height: 'auto', visibility: 'hidden' })\n    const h = dropdown.scrollHeight\n    // Set explicitly and not left to the close tween's onComplete: killing that\n    // tween mid-flight skips it, and the content spilled during the open\n    gsap.set(dropdown, { visibility: 'visible', overflow: 'hidden' })\n    gsap.fromTo(dropdown,\n      { height: 0 },\n      {\n        height: h,\n        duration: 0.5,\n        ease: 'power3.out',\n        onComplete: () => {\n          gsap.set(dropdown, { height: 'auto', overflow: 'visible' })\n          // CTA reveals bottom → top AFTER dropdown is fully expanded\n          if (cta) {\n            gsap.fromTo(cta,\n              { clipPath: 'inset(100% 0 0 0)' },\n              { clipPath: 'inset(0% 0 0 0)', duration: 0.5, ease: 'power3.inOut' }\n            )\n          }\n        },\n      }\n    )\n\n    // Staggered clip reveal left → right for text items\n    gsap.to(textItems, {\n      clipPath: 'inset(0 0% 0 0)',\n      duration: 0.65,\n      stagger: 0.1,\n      ease: 'power3.out',\n      delay: 0.12,\n    })\n\n    if (icon) gsap.to(icon, { rotation: 45, duration: 0.35, ease: 'power2.out' })\n\n    // Starts on item 0 and walks the rest on its own\n    setCardItem(card, cardIndex, 0)\n  }\n\n  // Image/video crossfade: index 0 → is-main image, index N → video from modal\n  function switchCardImage(card, cardIndex, index) {\n    const images = card.querySelectorAll('.product-overview_card-image')\n\n    // Item requested for this card: the deferred reveal below reads it to\n    // bail out if another item was picked while the video was still loading\n    currentItem.set(cardIndex, index)\n\n    // Dynamic videos of this card (the incoming one excluded)\n    const ownVideos = []\n    for (const [key, v] of cardVideos) {\n      if (key.startsWith(`${cardIndex}-`)) ownVideos.push(v)\n    }\n\n    // Also pause any static HTML videos\n    card.querySelectorAll('[data-modal-video=\"video\"]').forEach((v) => {\n      v.pause()\n      v.style.display = 'none'\n    })\n\n    // Try to show video for the selected item\n    const video = getOrCreateVideo(card, cardIndex, index)\n    mdlog('switch: card', cardIndex + 1, 'item', index + 1, '| video?', mdVid(video),\n      '| card on screen?', isCardVisible(card))\n    const mdSwitchAt = mdNow()\n    if (video) {\n      // The outgoing visual stays on screen until the new video has frames. A\n      // frameless <video> is transparent and the poster can lag behind (it\n      // queues up behind the video itself), so hiding the image any earlier\n      // leaves the area blank: measured 2-6 s with preload=\"none\" and 1080p.\n      // The outgoing one is frozen but left visible: covering it is worse\n      ownVideos.forEach((v) => { if (v !== video) v.pause() })\n      video.style.zIndex = '3' // above the outgoing visual while it loads\n      video.style.display = 'block'\n      video.currentTime = 0\n      // Marked before the visibility check on purpose. This is the item being\n      // shown, so the warm chain must never call load() on it -- that resets a\n      // playing video to zero. On the initial auto-open the observer has not\n      // reported yet, so isCardVisible is still false and the mark was being\n      // skipped: measured on reload, the video started at 519 ms and the warm\n      // chain reset it at 1533 ms, pushing first frame out to 3765 ms.\n      warmed.add(`${cardIndex}-${index}`)\n      // Only plays (= only downloads) while the card is on screen\n      if (isCardVisible(card)) {\n        video.preload = 'auto'\n        video.play().catch(() => {})\n        // disarm() in the head snippet is not idempotent for these: it sets\n        // preload=\"none\" and returns before marking mdDisarmed, because they\n        // carry no autoplay attribute. Its observer fires in a microtask right\n        // after insertion and undoes the line above. Chrome keeps fetching\n        // since play() already began, but Safari drops the pending load and\n        // the element then sits with no data until something calls play()\n        // again -- which reads as having to tap a second time. This rAF runs\n        // after that microtask, so it is the last word.\n        requestAnimationFrame(() => {\n          if (!video.isConnected || currentItem.get(cardIndex) !== index) return\n          video.preload = 'auto'\n          if (video.paused) video.play().catch(() => {})\n        })\n      }\n\n      // Polled instead of listening for a single event: the `currentTime = 0`\n      // above starts a seek that drops readyState below 2, and 'loadeddata'\n      // never fires a second time on an already-loaded video, so waiting for\n      // it left every video stacked and visible at once.\n      let waited = 0\n      const revealWhenReady = () => {\n        // Arrived late: another item was requested, or the card closed and\n        // its videos were destroyed\n        if (currentItem.get(cardIndex) !== index || !video.isConnected) return\n        if (video.readyState < 2) {\n          // Only on-screen time counts against the budget. Off screen the\n          // video is paused, so readyState is frozen and the timeout used to\n          // run out while nobody was looking: the reveal gave up for good and\n          // the card came back showing the image with the video playing\n          // invisibly behind it.\n          if (isCardVisible(card) && (waited += 100) > 20000) {\n            mdlog('GAVE UP revealing card', cardIndex + 1, 'item', index + 1,\n              mdVid(video), 'after 20s', mdVidState(video))\n            return\n          }\n          setTimeout(revealWhenReady, 100)\n          return\n        }\n        mdlog('REVEALED card', cardIndex + 1, 'item', index + 1, mdVid(video),\n          'after', (mdNow() - mdSwitchAt).toFixed(2) + 's', mdVidState(video))\n        video.style.zIndex = '2'\n        ownVideos.forEach((v) => {\n          if (v === video) return\n          v.pause()\n          v.style.display = 'none'\n          v.style.zIndex = '2'\n        })\n        images.forEach((img) => {\n          gsap.killTweensOf(img)\n          gsap.to(img, { opacity: 0, duration: 0.3, ease: 'power2.inOut' })\n        })\n      }\n\n      revealWhenReady()\n    } else {\n      // No video for this item: turn the videos off, the image takes over\n      ownVideos.forEach((v) => {\n        v.pause()\n        v.style.display = 'none'\n        v.style.zIndex = '2'\n      })\n      // No video — fall back to image crossfade\n      images.forEach((img) => {\n        gsap.killTweensOf(img)\n        const show = index === 0\n          ? img.classList.contains('is-main')\n          : img.classList.contains(`is-${index + 1}`)\n        gsap.to(img, { opacity: show ? 1 : 0, duration: 0.4, ease: 'power2.inOut' })\n      })\n    }\n  }\n\n  allCards.forEach((card, cardIndex) => {\n    const cardTop = card.querySelector('.product-overview_card-top')\n    const dropdown = card.querySelector('.product-overview_card-tablet')\n    if (!cardTop || !dropdown) return\n\n    // Accordion toggle\n    cardTop.addEventListener('click', () => {\n      if (openCard === card) {\n        mdlog('TAP card', cardIndex + 1, '-> closing it')\n        closeCard(card)\n        openCard = null\n      } else {\n        mdlog('TAP card', cardIndex + 1, '-> opening it | previously open:',\n          openCard ? [...allCards].indexOf(openCard) + 1 : 'none',\n          '| on screen?', isCardVisible(card))\n        // Instant, so the card below is already at its final offset\n        if (openCard) closeCard(openCard, false)\n        // Marked visible up front: the observer only reports asynchronously,\n        // so at this instant the set can still be empty and a deliberate tap\n        // would open a card that never plays. The scroll below makes it true,\n        // and the observer corrects it either way.\n        visibleCards.add(card)\n        openCard = card\n        openCardDropdown(card, cardIndex)\n        scrollCardIntoView(card)\n      }\n    }, { signal: _signal })\n\n    // Text item tap → switch image/video (first item keeps main image)\n    const textItems = dropdown.querySelectorAll('.product_modal-tab-text-item')\n    textItems.forEach((item, index) => {\n      item.addEventListener('click', (e) => {\n        e.stopPropagation()\n        // A manual tap restarts the chain from the chosen item\n        setCardItem(card, cardIndex, index)\n      }, { signal: _signal })\n    })\n  })\n\n  // First card starts open (no animation on load)\n  const firstCard = allCards[0]\n  if (firstCard && firstCard.querySelector('.product-overview_card-tablet')) {\n    openCardDropdown(firstCard, 0, false)\n    openCard = firstCard\n  }\n\n  // Tells the <head> snippet the accordion took over. That snippet paints the\n  // collapsed state before this file arrives -- otherwise the first paint shows\n  // all three cards expanded with no active item, for around 370 ms on a\n  // throttled mobile connection -- and it drops the class if this flag never\n  // shows up, so a failed or blocked script can never leave the cards shut.\n  window.__mdAccordionReady = true\n  mdlog('accordion ready | open card 1 | cards', allCards.length)\n\n  return () => {\n    _ac.abort()\n    stopCardAutoAdvance()\n    if (cardObserver) cardObserver.disconnect()\n    visibleCards.clear()\n    currentItem.clear()\n    warmed.clear()\n    warming = null\n    openCard = null\n    // Destroy all dynamic videos\n    for (const [, video] of cardVideos) {\n      video.pause()\n      video.remove()\n    }\n    cardVideos.clear()\n    // Reset: se borran los estilos inline (alturas en px, clip-paths, stacking de\n    // imagenes) para que al pasar a desktop manden el CSS y el otro contexto\n    allCards.forEach((card) => {\n      const dropdown = card.querySelector('.product-overview_card-tablet')\n      if (dropdown) {\n        gsap.killTweensOf(dropdown)\n        gsap.set(dropdown, { clearProps: 'height,overflow,visibility' })\n        dropdown.querySelectorAll('.product_modal-tab-text-item').forEach((t) => {\n          gsap.killTweensOf(t)\n          gsap.set(t, { clearProps: 'clipPath' })\n        })\n      }\n      const cta = card.querySelector('.product-overview_card-tablet .product_modal-cta')\n      if (cta) {\n        gsap.killTweensOf(cta)\n        gsap.set(cta, { clearProps: 'clipPath' })\n      }\n      const imageWrapper = card.querySelector('.product-overview_card-image-wrapper')\n      if (imageWrapper) imageWrapper.style.position = ''\n      card.querySelectorAll('.product-overview_card-image').forEach((img) => {\n        gsap.killTweensOf(img)\n        gsap.set(img, { clearProps: 'opacity,display,position,top,left,width,height,objectFit' })\n      })\n      const icon = card.querySelector('.icon_plus')\n      if (icon) {\n        gsap.killTweensOf(icon)\n        gsap.set(icon, { clearProps: 'rotation,transform' })\n      }\n    })\n  }\n})\n\n// ------------------------\n// Number\n// ------------------------\n\n// Number Counter with GSAP + ScrollTrigger\n// Supports multiple data-component=\"counter\" per page\n// Data attributes:\n// data-component=\"counter\" → wrapper that enables the script\n// data-counter=\"value\"     → target number (e.g., 5000)\n// data-counter-duration    → duration in seconds (optional, default: 2)\n// data-counter-suffix      → suffix like \"+\" or \"%\" (optional)\n// data-counter-prefix      → prefix like \"$\" (optional)\n\ndocument.addEventListener(\"DOMContentLoaded\", () => {\n  const components = document.querySelectorAll('[data-component=\"counter\"]');\n  if (!components.length) return;\n\n  components.forEach((component) => {\n    const triggerEl = component.querySelector(\".our-story_stats\") || component;\n    const counters = component.querySelectorAll(\"[data-counter]\");\n\n    // Set initial text with prefix/suffix and hide\n    counters.forEach((el) => {\n      const suffix = el.getAttribute(\"data-counter-suffix\") || \"\";\n      const prefix = el.getAttribute(\"data-counter-prefix\") || \"\";\n      const target = parseFloat(el.getAttribute(\"data-counter\")) || 0;\n      const hasDecimals = target % 1 !== 0;\n      el.textContent = `${prefix}${hasDecimals ? \"0.0\" : \"0\"}${suffix}`;\n    });\n    gsap.set(counters, { opacity: 0, y: 12 });\n\n    counters.forEach((el, index) => {\n      const target = parseFloat(el.getAttribute(\"data-counter\")) || 0;\n      const duration = parseFloat(el.getAttribute(\"data-counter-duration\")) || 2;\n      const suffix = el.getAttribute(\"data-counter-suffix\") || \"\";\n      const prefix = el.getAttribute(\"data-counter-prefix\") || \"\";\n      const hasDecimals = target % 1 !== 0;\n      const staggerDelay = index * 0.15;\n\n      // Fade in\n      gsap.to(el, {\n        opacity: 1,\n        y: 0,\n        duration: 0.6,\n        delay: staggerDelay,\n        ease: \"power2.out\",\n        scrollTrigger: {\n          trigger: triggerEl,\n          start: \"top 40%\",\n          toggleActions: \"play none none none\",\n        },\n      });\n\n      // Count up\n      const obj = { val: 0 };\n      gsap.to(obj, {\n        val: target,\n        duration: duration,\n        ease: \"power2.out\",\n        delay: staggerDelay,\n        scrollTrigger: {\n          trigger: triggerEl,\n          start: \"top 50%\",\n          toggleActions: \"play none none none\",\n        },\n        onUpdate: () => {\n          const formatted = hasDecimals\n            ? obj.val.toFixed(1)\n            : Math.round(obj.val).toLocaleString(\"en-US\");\n          el.textContent = `${prefix}${formatted}${suffix}`;\n        },\n      });\n    });\n  });\n\n  // Team list staggered fade-in\n  const teamItems = document.querySelectorAll(\".team_item\");\n  if (teamItems.length) {\n    gsap.set(teamItems, { opacity: 0, scale: 0.8 });\n\n    gsap.to(teamItems, {\n      opacity: 1,\n      scale: 1,\n      duration: 0.8,\n      stagger: 0.15,\n      ease: \"power2.out\",\n      scrollTrigger: {\n        trigger: \".team_list\",\n        start: \"top 80%\",\n        toggleActions: \"play none none none\",\n      },\n    });\n  }\n});\n\n// ------------------------\n// Reference Link Toggle\n// ------------------------\ndocument.querySelectorAll('.reference_link').forEach((link) => {\n  const quotes = link.closest('.chart_reference-wrapper')?.querySelector('.chart_quotes')\n    || link.parentElement?.querySelector('.chart_quotes')\n    || document.querySelector('.chart_quotes');\n  if (!quotes) return;\n\n  // Start closed\n  gsap.set(quotes, { height: 0, overflow: 'hidden', opacity: 0 });\n\n  let isOpen = false;\n\n  link.addEventListener('click', () => {\n    const icons = link.querySelectorAll('.reference_link-icon-inner');\n    icons.forEach((icon) => icon.classList.toggle('is-active'));\n\n    if (!isOpen) {\n      isOpen = true;\n      gsap.set(quotes, { height: 'auto' });\n      const fullHeight = quotes.scrollHeight;\n      gsap.fromTo(quotes,\n        { height: 0, opacity: 0 },\n        { height: fullHeight, opacity: 1, duration: 0.4, ease: 'power2.out',\n          onComplete: () => gsap.set(quotes, { height: 'auto', overflow: 'visible' })\n        }\n      );\n    } else {\n      isOpen = false;\n      gsap.to(quotes, {\n        height: 0, opacity: 0, duration: 0.35, ease: 'power2.in',\n        onComplete: () => gsap.set(quotes, { overflow: 'hidden' })\n      });\n    }\n  });\n});\n\n// ------------------------\n// Target Grid — Entrance Animation\n// ------------------------\n;(function () {\n  const list = document.querySelector('.target_list-item')\n  if (!list) return\n  const items = list.querySelectorAll('.target_item')\n  if (items.length < 9) return\n\n  // Desktop: Spiral entrance (3x3 grid)\n  mm.add('(min-width: 992px)', () => {\n    const spiralOrder = [0, 1, 2, 5, 8, 7, 6, 3, 4]\n\n    spiralOrder.forEach((domIndex) => {\n      if (domIndex === 0) return\n      gsap.set(items[domIndex], { opacity: 0, scale: 0.85 })\n      const tag = items[domIndex].querySelector('.target_tag')\n      if (tag) gsap.set(tag, { clipPath: 'inset(0 100% 0 0)' })\n    })\n\n    const tl = gsap.timeline({\n      scrollTrigger: {\n        trigger: list,\n        start: 'top 50%',\n        toggleActions: 'play none none none',\n      },\n    })\n\n    spiralOrder.forEach((domIndex, i) => {\n      if (domIndex === 0) return\n      const tag = items[domIndex].querySelector('.target_tag')\n      const offset = i * 0.2\n\n      tl.to(items[domIndex], {\n        opacity: 1,\n        scale: 1,\n        duration: 0.6,\n        ease: 'power2.out',\n      }, offset)\n\n      if (tag) {\n        tl.to(tag, {\n          clipPath: 'inset(0 0% 0 0)',\n          duration: 0.5,\n          ease: 'power3.out',\n        }, offset + 0.45)\n      }\n    })\n\n    return () => {\n      tl.kill()\n      items.forEach((item) => {\n        gsap.set(item, { opacity: 1, scale: 1 })\n        const tag = item.querySelector('.target_tag')\n        if (tag) gsap.set(tag, { clipPath: 'inset(0 0% 0 0)' })\n      })\n    }\n  })\n\n  // Tablet / Mobile / Landscape: Row-by-row pairs (2-column grid)\n  mm.add('(max-width: 991px)', () => {\n    // Sequential order, pairs per row: [0,1], [2,3], [4,5], [6,7], [8]\n    for (let i = 1; i < items.length; i++) {\n      gsap.set(items[i], { opacity: 0, scale: 0.85 })\n      const tag = items[i].querySelector('.target_tag')\n      if (tag) gsap.set(tag, { clipPath: 'inset(0 100% 0 0)' })\n    }\n\n    const tl = gsap.timeline({\n      scrollTrigger: {\n        trigger: list,\n        start: 'top 60%',\n        toggleActions: 'play none none none',\n      },\n    })\n\n    for (let i = 1; i < items.length; i++) {\n      const tag = items[i].querySelector('.target_tag')\n      // Items in the same row (pair) share the same offset\n      const row = Math.floor(i / 2)\n      const offset = row * 0.2\n\n      tl.to(items[i], {\n        opacity: 1,\n        scale: 1,\n        duration: 0.5,\n        ease: 'power2.out',\n      }, offset)\n\n      if (tag) {\n        tl.to(tag, {\n          clipPath: 'inset(0 0% 0 0)',\n          duration: 0.45,\n          ease: 'power3.out',\n        }, offset + 0.35)\n      }\n    }\n\n    return () => {\n      tl.kill()\n      items.forEach((item) => {\n        gsap.set(item, { opacity: 1, scale: 1 })\n        const tag = item.querySelector('.target_tag')\n        if (tag) gsap.set(tag, { clipPath: 'inset(0 0% 0 0)' })\n      })\n    }\n  })\n})()"]}