// Wov3 — data + shared components
// Exposes: PRODUCTS, FEATURES, Nav, Footer, ScrollReveal, CtaBanner, Hero, useScrollReveal

// ---------- Data (real Wov3/Pyxl copy from reference) ----------
const ATHLETES = [
  {
    name: "Jadarian Price",
    team: "Notre Dame Football · Running Back",
    quote: "I couldn't believe the comfort level until I tried the WV1. After slipping them on, I immediately understood the hype. I love switching to these slides after practice and games — they're a game-changer for recovery.",
    image: "assets/athlete-jadarian-price.jpg",
  },
  {
    name: "Bree Bates",
    team: "Michigan Track",
    quote: "Being a track athlete I'm always searching for comfort-based footwear options and WV1 is the perfect combination of style and comfort that gives me everything I desire in a shoe. Out of all my options these are my go-to.",
    image: "assets/athlete-bree-bates.png",
  },
  {
    name: "Charles Du",
    team: "Notre Dame Football · Cornerback",
    quote: "WV1 is unbelievably comfy — they're the first thing I put on after a game. The instant relief and cushioning help me unwind both physically and mentally, making it easier to relax and recover.",
    image: "assets/athlete-charles-du.jpg",
  },
  {
    name: "Ethan Edwards",
    team: "Michigan Ice Hockey",
    quote: "As a hockey player, my feet take a beating, so finding a comfortable recovery slide is huge. The WV-1 molds to my feet perfectly, giving me the support and cushioning I need to recover after a long day on the ice.",
    image: "assets/athlete-ethan-edwards.jpg",
  },
  {
    name: "Nolan Miller",
    team: "Michigan Men's Soccer · Captain",
    quote: "WV1 has been up there in terms of my favorite purchases lately. There's nothing better than finishing my workouts for the day, slipping my slides on, and letting my body comfortably recover.",
    image: "assets/athlete-nolan-miller.jpg",
  },
  {
    name: "Nick Butler-Simpson",
    team: "Michigan Track",
    quote: "I've had the WV1s for a while now, and I let all my teammates try them — every single one of them loved it. The moment I put them on, I never wanted to take them off. The comfort and recovery support are unmatched.",
    image: "assets/athlete-nick-butler.png",
  },
];

const PARTNERS = [
  { src: "assets/partner-1.png",            name: "TU/e Sports Lab" },
  { src: "assets/partner-2.jpg",            name: "Polly Polymer" },
  { src: "assets/partner-3.png",            name: "Omnia" },
  { src: "assets/partner-4.png",            name: "Upthere Sports" },
  { src: "assets/partner-hacker-dojo.png",  name: "Hacker Dojo" },
];

const PRODUCTS = [
  {
    sku: "WV-1",
    skuLabel: "WV-1 Recovery Slides",
    name: "Premium Recovery Footwear",
    image: "assets/wv1-side.webp",
    shopUrl: "https://wov3.com/products/wv-1",
    short: "Wov3 lattice slides engineered for post-training pressure relief and rapid recovery.",
    long: "The WV-1 Recovery Slide pairs a tuned 3D-printed lattice with anatomical contouring to relieve plantar pressure after training. Designed to be worn in the locker room, the recovery suite, or anywhere your body needs to reset.",
    specs: [
      ["Sole", "Wov3 Lattice TPU"],
      ["Drop", "8 mm"],
      ["Weight", "212 g / size 9"],
      ["Use", "Post-training, daily wear"],
    ],
  },
  {
    sku: "WV-2",
    skuLabel: "WV-2 Running Shoes",
    name: "Performance Recovery Design",
    image: "assets/wv2.jpg?v=2",
    shopUrl: "https://wov3.com/products/wv2",
    short: "Distance running shoes with a 3D-printed midsole that distributes force at every step.",
    long: "Wov3 micro-lattice geometry replaces traditional foam in the midsole. The result is a responsive ride that holds its shape over hundreds of kilometers, designed for athletes who train hard and recover harder.",
    specs: [
      ["Midsole", "Open Lattice 3D"],
      ["Drop", "6 mm"],
      ["Weight", "248 g / size 9"],
      ["Use", "Daily mileage, long runs"],
    ],
  },
  {
    sku: "WV-3",
    skuLabel: "WV-3 Massage Clogs",
    name: "Acupressure Recovery Technology",
    image: "assets/wv3-massage-clog.webp",
    shopUrl: "https://wov3.com/products/wv-3",
    short: "Textured stimulation nodes inspired by acupressure principles for parasympathetic recovery.",
    long: "Each Massage Clog footbed is mapped with stimulation nodes positioned along key reflexology zones. Slip them on after a long day to ease tension, lower stress response, and signal the body that it's safe to recover.",
    specs: [
      ["Footbed", "Textured Wov3 Mesh"],
      ["Closure", "Slip-on"],
      ["Weight", "186 g / size 9"],
      ["Use", "Home, recovery, downtime"],
    ],
  },
  {
    sku: "CUSTOM",
    skuLabel: "Custom 3D Insoles",
    name: "Personalized Arch Support",
    image: "assets/custom-insole-3d.png",
    gallery: [
      "assets/custom-insole-3d.png",
      "assets/custom-insole-detail-1.jpg?v=2",
      "assets/custom-insole-detail-2.jpg?v=3",
      "assets/custom-insole-detail-3.jpg",
    ],
    short: "Scanned, modeled, and printed to your arch profile for tailored biomechanical alignment.",
    long: "Scanning captures the geometry of your foot in seconds. We model the lattice density to your gait, then print a tailored insole that adapts to your arch, balance, and force transfer needs.",
    specs: [
      ["Material", "Custom Lattice"],
      ["Process", "3D Scan + Print"],
      ["Use", "Daily, recovery, training"],
    ],
  },
];

const FEATURES = [
  {
    title: "2,500 Independent Support Points",
    lead: "Pressure dispersed, not absorbed.",
    body: "A woven TPU lattice cradles each footstep across thousands of micro-cells instead of one flat foam slab. Pressure that used to land in one spot now spreads through the whole sole — so your arches, heels and joints stop carrying the day on their own.",
    image: "assets/feature-support-points.jpeg",
  },
  {
    title: "Acupressure Nervous System Stimulation",
    lead: "A direct line to your \u201Crest and digest\u201D mode.",
    body: "The sole of the foot is one of the most sensitive places on the body. Soft pressure nodes in the WV-3 footbed gently meet the reflex points reflexology traditions have worked with for centuries \u2014 a quiet cue for the body to soften, exhale, and drop out of fight-or-flight.",
    image: "assets/feature-acupressure.jpeg",
  },
  {
    title: "Custom Arch-Matched Insoles",
    lead: "Engineered from your foot, not a size chart.",
    body: "A 2-minute structured-light scan reads the shape of your foot, then our team tunes lattice density across the heel, arch and forefoot to the way you actually walk and stand. The result is a pair of insoles that feel made-for-you because they are — no off-the-shelf compromise, no break-in period.",
    image: "assets/feature-arch-insoles.jpeg",
  },
  {
    title: "Breathable Recovery Structure",
    lead: "No sweat, no swelling, no sticky 12-hour shifts.",
    body: "Open lattice geometry lets air move through your sole the way it moves through mesh — so feet stay cool, calm and un-swollen through long days, long flights and long evenings on your feet. Less trapped heat, less end-of-day ache.",
    image: "assets/feature-breathable.jpeg",
  },
  {
    title: "Zero-Waste, On-Demand Manufacturing",
    lead: "Printed when you order. Recyclable when you’re done.",
    body: "Each pair is printed only after it’s ordered — no molds, no tooling, no overstock. Because the lattice is a single TPU material (not the 30+ glued components in a typical sneaker), the whole shoe can be ground down and re-extruded at end of life.",
    image: "assets/feature-3d-printed.jpeg",
  },
];

const NAV_ITEMS = [
  { id: "home", label: "Home" },
  { id: "collection", label: "Collection" },
  { id: "technology", label: "Technology" },
  { id: "blog", label: "Blog" },
  { id: "about", label: "About" },
  { id: "contact", label: "Contact" },
];

// ---------- Hooks ----------
function useScrollReveal() {
  // Strategy: reveals are visible by default. At mount, JS adds `.pre` to
  // anything currently below the fold to hide it. IO removes `.pre` when each
  // element intersects, triggering the transition. If JS fails for any reason,
  // the page is still fully readable.
  React.useEffect(() => {
    const vh = window.innerHeight || document.documentElement.clientHeight;
    const els = Array.from(document.querySelectorAll(".reveal"));
    if (!els.length) return;

    const toAnimate = [];
    els.forEach((el) => {
      const r = el.getBoundingClientRect();
      // Anything starting below the fold becomes hidden + animatable
      if (r.top >= vh - 40) {
        el.classList.add("pre");
        toAnimate.push(el);
      }
    });

    if (!toAnimate.length) return;

    if (!("IntersectionObserver" in window)) {
      toAnimate.forEach((el) => el.classList.remove("pre"));
      return;
    }
    const io = new IntersectionObserver(
      (entries) => {
        entries.forEach((e) => {
          if (e.isIntersecting) {
            e.target.classList.remove("pre");
            io.unobserve(e.target);
          }
        });
      },
      { threshold: 0.05, rootMargin: "0px 0px -40px 0px" }
    );
    toAnimate.forEach((el) => io.observe(el));

    // Safety: if IO somehow never fires, reveal anything still hidden after 1s
    const safety = setTimeout(() => {
      document.querySelectorAll(".reveal.pre").forEach((el) => {
        const r = el.getBoundingClientRect();
        if (r.top < vh + 200) el.classList.remove("pre");
      });
    }, 1200);

    return () => { io.disconnect(); clearTimeout(safety); };
  });
}

// ---------- Components ----------
function Nav({ route, onNavigate }) {
  const [menuOpen, setMenuOpen] = React.useState(false);
  React.useEffect(() => {
    document.body.style.overflow = menuOpen ? "hidden" : "";
    return () => { document.body.style.overflow = ""; };
  }, [menuOpen]);

  return (
    <React.Fragment>
      <header className="nav">
        <div className="container nav-inner">
          <button className="brand" onClick={() => onNavigate("home")} aria-label="Wov3 home">
            <span className="brand-mark">W</span>
            <span className="brand-name">Wov3<sup>™</sup></span>
          </button>
          <nav>
            <ul className="nav-links">
              {NAV_ITEMS.map((n) => (
                <li key={n.id}>
                  <button
                    className="nav-link"
                    aria-current={route === n.id ? "page" : undefined}
                    onClick={() => onNavigate(n.id)}
                  >
                    {n.label}
                  </button>
                </li>
              ))}
            </ul>
          </nav>
          <button className="nav-cta" onClick={() => window.openCalendly()}>Request a fitting</button>
          <button className="nav-burger" onClick={() => setMenuOpen(true)} aria-label="Menu">
            <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
              <line x1="4" y1="7" x2="20" y2="7"/><line x1="4" y1="17" x2="20" y2="17"/>
            </svg>
          </button>
        </div>
      </header>

      <div className={"mobile-menu" + (menuOpen ? " open" : "")} aria-hidden={!menuOpen}>
        <div className="mobile-menu-head">
          <button className="brand" onClick={() => { onNavigate("home"); setMenuOpen(false); }}>
            <span className="brand-mark">W</span>
            <span className="brand-name">Wov3<sup>™</sup></span>
          </button>
          <button className="nav-burger" onClick={() => setMenuOpen(false)} aria-label="Close menu">
            <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
              <line x1="6" y1="6" x2="18" y2="18"/><line x1="18" y1="6" x2="6" y2="18"/>
            </svg>
          </button>
        </div>
        <nav>
          {NAV_ITEMS.map((n) => (
            <button key={n.id} onClick={() => { onNavigate(n.id); setMenuOpen(false); }}>
              {n.label}
            </button>
          ))}
        </nav>
      </div>
    </React.Fragment>
  );
}

function Footer({ onNavigate }) {
  return (
    <footer className="footer">
      <div className="container">
        <div className="footer-grid">
          <div className="footer-col">
            <button className="brand" onClick={() => onNavigate("home")}>
              <span className="brand-mark">W</span>
              <span className="brand-name">Wov3<sup>™</sup></span>
            </button>
            <p className="footer-bio">
              <strong>Our mission</strong><br />
              We help people understand their feet — then build the recovery footwear and custom insoles their body actually needs, scanned and shaped to how they move.
            </p>
          </div>
          <div className="footer-col">
            <h4>Explore</h4>
            <ul>
              <li><button className="link" onClick={() => onNavigate("collection")}>Collection</button></li>
              <li><button className="link" onClick={() => onNavigate("technology")}>Technology</button></li>
              <li><button className="link" onClick={() => onNavigate("contact")}>Contact</button></li>
              <li><button className="link" onClick={() => onNavigate("home")}>Lookbook</button></li>
            </ul>
          </div>
          <div className="footer-col">
            <h4>Programs</h4>
            <ul>
              <li><button className="link" onClick={() => onNavigate("technology")}>Custom Orthotics</button></li>
              <li><button className="link">Custom Fitting</button></li>
            </ul>
          </div>
          <div className="footer-col">
            <h4>Connect</h4>
            <ul>
              <li><a className="link" href="mailto:Lhu@wov3.com">Lhu@wov3.com</a></li>
              <li><a className="link" href="https://www.instagram.com/wov3inc" target="_blank" rel="noopener noreferrer">Instagram</a></li>
              <li><a className="link" href="https://www.linkedin.com/company/wov3/" target="_blank" rel="noopener noreferrer">LinkedIn</a></li>
            </ul>
          </div>
        </div>
        <div className="footer-bottom">
          <span>© 2026 Wov3 Studio. All rights reserved.</span>
        </div>
      </div>
    </footer>
  );
}

function CtaBanner({ onNavigate }) {
  return (
    <section className="container">
      <div className="cta-banner reveal">
        <div className="cta-banner-inner">
          <div>
            <h2>Engineered footwear, <em>designed for recovery.</em></h2>
            <p>Book a fitting at our Eindhoven studio. We'll scan, model, and print to your gait in under a week.</p>
          </div>
          <button className="btn-primary" onClick={() => window.openCalendly()}>
            Request a fitting <span aria-hidden="true">→</span>
          </button>
        </div>
      </div>
    </section>
  );
}

// ───────────────────────────────────────────────────────────
// ProductGallery — swipeable / clickable image carousel
// ───────────────────────────────────────────────────────────
function ProductGallery({ images, alt }) {
  const [idx, setIdx] = React.useState(0);
  const trackRef = React.useRef(null);

  const go = (i) => {
    const n = images.length;
    const next = ((i % n) + n) % n;
    setIdx(next);
    const t = trackRef.current;
    if (t) {
      const slide = t.children[next];
      if (slide) slide.scrollIntoView({ behavior: "smooth", inline: "start", block: "nearest" });
    }
  };

  // Sync dot indicator with scroll (when user swipes)
  const onScroll = React.useCallback(() => {
    const t = trackRef.current;
    if (!t) return;
    const w = t.clientWidth;
    const i = Math.round(t.scrollLeft / w);
    if (i !== idx) setIdx(i);
  }, [idx]);

  return (
    <div className="product-gallery">
      <div className="product-gallery-track" ref={trackRef} onScroll={onScroll}>
        {images.map((src, i) => (
          <div className="product-gallery-slide" key={src + i}>
            <img src={src} alt={i === 0 ? alt : `${alt} — view ${i + 1}`} loading="lazy" />
          </div>
        ))}
      </div>
      {images.length > 1 && (
        <>
          <button
            className="product-gallery-arrow product-gallery-arrow-prev"
            aria-label="Previous image"
            onClick={() => go(idx - 1)}
          >‹</button>
          <button
            className="product-gallery-arrow product-gallery-arrow-next"
            aria-label="Next image"
            onClick={() => go(idx + 1)}
          >›</button>
          <div className="product-gallery-dots" role="tablist">
            {images.map((_, i) => (
              <button
                key={i}
                className={"product-gallery-dot " + (i === idx ? "is-active" : "")}
                aria-label={`Go to image ${i + 1}`}
                aria-selected={i === idx}
                onClick={() => go(i)}
              />
            ))}
          </div>
        </>
      )}
    </div>
  );
}

// ───────────────────────────────────────────────────────────
// PromoBanner — top-of-page announcement bar
// ───────────────────────────────────────────────────────────
function PromoBanner() {
  const STORAGE_KEY = "wov3-promo-banner";
  const PROMO_ID = "rendezvous-worldcup-2026"; // bump this to re-show after dismissals
  const [open, setOpen] = React.useState(false);

  React.useEffect(() => {
    let state = {};
    try { state = JSON.parse(localStorage.getItem(STORAGE_KEY) || "{}"); } catch (e) {}
    if (state.dismissedId !== PROMO_ID) setOpen(true);
  }, []);

  const close = () => {
    setOpen(false);
    try { localStorage.setItem(STORAGE_KEY, JSON.stringify({ dismissedId: PROMO_ID })); } catch (e) {}
  };

  if (!open) return null;
  return (
    <div className="promo-banner promo-banner--event" role="region" aria-label="Wov3 × The Rendezvous pop-up event">
      <div className="promo-banner-inner">
        <span className="promo-banner-tag">Wov3 × The Rendezvous</span>
        <span className="promo-banner-text">
          <strong>World Cup Pop-Up Experience</strong>
          <em className="promo-banner-sub">
            French cuisine · curated wines · live World Cup energy · unexpected surprises
          </em>
        </span>
        <a
          className="promo-banner-cta"
          href="https://maps.app.goo.gl/MuZ25bxy1ZkkE6PUA"
          target="_blank"
          rel="noopener noreferrer"
        >
          Get directions <span aria-hidden="true">→</span>
        </a>
        <button className="promo-banner-close" onClick={close} aria-label="Dismiss event banner">×</button>
      </div>
    </div>
  );
}

// ───────────────────────────────────────────────────────────
// LatticeLab — first-time educational reveal + email capture
// ───────────────────────────────────────────────────────────
const LATTICE_PANELS = [
  {
    eyebrow: "01 · The Problem",
    title: "Foam wasn't designed for recovery.",
    body: "Compresses under load. Traps heat. Loses up to 50% of its rebound in six months. The foot is doing the work the shoe should be doing.",
    visual: "foam",
  },
  {
    eyebrow: "02 · The Lattice",
    title: "Open-cell TPU lattice. Always returns.",
    body: "14,000+ tuned micro-cells per pair. Airflow runs through the sole the way it runs through mesh. Bounce-back holds for thousands of miles — same single material, end-to-end recyclable.",
    visual: "lattice",
  },
  {
    eyebrow: "03 · The Biomechanics",
    title: "Pressure distributed, not absorbed.",
    body: "Each step spreads across thousands of micro-cells instead of one flat foam slab. −38% peak plantar pressure vs. standard EVA in lab testing. Your heels, arches and joints stop carrying the day alone.",
    visual: "pressure",
  },
  {
    eyebrow: "04 · Try It",
    title: "Try the science yourself.",
    body: "25% off your first pair plus early access to new colorways. Drop your email — we'll send the code and the next-drop heads-up.",
    visual: "offer",
  },
];

function LatticeLabVisual({ kind }) {
  if (kind === "foam") {
    return (
      <div className="lab-viz-insole" aria-hidden="true">
        <img src="assets/foam-insole.jpg" alt="" loading="lazy" />
      </div>
    );
  }
  if (kind === "lattice") {
    return (
      <div className="lab-viz-insole" aria-hidden="true">
        <img src="assets/lattice-3d-print.png" alt="" loading="lazy" />
      </div>
    );
  }
  if (kind === "pressure") {
    return (
      <div className="lab-viz-insole" aria-hidden="true">
        <img src="assets/wov3-insole-biomechanics.png" alt="" loading="lazy" />
      </div>
    );
  }
  // offer
  return (
    <svg viewBox="0 0 200 120" className="lab-viz" aria-hidden="true">
      <rect x="20" y="30" width="160" height="60" rx="30" fill="none" stroke="currentColor" strokeOpacity="0.45" strokeDasharray="4 4" />
      <text x="100" y="68" textAnchor="middle" fontSize="22" fontWeight="700" fill="currentColor" letterSpacing="-1">25% OFF</text>
    </svg>
  );
}

function LatticeLab() {
  const STORAGE_KEY = "wov3-lattice-lab";
  const [open, setOpen] = React.useState(false);
  const [hasSeen, setHasSeen] = React.useState(true); // Assume "seen" until we know otherwise — hides FAB until first auto-open
  const [idx, setIdx] = React.useState(0);
  const [email, setEmail] = React.useState("");
  const [submitted, setSubmitted] = React.useState(false);
  const trackRef = React.useRef(null);

  // First-visit auto-open — fires immediately when someone lands fresh.
  React.useEffect(() => {
    let state = {};
    try { state = JSON.parse(localStorage.getItem(STORAGE_KEY) || "{}"); } catch (e) {}
    const alreadySeen = !!(state.dismissed || state.subscribed);
    setHasSeen(alreadySeen);
    if (alreadySeen) return;
    // Tiny delay just so the page paints first.
    const t = setTimeout(() => setOpen(true), 300);
    return () => clearTimeout(t);
  }, []);

  // Lock body scroll when modal open
  React.useEffect(() => {
    document.body.style.overflow = open ? "hidden" : "";
    return () => { document.body.style.overflow = ""; };
  }, [open]);

  // Esc to close
  React.useEffect(() => {
    if (!open) return;
    const onKey = (e) => { if (e.key === "Escape") close(); };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [open]);

  const persist = (patch) => {
    let state = {};
    try { state = JSON.parse(localStorage.getItem(STORAGE_KEY) || "{}"); } catch (e) {}
    try { localStorage.setItem(STORAGE_KEY, JSON.stringify({ ...state, ...patch })); } catch (e) {}
  };

  const close = () => {
    setOpen(false);
    setHasSeen(true);
    persist({ dismissed: true });
  };

  const goto = (i) => {
    const n = LATTICE_PANELS.length;
    const next = Math.max(0, Math.min(n - 1, i));
    setIdx(next);
    const t = trackRef.current;
    if (t) {
      const slide = t.children[next];
      if (slide) slide.scrollIntoView({ behavior: "smooth", inline: "start", block: "nearest" });
    }
  };

  const onScroll = () => {
    const t = trackRef.current;
    if (!t) return;
    const i = Math.round(t.scrollLeft / t.clientWidth);
    if (i !== idx) setIdx(i);
  };

  const onSubmit = (e) => {
    e.preventDefault();
    if (!email || !email.includes("@")) return;
    setSubmitted(true);
    persist({ subscribed: true, email });
    // Hook for real submission (e.g. Klaviyo) — fire a window event
    try { window.dispatchEvent(new CustomEvent("wov3-lab-signup", { detail: { email } })); } catch (e) {}
  };

  return (
    <React.Fragment>
      <button
        className={"lattice-lab-fab " + (open || !hasSeen ? "is-hidden" : "")}
        onClick={() => setOpen(true)}
        aria-label="Open the Wov3 Lab"
      >
        <span className="lattice-lab-fab-dot" aria-hidden="true"></span>
        <span className="lattice-lab-fab-text">
          <em>Wov3 Lab</em>
          <small>Why a lattice? See the science →</small>
        </span>
      </button>

      {open && (
        <div className="lattice-lab-overlay" role="dialog" aria-modal="true" aria-label="Wov3 Lab">
          <div className="lattice-lab-modal">
            <div className="lattice-lab-head">
              <div className="lattice-lab-brand">
                <span className="lattice-lab-brand-mark">W</span>
                <span>Wov3 Lab</span>
              </div>
              <button className="lattice-lab-close" onClick={close} aria-label="Close">×</button>
            </div>

            <div className="lattice-lab-track" ref={trackRef} onScroll={onScroll}>
              {LATTICE_PANELS.map((p, i) => (
                <section className="lattice-lab-panel" key={p.eyebrow}>
                  <div className="lattice-lab-visual">
                    <LatticeLabVisual kind={p.visual} />
                  </div>
                  <div className="lattice-lab-body">
                    <div className="lattice-lab-eyebrow">{p.eyebrow}</div>
                    <h2 className="lattice-lab-title">{p.title}</h2>
                    <p className="lattice-lab-text">{p.body}</p>

                    {i === LATTICE_PANELS.length - 1 && (
                      submitted ? (
                        <div className="lattice-lab-success">
                          <strong>You're in.</strong> Check your inbox for the code + the next-drop heads-up.
                        </div>
                      ) : (
                        <form className="lattice-lab-form" onSubmit={onSubmit}>
                          <input
                            type="email"
                            required
                            placeholder="you@studio.com"
                            value={email}
                            onChange={(e) => setEmail(e.target.value)}
                            aria-label="Email"
                          />
                          <button type="submit" className="btn-primary">
                            Send me 25% off →
                          </button>
                        </form>
                      )
                    )}

                    {i < LATTICE_PANELS.length - 1 && (
                      <button className="lattice-lab-next" onClick={() => goto(idx + 1)}>
                        Next <span aria-hidden="true">→</span>
                      </button>
                    )}
                  </div>
                </section>
              ))}
            </div>

            <div className="lattice-lab-foot">
              <div className="lattice-lab-dots" role="tablist">
                {LATTICE_PANELS.map((_, i) => (
                  <button
                    key={i}
                    className={"lattice-lab-dot " + (i === idx ? "is-active" : "")}
                    aria-label={`Panel ${i + 1} of ${LATTICE_PANELS.length}`}
                    aria-selected={i === idx}
                    onClick={() => goto(i)}
                  />
                ))}
              </div>
              <button className="lattice-lab-skip" onClick={close}>
                Skip for now
              </button>
            </div>
          </div>
        </div>
      )}
    </React.Fragment>
  );
}

// Expose to window for cross-script use
Object.assign(window, {
  PRODUCTS, FEATURES, NAV_ITEMS, ATHLETES, PARTNERS,
  Nav, Footer, CtaBanner, ProductGallery, LatticeLab, PromoBanner,
  useScrollReveal,
});
