/* ============================================================
   TAURIST — APP
   Header (logo + studios dropdown) · Hero (statement + staged sunrise) ·
   Product carousel (3D coverflow) · Manifesto · Footer · Tweaks.
   ============================================================ */
const { useState: useS, useEffect: useE, useRef: useR, useCallback } = React;
const T = window.TAURIST;
const DS = window.TauristDesignSystem_fba798 || {};

const MOTION = { subtle: 0.5, balanced: 0.8, cinematic: 1.12 };

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "accentIntensity": 1,
  "motion": "cinematic"
}/*EDITMODE-END*/;

/* ---------- inline icons ---------- */
const IconArrow = (p) => (
  <svg width={p.s || 16} height={p.s || 16} viewBox="0 0 24 24" fill="none" aria-hidden="true">
    <path d="M5 12h14M13 6l6 6-6 6" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round" />
  </svg>
);
const IconExt = (p) => (
  <svg className={p.className} viewBox="0 0 24 24" fill="none" aria-hidden="true" style={{ width: p.s || 13, height: p.s || 13 }}>
    <path d="M14 5h5v5M19 5l-9 9M9 6H6a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2v-3"
      stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round" />
  </svg>
);
const IconChevron = (p) => (
  <svg width={p.s || 13} height={p.s || 13} viewBox="0 0 24 24" fill="none" aria-hidden="true">
    <path d="M6 9l6 6 6-6" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" />
  </svg>
);
const IconLogin = (p) => (
  <svg width={p.s || 15} height={p.s || 15} viewBox="0 0 24 24" fill="none" aria-hidden="true">
    <path d="M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4M10 17l5-5-5-5M15 12H3"
      stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round" />
  </svg>
);

/* ============================================================ STUDIOS DROPDOWN */
function StudiosDropdown() {
  const [open, setOpen] = useS(false);
  const ref = useR(null);
  const closeT = useR(null);
  const dd = T.nav.studios;
  useE(() => {
    const onDoc = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener("mousedown", onDoc);
    return () => document.removeEventListener("mousedown", onDoc);
  }, []);
  const enter = () => { clearTimeout(closeT.current); setOpen(true); };
  const leave = () => { closeT.current = setTimeout(() => setOpen(false), 130); };
  return (
    <div className={"nav-dd" + (open ? " is-open" : "")} ref={ref} onMouseEnter={enter} onMouseLeave={leave}>
      <button className="nav-dd__btn" aria-haspopup="true" aria-expanded={open}
        onClick={() => setOpen((o) => !o)}
        onKeyDown={(e) => { if (e.key === "Escape") setOpen(false); }}>
        {dd.label}<span className="cc"><IconChevron s={12} /></span>
      </button>
      <div className="nav-dd__menu" role="menu">
        {dd.items.map((it) => (
          <React.Fragment key={it.label}>
            {it.separatorBefore && <div className="nav-dd__sep" role="separator" />}
            <a role="menuitem" className="nav-dd__item" href={it.href}
              target={it.external ? "_blank" : undefined}
              rel={it.external ? "noopener noreferrer" : undefined}
              aria-label={`${it.label}${it.external ? " (opens in a new tab)" : ""}`}>
              {it.logo
                ? <img className="nav-dd__logo" src={it.logo} alt={it.label} />
                : <span>{it.label}</span>}
              {it.external && <IconExt className="ext" />}
            </a>
          </React.Fragment>
        ))}
      </div>
    </div>
  );
}

/* ============================================================ HEADER */
function Header({ onJump, entered }) {
  const [scrolled, setScrolled] = useS(false);
  const [menu, setMenu] = useS(false);
  const [settled, setSettled] = useS(false);
  useE(() => {
    if (!entered) return;
    const id = setTimeout(() => setSettled(true), 4900);
    return () => clearTimeout(id);
  }, [entered]);
  useE(() => {
    const onScroll = () => setScrolled(window.scrollY > 24);
    onScroll();
    window.addEventListener("scroll", onScroll, { passive: true });
    return () => window.removeEventListener("scroll", onScroll);
  }, []);
  useE(() => {
    document.body.style.overflow = menu ? "hidden" : "";
    return () => { document.body.style.overflow = ""; };
  }, [menu]);
  const go = (e, href) => { e.preventDefault(); setMenu(false); onJump(href); };
  const dd = T.nav.studios;

  return (
    <React.Fragment>
      <header className={"site-header" + (scrolled ? " is-scrolled" : "") + (entered ? " is-entered" : "") + (settled ? " is-settled" : "") + (menu ? " is-menu-open" : "")}>
        <div className="header-left">
          <a className="header-brand" href="#top" onClick={(e) => go(e, "#top")} aria-label="Taurist Technologies — home">
            <img className="header-brand__logo" src={T.brand.logoWhite} alt="Taurist Technologies" />
          </a>
          <span className="header-div" aria-hidden="true" />
          <StudiosDropdown />
        </div>
        <div className="header-right">
          <nav className="header-nav" aria-label="Primary">
            {T.nav.links.map((l) => (
              <a key={l.label} href={l.href} onClick={(e) => go(e, l.href)}>{l.label}</a>
            ))}
          </nav>
          <a className="header-cta" href={T.nav.dashboard.href} target="_blank" rel="noopener noreferrer"
            aria-label={`${T.nav.dashboard.label} (opens in a new tab)`}>
            <span>{T.nav.dashboard.label}</span>
            <IconExt className="ext" s={11} />
          </a>
          <button className={"header-menu-btn" + (menu ? " is-open" : "")} onClick={() => setMenu(!menu)} aria-label={menu ? "Close menu" : "Open menu"} aria-expanded={menu}>
            <span className="hb" aria-hidden="true"><span /><span /><span /></span>
          </button>
        </div>
      </header>

      <div className={"menu-sheet" + (menu ? " is-open" : "")} role="dialog" aria-modal="true" aria-label="Menu" aria-hidden={!menu}>
        <nav aria-label="Mobile">
          <span className="menu-sheet__label">{dd.label}</span>
          {dd.items.map((it) => (
            <a key={it.label} className="menu-sheet__sub" href={it.href} target="_blank" rel="noopener noreferrer" onClick={() => setMenu(false)}>
              {it.label}<IconExt className="ext" />
            </a>
          ))}
          <span className="menu-sheet__label">More</span>
          {T.nav.links.map((l) => (
            <a key={l.label} href={l.href} onClick={(e) => go(e, l.href)}>{l.label}</a>
          ))}
        </nav>
      </div>
    </React.Fragment>
  );
}

/* ============================================================ HERO */
function Hero({ reduced, started }) {
  const [ready, setReady] = useS(false);
  const [done, setDone] = useS(false);
  useE(() => {
    if (!started) return;
    const a = setTimeout(() => setReady(true), 60);
    const b = setTimeout(() => setDone(true), reduced ? 80 : 2000);
    return () => { clearTimeout(a); clearTimeout(b); };
  }, [started, reduced]);
  const b = T.brand;
  return (
    <section className={"hero" + (ready ? " is-ready" : "") + (done ? " entrance-done" : "")} id="top">
      <div className="hero-top">
        <h1 className="hero-statement">{b.heroStatement}</h1>
        <p className="hero-sub">{T.intro.body}</p>
      </div>
      <div className="hero-burst" aria-hidden="true">
        <div className="hero-burst__glow" />
        <img className="burst-layer burst-h" src={b.heroBurst} alt="" />
        <img className="burst-layer burst-full" src={b.heroBurst} alt="" />
      </div>
    </section>
  );
}

/* Playback controller injected into each product demo iframe. The demos are
   animations.jsx Stages (autoplay + loop). This wraps their rAF clock so the
   carousel can: keep them frozen on frame 0, replay one full pass from the
   start on command, then hold the final frame and report 'done'. It also
   sandboxes the engine's playhead persistence (keys ending ':t') to an
   in-memory store so demos always start at 0 and never touch shared storage. */
const DEMO_CTL = `
(function(){ try{
  var TKEY=':t', tStore={}, real=null;
  try{ real=window.localStorage; }catch(e){}
  var proxy={
    getItem:function(k){ if(k&&k.slice(-2)===TKEY){ return (k in tStore)?tStore[k]:'0'; } try{return real.getItem(k);}catch(e){return null;} },
    setItem:function(k,v){ if(k&&k.slice(-2)===TKEY){ tStore[k]=String(v); return; } try{real.setItem(k,v);}catch(e){} },
    removeItem:function(k){ if(k&&k.slice(-2)===TKEY){ delete tStore[k]; } },
    clear:function(){},
    key:function(i){ try{return real.key(i);}catch(e){return null;} },
    get length(){ try{return real.length;}catch(e){return 0;} }
  };
  try{ Object.defineProperty(window,'localStorage',{configurable:true,get:function(){return proxy;}}); }catch(e){}
  function curT(){ for(var k in tStore){ if(k.slice(-2)===TKEY) return parseFloat(tStore[k])||0; }
    try{ for(var i=0;i<real.length;i++){ var rk=real.key(i); if(rk&&rk.slice(-2)===TKEY) return parseFloat(real.getItem(rk))||0; } }catch(e){} return 0; }

  var Ctl=window.__animCtl={ playing:false, vt:0, pendingCb:null, driver:false, resetPhase:0,
    prevT:null, peak:0, doneSent:false, seekTo:null, idle:0, lastTs:null };
  var realRAF=window.requestAnimationFrame.bind(window);
  window.requestAnimationFrame=function(cb){ Ctl.pendingCb=cb; if(!Ctl.driver) startDriver(); return (Ctl._id=(Ctl._id||0)+1); };
  window.cancelAnimationFrame=function(){ Ctl.pendingCb=null; };
  function invoke(dtMs){ var cb=Ctl.pendingCb; if(!cb) return; Ctl.pendingCb=null; Ctl.vt+=dtMs; cb(Ctl.vt); }
  function done(){ if(!Ctl.doneSent){ Ctl.doneSent=true; Ctl.playing=false; try{parent.postMessage({__anim:'done'},'*');}catch(e){} } }
  function startDriver(){ Ctl.driver=true; Ctl.lastTs=null; realRAF(function frame(realTs){ if(!Ctl.driver) return;
    if(Ctl.seekTo!=null && Ctl.pendingCb){ var ts=curT(); invoke((Ctl.seekTo-ts)*1000); Ctl.seekTo=null; done(); }
    else if(Ctl.resetPhase===1 && Ctl.pendingCb){ invoke(1000/60); Ctl.resetPhase=2; }
    else if(Ctl.resetPhase===2 && Ctl.pendingCb){ var t2=curT(); invoke(-(t2*1000)); Ctl.resetPhase=0; Ctl.prevT=null; Ctl.peak=0; Ctl.doneSent=false; Ctl.idle=0; }
    else if(Ctl.playing && Ctl.pendingCb){ var dt=(Ctl.lastTs!=null)?(realTs-Ctl.lastTs):(1000/60); if(dt<0||dt>120) dt=1000/60; invoke(dt); var nt=curT();
      if(Ctl.prevT!=null && nt < Ctl.prevT-0.05){ Ctl.seekTo=Ctl.peak; } else { if(nt>Ctl.peak) Ctl.peak=nt; Ctl.prevT=nt; } Ctl.idle=0; }
    else if(Ctl.playing && !Ctl.pendingCb && Ctl.resetPhase===0 && Ctl.seekTo==null){ if(++Ctl.idle>4) done(); }
    Ctl.lastTs=realTs;
    realRAF(frame);
  }); }
  window.addEventListener('message',function(e){ var d=e.data||{};
    if(d.__anim==='play'){ Ctl.resetPhase=1; Ctl.playing=true; Ctl.seekTo=null; Ctl.idle=0; }
    else if(d.__anim==='freeze'){ Ctl.playing=false; Ctl.resetPhase=0; Ctl.seekTo=null; } });
}catch(err){} })();
`;

/* ============================================================ PRODUCT ARC (GSAP 3D concave carousel) */
function ProductArc({ reduced, motionMult, started }) {
  const N = T.products.length;
  const sectionRef = useR(null);
  const cardRefs = useR([]);
  const stateRef = useR({ entrance: reduced ? 1 : 0, phase: (N - 1) / 2, hovered: -1, sel: new Array(N).fill(0), spacing: 400, centered: new Array(N).fill(false) });
  const loopRef = useR(null);
  const demoRefs = useR([]);
  const centerRef = useR(-1);
  const navRef = useR(null);
  const [hovered, setHovered] = useS(-1);
  const [centerIdx, setCenterIdx] = useS(-1);
  const [tokens, setTokens] = useS(() => new Array(N).fill(0));

  // continuous concave-arc placement driven by one phase value (loops)
  const apply = () => {
    const st = stateRef.current;
    const ep = st.entrance;
    const SP = st.spacing;
    for (let i = 0; i < N; i++) {
      const el = cardRefs.current[i];
      if (!el) continue;
      let o = ((i - st.phase) % N + N) % N;   // 0..N
      if (o > N / 2) o -= N;                    // wrap to [-N/2, N/2)
      const a = Math.abs(o);
      const s = st.sel[i];
      // replay the card's animation as it rotates into the centre
      if (a < 0.3 && !st.centered[i]) { st.centered[i] = true; const idx = i; setTokens((tk) => { const n = [...tk]; n[idx] += 1; return n; }); }
      else if (a >= 0.72 && st.centered[i]) { st.centered[i] = false; }
      const x = o * SP;
      let ry = -o * 22;
      let z = -a * 168;
      let scale = 1 - a * 0.05;
      const edge = clamp01(1 - Math.max(0, a - 1.3) / 0.7);   // fade the wrap
      const y = lerp(74, a * 14, ep);
      z = lerp(z - 220, z, ep);
      z += s * 120;
      ry *= (1 - s);
      scale += s * 0.05;
      const otherHover = st.hovered >= 0 && st.hovered !== i;
      const centreBright = 1 - clamp01(a) * 0.22;
      const bright = otherHover ? 0.5 : centreBright;
      const blur = lerp(8, 0, ep) + (otherHover ? 1.6 : 0) + a * 0.4;
      const opacity = edge * ep;
      el.style.transform = `translate3d(${x}px, ${y}px, ${z}px) translate(-50%, -50%) rotateY(${ry}deg) scale(${scale})`;
      el.style.opacity = String(opacity);
      el.style.filter = `blur(${blur}px) brightness(${bright})`;
      el.style.zIndex = String((st.hovered === i ? 100 : 0) + Math.round(50 - a * 12));
      el.style.pointerEvents = opacity > 0.2 ? "auto" : "none";
    }
  };

  useE(() => {
    const st = stateRef.current;
    const measure = () => { const cw = (cardRefs.current[0] && cardRefs.current[0].offsetWidth) || 340; st.spacing = cw + 24; apply(); };
    if (reduced) { st.entrance = 1; return; }
    if (!started) return;   // hold the cards until the page reveal begins
    measure();
    window.addEventListener("resize", measure);
    const G = window.gsap;
    let entTween = null, stopped = false, seqStarted = false;
    const stepDur = 0.85;      // travel time to the next card (seconds)
    const holdMs = 320;        // brief beat on the finished frame before moving on
    const safetyMs = 12000;    // advance even if a demo never reports completion
    const centreOf = (ph) => (((Math.round(ph) % N) + N) % N);

    let curPhase = Math.round(st.phase);
    let waitIdx = -1, arriveAt = 0, safetyT = null, holdT = null;

    const post = (i, cmd) => { const f = demoRefs.current[i]; if (f && f.contentWindow) { try { f.contentWindow.postMessage({ __anim: cmd }, "*"); } catch (e) {} } };

    // bring the next card to centre
    const advanceTo = (toPhase) => {
      loopRef.current = G.to(st, { phase: toPhase, duration: stepDur, ease: "power2.inOut", onUpdate: apply, onComplete: () => onArrive(toPhase) });
    };
    // resume the cycle (waiting out any active hover)
    const moveOn = () => {
      if (stopped) return;
      if (st.hovered >= 0) { holdT = setTimeout(moveOn, 400); return; }
      advanceTo(curPhase - 1);
    };
    // the centred demo has played through once — hold a brief beat, then advance
    const finishCard = () => {
      if (stopped || waitIdx < 0) return;
      waitIdx = -1;
      if (safetyT) { clearTimeout(safetyT); safetyT = null; }
      holdT = setTimeout(moveOn, holdMs);
    };
    // a card just parked dead-centre: freeze the rest, replay this one from frame 0
    const onArrive = (toPhase) => {
      curPhase = toPhase;
      const c = centreOf(toPhase);
      centerRef.current = c;
      setCenterIdx(c);
      waitIdx = c;
      arriveAt = performance.now();
      demoRefs.current.forEach((f, j) => post(j, j === c ? "play" : "freeze"));
      if (safetyT) clearTimeout(safetyT);
      safetyT = setTimeout(finishCard, safetyMs);
    };
    const startSequence = () => { if (seqStarted || stopped) return; seqStarted = true; advanceTo(Math.round(st.phase)); };

    // manual nav (the side arrows) — jump straight to the next/prev card
    const goManual = (toPhase) => {
      if (stopped) return;
      if (safetyT) { clearTimeout(safetyT); safetyT = null; }
      if (holdT) { clearTimeout(holdT); holdT = null; }
      waitIdx = -1; seqStarted = true;
      if (loopRef.current) loopRef.current.kill();
      advanceTo(toPhase);
    };
    navRef.current = {
      next: () => goManual(curPhase - 1),
      prev: () => goManual(curPhase + 1),
      to: (c) => goManual(c + N * Math.round((curPhase - c) / N)),   // shortest path to a given card
    };

    // the centred demo posts {__anim:'done'} when it completes one play
    const onMsg = (e) => {
      if (!e.data || e.data.__anim !== "done" || waitIdx < 0) return;
      const f = demoRefs.current[waitIdx];
      if (f && e.source === f.contentWindow) finishCard();
    };
    window.addEventListener("message", onMsg);

    const forced = setTimeout(() => { if (st.entrance < 0.99) { st.entrance = 1; apply(); } startSequence(); }, 3000);
    let kick = null;
    const startEntrance = () => {
      if (!G) { st.entrance = 1; apply(); startSequence(); return; }
      entTween = G.to(st, {
        entrance: 1, ease: "power3.out", duration: 1.0,
        onUpdate: apply, onComplete: () => { clearTimeout(forced); startSequence(); },
      });
    };
    // the lightbeam "gives birth" to the cards just after the headline — synced with the burst
    kick = setTimeout(startEntrance, 850);
    return () => {
      stopped = true;
      clearTimeout(forced); if (kick) clearTimeout(kick); if (safetyT) clearTimeout(safetyT); if (holdT) clearTimeout(holdT);
      window.removeEventListener("resize", measure);
      window.removeEventListener("message", onMsg);
      navRef.current = null;
      if (entTween) { if (entTween.scrollTrigger) entTween.scrollTrigger.kill(); entTween.kill(); }
      if (loopRef.current) loopRef.current.kill();
    };
  }, [reduced, started]);

  const setSel = (i, v) => {
    const st = stateRef.current;
    const G = window.gsap;
    if (G && !reduced) G.to(st.sel, { [i]: v, duration: 0.5, ease: "power3.out", onUpdate: apply });
    else { st.sel[i] = v; apply(); }
  };
  const postDemo = (i, cmd) => { const f = demoRefs.current[i]; if (f && f.contentWindow) { try { f.contentWindow.postMessage({ __anim: cmd }, "*"); } catch (e) {} } };
  const onEnter = (i) => {
    if (stateRef.current.hovered === i) return;
    stateRef.current.hovered = i;
    setHovered(i);
    setTokens((t) => { const n = [...t]; n[i] += 1; return n; });
    if (loopRef.current) loopRef.current.pause();
    postDemo(i, "play");          // replay the hovered card's animation from the start
    setSel(i, 1);
    apply();
  };
  const onLeave = (i) => {
    if (stateRef.current.hovered === i) { stateRef.current.hovered = -1; setHovered(-1); }
    if (loopRef.current && stateRef.current.hovered === -1) loopRef.current.resume();
    if (centerRef.current !== i) postDemo(i, "freeze");   // a side card stops once you leave it
    setSel(i, 0);
    apply();
  };

  // load embedded product demos (e.g. /cro) via srcdoc so they run in-realm
  useE(() => {
    const sec = sectionRef.current;
    if (!sec) return;
    let cancelled = false;
    const timers = [];
    // bundles re-create their document on unpack, wiping any injected <head> style,
    // and a global error sink can paint a "[bundle] error" box afterwards. Poll into
    // each (same-origin) iframe and keep the error sink + loader badge hidden.
    const killErr = (f) => {
      try {
        const d = f.contentDocument;
        if (!d) return;
        if (!d.getElementById("__omx_hide")) {
          const s = d.createElement("style");
          s.id = "__omx_hide";
          s.textContent = "#__bundler_err,#__bundler_loading,#__bundler_placeholder{display:none!important}";
          (d.head || d.documentElement).appendChild(s);
        }
        const e = d.getElementById("__bundler_err");
        if (e) e.remove();
        // hide the animation's playback/scrubber bar (engine renders it with max-width:680 + a top border)
        d.querySelectorAll("div[style]").forEach((el) => {
          const s = el.getAttribute("style") || "";
          if (s.indexOf("680px") > -1 && (s.indexOf("border-top") > -1 || s.indexOf("20,20,20") > -1 || s.indexOf("20, 20, 20") > -1)) el.style.display = "none";
        });
      } catch (_) {}
    };
    sec.querySelectorAll(".arc-card__demo[data-src]").forEach((f) => {
      fetch(f.getAttribute("data-src")).then((r) => r.text()).then((t) => {
        if (cancelled) return;
        // inject the playback controller so the carousel can play each demo once,
        // on demand, instead of letting it loop on its own
        const inject = "<scr" + "ipt>" + DEMO_CTL + "</scr" + "ipt>";
        f.srcdoc = t.indexOf("</head>") > -1 ? t.replace("</head>", inject + "</head>") : inject + t;
        const iv = setInterval(() => killErr(f), 250);
        timers.push(iv);
        timers.push(setTimeout(() => clearInterval(iv), 9000));
      }).catch(() => {});
    });
    return () => { cancelled = true; timers.forEach((id) => { clearInterval(id); clearTimeout(id); }); };
  }, []);

  return (
    <section className={"arc" + (reduced ? " is-static" : "")} id="products" ref={sectionRef}
      aria-label="Products and studios" data-hovered={hovered}>
      <div className="arc__head">
        <h2>{T.intro.headline}</h2>
      </div>
      <div className="arc__tabs" role="tablist" aria-label="Jump to a studio">
        {[["conversionworks", "Conversion Works"], ["workers", "Workers"], ["siteworks", "Siteworks"], ["cro", "/cro"]].map(([id, label]) => {
          const idx = T.products.findIndex((p) => p.id === id);
          if (idx < 0) return null;
          const p = T.products[idx];
          const on = centerIdx === idx;
          return (
            <button key={id} className={"arc__tab" + (on ? " is-active" : "")} role="tab" aria-selected={on}
              style={{ "--accent": p.accent }} onClick={() => navRef.current && navRef.current.to(idx)}>
              {label}
            </button>
          );
        })}
      </div>
      <div className="arc__stage">
        <button className="arc__nav arc__nav--prev" aria-label="Previous" onClick={() => navRef.current && navRef.current.prev()}>
          <IconArrow s={20} />
        </button>
        <button className="arc__nav arc__nav--next" aria-label="Next" onClick={() => navRef.current && navRef.current.next()}>
          <IconArrow s={20} />
        </button>
        <div className="arc__track">
          {T.products.map((p, i) => (
            <article className="arc-card" key={p.id} ref={(el) => (cardRefs.current[i] = el)}
              onMouseEnter={() => onEnter(i)} onMouseLeave={() => onLeave(i)}>
              <a className={"arc-card__inner" + (hovered === i ? " is-sel" : "")} href={p.href}
                target="_blank" rel="noopener noreferrer" style={{ "--accent": p.accent }}
                aria-label={`${p.name} — ${p.type}. ${p.cta} (opens in a new tab)`}
                onFocus={() => onEnter(i)} onBlur={() => onLeave(i)}>
                <div className="arc-card__copy">
                  <div className="arc-card__head">
                    {p.mark
                      ? <img className="arc-card__logo" src={p.mark} alt={p.name} />
                      : <span className="arc-card__logo is-text"><span className="slash">/</span>cro</span>}
                    {p.pill && <img className="arc-card__pill" src={p.pill} alt={p.type} />}
                  </div>
                  <h3 className="arc-card__headline">{p.headline}</h3>
                  <p className="arc-card__desc">{p.description}</p>
                  <p className="arc-card__bestfor"><b>Best for</b>{p.bestFor}</p>
                  <span className="arc-card__cta">{p.cta}<span className="arrow"><IconArrow s={14} /></span><IconExt className="ext" /></span>
                </div>
                <div className="arc-card__visual" aria-hidden="true">
                  {p.demo
                    ? <iframe className="arc-card__demo" data-src={p.demo} ref={(el) => (demoRefs.current[i] = el)} title={`${p.name} demo`} tabIndex={-1} scrolling="no"></iframe>
                    : <ProductCanvas kind={p.animation} accent={p.accent} active={reduced || centerIdx === i || hovered === i} playToken={tokens[i]} reduced={reduced} motionMult={motionMult} />}
                  {p.still && <img className={"arc-card__still" + ((centerIdx === i || hovered === i) ? " is-hidden" : "")} src={p.still} alt="" aria-hidden="true" />}
                </div>
              </a>
            </article>
          ))}
        </div>
      </div>
    </section>
  );
}

/* ============================================================ MANIFESTO */
function Manifesto() {
  const secRef = useR(null);
  const lineRefs = useR([]);
  const m = T.manifesto;
  const [inLines, setInLines] = useS(() => new Set());
  const [secIn, setSecIn] = useS(false);
  const [forced, setForced] = useS(false);
  useE(() => {
    const obs = new IntersectionObserver(
      (ents) => {
        const hits = ents.filter((e) => e.isIntersecting).map((e) => Number(e.target.getAttribute("data-i")));
        if (hits.length) setInLines((prev) => { const n = new Set(prev); hits.forEach((i) => n.add(i)); return n; });
      },
      { threshold: 0.5 }
    );
    lineRefs.current.forEach((el) => el && obs.observe(el));
    const sObs = new IntersectionObserver(
      (ents) => ents.forEach((e) => { if (e.isIntersecting) setSecIn(true); }),
      { threshold: 0.3 }
    );
    if (secRef.current) sObs.observe(secRef.current);
    const net = setTimeout(() => { setForced(true); setInLines(new Set(m.lines.map((_, i) => i))); setSecIn(true); }, 5200);
    return () => { obs.disconnect(); sObs.disconnect(); clearTimeout(net); };
  }, []);
  return (
    <section className={"manifesto" + (secIn ? " is-in" : "") + (forced ? " force-reveal" : "")} id="contact" ref={secRef}>
      <div className="manifesto__lines">
        {m.lines.map((l, i) => (
          <p key={i} data-i={i} className={inLines.has(i) ? "is-in" : ""}
             ref={(el) => (lineRefs.current[i] = el)} style={{ "--lineaccent": l.color, "--linegrad": l.grad }}>
            {l.text}<span className="accent">{l.accent}{l.tail}</span>
          </p>
        ))}
      </div>
      <a className="manifesto__link" href={m.link.href}>
        {m.link.pre}<span className="u">{m.link.emph}</span>
        <span className="arrow"><IconArrow s={16} /></span>
      </a>
    </section>
  );
}

/* ============================================================ FOOTER */
function Footer() {
  const f = T.footer;
  const year = new Date().getFullYear();
  const ICON = {
    x: "M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z",
    linkedin: "M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 01-2.063-2.065 2.064 2.064 0 112.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.225 0z",
  };
  return (
    <footer className="foot" aria-label="Footer">
      <div className="foot__row">
        <nav className="foot__brands" aria-label="Our brands">
          {f.brands.map((b) => (
            <a key={b.label} href={b.href} target="_blank" rel="noopener noreferrer">{b.label}</a>
          ))}
        </nav>
        <div className="foot__social" aria-label="Social">
          {(f.social || []).map((s) => (
            <a key={s.label} href={s.href} target="_blank" rel="noopener noreferrer" aria-label={s.label}>
              <svg viewBox="0 0 24 24" aria-hidden="true"><path d={ICON[s.icon]} /></svg>
            </a>
          ))}
        </div>
      </div>
      <div className="foot__base">
        <nav className="foot__links" aria-label="Site">
          {(f.links || []).map((l) => (
            <a key={l.label} href={l.href}>{l.label}</a>
          ))}
        </nav>
        <a className="foot__addr" href={f.addressHref} target="_blank" rel="noopener noreferrer">
          <svg className="foot__nj" viewBox="0 0 64 80" aria-hidden="true"><path d="M22 4 40 14 44 22 39 29 52 34 49 47 42 60 30 78 25 64 20 52 11 43 7 33 16 21 19 11Z" /></svg>
          {f.address}
        </a>
      </div>
      <span className="foot__sig">© {year} Taurist Technologies Inc. · <b>{f.signature.lead} {f.signature.trail}</b></span>
    </footer>
  );
}

/* ============================================================ TWEAKS */
function Controls({ t, setTweak }) {
  return (
    <TweaksPanel>
      <TweakSection label="Atmosphere" />
      <TweakSlider label="Accent intensity" value={t.accentIntensity} min={0.4} max={1.6} step={0.05}
        onChange={(v) => setTweak("accentIntensity", v)} />
      <TweakRadio label="Motion" value={t.motion} options={["subtle", "balanced", "cinematic"]}
        onChange={(v) => setTweak("motion", v)} />
    </TweaksPanel>
  );
}

/* ============================================================ APP */
function App() {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const reduced = useReducedMotion();
  const motionMult = MOTION[t.motion] ?? 0.8;
  const loaded = true;

  useE(() => {
    const r = document.documentElement.style;
    r.setProperty("--accent-mult", String(t.accentIntensity));
    r.setProperty("--motion-mult", String(motionMult));
  }, [t.accentIntensity, motionMult]);

  const jump = useCallback((href) => {
    if (!href) return;
    // real page / external link → navigate
    if (href.charAt(0) !== "#") { window.location.href = href; return; }
    if (href === "#top") { window.scrollTo({ top: 0, behavior: reduced ? "auto" : "smooth" }); return; }
    const el = document.querySelector(href);
    if (el) window.scrollTo({ top: el.offsetTop - 30, behavior: reduced ? "auto" : "smooth" });
  }, [reduced]);

  return (
    <React.Fragment>
      <Header onJump={jump} entered={loaded} />
      <main>
        <Hero reduced={reduced} started={loaded} />
        <ProductArc reduced={reduced} motionMult={motionMult} started={loaded} />
        <Manifesto />
      </main>
      <Footer />
      <Controls t={t} setTweak={setTweak} />
    </React.Fragment>
  );
}

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