// Marco Mini Mécanique — v4 · lusion.io-style: WebGL hero, GSAP, smooth scroll
const { useState, useEffect, useRef, useMemo } = React;

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "accent": "#FF6A1A",
  "shaderIntensity": 1.0,
  "liquidStrength": 14,
  "density": "regular",
  "showCursor": false
}/*EDITMODE-END*/;

// v5: inline useTweaks (the dev tweaks-panel.jsx is stripped from production).
// Returns the same [values, setter] tuple shape so existing call sites keep working.
function useTweaks(defaults) {
  const [values, setValues] = React.useState(defaults);
  const setTweak = (keyOrPatch, maybeValue) => {
    setValues((prev) => {
      if (typeof keyOrPatch === "string") return { ...prev, [keyOrPatch]: maybeValue };
      if (keyOrPatch && typeof keyOrPatch === "object") return { ...prev, ...keyOrPatch };
      return prev;
    });
  };
  return [values, setTweak];
}

// ════════════════════════════════════════════════════
// WebGL fluid noise hero shader
// ════════════════════════════════════════════════════
const VERT = `
attribute vec2 a_pos;
varying vec2 v_uv;
void main() {
  v_uv = a_pos * 0.5 + 0.5;
  gl_Position = vec4(a_pos, 0.0, 1.0);
}
`;

const FRAG = `
precision highp float;
varying vec2 v_uv;
uniform vec2 u_res;
uniform float u_time;
uniform vec2 u_mouse;
uniform float u_intensity;
uniform vec3 u_accent;

// 3D simplex noise — Ian McEwan / Ashima
vec3 mod289(vec3 x){return x-floor(x*(1.0/289.0))*289.0;}
vec4 mod289(vec4 x){return x-floor(x*(1.0/289.0))*289.0;}
vec4 permute(vec4 x){return mod289(((x*34.0)+1.0)*x);}
vec4 taylorInvSqrt(vec4 r){return 1.79284291400159 - 0.85373472095314 * r;}
float snoise(vec3 v){
  const vec2 C = vec2(1.0/6.0, 1.0/3.0);
  const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);
  vec3 i = floor(v + dot(v, C.yyy));
  vec3 x0 = v - i + dot(i, C.xxx);
  vec3 g = step(x0.yzx, x0.xyz);
  vec3 l = 1.0 - g;
  vec3 i1 = min(g.xyz, l.zxy);
  vec3 i2 = max(g.xyz, l.zxy);
  vec3 x1 = x0 - i1 + C.xxx;
  vec3 x2 = x0 - i2 + C.yyy;
  vec3 x3 = x0 - D.yyy;
  i = mod289(i);
  vec4 p = permute(permute(permute(
            i.z + vec4(0.0, i1.z, i2.z, 1.0))
          + i.y + vec4(0.0, i1.y, i2.y, 1.0))
          + i.x + vec4(0.0, i1.x, i2.x, 1.0));
  float n_ = 0.142857142857;
  vec3 ns = n_ * D.wyz - D.xzx;
  vec4 j = p - 49.0 * floor(p * ns.z * ns.z);
  vec4 x_ = floor(j * ns.z);
  vec4 y_ = floor(j - 7.0 * x_);
  vec4 x = x_ * ns.x + ns.yyyy;
  vec4 y = y_ * ns.x + ns.yyyy;
  vec4 h = 1.0 - abs(x) - abs(y);
  vec4 b0 = vec4(x.xy, y.xy);
  vec4 b1 = vec4(x.zw, y.zw);
  vec4 s0 = floor(b0)*2.0 + 1.0;
  vec4 s1 = floor(b1)*2.0 + 1.0;
  vec4 sh = -step(h, vec4(0.0));
  vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy;
  vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww;
  vec3 p0 = vec3(a0.xy, h.x);
  vec3 p1 = vec3(a0.zw, h.y);
  vec3 p2 = vec3(a1.xy, h.z);
  vec3 p3 = vec3(a1.zw, h.w);
  vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2,p2), dot(p3,p3)));
  p0 *= norm.x; p1 *= norm.y; p2 *= norm.z; p3 *= norm.w;
  vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);
  m = m*m;
  return 42.0 * dot(m*m, vec4(dot(p0,x0), dot(p1,x1), dot(p2,x2), dot(p3,x3)));
}

void main(){
  vec2 uv = v_uv;
  vec2 p = (uv * 2.0 - 1.0);
  p.x *= u_res.x / u_res.y;

  vec2 mouseN = (u_mouse / u_res) * 2.0 - 1.0;
  mouseN.y = -mouseN.y;
  mouseN.x *= u_res.x / u_res.y;

  float t = u_time * 0.08;
  vec2 q = p * 1.1 + vec2(t * 0.4, -t * 0.3);
  float n = snoise(vec3(q, t));
  n += 0.5 * snoise(vec3(q * 2.3 + 11.0, t * 1.4));
  n += 0.25 * snoise(vec3(q * 5.0 + 23.0, t * 2.0));

  // mouse pull
  float md = length(p - mouseN);
  float pull = exp(-md * 1.6) * 0.7;
  n += pull * sin(u_time * 1.8) * 0.6;

  n *= u_intensity;

  // colors
  vec3 bg = vec3(0.038, 0.035, 0.031);
  vec3 mid = vec3(0.10, 0.09, 0.085);
  vec3 hot = u_accent;
  vec3 deep = vec3(1.0, 0.34, 0.13);

  float band1 = smoothstep(-0.05, 0.45, n);
  float band2 = smoothstep(0.30, 0.85, n);
  float band3 = smoothstep(0.70, 1.05, n);

  vec3 col = mix(bg, mid, band1);
  col = mix(col, deep * 0.7, band2 * 0.55);
  col = mix(col, hot, band3 * 0.85);

  // soft vignette
  float v = smoothstep(1.4, 0.2, length(p));
  col *= mix(0.55, 1.0, v);

  // fbm grain
  float g = snoise(vec3(uv * 1400.0, u_time * 0.4)) * 0.04;
  col += g;

  gl_FragColor = vec4(col, 1.0);
}
`;

function useWebGLHero(ref, tweaks) {
  useEffect(() => {
    const canvas = ref.current;
    if (!canvas) return;
    const gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
    if (!gl) return;

    function compile(type, src) {
      const s = gl.createShader(type);
      gl.shaderSource(s, src);
      gl.compileShader(s);
      if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) {
        console.warn("shader", gl.getShaderInfoLog(s));
      }
      return s;
    }
    const vs = compile(gl.VERTEX_SHADER, VERT);
    const fs = compile(gl.FRAGMENT_SHADER, FRAG);
    const prog = gl.createProgram();
    gl.attachShader(prog, vs);
    gl.attachShader(prog, fs);
    gl.linkProgram(prog);
    gl.useProgram(prog);

    const buf = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, buf);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1,-1, 1,-1, -1,1, -1,1, 1,-1, 1,1]), gl.STATIC_DRAW);
    const loc = gl.getAttribLocation(prog, "a_pos");
    gl.enableVertexAttribArray(loc);
    gl.vertexAttribPointer(loc, 2, gl.FLOAT, false, 0, 0);

    const uRes = gl.getUniformLocation(prog, "u_res");
    const uTime = gl.getUniformLocation(prog, "u_time");
    const uMouse = gl.getUniformLocation(prog, "u_mouse");
    const uInt = gl.getUniformLocation(prog, "u_intensity");
    const uAcc = gl.getUniformLocation(prog, "u_accent");

    let raf, mx = 0, my = 0, tx = 0, ty = 0;
    function resize() {
      const dpr = Math.min(window.devicePixelRatio, 2);
      canvas.width = canvas.clientWidth * dpr;
      canvas.height = canvas.clientHeight * dpr;
      gl.viewport(0, 0, canvas.width, canvas.height);
    }
    resize();
    window.addEventListener("resize", resize);

    function onMove(e) {
      const rect = canvas.getBoundingClientRect();
      tx = (e.clientX - rect.left) * (canvas.width / rect.width);
      ty = (e.clientY - rect.top) * (canvas.height / rect.height);
    }
    window.addEventListener("pointermove", onMove);

    function parseHex(hex) {
      const c = hex.replace("#", "");
      return [
        parseInt(c.slice(0, 2), 16) / 255,
        parseInt(c.slice(2, 4), 16) / 255,
        parseInt(c.slice(4, 6), 16) / 255,
      ];
    }
    const start = performance.now();
    function frame() {
      mx += (tx - mx) * 0.06;
      my += (ty - my) * 0.06;
      const t = (performance.now() - start) / 1000;
      gl.uniform2f(uRes, canvas.width, canvas.height);
      gl.uniform2f(uMouse, mx, my);
      gl.uniform1f(uTime, t);
      gl.uniform1f(uInt, tweaks?.shaderIntensity ?? 1.0);
      const [r, g, b] = parseHex(tweaks?.accent || "#C2FF4D");
      gl.uniform3f(uAcc, r, g, b);
      gl.drawArrays(gl.TRIANGLES, 0, 6);
      raf = requestAnimationFrame(frame);
    }
    frame();
    return () => {
      cancelAnimationFrame(raf);
      window.removeEventListener("resize", resize);
      window.removeEventListener("pointermove", onMove);
    };
  }, [tweaks?.accent, tweaks?.shaderIntensity]);
}

// ════════════════════════════════════════════════════
// Custom cursor
// ════════════════════════════════════════════════════
function Cursor({ show }) {
  const dot = useRef(null);
  const ring = useRef(null);
  // Codex fix #1: only hide the native cursor when the custom one is actually rendered.
  useEffect(() => {
    if (show) {
      document.body.classList.add("has-custom-cursor");
      return () => document.body.classList.remove("has-custom-cursor");
    }
  }, [show]);
  useEffect(() => {
    if (!show) return;
    let dx = 0, dy = 0, rx = 0, ry = 0, tx = 0, ty = 0;
    function onMove(e) { tx = e.clientX; ty = e.clientY; }
    window.addEventListener("pointermove", onMove);

    let raf;
    function frame() {
      dx += (tx - dx) * 0.42;
      dy += (ty - dy) * 0.42;
      rx += (tx - rx) * 0.14;
      ry += (ty - ry) * 0.14;
      if (dot.current) dot.current.style.transform = `translate(${dx}px, ${dy}px) translate(-50%, -50%)`;
      if (ring.current) ring.current.style.transform = `translate(${rx}px, ${ry}px) translate(-50%, -50%)`;
      raf = requestAnimationFrame(frame);
    }
    frame();

    function over(e) {
      const t = e.target.closest && e.target.closest("a, button, .brand-card, .vid__thumb, .cats li, .zones li, [data-cursor]");
      if (!dot.current || !ring.current) return;
      if (t) {
        dot.current.classList.add("is-link");
        ring.current.classList.add("is-link");
      } else {
        dot.current.classList.remove("is-link");
        ring.current.classList.remove("is-link");
      }
    }
    window.addEventListener("pointerover", over);
    return () => {
      cancelAnimationFrame(raf);
      window.removeEventListener("pointermove", onMove);
      window.removeEventListener("pointerover", over);
    };
  }, [show]);
  if (!show) return null;
  return (
    <>
      <div ref={dot} className="cursor" />
      <div ref={ring} className="cursor-ring" />
    </>
  );
}

// ════════════════════════════════════════════════════
// Smooth scroll + GSAP setup
// ════════════════════════════════════════════════════
function useLenis() {
  useEffect(() => {
    if (!window.Lenis || !window.gsap) return;
    const lenis = new window.Lenis({ lerp: 0.08, smoothWheel: true });
    window.__lenis = lenis;
    lenis.on("scroll", () => window.ScrollTrigger?.update());
    window.gsap.ticker.add((time) => lenis.raf(time * 1000));
    window.gsap.ticker.lagSmoothing(0);
    if (window.ScrollTrigger) window.gsap.registerPlugin(window.ScrollTrigger);
    return () => { lenis.destroy(); window.__lenis = null; };
  }, []);
}

// ════════════════════════════════════════════════════
// Text reveal helpers
// ════════════════════════════════════════════════════
function splitWords(text) {
  return text.split(/(\s+)/).map((w, i) =>
    /\s+/.test(w)
      ? <React.Fragment key={i}>{w}</React.Fragment>
      : <span className="word" key={i}><span data-word>{w}</span></span>
  );
}

function useReveal(ref, deps = []) {
  useEffect(() => {
    if (!ref.current || !window.gsap || !window.ScrollTrigger) return;
    const words = ref.current.querySelectorAll("[data-word]");
    const ctx = window.gsap.context(() => {
      window.gsap.set(words, { y: 28, opacity: 0 });
      words.forEach((w) => w.setAttribute("data-ready", ""));
      window.gsap.to(words, {
        y: 0,
        opacity: 1,
        stagger: 0.04,
        duration: 1.0,
        ease: "expo.out",
        scrollTrigger: { trigger: ref.current, start: "top 85%" },
      });
    }, ref);
    return () => ctx.revert();
    // eslint-disable-next-line
  }, deps);
}

// v5: stagger-reveal any child set on scroll-trigger.
// Pass a ref to the container + a CSS selector for the children to stagger.
function useStaggerReveal(ref, selector, opts = {}, deps = []) {
  useEffect(() => {
    if (!ref.current || !window.gsap || !window.ScrollTrigger) return;
    const items = ref.current.querySelectorAll(selector);
    if (!items.length) return;
    const ctx = window.gsap.context(() => {
      window.gsap.set(items, { y: opts.y ?? 36, opacity: 0 });
      window.gsap.to(items, {
        y: 0,
        opacity: 1,
        stagger: opts.stagger ?? 0.06,
        duration: opts.duration ?? 0.9,
        ease: opts.ease ?? "expo.out",
        scrollTrigger: {
          trigger: ref.current,
          start: opts.start ?? "top 80%",
        },
      });
    }, ref);
    return () => ctx.revert();
    // eslint-disable-next-line
  }, deps);
}

// ════════════════════════════════════════════════════
// Loader (once on mount)
// ════════════════════════════════════════════════════
function Loader({ onDone }) {
  const [pct, setPct] = useState(0);
  const ref = useRef(null);
  useEffect(() => {
    let n = 0;
    const id = setInterval(() => {
      n += Math.random() * 9 + 4;
      if (n >= 100) {
        n = 100;
        clearInterval(id);
        setTimeout(() => {
          if (!ref.current || !window.gsap) { onDone(); return; }
          window.gsap.to(ref.current, {
            yPercent: -100,
            duration: 1.1,
            ease: "expo.inOut",
            onComplete: onDone,
          });
        }, 220);
      }
      setPct(Math.floor(Math.min(n, 100)));
    }, 110);
    return () => clearInterval(id);
  }, [onDone]);
  return (
    <div className="loader" ref={ref}>
      <div className="loader__inner">
        <span>Atelier · 1999</span>
        <span className="loader__num">{String(pct).padStart(3, "0")}</span>
        <span>Wickham · QC</span>
      </div>
      <div className="loader__bar" style={{ transform: `scaleX(${pct / 100})` }} />
    </div>
  );
}

// ════════════════════════════════════════════════════
// Live clock for Wickham (America/Toronto)
// ════════════════════════════════════════════════════
function useClock() {
  const [t, setT] = useState(() => new Date());
  useEffect(() => {
    const id = setInterval(() => setT(new Date()), 1000);
    return () => clearInterval(id);
  }, []);
  return t;
}

// ════════════════════════════════════════════════════
// Nav
// ════════════════════════════════════════════════════
function Nav({ t, locale, onToggle }) {
  const now = useClock();
  const time = now.toLocaleTimeString("en-CA", {
    hour: "2-digit", minute: "2-digit", hour12: false, timeZone: "America/Toronto",
  });
  return (
    <header className="nav">
      <a href="#top" className="nav__wm">
        <span className="nav__wm-dot" />
        Marco Mini Méca
        <small>· 1999</small>
      </a>
      <nav className="nav__links">
        {t.nav.map(([id, label]) => (
          <a key={id} href={`#${id}`}>{label}</a>
        ))}
      </nav>
      <div className="nav__right">
        <span className="nav__time">QC · {time}</span>
        <button className="locale-toggle" onClick={onToggle}>
          {t.locale} / {t.altLocale}
        </button>
        <a href="#contact" className="nav__cta">
          {t.ctaService} →
        </a>
      </div>
    </header>
  );
}

// ════════════════════════════════════════════════════
// Hero
// ════════════════════════════════════════════════════
function Hero({ t, tweaks }) {
  const titleRef = useRef(null);
  const bgRef = useRef(null);
  const veilRef = useRef(null);

  // v5: word reveal + scroll-cue line draw + corner block stagger.
  // Codex review fix: rerun on locale change so new word spans
  // (e.g. "Wickham, QC.") receive `data-ready` after FR↔EN flip.
  useEffect(() => {
    if (!window.gsap || !titleRef.current) return;
    const words = titleRef.current.querySelectorAll("[data-word]");
    window.gsap.set(words, { y: 36, opacity: 0 });
    words.forEach((w) => w.setAttribute("data-ready", ""));
    const tl = window.gsap.timeline({ delay: 0.35 });
    tl.to(words, { y: 0, opacity: 1, stagger: 0.06, duration: 1.2, ease: "expo.out" });
    tl.from(".hero__eyebrow, .hero__meta > *", { y: 30, opacity: 0, stagger: 0.08, duration: 0.9, ease: "expo.out" }, "-=0.7");
    tl.from(".hero__corner > *", { x: -20, opacity: 0, stagger: 0.04, duration: 0.6, ease: "expo.out" }, "-=0.8");
    tl.from(".hero__scroll-line", { scaleY: 0, transformOrigin: "top center", duration: 1.0, ease: "expo.inOut" }, "-=0.4");
    return () => tl.kill();
  }, [t.locale]);

  // v5: animate the still image — Ken Burns slow zoom-and-drift + breath veil.
  // Respects prefers-reduced-motion: a single still frame, no loop.
  useEffect(() => {
    if (!window.gsap) return;
    const prefersReduced = window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
    if (prefersReduced) return;

    const tl = window.gsap.timeline({ repeat: -1, yoyo: true, defaults: { ease: "sine.inOut" } });
    if (bgRef.current) {
      window.gsap.set(bgRef.current, { transformOrigin: "50% 55%", scale: 1.04 });
      tl.to(bgRef.current, {
        scale: 1.12,
        xPercent: -2,
        yPercent: -1.5,
        duration: 18,
      }, 0);
    }
    if (veilRef.current) {
      tl.fromTo(veilRef.current, { opacity: 0.42 }, { opacity: 0.58, duration: 9 }, 0);
    }
    return () => tl.kill();
  }, []);

  // v5: subtle mouse-parallax — image drifts ~10px against pointer for liveness.
  useEffect(() => {
    if (!window.gsap || !bgRef.current) return;
    const prefersReduced = window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
    if (prefersReduced) return;
    const coarse = window.matchMedia && window.matchMedia("(pointer: coarse)").matches;
    if (coarse) return; // skip on touch — Ken Burns already covers it

    const qx = window.gsap.quickTo(bgRef.current, "x", { duration: 1.2, ease: "expo.out" });
    const qy = window.gsap.quickTo(bgRef.current, "y", { duration: 1.2, ease: "expo.out" });
    const onMove = (e) => {
      const x = (e.clientX / window.innerWidth - 0.5) * -12;
      const y = (e.clientY / window.innerHeight - 0.5) * -8;
      qx(x); qy(y);
    };
    window.addEventListener("pointermove", onMove);
    return () => window.removeEventListener("pointermove", onMove);
  }, []);

  // headline pulls out KEY words
  const isFr = t.locale === "FR";
  const HeadFR = (
    <>
      <span className="word"><span data-word>Atelier</span></span>{" "}
      <em><span className="word"><span data-word>de</span></span>{" "}<span className="word"><span data-word>petits</span></span></em>{" "}
      <span className="word"><span data-word>moteurs</span></span>{" "}
      <em><span className="word"><span data-word>à</span></span></em>{" "}
      <span className="word"><span data-word>Wickham.</span></span>
    </>
  );
  const HeadEN = (
    <>
      <span className="word"><span data-word>Small-engine</span></span>{" "}
      <em><span className="word"><span data-word>workshop</span></span></em>{" "}
      <span className="word"><span data-word>in</span></span>{" "}
      <span className="word"><span data-word>Wickham,</span></span>{" "}
      <em><span className="word"><span data-word>QC.</span></span></em>
    </>
  );

  return (
    <section className="hero" id="top">
      <div className="hero__bg-wrap">
        <img ref={bgRef} className="hero__bg" src={t.hero.img} alt={t.hero.imageAlt} />
      </div>
      <div ref={veilRef} className="hero__veil" />

      <div className="hero__corner">
        <span className="hero__corner-key">{isFr ? "Coord." : "Coord."}</span>
        <span>45.74° N · 72.50° W</span>
        <span className="hero__corner-key">{isFr ? "Surface" : "Floor"}</span>
        <span>6 000 pi² · 9 {isFr ? "marques" : "brands"}</span>
      </div>

      <div className="hero__row">
        <div>
          <div className="hero__eyebrow">
            <span /> {t.hero.eyebrow}
          </div>
          <h1 ref={titleRef} className="hero__title">
            {isFr ? HeadFR : HeadEN}
          </h1>
          <p className="hero__sub">{t.hero.subtitle}</p>
        </div>
        <div className="hero__meta">
          <p className="hero__lede">{t.hero.lede}</p>
          <div className="hero__cta">
            <a href="#contact" className="btn-pill" data-cursor>
              {t.hero.ctaPrimary}
              <span className="btn-pill__arrow">↗</span>
            </a>
            <a href={t.phoneHref} className="btn-pill btn-pill--ghost" data-cursor>
              {t.hero.ctaSecondary}
              <span className="btn-pill__arrow">↗</span>
            </a>
          </div>
        </div>
      </div>

      <div className="hero__scroll">
        <span>{isFr ? "Faire défiler" : "Scroll"}</span>
        <span className="hero__scroll-line" />
      </div>
    </section>
  );
}

// ════════════════════════════════════════════════════
// Marquee
// ════════════════════════════════════════════════════
function Marquee({ t }) {
  const items = t.showroom.brands.map((b) => b.name);
  const phrases = t.locale === "FR"
    ? ["Atelier depuis 1999", "Pièces d'origine", "Ramassage local", "Détaillant autorisé", "Centre-du-Québec"]
    : ["Workshop since 1999", "Original parts", "Local pickup", "Authorized dealer", "Centre-du-Québec"];
  const seq = [];
  items.forEach((it, i) => {
    seq.push({ kind: "brand", v: it });
    if (i % 2 === 1 && phrases.length) seq.push({ kind: "alt", v: phrases.shift() });
  });
  while (phrases.length) seq.push({ kind: "alt", v: phrases.shift() });
  const doubled = [...seq, ...seq];
  return (
    <div className="marquee" aria-hidden="true">
      <div className="marquee__track">
        {doubled.map((s, i) => (
          <span key={i} className={`marquee__item${s.kind === "alt" ? " --alt" : ""}`}>
            {s.v}
          </span>
        ))}
      </div>
    </div>
  );
}

// ════════════════════════════════════════════════════
// Reusable section head
// ════════════════════════════════════════════════════
function SectionHead({ num, title, lead, em }) {
  const r = useRef(null);
  // Codex fix #2: re-run reveals when locale changes (title/em/lead text swap).
  useReveal(r, [title, em, lead]);
  // title is "first second <em>third</em> rest" — accept arrays of plain strings + em string
  return (
    <header className="section__head" ref={r}>
      <div className="section__num">{num}</div>
      <div>
        <h2 className="section__title">
          {splitWords(title.split("//")[0])}
          {em && <> <em>{splitWords(em)}</em></>}
          {title.includes("//") && <> {splitWords(title.split("//")[1])}</>}
        </h2>
        {lead && <p className="section__lead">{lead}</p>}
      </div>
    </header>
  );
}

// ════════════════════════════════════════════════════
// Atelier
// ════════════════════════════════════════════════════
function Atelier({ t }) {
  const isFr = t.locale === "FR";
  const a = t.atelier;
  const bodyRef = useRef(null);
  const catsRef = useRef(null);
  useStaggerReveal(bodyRef, ".atelier__strip-item", { stagger: 0.08, y: 24, duration: 0.8 }, [t.locale]);
  useStaggerReveal(catsRef, ".cats li", { stagger: 0.05, y: 24, duration: 0.7 }, [t.locale]);
  // Atelier capability strip — facts pulled from existing brief data.
  const stripFR = [
    { k: "Depuis 1999", l: "Fondé par Marco Goyette" },
    { k: "6 000 pi²", l: "Surface d'atelier · 2 ponts" },
    { k: "3 + Marco", l: "Techniciens dédiés" },
    { k: "Toutes marques", l: "OEM lorsque disponible" },
  ];
  const stripEN = [
    { k: "Since 1999", l: "Founded by Marco Goyette" },
    { k: "6,000 sq.ft", l: "Shop floor · 2 lifts" },
    { k: "3 + Marco", l: "Dedicated technicians" },
    { k: "All brands", l: "OEM when available" },
  ];
  const strip = isFr ? stripFR : stripEN;
  return (
    <section className="section" id="atelier">
      <SectionHead
        num={a.eyebrow}
        title={isFr ? "Tout ce qui a un" : "Everything with a"}
        em={isFr ? "moteur" : "motor"}
        lead={a.lead}
      />
      <div className="atelier">
        <div className="atelier__body" ref={bodyRef}>
          <p className="atelier__approach">
            {isFr
              ? "L'atelier prend en charge tous les modèles et toutes les marques, en se concentrant sur le diagnostic clair et la réparation durable. Les pièces d'origine sont utilisées lorsque disponibles, ou des équivalents validés selon la compatibilité."
              : "The shop services every model and every brand, focused on clear diagnostic work and lasting repairs. Original parts are used when available, or vetted equivalents that match for compatibility."}
          </p>
          <dl className="atelier__strip">
            {strip.map((s, i) => (
              <div className="atelier__strip-item" key={i}>
                <dt>{s.k}</dt>
                <dd>{s.l}</dd>
              </div>
            ))}
          </dl>
          <p className="atelier__addr">
            <span className="atelier__addr-label">{isFr ? "Adresse de l'atelier" : "Shop address"}</span>
            <span className="atelier__addr-line">{t.address}</span>
            <a className="atelier__addr-phone" href={t.phoneHref}>{t.phone}</a>
          </p>
        </div>
        <div className="atelier__cats-wrap" ref={catsRef}>
          <h3 className="atelier__cats-title">{isFr ? "Ce que l'atelier répare" : "What the shop services"}</h3>
          <ul className="cats">
            {a.categories.map((c, i) => (
              <li key={i}>
                <span className="cats__num">{String(i + 1).padStart(2, "0")}</span>
                <span className="cats__name">{c}</span>
                <span className="cats__arrow">↗</span>
              </li>
            ))}
          </ul>
        </div>
      </div>
    </section>
  );
}

// ════════════════════════════════════════════════════
// Showroom — per-brand product carousel + modal
// ════════════════════════════════════════════════════
function Showroom({ t }) {
  const s = t.showroom;
  const isFr = t.locale === "FR";
  // Per-brand product index inside each card carousel
  const [cardIdx, setCardIdx] = useState(() => s.brands.map(() => 0));
  // [brandIdx, productIdx] when modal open, else null
  const [active, setActive] = useState(null);
  // v5: stagger-reveal the 9 brand cards on scroll
  const gridRef = useRef(null);
  useStaggerReveal(gridRef, ".brand-card", { stagger: 0.08, y: 48, duration: 1.0 }, [t.locale]);

  // Reset card indices when locale flips (brand list length stable, products length matches)
  useEffect(() => {
    setCardIdx(s.brands.map(() => 0));
  }, [t.locale, s.brands.length]);

  const isOpen = active !== null;
  useEffect(() => {
    if (isOpen) {
      document.body.style.overflow = "hidden";
      window.__lenis?.stop?.();
    } else {
      document.body.style.overflow = "";
      window.__lenis?.start?.();
    }
    return () => {
      document.body.style.overflow = "";
      window.__lenis?.start?.();
    };
  }, [isOpen]);

  const stepCard = (cardI, dir) => {
    setCardIdx((prev) => {
      const out = prev.slice();
      const len = s.brands[cardI].products.length;
      out[cardI] = (out[cardI] + dir + len) % len;
      return out;
    });
  };

  const openModal = (brandI, productI) => setActive([brandI, productI]);
  const closeModal = () => setActive(null);

  const advanceModal = (dir) => {
    setActive(([bi, pi]) => {
      const brand = s.brands[bi];
      const plen = brand.products.length;
      let nbi = bi;
      let npi = pi + dir;
      if (npi >= plen) {
        nbi = (bi + 1) % s.brands.length;
        npi = 0;
      } else if (npi < 0) {
        nbi = (bi - 1 + s.brands.length) % s.brands.length;
        npi = s.brands[nbi].products.length - 1;
      }
      return [nbi, npi];
    });
  };

  useEffect(() => {
    if (!isOpen) return;
    const onKey = (e) => {
      if (e.key === "Escape") closeModal();
      if (e.key === "ArrowRight") advanceModal(1);
      if (e.key === "ArrowLeft") advanceModal(-1);
    };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [isOpen, s.brands]);

  return (
    <section className="section section--alt" id="showroom">
      <SectionHead
        num={s.eyebrow}
        title={isFr ? "Salle" : "Showroom"}
        em={isFr ? "de montre" : "lineup"}
        lead={s.lead}
      />
      <div className="showroom" ref={gridRef}>
        {s.brands.map((b, bi) => {
          const p = b.products[cardIdx[bi]];
          const plen = b.products.length;
          return (
            <div key={b.slug} className="brand-card" data-cursor>
              <div className="brand-card__media">
                <button
                  type="button"
                  className="brand-card__img"
                  onClick={() => openModal(bi, cardIdx[bi])}
                  aria-label={`${isFr ? "Ouvrir" : "Open"} ${b.name} ${p.model}`}
                  data-cursor
                >
                  <img src={p.img} alt={`${b.name} ${p.model}`} loading="lazy" />
                </button>
                {plen > 1 && (
                  <>
                    <button
                      type="button"
                      className="brand-card__arrow brand-card__arrow--prev"
                      onClick={(e) => { e.stopPropagation(); stepCard(bi, -1); }}
                      aria-label={isFr ? "Produit précédent" : "Previous product"}
                      data-cursor
                    >‹</button>
                    <button
                      type="button"
                      className="brand-card__arrow brand-card__arrow--next"
                      onClick={(e) => { e.stopPropagation(); stepCard(bi, 1); }}
                      aria-label={isFr ? "Produit suivant" : "Next product"}
                      data-cursor
                    >›</button>
                  </>
                )}
              </div>
              <div className="brand-card__inner">
                <h3 className="brand-card__name">{b.name}</h3>
                <p className="brand-card__model">{p.model}</p>
                {plen > 1 && (
                  <div className="brand-card__dots" role="tablist" aria-label={`${b.name} ${isFr ? "produits" : "products"}`}>
                    {b.products.map((_, di) => (
                      <button
                        key={di}
                        type="button"
                        className={"brand-card__dot" + (di === cardIdx[bi] ? " is-on" : "")}
                        onClick={(e) => { e.stopPropagation(); setCardIdx((prev) => { const out = prev.slice(); out[bi] = di; return out; }); }}
                        aria-label={`${b.name} ${di + 1} / ${plen}`}
                        aria-selected={di === cardIdx[bi]}
                        role="tab"
                        data-cursor
                      />
                    ))}
                  </div>
                )}
                <p className="brand-card__count">{plen} {isFr ? (plen > 1 ? "fiches" : "fiche") : (plen > 1 ? "models" : "model")}</p>
              </div>
            </div>
          );
        })}
      </div>

      {isOpen && (
        <ProductModal
          t={t}
          brand={s.brands[active[0]]}
          productIdx={active[1]}
          brandIdx={active[0]}
          totalBrands={s.brands.length}
          onClose={closeModal}
          onPrev={() => advanceModal(-1)}
          onNext={() => advanceModal(1)}
        />
      )}
    </section>
  );
}

function ProductModal({ t, brand, productIdx, brandIdx, totalBrands, onClose, onPrev, onNext }) {
  const isFr = t.locale === "FR";
  const ref = useRef(null);
  const product = brand.products[productIdx];
  const productCount = brand.products.length;
  useEffect(() => {
    if (!window.gsap || !ref.current) return;
    window.gsap.fromTo(
      ref.current.querySelector(".pmodal__panel"),
      { yPercent: 6, opacity: 0 },
      { yPercent: 0, opacity: 1, duration: 0.55, ease: "expo.out" }
    );
    window.gsap.fromTo(
      ref.current.querySelector(".pmodal__overlay"),
      { opacity: 0 },
      { opacity: 1, duration: 0.35, ease: "power2.out" }
    );
  }, [brand.slug, productIdx]);
  return (
    <div className="pmodal" ref={ref} role="dialog" aria-modal="true" aria-label={`${brand.name} ${product.model}`}>
      <div className="pmodal__overlay" onClick={onClose} />
      <div className="pmodal__panel">
        <header className="pmodal__head">
          <div className="pmodal__step">
            <span className="pmodal__step-brand">{String(brandIdx + 1).padStart(2, "0")} / {String(totalBrands).padStart(2, "0")}</span>
            <span className="pmodal__step-sep">·</span>
            <span className="pmodal__step-product">{brand.name} {String(productIdx + 1).padStart(2, "0")} / {String(productCount).padStart(2, "0")}</span>
          </div>
          <div className="pmodal__nav">
            <button onClick={onPrev} aria-label={isFr ? "Précédent" : "Previous"} data-cursor>←</button>
            <button onClick={onNext} aria-label={isFr ? "Suivant" : "Next"} data-cursor>→</button>
            <button onClick={onClose} aria-label={isFr ? "Fermer" : "Close"} className="pmodal__close" data-cursor>×</button>
          </div>
        </header>

        <div className="pmodal__body" data-lenis-prevent>
          <div className="pmodal__media">
            <img src={product.img} alt={`${brand.name} ${product.model}`} />
          </div>
          <div className="pmodal__copy">
            <div className="pmodal__eyebrow">
              <span /> {brand.since}
            </div>
            <h3 className="pmodal__brand">{brand.name}</h3>
            <p className="pmodal__model">{product.model}</p>
            <p className="pmodal__feat">{product.featured}</p>

            <ul className="pmodal__spec">
              <li>
                <span>{isFr ? "Type" : "Type"}</span>
                <strong>{product.featured.split(" · ")[0] || "—"}</strong>
              </li>
              <li>
                <span>{isFr ? "Motorisation" : "Power"}</span>
                <strong>{product.featured.split(" · ")[1] || "—"}</strong>
              </li>
              <li>
                <span>{isFr ? "Fiches" : "Sheets"}</span>
                <strong>{productCount} {isFr ? (productCount > 1 ? "modèles" : "modèle") : (productCount > 1 ? "models" : "model")}</strong>
              </li>
              <li>
                <span>{isFr ? "Statut" : "Status"}</span>
                <strong>{isFr ? "Vérifier en atelier" : "Check with shop"}</strong>
              </li>
            </ul>

            <div className="pmodal__cta">
              <a className="btn-pill" href={t.phoneHref}>
                {isFr ? "Appeler l'atelier" : "Call the shop"}
                <span className="btn-pill__arrow">↗</span>
              </a>
              <a
                className="btn-pill btn-pill--ghost"
                href={`mailto:${t.email}?subject=${encodeURIComponent(brand.name + " " + product.model)}`}
              >
                {isFr ? "Demande d'info" : "Request info"}
                <span className="btn-pill__arrow">↗</span>
              </a>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}

// ════════════════════════════════════════════════════
// Videos
// ════════════════════════════════════════════════════
function Videos({ t }) {
  const v = t.videos;
  const isFr = t.locale === "FR";
  const featuredCount = v.featuredCount || 3;
  const featured = v.list.slice(0, featuredCount);
  const rest = v.list.slice(featuredCount);
  // null when no video is playing; otherwise the index into v.list
  const [open, setOpen] = useState(null);
  // separate flag for the "all videos" modal
  const [showAll, setShowAll] = useState(false);

  // Lock body scroll when the all-videos modal is open
  useEffect(() => {
    if (showAll) {
      document.body.style.overflow = "hidden";
      window.__lenis?.stop?.();
    } else {
      document.body.style.overflow = "";
      window.__lenis?.start?.();
    }
    return () => {
      document.body.style.overflow = "";
      window.__lenis?.start?.();
    };
  }, [showAll]);

  // Esc closes the all-videos modal
  useEffect(() => {
    if (!showAll) return;
    const onKey = (e) => { if (e.key === "Escape") { setShowAll(false); setOpen(null); } };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [showAll]);

  const renderCard = (it, i, variant) => {
    const thumb = `https://i.ytimg.com/vi/${it.id}/hqdefault.jpg`;
    const isOpen = open === i;
    return (
      <article key={i} className={"vid" + (variant === "compact" ? " vid--compact" : "") + (isOpen ? " is-open" : "")}>
        {isOpen ? (
          <div className="vid__thumb vid__thumb--playing">
            <iframe
              src={`https://www.youtube.com/embed/${it.id}?autoplay=1`}
              title={it.title}
              allow="autoplay; encrypted-media"
              allowFullScreen
            />
            <button
              type="button"
              className="vid__close"
              onClick={() => setOpen(null)}
              aria-label={isFr ? "Fermer la vidéo" : "Close video"}
              data-cursor
            >×</button>
          </div>
        ) : (
          <button
            type="button"
            className="vid__thumb"
            onClick={() => setOpen(i)}
            aria-label={`${isFr ? "Lire" : "Play"} ${it.title}`}
            data-cursor
          >
            <img src={thumb} alt={it.title} loading="lazy" />
            <span className="vid__play"><span>{isFr ? "Lire" : "Play"}</span></span>
          </button>
        )}
        <span className="vid__brand">{it.brand}</span>
        <h3 className="vid__title">{it.title}</h3>
      </article>
    );
  };

  return (
    <section className="section" id="videos">
      <SectionHead
        num={v.eyebrow}
        title={isFr ? "Vidéos" : "Curated"}
        em={isFr ? "curatées" : "videos"}
        lead={v.lead}
      />
      <div className="videos">
        {featured.map((it, i) => renderCard(it, i, "featured"))}
      </div>
      {rest.length > 0 && (
        <div className="videos__more">
          <button
            type="button"
            className="btn-pill btn-pill--ghost"
            onClick={() => { setOpen(null); setShowAll(true); }}
            data-cursor
          >
            {v.showMore || (isFr ? "Voir toutes les vidéos" : "Watch all videos")}
            <span className="btn-pill__count">+{rest.length}</span>
            <span className="btn-pill__arrow">↗</span>
          </button>
        </div>
      )}

      {showAll && (
        <div className="vmodal" role="dialog" aria-modal="true" aria-label={v.modalTitle || "Video library"}>
          <div className="vmodal__overlay" onClick={() => { setShowAll(false); setOpen(null); }} />
          <div className="vmodal__panel" data-lenis-prevent>
            <header className="vmodal__head">
              <div className="vmodal__title">
                <span className="vmodal__eyebrow">{v.featuredLabel || (isFr ? "Bibliothèque" : "Library")}</span>
                <h2>{v.modalTitle || (isFr ? "Bibliothèque vidéo" : "Video library")}</h2>
                <p className="vmodal__count">{v.list.length} {isFr ? "vidéos · source marcominimecanique.com" : "videos · source marcominimecanique.com"}</p>
              </div>
              <button
                type="button"
                className="vmodal__close"
                onClick={() => { setShowAll(false); setOpen(null); }}
                aria-label={isFr ? "Fermer" : "Close"}
                data-cursor
              >×</button>
            </header>
            <div className="vmodal__grid">
              {v.list.map((it, i) => renderCard(it, i, "compact"))}
            </div>
          </div>
        </div>
      )}
    </section>
  );
}

// ════════════════════════════════════════════════════
// Parts
// ════════════════════════════════════════════════════
function Parts({ t }) {
  const p = t.parts;
  const isFr = t.locale === "FR";
  const ref = useRef(null);
  // Codex fix #2: re-run reveals when locale changes.
  useReveal(ref, [t.locale]);
  return (
    <section className="section section--alt" id="pieces">
      <header className="section__head">
        <div className="section__num">{p.eyebrow}</div>
      </header>
      <div className="parts" ref={ref}>
        <h2 className="parts__big">
          {splitWords(isFr ? "Pièces" : "Original")} <em>{splitWords(isFr ? "d'origine" : "parts")}</em> {splitWords(isFr ? "lorsque possible." : "where possible.")}
        </h2>
        <div className="parts__body">
          <p>{p.lead}</p>
          <p>{p.body}</p>
          <ul className="parts__tags">
            {p.tags.map((tag, i) => <li key={i}>{tag}</li>)}
          </ul>
        </div>
      </div>
    </section>
  );
}

// ════════════════════════════════════════════════════
// Pickup
// ════════════════════════════════════════════════════
function Pickup({ t }) {
  const p = t.pickup;
  const isFr = t.locale === "FR";
  const zonesRef = useRef(null);
  useStaggerReveal(zonesRef, "li", { stagger: 0.04, y: 22, duration: 0.65 }, [t.locale]);
  return (
    <section className="section" id="ramassage">
      <SectionHead
        num={p.eyebrow}
        title={isFr ? "On vient" : "We pick up"}
        em={isFr ? "chez vous" : "and deliver"}
        lead={p.lead}
      />
      <div className="pickup">
        <div className="pickup__cta">
          <div className="pickup__label">{p.territoryLabel}</div>
          <a className="btn-pill" href={t.phoneHref} data-cursor>
            {isFr ? "Réserver un ramassage" : "Schedule a pickup"}
            <span className="btn-pill__arrow">↗</span>
          </a>
        </div>
        <ul className="zones" ref={zonesRef}>
          {p.areas.map((a, i) => (
            <li key={i} data-cursor>
              <span className="zones__n">{String(i + 1).padStart(2, "0")}</span>
              <span>{a}</span>
            </li>
          ))}
        </ul>
      </div>
    </section>
  );
}

// ════════════════════════════════════════════════════
// About
// ════════════════════════════════════════════════════
function About({ t }) {
  const a = t.about;
  const isFr = t.locale === "FR";
  const timelineRef = useRef(null);
  const statsRef = useRef(null);
  const mediaRef = useRef(null);
  useStaggerReveal(timelineRef, "li", { stagger: 0.08, y: 28, duration: 0.85 }, [t.locale]);
  useStaggerReveal(statsRef, "div", { stagger: 0.08, y: 24, duration: 0.85 }, [t.locale]);
  // Ken-Burns the About portrait too — subtle scale on scroll-into-view.
  useEffect(() => {
    if (!window.gsap || !window.ScrollTrigger || !mediaRef.current) return;
    const img = mediaRef.current.querySelector("img");
    if (!img) return;
    const tween = window.gsap.fromTo(
      img,
      { scale: 1.0, y: 0 },
      {
        scale: 1.08,
        y: -16,
        ease: "none",
        scrollTrigger: {
          trigger: mediaRef.current,
          start: "top bottom",
          end: "bottom top",
          scrub: 1.2,
        },
      }
    );
    return () => tween.scrollTrigger?.kill();
  }, []);
  return (
    <section className="section section--alt" id="apropos">
      <SectionHead
        num={a.eyebrow}
        title={isFr ? "Depuis" : "Since"}
        em="1999"
        lead={a.lead}
      />
      <div className="about-grid">
        <div className="about__media" data-cursor ref={mediaRef}>
          <img src={a.img} alt={a.mediaAlt} loading="lazy" />
        </div>
        <div>
          <ol className="timeline" ref={timelineRef}>
            {a.milestones.map(([y, l], i) => (
              <li key={i}>
                <span className="timeline__y">{y}</span>
                <span className="timeline__l">{l}</span>
              </li>
            ))}
          </ol>
        </div>
      </div>

      <figure className="quote-block">
        <blockquote>{a.quote}</blockquote>
        <cite>{a.quoteAttr}</cite>
      </figure>

      <dl className="about-stats" ref={statsRef}>
        {a.stats.map((s, i) => (
          <div key={i}>
            <dt>{s.k.includes("+") || s.k.includes("≈") || s.k.includes(",") ? (
              <span dangerouslySetInnerHTML={{ __html: s.k.replace(/(≈|\+|,)/g, "<em>$1</em>") }} />
            ) : s.k}</dt>
            <dd>{s.l}</dd>
          </div>
        ))}
      </dl>
    </section>
  );
}

// ════════════════════════════════════════════════════
// Contact
// ════════════════════════════════════════════════════
function Contact({ t }) {
  const c = t.contact;
  const isFr = t.locale === "FR";
  return (
    <section className="section" id="contact">
      <SectionHead
        num={c.eyebrow}
        title={isFr ? "Joindre" : "Reach"}
        em={isFr ? "l'atelier" : "the shop"}
      />
      <div className="contact-shell">
        <div className="contact-col">
          <h3>{c.shopLabel}</h3>
          <p className="contact-col__name">Marco Mini Mécanique</p>
          <p>{t.address}</p>
          <a className="contact-col__phone" href={t.phoneHref} data-cursor>{t.phone}</a>
          <p className="m3-muted" style={{ color: "var(--c-muted)", fontSize: 13 }}>
            {isFr ? "Téléc." : "Fax"} {t.fax}
          </p>
          <p><a href={`mailto:${t.email}`} style={{ borderBottom: "1px solid var(--c-line-2)", paddingBottom: 1 }}>{t.email}</a></p>
          <div className="contact-cta">
            <a className="btn-pill" href={t.phoneHref} data-cursor>{c.ctaCall}<span className="btn-pill__arrow">↗</span></a>
            <a className="btn-pill btn-pill--ghost" href={`mailto:${t.email}`} data-cursor>{c.ctaEmail}<span className="btn-pill__arrow">↗</span></a>
          </div>
        </div>

        <div className="contact-col">
          <h3>{c.hoursLabel}</h3>
          <table>
            <tbody>
              {c.hoursLines.map(([d, h], i) => (
                <tr key={i}><td>{d}</td><td>{h}</td></tr>
              ))}
            </tbody>
          </table>
          <p style={{ marginTop: 16, color: "var(--c-muted)", fontSize: 13 }}>{c.hoursNote}</p>
        </div>

        <div className="contact-col">
          <h3>{c.payLabel}</h3>
          <p>{c.pay}</p>
          <h3 style={{ marginTop: 32 }}>{isFr ? "Atelier" : "Workshop"}</h3>
          <p>{isFr ? "6 000 pi² · 2 ponts" : "6,000 sq.ft · 2 lifts"}</p>
          <p>{isFr ? "3 techniciens + Marco" : "3 technicians + Marco"}</p>
        </div>
      </div>
      {/* v5: big Contact satellite map removed — the map now lives in the footer CTA block. */}
    </section>
  );
}

// (Finale removed — WebGL moved into Footer)
function FinaleRemoved({ t, tweaks }) {
  const cv = useRef(null);
  const titleRef = useRef(null);
  useWebGLHero(cv, tweaks);
  useReveal(titleRef);

  const isFr = t.locale === "FR";
  return (
    <section className="finale" aria-label={isFr ? "Appel à l'action" : "Call to action"}>
      <canvas ref={cv} className="finale__canvas" />
      <div className="finale__veil" />
      <div className="finale__inner" ref={titleRef}>
        <div className="finale__eyebrow">
          <span /> {isFr ? "§08 — Atelier ouvert" : "§08 — Shop open"}
        </div>
        <h2 className="finale__title">
          {splitWords(isFr ? "Apportez-nous" : "Bring us")} <em>{splitWords(isFr ? "votre machine." : "your machine.")}</em>
        </h2>
        <p className="finale__sub">
          {isFr
            ? "765 route Principale, Wickham — ou un ramassage chez vous dans 12 municipalités."
            : "765 route Principale, Wickham — or schedule a local pickup across 12 municipalities."}
        </p>
        <div className="finale__cta">
          <a className="btn-pill" href={t.phoneHref} data-cursor>
            {t.phone}
            <span className="btn-pill__arrow">↗</span>
          </a>
          <a className="btn-pill btn-pill--ghost" href={`mailto:${t.email}`} data-cursor>
            {isFr ? "Écrire à l'atelier" : "Email the shop"}
            <span className="btn-pill__arrow">↗</span>
          </a>
        </div>
      </div>
    </section>
  );
}

// ════════════════════════════════════════════════════
// Codex review fix: map footer column labels → real section anchors.
// Brand names route to #showroom; everything else uses a substring match
// against the canonical section IDs (FR + EN both flow through the same map).
function footerLinkHref(label, t) {
  const brandSlugs = (t.showroom?.brands || []).map((b) => b.name.toLowerCase());
  const l = (label || "").toLowerCase();
  if (brandSlugs.some((b) => l.includes(b.split(" ")[0]))) return "#showroom";
  if (/(réparation|repair|atelier|workshop)/i.test(l)) return "#atelier";
  if (/(pièce|parts)/i.test(l)) return "#pieces";
  if (/(ramassage|pickup|delivery|livraison)/i.test(l)) return "#ramassage";
  if (/(vidéo|video)/i.test(l)) return "#videos";
  if (/(propos|about|history|histor)/i.test(l)) return "#apropos";
  if (/(heure|hour|access|accès|joindre|contact|email|écrire|reach)/i.test(l)) return "#contact";
  if (/(service|demande)/i.test(l)) return "#contact";
  return "#contact";
}

function Footer({ t, tweaks }) {
  const isFr = t.locale === "FR";
  const cv = useRef(null);
  useWebGLHero(cv, tweaks);
  return (
    <footer className="footer">
      <canvas ref={cv} className="footer__canvas" />
      <div className="footer__veil" />

      <div className="footer__cta">
        <div className="footer__cta-text">
          <div className="footer__eyebrow">
            <span /> {isFr ? "§08 — Atelier ouvert" : "§08 — Shop open"}
          </div>
          <h2 className="footer__title">
            {isFr ? "Apportez-nous " : "Bring us "}<em>{isFr ? "votre machine." : "your machine."}</em>
          </h2>
          <p className="footer__sub">
            {isFr
              ? "765 route Principale, Wickham — ou un ramassage chez vous."
              : "765 route Principale, Wickham — or schedule a local pickup."}
          </p>
          <div className="footer__pills">
            <a className="btn-pill" href={t.phoneHref}>
              {t.phone}
              <span className="btn-pill__arrow">↗</span>
            </a>
            <a className="btn-pill btn-pill--ghost" href={`mailto:${t.email}`}>
              {isFr ? "Écrire à l'atelier" : "Email the shop"}
              <span className="btn-pill__arrow">↗</span>
            </a>
          </div>
        </div>
        <div className="footer__map">
          <iframe
            className="footer__map-iframe"
            src="https://www.google.com/maps?q=765+route+Principale,+Wickham,+QC&t=k&z=18&output=embed"
            title={isFr ? "Carte satellite — 765 route Principale, Wickham" : "Satellite map — 765 route Principale, Wickham"}
            loading="lazy"
            referrerPolicy="no-referrer-when-downgrade"
          />
          <a
            className="footer__map-cta"
            href="https://www.google.com/maps/dir/?api=1&destination=765+route+Principale,+Wickham,+QC+J0C+1S0"
            target="_blank"
            rel="noopener noreferrer"
          >
            {isFr ? "Itinéraire" : "Directions"}
            <span className="btn-pill__arrow">↗</span>
          </a>
        </div>
      </div>

      <div className="footer__row">
        <div className="footer__col">
          <h4>{isFr ? "Atelier" : "Workshop"}</h4>
          <p>765 route Principale</p>
          <p>Wickham, QC J0C 1S0</p>
          <a className="footer__phone" href={t.phoneHref}>{t.phone}</a>
        </div>
        {t.footer.cols.map((col, i) => (
          <div className="footer__col" key={i}>
            <h4>{col.h}</h4>
            <ul>
              {col.links.map((l, j) => <li key={j}><a href={footerLinkHref(l, t)}>{l}</a></li>)}
            </ul>
          </div>
        ))}
      </div>
      <div className="footer__bot">
        <span>{t.footer.legal}</span>
        <span>{t.footer.signoff}</span>
      </div>
    </footer>
  );
}

// ════════════════════════════════════════════════════
// Tweaks
// ════════════════════════════════════════════════════
function MmTweaks() {
  const [t, setT] = useTweaks(TWEAK_DEFAULTS);
  // animate liquid filter strength
  useEffect(() => {
    const map = document.querySelector("#liquid feDisplacementMap");
    if (map) map.setAttribute("scale", String(t.liquidStrength));
    // gentle baseFrequency animation
    const turb = document.querySelector("#liquidTurb");
    if (!turb || !window.gsap) return;
    const tl = window.gsap.timeline({ repeat: -1, yoyo: true });
    tl.to({ x: 0.012 }, {
      x: 0.024,
      duration: 6,
      ease: "sine.inOut",
      onUpdate() {
        turb.setAttribute("baseFrequency", `${this.targets()[0].x} 0.02`);
      },
    });
    return () => tl.kill();
  }, [t.liquidStrength]);
  return (
    <TweaksPanel title="Tweaks">
      <TweakSection title="Shader">
        <TweakColor
          label="Accent"
          value={t.accent}
          onChange={(v) => setT("accent", v)}
          options={["#FF6A1A", "#FF5722", "#E64A19", "#FFB81C", "#C2FF4D"]}
        />
        <TweakSlider
          label="Intensity"
          value={t.shaderIntensity}
          min={0.4}
          max={1.8}
          step={0.05}
          onChange={(v) => setT("shaderIntensity", v)}
        />
        <TweakSlider
          label="Liquid strength"
          value={t.liquidStrength}
          min={0}
          max={40}
          step={1}
          onChange={(v) => setT("liquidStrength", v)}
        />
      </TweakSection>
      <TweakSection title="Interaction">
        <TweakToggle
          label="Custom cursor"
          value={t.showCursor}
          onChange={(v) => setT("showCursor", v)}
        />
      </TweakSection>
    </TweaksPanel>
  );
}

// ════════════════════════════════════════════════════
// Root
// ════════════════════════════════════════════════════
function App() {
  const [locale, setLocale] = useState("fr");
  const [ready, setReady] = useState(false);
  const t = window.COPY[locale];
  const [tweaks] = useTweaks(TWEAK_DEFAULTS);

  useLenis();

  useEffect(() => {
    document.documentElement.lang = locale;
  }, [locale]);

  useEffect(() => {
    document.documentElement.style.setProperty("--c-accent", tweaks.accent);
  }, [tweaks.accent]);

  return (
    <>
      {!ready && <Loader onDone={() => setReady(true)} />}
      <Cursor show={tweaks.showCursor !== false} />
      <Nav t={t} locale={t.locale} onToggle={() => setLocale((l) => (l === "fr" ? "en" : "fr"))} />
      <main>
        <Hero t={t} tweaks={tweaks} />
        <Marquee t={t} />
        <Atelier t={t} />
        <Showroom t={t} />
        <Videos t={t} />
        <Parts t={t} />
        <Pickup t={t} />
        <About t={t} />
        <Contact t={t} />
      </main>
      <Footer t={t} tweaks={tweaks} />
      {/* v5: <MmTweaks /> dev panel removed (tweaks-panel.jsx not bundled). */}
    </>
  );
}

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App />);
