/* ============================================================================
   GOG 3D CYLINDER CAROUSEL
   Adapted from motionsites.ai bank-card carousel prompt.
   Rewritten for GOG streetwear: blackletter aesthetic, ember accent.

   Mount points:
   - #react-carousel-root (index.html — uses baked-in PRODUCTS)
   - #shop-carousel-root  (shop.html — loads from /api/products, full catalog)

   Tap card → navigate to product.html?slug=xxx
   Add-to-cart pill  → window.gogCart.add()
   Touch/swipe: vertical drag rotates the cylinder with inertia
   ============================================================================ */

const { useState, useEffect, useRef } = React;

// ---- Fallback product catalog (used on homepage when API not fetched) ----
const DEFAULT_PRODUCTS = [
  {
    id: 'golgotha-hoodie',
    name: 'Golgotha Hoodie',
    variant: 'Obsidian',
    price: 128,
    badge: 'New',
    image: 'img/brand-clean-card.jpg',
    photoPending: true,
    spec: '12 OZ HEAVYWEIGHT',
    fit: 'OVERSIZED · BOXY',
    detail: 'SOLID SCREEN PRINT · BLACKLETTER PRINT',
    material: 'RING-SPUN COTTON FLEECE',
    accent: '#b8391a',
  },
  {
    id: 'monogram-tee',
    name: 'Monogram Tee',
    variant: 'Obsidian',
    price: 58,
    badge: 'Signature',
    image: 'img/brand-clean-card.jpg',
    photoPending: true,
    spec: '8 OZ HEAVYWEIGHT',
    fit: 'BOXY · DROP-SHOULDER',
    detail: 'CENTER-CHEST MONOGRAM',
    material: 'GARMENT-DYED COTTON',
    accent: '#b8391a',
  },
  {
    id: 'ash-hoodie',
    name: 'Ash Hoodie',
    variant: 'Bone White',
    price: 138,
    badge: 'Limited',
    image: 'img/brand-clean-card.jpg',
    photoPending: true,
    spec: '12 OZ HEAVYWEIGHT',
    fit: 'OVERSIZED · SUN-FADED',
    detail: 'HAND-WASHED · SOLID PRINT',
    material: 'RING-SPUN COTTON FLEECE',
    accent: '#b8391a',
  },
  {
    id: 'iron-snapback',
    name: 'Iron Snapback',
    variant: 'Black',
    price: 42,
    badge: null,
    image: 'img/brand-clean-card.jpg',
    photoPending: true,
    spec: '6-PANEL · STRUCTURED',
    fit: 'ADJUSTABLE · CLASSIC',
    detail: 'BLACKLETTER EMBROIDERY',
    material: 'BRUSHED COTTON TWILL',
    accent: '#b8391a',
  },
];

// Normalize API product shape to component shape
function normalizeApiProduct(p) {
  // API shape: { slug, name, price, image_url, badge, hint, description, in_stock }
  // Component shape adds: variant, spec, fit, material, detail
  // We synthesize sensible defaults from hint/description when API doesn't have them
  const hint = (p.hint || '').toString();
  const description = (p.description || '').toString();

  // Split on periods, middle-dots, bullets, pipes, newlines — pick shortest
  const parts = description
    .split(/[.\u00b7\u2022|\n]+/)
    .map((s) => s.trim())
    .filter(Boolean);

  function pickShort(arr, max) {
    for (let i = 0; i < arr.length; i++) {
      if (arr[i].length <= max) return arr.splice(i, 1)[0];
    }
    return null;
  }
  const spec = pickShort(parts, 30) || parts.shift() || hint || '12 oz heavyweight';
  const fit = pickShort(parts, 30) || parts.shift() || 'Oversized \u00b7 boxy';
  const material = pickShort(parts, 30) || parts.shift() || 'Ring-spun cotton';
  const detail = parts.join(' \u00b7 ') || 'Solid screen print';

  return {
    id: p.slug || p.id || String(p.name || '').toLowerCase().replace(/\s+/g, '-'),
    slug: p.slug,
    name: p.name || 'Untitled',
    variant: p.variant || (hint.includes('\u00b7') ? hint.split('\u00b7')[0].trim() : hint || 'Obsidian'),
    price: Number(p.price || 0).toFixed(2),
    badge: p.badge || null,
    image: p.image_url || p.image || 'img/brand-clean-card.jpg',
    photoPending: !(p.image_url || p.image),
    spec: spec.toUpperCase(),
    fit: fit.toUpperCase(),
    detail: detail.toUpperCase(),
    material: material.toUpperCase(),
    inStock: p.in_stock !== false,
    accent: '#b8391a',
  };
}

function GogCarousel(props) {
  const products = (props && props.products && props.products.length) ? props.products : DEFAULT_PRODUCTS;
  const cardCount = products.length;
  const cardsRefs = useRef([]);
  const frameId = useRef(0);
  const progress = useRef(0);
  const mouse = useRef({ x: 0, y: 0, targetX: 0, targetY: 0 });
  const isPaused = useRef(false);
  const rootEl = useRef(null);
  const stageEl = useRef(null);

  // Touch/swipe state — vertical drag rotates the cylinder
  const drag = useRef({
    active: false,
    startY: 0,
    startProgress: 0,
    lastY: 0,
    lastT: 0,
    velocity: 0,
    pointerId: null,
  });
  const inertia = useRef({ active: false, v: 0 });

  // Trigger a re-render for nav dots when active card changes
  const [activeDot, setActiveDot] = useState(0);
  const lastRenderedActive = useRef(0);

  const [metrics, setMetrics] = useState({ cardW: 336, cardH: 431 });
  const [reducedMotion, setReducedMotion] = useState(false);

  // Detect prefers-reduced-motion
  useEffect(() => {
    const mq = window.matchMedia('(prefers-reduced-motion: reduce)');
    setReducedMotion(mq.matches);
    const handler = () => setReducedMotion(mq.matches);
    mq.addEventListener('change', handler);
    return () => mq.removeEventListener('change', handler);
  }, []);

  // Mouse tracking scoped to carousel (not whole window) so it doesn't fight
  // with the rest of the page
  useEffect(() => {
    const el = rootEl.current;
    if (!el) return;

    const handleMouseMove = (e) => {
      const rect = el.getBoundingClientRect();
      const cx = rect.left + rect.width / 2;
      const cy = rect.top + rect.height / 2;
      const rx = (e.clientX - cx) / (rect.width / 2);
      const ry = (e.clientY - cy) / (rect.height / 2);
      mouse.current.targetX = Math.max(-1, Math.min(1, rx));
      mouse.current.targetY = Math.max(-1, Math.min(1, ry));
    };
    const handleMouseLeave = () => {
      mouse.current.targetX = 0;
      mouse.current.targetY = 0;
    };
    const handleEnter = () => { isPaused.current = false; };
    const handleLeave = () => {
      isPaused.current = false; // still auto-rotates
      handleMouseLeave();
    };

    el.addEventListener('mousemove', handleMouseMove);
    el.addEventListener('mouseenter', handleEnter);
    el.addEventListener('mouseleave', handleLeave);
    return () => {
      el.removeEventListener('mousemove', handleMouseMove);
      el.removeEventListener('mouseenter', handleEnter);
      el.removeEventListener('mouseleave', handleLeave);
    };
  }, []);

  // Responsive sizing — square-ish cards for product photos (1:1.28 for hoodie hero shots)
  useEffect(() => {
    const handleResize = () => {
      const w = window.innerWidth;
      const h = window.innerHeight;
      let cardW = Math.round(w * 0.22 + 90);
      const heightFactor = Math.min(1.0, Math.max(0.62, h / 900));
      cardW = Math.round(cardW * heightFactor);
      cardW = Math.min(360, Math.max(180, cardW));
      const cardH = Math.round(cardW * 1.28); // taller than credit-card ratio — showcases apparel
      setMetrics({ cardW, cardH });
    };
    handleResize();
    window.addEventListener('resize', handleResize);
    return () => window.removeEventListener('resize', handleResize);
  }, []);

  // Render loop — same core math as original prompt, tuned for 4 cards + apparel ratio
  const renderLoop = () => {
    if (!isPaused.current) {
      progress.current += reducedMotion ? 0 : 0.0016;
    }
    mouse.current.x += (mouse.current.targetX - mouse.current.x) * 0.08;
    mouse.current.y += (mouse.current.targetY - mouse.current.y) * 0.08;

    const cards = cardsRefs.current;
    // Use STAGE height (not full carousel wrapper) so the 3D math accounts for the
    // controls row at the bottom. Falls back to wrapper height, then window height.
    const h = (stageEl.current && stageEl.current.clientHeight) ||
              (rootEl.current && rootEl.current.clientHeight) ||
              window.innerHeight;
    const { cardH } = metrics;

    // Inertia — continue rotation after swipe with exponential decay
    if (inertia.current.active && !drag.current.active) {
      progress.current += inertia.current.v;
      inertia.current.v *= 0.94; // decay
      if (Math.abs(inertia.current.v) < 0.001) {
        // Snap to nearest card
        inertia.current.active = false;
        inertia.current.v = 0;
        progress.current = Math.round(progress.current);
      }
    }

    // Snap to nearest when no drag, no inertia, and not auto-rotating (reduced motion)
    // We still let auto-rotate flow through in normal mode.

    // Update active dot state only when it actually changes (avoid re-render churn)
    const currentActive = ((Math.round(progress.current) % cardCount) + cardCount) % cardCount;
    if (currentActive !== lastRenderedActive.current) {
      lastRenderedActive.current = currentActive;
      setActiveDot(currentActive);
    }

    const continuousProgress = progress.current;
    const roundedIndex = Math.round(continuousProgress);
    const diffFromRound = continuousProgress - roundedIndex;
    const easedDiff = Math.sign(diffFromRound) * Math.pow(Math.abs(diffFromRound) * 2, 4.2) / 2;
    const virtualActiveIndex = roundedIndex + easedDiff;

    for (let i = 0; i < cardCount; i++) {
      const card = cards[i];
      if (!card) continue;

      let offset = i - virtualActiveIndex;
      const halfCount = cardCount / 2;
      while (offset > halfCount) offset -= cardCount;
      while (offset < -halfCount) offset += cardCount;

      const absOffset = Math.abs(offset);
      const sign = Math.sign(offset);

      if (absOffset > 3.0) {
        card.style.visibility = 'hidden';
        continue;
      } else {
        card.style.visibility = 'visible';
      }

      const gap = 42;
      const peekAmount = -55;
      const D = 1350;

      let y = 0, z = 0, rot = 0;

      if (absOffset <= 1) {
        const t = absOffset;
        const easedT = t * t * (3 - 2 * t);
        const targetY = cardH + gap;
        y = -sign * (easedT * targetY);
        z = 400 + easedT * (220 - 400);
        rot = easedT * 132;
      } else if (absOffset <= 2) {
        const t = absOffset - 1;
        const easedT = t * t * (3 - 2 * t);
        const yStart = cardH + gap;
        const zStart = 220;
        const rotStart = 132;
        const zEnd = -60;
        const rotEnd = 175;
        const sEnd = D / (D - zEnd);
        const yEnd = (h / 2 - peekAmount) / sEnd - (cardH / 2);
        const currentY = yStart + easedT * (yEnd - yStart);
        y = -sign * currentY;
        z = zStart + easedT * (zEnd - zStart);
        rot = rotStart + easedT * (rotEnd - rotStart);
      } else {
        const t = Math.min(absOffset - 2, 1);
        const easedT = t * t * (3 - 2 * t);
        const zStart = -60;
        const rotStart = 175;
        const zEnd3 = -250;
        const rotEnd3 = 195;
        const sEnd2 = D / (D - zStart);
        const yEnd2 = (h / 2 - peekAmount) / sEnd2 - (cardH / 2);
        const sEnd3 = D / (D - zEnd3);
        const yEnd3 = (h / 2 + 100) / sEnd3 + (cardH / 2);
        const currentY = yEnd2 + easedT * (yEnd3 - yEnd2);
        y = -sign * currentY;
        z = zStart + easedT * (zEnd3 - zStart);
        rot = rotStart + easedT * (rotEnd3 - rotStart);
      }

      const localCardRotation = -sign * rot;
      const centerFactor = Math.max(0, 1 - absOffset);
      const maxTiltY = 15;
      const maxTiltX = 12;
      const activeTiltX = -mouse.current.y * maxTiltX * centerFactor;
      const activeTiltY = mouse.current.x * maxTiltY * centerFactor;
      const totalRotX = localCardRotation + activeTiltX;
      const totalRotY = activeTiltY;

      card.style.zIndex = Math.round(z).toString();
      card.style.opacity = '1';
      card.style.transform = `translateY(${y.toFixed(2)}px) translateZ(${z.toFixed(2)}px) rotateX(${totalRotX.toFixed(2)}deg) rotateY(${totalRotY.toFixed(2)}deg) rotateZ(-3deg)`;
    }
  };

  useEffect(() => {
    const tick = () => {
      renderLoop();
      frameId.current = requestAnimationFrame(tick);
    };
    frameId.current = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(frameId.current);
  }, [metrics, reducedMotion]);

  // ---- Advance controls ----
  const advance = (dir) => {
    // Stop any inertia when the user presses a nav button
    inertia.current.active = false;
    inertia.current.v = 0;
    progress.current += dir;
  };

  // ---- Add-to-cart pill (ember button) ----
  const addToCart = (product, e) => {
    e.stopPropagation();
    if (window.gogCart && typeof window.gogCart.add === 'function') {
      window.gogCart.add({
        id: product.slug || product.id,
        title: `${product.name} \u00b7 ${product.variant}`,
        price: product.price,
      });
    } else {
      console.warn('[gog] cart not ready');
    }
  };

  // ---- Card tap — open product-detail page ----
  const openProduct = (product) => {
    const slug = product.slug || product.id;
    if (!slug) return;
    window.location.href = `product.html?slug=${encodeURIComponent(slug)}`;
  };

  // ---- Touch / pointer drag for vertical swipe ----
  useEffect(() => {
    const el = stageEl.current;
    if (!el) return;

    // We attach to stageEl (the perspective container). It has pointerEvents:'none'
    // but the cards inside are pointerEvents:'auto', so pointer events bubble from
    // the cards to the stage.
    // We use pointer events (unified touch + mouse + pen).

    const onPointerDown = (e) => {
      // Only track primary button / single touch
      if (e.button !== undefined && e.button !== 0) return;
      // Store the initial pointer target (which card was tapped) so we can
      // trigger openProduct on pointerup if no meaningful drag occurred.
      drag.current.active = true;
      drag.current.startY = e.clientY;
      drag.current.startX = e.clientX;
      drag.current.startProgress = progress.current;
      drag.current.lastY = e.clientY;
      drag.current.lastT = performance.now();
      drag.current.velocity = 0;
      drag.current.pointerId = e.pointerId;
      drag.current.moved = false;
      drag.current.downCard = e.target && e.target.closest ? e.target.closest('.gog-card') : null;
      inertia.current.active = false;
      inertia.current.v = 0;
      isPaused.current = true;
      // NOTE: no setPointerCapture on stageEl — that would prevent the browser
      // from firing a native click event on the tapped card. Pointermove/up on the
      // window instead — we track them below.
    };

    const onPointerMove = (e) => {
      if (!drag.current.active) return;
      if (drag.current.pointerId !== null && e.pointerId !== drag.current.pointerId) return;

      const dy = e.clientY - drag.current.startY;
      const dx = e.clientX - drag.current.startX;
      // Track velocity for inertia (px/ms)
      const now = performance.now();
      const dt = Math.max(1, now - drag.current.lastT);
      const instantV = (e.clientY - drag.current.lastY) / dt; // px/ms
      drag.current.velocity = drag.current.velocity * 0.7 + instantV * 0.3;
      drag.current.lastY = e.clientY;
      drag.current.lastT = now;

      // Convert px → card index. Cards are ~cardH tall in the layout, and one full
      // rotation cycle is ~cardH+gap. Use a divisor that feels natural on touch.
      const cardH = metrics.cardH;
      const swipeUnit = Math.max(180, cardH * 0.55); // px to advance one card
      progress.current = drag.current.startProgress - (dy / swipeUnit);

      // Mark as "moved enough" so we can suppress the click-tap after real drag.
      // Use combined displacement so a small horizontal jiggle doesn't count as a swipe.
      const dist = Math.sqrt(dx*dx + dy*dy);
      if (dist > 8) drag.current.moved = true;

      // Prevent page scroll while actively dragging (only when we've actually moved)
      if (drag.current.moved && e.cancelable) e.preventDefault();
    };

    const onPointerUp = (e) => {
      if (!drag.current.active) return;
      if (drag.current.pointerId !== null && e.pointerId !== drag.current.pointerId) return;

      const wasMoved = drag.current.moved;
      const downCard = drag.current.downCard;
      drag.current.active = false;
      isPaused.current = false;

      // Kick off inertia: convert px/ms velocity into progress delta per frame (16ms)
      const cardH = metrics.cardH;
      const swipeUnit = Math.max(180, cardH * 0.55);
      const framesPerSec = 60;
      const perFrame = (-drag.current.velocity / swipeUnit) * (1000 / framesPerSec);
      inertia.current.v = wasMoved ? perFrame : 0;
      inertia.current.active = wasMoved && Math.abs(perFrame) > 0.002;

      drag.current.pointerId = null;
      drag.current.downCard = null;

      // If no meaningful drag AND the pointer went down on a card, treat as a tap:
      // rotate to that card if peripheral, or open PDP if it's the front card.
      // We do this here (instead of relying on native click) because on some browsers
      // pointerdown+pointerup+pointermove on the stageEl swallows the click event.
      if (!wasMoved && downCard && stageEl.current && stageEl.current.contains(downCard)) {
        const cards = Array.from(stageEl.current.querySelectorAll('.gog-card'));
        const cardIdx = cards.indexOf(downCard);
        if (cardIdx >= 0) {
          const cardCountLocal = cards.length;
          const activeIdx = ((Math.round(progress.current) % cardCountLocal) + cardCountLocal) % cardCountLocal;
          if (activeIdx === cardIdx) {
            // Tap on the front card — open PDP. Look up product from the closure via slug on the img.
            const slug = downCard.getAttribute('data-slug');
            if (slug) window.location.href = 'product.html?slug=' + encodeURIComponent(slug);
          } else {
            // Rotate to the tapped peripheral card
            inertia.current.active = false;
            inertia.current.v = 0;
            progress.current = cardIdx;
          }
        }
      }
    };

    const onWheel = (e) => {
      // Optional: allow scroll wheel to rotate when hovered over carousel.
      // Skip for now — keeps native page scroll behavior intact.
    };

    // pointerdown must originate on the stage/card. Move/up attach to window so
    // dragging off the stage still gets tracked.
    el.addEventListener('pointerdown', onPointerDown);
    window.addEventListener('pointermove', onPointerMove, { passive: false });
    window.addEventListener('pointerup', onPointerUp);
    window.addEventListener('pointercancel', onPointerUp);
    return () => {
      el.removeEventListener('pointerdown', onPointerDown);
      window.removeEventListener('pointermove', onPointerMove);
      window.removeEventListener('pointerup', onPointerUp);
      window.removeEventListener('pointercancel', onPointerUp);
    };
  }, [metrics]);

  const thicknessLayers = [-1.47, -0.73, 0, 0.73, 1.47];

  return React.createElement(
    'div',
    {
      ref: rootEl,
      className: 'gog-carousel',
      style: {
        position: 'relative',
        width: '100%',
        height: 'clamp(700px, 88vh, 920px)',
        background: 'transparent',
        userSelect: 'none',
        display: 'flex',
        flexDirection: 'column',
        alignItems: 'stretch',
      },
    },
    [
      // Perspective stage — 3D cylinder lives here, controls sit BELOW
      React.createElement(
        'div',
        {
          key: 'stage',
          ref: stageEl,
          style: {
            position: 'relative',
            flex: '1 1 auto',
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
            perspective: '1350px',
            pointerEvents: 'none',
            overflow: 'hidden',
          },
        },
        React.createElement(
          'div',
          {
            style: {
              position: 'absolute',
              width: `${metrics.cardW}px`,
              height: `${metrics.cardH}px`,
              transformStyle: 'preserve-3d',
            },
          },
          products.map((product, i) =>
            React.createElement(
              'div',
              {
                key: product.slug || product.id,
                ref: (el) => { cardsRefs.current[i] = el; },
                className: 'gog-card',
                'data-slug': product.slug || product.id,
                style: {
                  position: 'absolute',
                  inset: 0,
                  width: `${metrics.cardW}px`,
                  height: `${metrics.cardH}px`,
                  transformStyle: 'preserve-3d',
                  backfaceVisibility: 'visible',
                  pointerEvents: 'auto',
                  cursor: 'pointer',
                  touchAction: 'pan-x', // allow horizontal page pan; block vertical (we handle it)
                },
                onClick: (e) => {
                  // Suppress the tap if we just dragged
                  if (drag.current.moved) return;
                  const activeIdx = ((Math.round(progress.current) % cardCount) + cardCount) % cardCount;
                  if (activeIdx === i) {
                    // Tapping the FRONT card — open product detail
                    openProduct(product);
                  } else {
                    // Tapping a peripheral card — rotate to it
                    inertia.current.active = false;
                    inertia.current.v = 0;
                    progress.current = i;
                  }
                },
              },
              thicknessLayers.map((zOffset, layerIdx) => {
                const isFrontFace = layerIdx === thicknessLayers.length - 1;
                const isBackFace = layerIdx === 0;

                // Middle structural slice — matte edge ("iron edge")
                if (!isFrontFace && !isBackFace) {
                  return React.createElement('div', {
                    key: layerIdx,
                    style: {
                      position: 'absolute',
                      inset: 0,
                      borderRadius: '18px',
                      border: '1px solid #2a2a2c',
                      background: '#1a1a1c',
                      pointerEvents: 'none',
                      overflow: 'hidden',
                      transform: `translateZ(${zOffset}px)`,
                    },
                  });
                }

                // FRONT FACE — product image + name + price + badge
                if (isFrontFace) {
                  return React.createElement(
                    'div',
                    {
                      key: layerIdx,
                      className: 'gog-card__front',
                      style: {
                        position: 'absolute',
                        inset: 0,
                        borderRadius: '18px',
                        border: '1px solid rgba(255,255,255,0.08)',
                        background: '#0d0c0f',
                        pointerEvents: 'none',
                        overflow: 'hidden',
                        transform: `translateZ(${zOffset}px)`,
                        backfaceVisibility: 'hidden',
                        boxShadow: 'inset 0 1px 1px rgba(255,255,255,0.06), 0 30px 60px -20px rgba(0,0,0,0.7)',
                      },
                    },
                    [
                      // Product image, full bleed
                      React.createElement('img', {
                        key: 'img',
                        src: product.image,
                        alt: product.photoPending ? 'Gangster of God logo; product photography coming soon' : product.name,
                        style: {
                          position: 'absolute',
                          inset: 0,
                          width: '100%',
                          height: '100%',
                          objectFit: 'cover',
                          borderRadius: '18px',
                          filter: 'contrast(1.05) brightness(0.92)',
                        },
                        draggable: false,
                      }),

                      // Subtle top-to-bottom scrim for text legibility
                      React.createElement('div', {
                        key: 'scrim',
                        style: {
                          position: 'absolute',
                          inset: 0,
                          background: 'linear-gradient(180deg, rgba(0,0,0,0.15) 0%, rgba(0,0,0,0) 40%, rgba(0,0,0,0.35) 78%, rgba(0,0,0,0.72) 100%)',
                          pointerEvents: 'none',
                        },
                      }),

                      // Badge top-left
                      product.badge && React.createElement('div', {
                        key: 'badge',
                        style: {
                          position: 'absolute',
                          left: '14px',
                          top: '14px',
                          padding: '5px 10px',
                          background: product.badge === 'Signature' ? 'rgba(20,20,22,0.85)' : product.accent,
                          color: '#f4efe6',
                          fontSize: '10px',
                          letterSpacing: '0.18em',
                          fontWeight: 600,
                          textTransform: 'uppercase',
                          borderRadius: '2px',
                          border: '1px solid rgba(255,255,255,0.1)',
                        },
                      }, product.badge),

                      // Approved clean GOG mark, matching the navigation.
                      React.createElement('img', {
                        key: 'mark',
                        src: 'img/logo-clean-mark.png',
                        alt: 'GOG',
                        style: {
                          position: 'absolute',
                          right: '14px',
                          top: '16px',
                          width: '52px',
                          height: 'auto',
                          objectFit: 'contain',
                        },
                      }),
                      product.photoPending && React.createElement('div', {
                        key: 'photo-note',
                        style: {
                          position: 'absolute',
                          top: '25%',
                          left: '12px',
                          right: '12px',
                          textAlign: 'center',
                          color: '#c9c4bc',
                          fontFamily: 'Inter, sans-serif',
                          fontSize: '12px',
                          lineHeight: 1.5,
                        },
                      }, 'Photo coming soon'),

                      // Product name + price bottom-left
                      React.createElement('div', {
                        key: 'meta',
                        style: {
                          position: 'absolute',
                          left: '16px',
                          right: '16px',
                          bottom: '14px',
                          display: 'flex',
                          justifyContent: 'space-between',
                          alignItems: 'flex-end',
                          gap: '10px',
                          color: '#f0ece2',
                          pointerEvents: 'auto',
                        },
                      }, [
                        React.createElement('div', { key: 'names' }, [
                          React.createElement('div', {
                            key: 'name',
                            style: {
                              fontFamily: '"Cormorant Garamond", Georgia, serif',
                              fontSize: '20px',
                              fontStyle: 'italic',
                              fontWeight: 500,
                              lineHeight: 1.05,
                              letterSpacing: '0.005em',
                              marginBottom: '3px',
                              textShadow: '0 1px 2px rgba(0,0,0,0.5)',
                            },
                          }, product.name),
                          React.createElement('div', {
                            key: 'variant',
                            style: {
                              fontFamily: 'Oswald, sans-serif',
                              fontSize: '9px',
                              letterSpacing: '0.28em',
                              textTransform: 'uppercase',
                              color: 'rgba(240, 236, 226, 0.65)',
                            },
                          }, product.variant),
                        ]),
                        React.createElement('button', {
                          key: 'add',
                          onClick: (e) => addToCart(product, e),
                          style: {
                            fontFamily: 'Oswald, sans-serif',
                            fontSize: '11px',
                            letterSpacing: '0.16em',
                            textTransform: 'uppercase',
                            color: '#f0ece2',
                            background: 'rgba(184, 57, 26, 0.9)',
                            border: '1px solid rgba(255,255,255,0.15)',
                            padding: '8px 12px',
                            borderRadius: '2px',
                            cursor: 'pointer',
                            fontWeight: 600,
                            whiteSpace: 'nowrap',
                          },
                        }, `$${product.price}`),
                      ]),
                    ]
                  );
                }

                // BACK FACE — intentionally empty. Clean matte back so mid-flip looks calm.
                if (isBackFace) {
                  return React.createElement(
                    'div',
                    {
                      key: layerIdx,
                      className: 'gog-card__back',
                      style: {
                        position: 'absolute',
                        inset: 0,
                        borderRadius: '18px',
                        border: '1px solid rgba(255,255,255,0.06)',
                        background: '#0a090c',
                        pointerEvents: 'none',
                        overflow: 'hidden',
                        transform: `translateZ(${zOffset}px) rotateX(180deg)`,
                        backfaceVisibility: 'hidden',
                        boxShadow: 'inset 0 1px 1px rgba(255,255,255,0.04)',
                      },
                    },
                    []
                  );
                }


                return null;
              })
            )
          )
        )
      ),

      // Nav dots + prev/next controls (in-flow BELOW the stage — no overlap possible)
      React.createElement(
        'div',
        {
          key: 'controls',
          className: 'gog-carousel__controls',
          style: {
            flex: '0 0 auto',
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
            gap: '20px',
            padding: '20px 0 6px',
            zIndex: 100,
            pointerEvents: 'auto',
          },
        },
        [
          React.createElement('button', {
            key: 'prev',
            onClick: () => advance(-1),
            'aria-label': 'Previous product',
            style: {
              width: '36px', height: '36px', borderRadius: '50%',
              background: 'rgba(20,20,22,0.75)',
              border: '1px solid rgba(255,255,255,0.15)',
              color: '#f0ece2',
              cursor: 'pointer',
              fontSize: '16px',
              display: 'flex', alignItems: 'center', justifyContent: 'center',
              backdropFilter: 'blur(6px)',
            },
          }, '↑'),
          React.createElement('div', {
            key: 'dots',
            style: { display: 'flex', gap: '10px' },
          }, products.map((_, i) => {
            return React.createElement('button', {
              key: i,
              onClick: () => {
                inertia.current.active = false;
                inertia.current.v = 0;
                progress.current = i;
              },
              'aria-label': `Product ${i + 1}`,
              style: {
                width: activeDot === i ? '22px' : '8px',
                height: '3px',
                borderRadius: '2px',
                border: 'none',
                background: activeDot === i ? '#b8391a' : 'rgba(240,236,226,0.35)',
                cursor: 'pointer',
                transition: 'width 300ms ease, background 300ms ease',
                padding: 0,
              },
            });
          })),
          React.createElement('button', {
            key: 'next',
            onClick: () => advance(1),
            'aria-label': 'Next product',
            style: {
              width: '36px', height: '36px', borderRadius: '50%',
              background: 'rgba(20,20,22,0.75)',
              border: '1px solid rgba(255,255,255,0.15)',
              color: '#f0ece2',
              cursor: 'pointer',
              fontSize: '16px',
              display: 'flex', alignItems: 'center', justifyContent: 'center',
              backdropFilter: 'blur(6px)',
            },
          }, '↓'),
        ]
      ),
    ]
  );
}

// ---- Mount helpers ----
// Homepage: static 4 products baked in.
// Shop page: fetch from /api/products and pass in dynamically.

async function mountHomepageCarousel() {
  const el = document.getElementById('react-carousel-root');
  if (!el) return;
  const reactRoot = ReactDOM.createRoot(el);

  reactRoot.render(React.createElement('p', { style: { textAlign:'center', padding:'80px 24px', color:'#aaa' } }, 'Loading the drop…'));

  // Then upgrade to live DB products in the background.
  try {
    const res = await fetch('/api/products', { cache: 'no-store' });
    if (!res.ok) throw new Error('Catalog unavailable');
    const raw = await res.json();
    const products = raw.map(normalizeApiProduct);
    if (products.length) {
      reactRoot.render(React.createElement(GogCarousel, { products }));
    } else reactRoot.render(React.createElement('p', { style: { textAlign:'center', padding:'80px 24px' } }, 'New drop coming soon.'));
  } catch (err) {
    reactRoot.render(React.createElement('p', { style: { textAlign:'center', padding:'80px 24px' } }, 'The collection is temporarily unavailable. Please refresh to try again.'));
  }
}

async function mountShopCarousel() {
  const el = document.getElementById('shop-carousel-root');
  if (!el) return;
  const reactRoot = ReactDOM.createRoot(el);

  // Show a subtle loading state first
  reactRoot.render(
    React.createElement('div', {
      style: {
        minHeight: '640px',
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'center',
        color: 'rgba(240,236,226,0.4)',
        fontFamily: 'Oswald, sans-serif',
        fontSize: '12px',
        letterSpacing: '0.3em',
        textTransform: 'uppercase',
      },
    }, 'Loading the drop\u2026')
  );

  try {
    const raw = await window.gogShopCatalog;
    function renderCategory() {
    const products = window.gogFilterProducts(raw).map(normalizeApiProduct);
    if (!products.length) {
      reactRoot.render(
        React.createElement('div', {
          style: {
            minHeight: '640px',
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
            color: 'rgba(240,236,226,0.5)',
            fontStyle: 'italic',
          },
        }, window.gogShopCategory ? 'No products in this category yet. Check back for the next drop.' : 'New drop coming soon.')
      );
      return;
    }
    reactRoot.render(React.createElement(GogCarousel, { products, key: window.gogShopCategory || 'all' }));
    }
    renderCategory();
    window.addEventListener('gog:category-change', renderCategory);
  } catch (err) {
    reactRoot.render(React.createElement('p', { style: { textAlign:'center', padding:'80px 24px' } }, 'The collection is temporarily unavailable. Please refresh to try again.'));
  }
}

function mountAll() {
  mountHomepageCarousel();
  mountShopCarousel();
}

if (document.readyState === 'loading') {
  document.addEventListener('DOMContentLoaded', mountAll);
} else {
  mountAll();
}
