// GPTech Studio — Lab variant: boot intro, custom cursor, pinned manifesto,
// expandable services, animated counters, marquee, scroll progress.
const { useState, useEffect, useRef, useMemo, useCallback } = React;

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "accent": "#1d4ed8",
  "fontPair": "geist",
  "dark": false,
  "lang": "it",
  "heroAnim": "type"
}/*EDITMODE-END*/;

const FONT_PAIRS = {
  geist:    { sans: '"Geist", "Helvetica Neue", sans-serif',  mono: '"IBM Plex Mono", ui-monospace, monospace', serif: '"Instrument Serif", Georgia, serif' },
  inter:    { sans: '"Inter Tight", "Helvetica Neue", sans-serif', mono: '"JetBrains Mono", ui-monospace, monospace', serif: '"Instrument Serif", Georgia, serif' },
  helvetica:{ sans: '"Helvetica Neue", Helvetica, Arial, sans-serif', mono: '"IBM Plex Mono", ui-monospace, monospace', serif: '"Source Serif 4", Georgia, serif' },
};

const ACCENT_OPTIONS = ["#1d4ed8", "#0a0a0a", "#15803d", "#ea580c"];

// ---------- hooks ----------
function useHashRoute() {
  const [route, setRoute] = useState(() => (location.hash.replace("#/", "") || "home"));
  useEffect(() => {
    const onHash = () => setRoute(location.hash.replace("#/", "") || "home");
    window.addEventListener("hashchange", onHash);
    return () => window.removeEventListener("hashchange", onHash);
  }, []);
  return [route, (r) => { location.hash = "#/" + r; window.scrollTo({ top: 0, behavior: "instant" }); }];
}
function useClock() {
  const [now, setNow] = useState(new Date());
  useEffect(() => {
    const id = setInterval(() => setNow(new Date()), 1000);
    return () => clearInterval(id);
  }, []);
  const hh = String(now.getHours()).padStart(2, "0");
  const mm = String(now.getMinutes()).padStart(2, "0");
  const ss = String(now.getSeconds()).padStart(2, "0");
  return `${hh}:${mm}:${ss} CET`;
}
function useInView(opts = { threshold: 0.18 }) {
  const ref = useRef(null);
  const [inView, setInView] = useState(false);
  useEffect(() => {
    if (!ref.current) return;
    const io = new IntersectionObserver(([e]) => { if (e.isIntersecting) setInView(true); }, opts);
    io.observe(ref.current);
    return () => io.disconnect();
  }, []);
  return [ref, inView];
}

// ---------- per-character stagger ----------
function Stagger({ text, baseDelay = 0, step = 0.018, className = "" }) {
  const chars = useMemo(() => Array.from(text), [text]);
  return (
    <span className={"stagger " + className}>
      {chars.map((c, i) => (
        <span key={i} style={{ animationDelay: `${baseDelay + i * step}s` }}>
          {c === " " ? "\u00A0" : c}
        </span>
      ))}
    </span>
  );
}

// ---------- Crosshair cursor ----------
function CrosshairCursor() {
  const ref = useRef(null);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    let raf = 0, x = 0, y = 0;
    const onMove = (e) => {
      x = e.clientX; y = e.clientY;
      cancelAnimationFrame(raf);
      raf = requestAnimationFrame(() => {
        el.style.transform = `translate(${x}px, ${y}px) translate(-50%, -50%)`;
        el.style.opacity = "1";
      });
      const t = e.target;
      const interactive = t && t.closest && t.closest('a, button, [data-cursor="hover"]');
      el.dataset.state = interactive ? "hover" : "idle";
    };
    const onLeave = () => el.style.opacity = "0";
    window.addEventListener("mousemove", onMove);
    document.addEventListener("mouseleave", onLeave);
    return () => {
      window.removeEventListener("mousemove", onMove);
      document.removeEventListener("mouseleave", onLeave);
      cancelAnimationFrame(raf);
    };
  }, []);
  return (
    <div className="cursor" ref={ref} data-state="idle">
      <span className="ring" />
    </div>
  );
}

// ---------- Scroll progress ----------
function ScrollProgress() {
  const ref = useRef(null);
  useEffect(() => {
    const onScroll = () => {
      const max = document.documentElement.scrollHeight - window.innerHeight;
      const p = max > 0 ? Math.min(100, (window.scrollY / max) * 100) : 0;
      if (ref.current) ref.current.style.setProperty("--p", p + "%");
    };
    window.addEventListener("scroll", onScroll, { passive: true });
    onScroll();
    return () => window.removeEventListener("scroll", onScroll);
  }, []);
  return <div className="scroll-progress" ref={ref} aria-hidden="true" />;
}

// ---------- Ticker / Nav ----------
function Ticker({ lang }) {
  const time = useClock();
  return (
    <div className="ticker">
      <div className="ticker-inner">
        <span><b>{time}</b></span>
        <div className="ticker-mid">
          <span>{lang === "it" ? "Q3 26 · " : "Q3 26 · "}<b>{lang === "it" ? "2 slot aperti" : "2 slots open"}</b></span>
          <span className="live">{lang === "it" ? "Risposta ≤ 24h" : "Reply ≤ 24h"}</span>
        </div>
        <span>{lang === "it" ? "Italia · remoto EU" : "Italy · EU remote"}</span>
      </div>
    </div>
  );
}
// ---------- Bandiere (toggle lingua) ----------
function FlagIT() {
  return (
    <svg viewBox="0 0 24 24" className="flag" aria-hidden="true">
      <defs><clipPath id="flag-it"><circle cx="12" cy="12" r="11.5" /></clipPath></defs>
      <g clipPath="url(#flag-it)">
        <rect x="0" y="0" width="8" height="24" fill="#009246" />
        <rect x="8" y="0" width="8" height="24" fill="#ffffff" />
        <rect x="16" y="0" width="8" height="24" fill="#ce2b37" />
      </g>
      <circle cx="12" cy="12" r="11.5" fill="none" stroke="rgba(0,0,0,0.12)" />
    </svg>
  );
}
function FlagEN() {
  return (
    <svg viewBox="0 0 24 24" className="flag" aria-hidden="true">
      <defs><clipPath id="flag-en"><circle cx="12" cy="12" r="11.5" /></clipPath></defs>
      <g clipPath="url(#flag-en)">
        <rect x="0" y="0" width="24" height="24" fill="#012169" />
        <path d="M0 0 L24 24 M24 0 L0 24" stroke="#ffffff" strokeWidth="4.5" />
        <path d="M0 0 L24 24 M24 0 L0 24" stroke="#c8102e" strokeWidth="1.8" />
        <path d="M12 0 V24 M0 12 H24" stroke="#ffffff" strokeWidth="6.5" />
        <path d="M12 0 V24 M0 12 H24" stroke="#c8102e" strokeWidth="3.6" />
      </g>
      <circle cx="12" cy="12" r="11.5" fill="none" stroke="rgba(0,0,0,0.12)" />
    </svg>
  );
}

// ---------- Bottoni flottanti (al posto della navbar) ----------
function FloatingNav({ lang, setLang, t, requestCheck }) {
  const goTop = () => window.scrollTo({ top: 0, behavior: "smooth" });
  return (
    <div className="fab-bar">
      <button className="fab fab-logo" onClick={goTop} aria-label="GP Tech Studio — torna su">
        <img src="uploads/logogptech.png?v=2" alt="GP Tech Studio" />
      </button>
      <div className="fab-right">
        <button className="fab fab-flag" onClick={() => setLang(lang === "it" ? "en" : "it")}
                aria-label={lang === "it" ? "Switch to English" : "Passa all'italiano"}
                title={lang === "it" ? "English" : "Italiano"}>
          {lang === "it" ? <FlagIT /> : <FlagEN />}
        </button>
        <button className="fab fab-check" onClick={() => requestCheck("")}>
          <span className="fab-dot" aria-hidden="true" />
          {t.nav.cta}
        </button>
      </div>
    </div>
  );
}

// ---------- Section divider ----------
function SecDiv({ left, right }) {
  return (
    <div className="sec-div">
      <span>{left}</span>
      <span className="dashes" aria-hidden="true" />
      <span>{right}</span>
    </div>
  );
}

// ---------- Hero diagram ----------
function HeroDiagram() {
  return (
    <div className="hero-diagram">
      <div className="hero-diagram-head">
        <span>system / architecture</span>
        <span className="dots"><span /><span /><span /></span>
      </div>
      <svg viewBox="0 0 320 220" fill="none">
        <defs>
          <pattern id="dotgrid" width="10" height="10" patternUnits="userSpaceOnUse">
            <circle cx="1" cy="1" r="0.6" fill="currentColor" opacity="0.18" />
          </pattern>
        </defs>
        <rect width="320" height="220" fill="url(#dotgrid)" color="currentColor" />
        <g stroke="currentColor" strokeWidth="1" className="draw" fill="var(--bg)">
          <rect x="20" y="22" width="84" height="34" rx="3" />
          <rect x="118" y="22" width="84" height="34" rx="3" />
          <rect x="216" y="22" width="84" height="34" rx="3" />
          <rect x="20" y="92" width="280" height="44" rx="3" />
          <rect x="20" y="172" width="84" height="34" rx="3" />
          <rect x="118" y="172" width="84" height="34" rx="3" />
          <rect x="216" y="172" width="84" height="34" rx="3" />
        </g>
        <g stroke="currentColor" strokeWidth="1" strokeDasharray="3 4" opacity="0.55" className="draw-fast">
          <path d="M62 56 L62 92" />
          <path d="M160 56 L160 92" />
          <path d="M258 56 L258 92" />
          <path d="M62 136 L62 172" />
          <path d="M160 136 L160 172" />
          <path d="M258 136 L258 172" />
        </g>
        <g className="pop">
          <rect x="118" y="92" width="84" height="44" rx="3" fill="var(--accent)" opacity="0.08" />
          <rect x="118" y="92" width="84" height="44" rx="3" fill="none" stroke="var(--accent)" strokeWidth="1" />
        </g>
        <g fill="currentColor" fontFamily="var(--ff-mono)" fontSize="8" letterSpacing="0.08em" style={{ textTransform: "uppercase" }} opacity="0.7" className="pop">
          <text x="62" y="44" textAnchor="middle">Web</text>
          <text x="160" y="44" textAnchor="middle">App</text>
          <text x="258" y="44" textAnchor="middle">Ads</text>
          <text x="160" y="120" textAnchor="middle" fill="var(--accent)" opacity="1">Studio</text>
          <text x="62" y="194" textAnchor="middle">Build</text>
          <text x="160" y="194" textAnchor="middle">Ship</text>
          <text x="258" y="194" textAnchor="middle">Scale</text>
        </g>
      </svg>
    </div>
  );
}

// ---------- Hero rotating phrase ----------
function RotatingPhrase({ phrases, baseDelay }) {
  const [idx, setIdx] = useState(0);
  const [phase, setPhase] = useState("in"); // "in" | "hold" | "out"
  const firstRender = useRef(true);
  useEffect(() => {
    // Initial: skip the in-animation duration (Stagger handles it), then hold
    const inDur = firstRender.current ? Math.max(600, baseDelay * 1000 + phrases[idx].length * 18 + 400) : 700;
    firstRender.current = false;
    const holdMs = 2600;
    const outMs = 500;
    const tHold = setTimeout(() => setPhase("out"), inDur + holdMs);
    const tNext = setTimeout(() => {
      setIdx((i) => (i + 1) % phrases.length);
      setPhase("in");
    }, inDur + holdMs + outMs);
    return () => { clearTimeout(tHold); clearTimeout(tNext); };
  }, [idx, phrases, baseDelay]);
  const cur = phrases[idx];
  return (
    <span className={"rot-phrase " + (phase === "out" ? "rot-out" : "rot-in")} key={idx}>
      <span>{cur.lead}</span>
      <span className="ital accent">{cur.em}</span>
      <span>{cur.tail}</span>
    </span>
  );
}

// ---------- Hero typewriter phrase ----------
function TypewriterPhrase({ phrases }) {
  const [idx, setIdx] = useState(0);
  const [text, setText] = useState("");
  const [mode, setMode] = useState("type"); // type | hold | erase
  const cur = phrases[idx];
  const full = cur.lead + cur.em + cur.tail;
  useEffect(() => {
    let t;
    if (mode === "type") {
      if (text.length < full.length) {
        t = setTimeout(() => setText(full.slice(0, text.length + 1)), 55 + Math.random() * 40);
      } else {
        t = setTimeout(() => setMode("hold"), 1400);
      }
    } else if (mode === "hold") {
      t = setTimeout(() => setMode("erase"), 600);
    } else if (mode === "erase") {
      if (text.length > 0) {
        t = setTimeout(() => setText(full.slice(0, text.length - 1)), 26);
      } else {
        setIdx((i) => (i + 1) % phrases.length);
        setMode("type");
      }
    }
    return () => clearTimeout(t);
  }, [text, mode, idx, full, phrases.length]);
  // Split rendered text into lead / em / tail spans
  const leadLen = cur.lead.length;
  const emEnd = leadLen + cur.em.length;
  const visLead = text.slice(0, Math.min(text.length, leadLen));
  const visEm = text.slice(leadLen, Math.min(text.length, emEnd));
  const visTail = text.slice(emEnd);
  return (
    <span className="rot-phrase type-phrase">
      <span>{visLead}</span>
      <span className="ital accent">{visEm}</span>
      <span>{visTail}</span>
      <span className="caret" aria-hidden="true">|</span>
    </span>
  );
}

// ---------- Hero ----------
function Hero({ t, lang, heroAnim, requestCheck }) {
  const baseDelay = 0.1;
  const phrases = lang === "it" ? [
    { lead: "alla ", em: "tua presenza", tail: " digitale." },
    { lead: "alle ", em: "tue automazioni", tail: " AI." },
    { lead: "ai ", em: "tuoi canali", tail: " di acquisizione." },
    { lead: "agli ", em: "strumenti", tail: " del tuo team." },
    { lead: "al ", em: "tuo prodotto", tail: " digitale." },
    { lead: "alla ", em: "tua strategia", tail: " di marketing." },
  ] : [
    { lead: "your ", em: "digital", tail: " presence." },
    { lead: "your ", em: "website", tail: " and brand." },
    { lead: "your ", em: "acquisition", tail: " channels." },
    { lead: "your ", em: "marketing", tail: " stack." },
    { lead: "your ", em: "digital", tail: " product." },
    { lead: "your ", em: "AI", tail: " workflows." },
  ];
  return (
    <section className="hero" data-cursor-label="Home" data-screen-label="Hero">
      <div className="wrap">
        <div className="hero-grid">
          <h1>
            {lang === "it" ? (
              <>
                <Stagger text="Diamo forma " baseDelay={baseDelay} />
                <br />
                {heroAnim === "type"
                  ? <TypewriterPhrase phrases={phrases} />
                  : <RotatingPhrase phrases={phrases} baseDelay={baseDelay + 0.45} />}
              </>
            ) : (
              <>
                <Stagger text="Shaping " baseDelay={baseDelay} />
                <br />
                {heroAnim === "type"
                  ? <TypewriterPhrase phrases={phrases} />
                  : <RotatingPhrase phrases={phrases} baseDelay={baseDelay + 0.45} />}
              </>
            )}
          </h1>
          <img className="hero-logo" src="uploads/logogptechorizzontale.png?v=2" alt="" aria-hidden="true" />
        </div>
        <div className="hero-bottom">
          <p className="hero-lead">
            {lang === "it"
              ? <>Progettiamo la presenza online, i canali di acquisizione clienti e le automazioni AI che <strong>fanno crescere il tuo business</strong>. Dalla strategia all'esecuzione, con <strong>risultati che si misurano</strong>.</>
              : <>We design the online presence, the customer-acquisition channels and the AI automations that <strong>grow your business</strong>. From strategy to execution, with <strong>results you can measure</strong>.</>}
          </p>
          <div className="hero-actions">
            <div className="hero-actions-row">
              <a className="btn btn-ink" href="#contatti" onClick={(e) => { e.preventDefault(); requestCheck(""); }}>
                {t.contact.cta_primary} <span aria-hidden>→</span>
              </a>
            </div>
          </div>
        </div>
        <div className="hero-scroll-hint" aria-hidden="true"></div>
      </div>
    </section>
  );
}

// ---------- Section + reveal wrapper ----------
function Section({ id, label, children }) {
  const [ref, inView] = useInView({ threshold: 0.15 });
  return (
    <section ref={ref} id={id} className={"section" + (inView ? " in-view" : "")} data-cursor-label={label}>
      {children}
    </section>
  );
}
function SectionHead({ title }) {
  return (
    <div className="section-head">
      <h2 className="section-title">{title}</h2>
    </div>
  );
}

// ---------- Services (expandable rows) ----------
const SVC_DETAILS = {
  it: {
    "01": { deliver: ["Analisi del posizionamento attuale", "Sito su misura e identità visiva", "Contenuti e ottimizzazione SEO", "Pubblicazione e monitoraggio"], outcome: ["Una presenza online che ti rappresenta", "Visibilità organica nel tempo", "Clienti che ti trovano e ti capiscono"] },
    "02": { deliver: ["Strategia di acquisizione", "Campagne pubblicitarie e landing page", "Tracciamento e attribuzione corretta", "Reportistica chiara mensile"], outcome: ["Costo per contatto sostenibile", "Crescita misurabile dei clienti", "Decisioni basate su dati reali"] },
    "03": { deliver: ["Mappatura dei casi d'uso", "Configurazione di strumenti AI", "Automazioni e integrazioni", "Formazione del team"], outcome: ["Più tempo per ciò che conta", "Adozione concreta, non imposta", "Decisioni più informate ogni giorno"] },
    "04": { deliver: ["Architettura tecnica su misura", "Sviluppo applicazioni e dashboard", "Integrazioni con i sistemi esistenti", "Documentazione e passaggio di consegne"], outcome: ["Soluzione che cresce con te", "Codice di proprietà, nessun vincolo", "Team autonomo nell'evoluzione"] },
  },
  en: {
    "01": { deliver: ["Current positioning analysis", "Tailored site and visual identity", "Content and SEO optimization", "Launch and monitoring"], outcome: ["An online presence that represents you", "Organic visibility over time", "Customers who find and understand you"] },
    "02": { deliver: ["Acquisition strategy", "Ad campaigns and landing pages", "Tracking and proper attribution", "Clear monthly reporting"], outcome: ["Sustainable cost per lead", "Measurable customer growth", "Decisions based on real data"] },
    "03": { deliver: ["Use-case mapping", "AI tooling setup", "Automations and integrations", "Team training"], outcome: ["More time for what matters", "Real adoption, not imposed", "Better-informed decisions every day"] },
    "04": { deliver: ["Bespoke technical architecture", "App and dashboard development", "Integration with existing systems", "Documentation and handover"], outcome: ["A solution that grows with you", "Code you own, no lock-in", "A team autonomous in its evolution"] },
  },
};

function Services({ t, lang, open, setOpen, focused, requestCheck }) {
  const lbl = lang === "it" ? { d: "Cosa includo", o: "Cosa otterrai" } : { d: "What I deliver", o: "Expected outcomes" };
  const ordered = useMemo(() => {
    if (!focused) return t.services;
    const sel = t.services.find((s) => s.n === focused);
    if (!sel) return t.services;
    return [sel, ...t.services.filter((s) => s.n !== focused)];
  }, [t.services, focused]);
  return (
    <>
      <Section id="servizi" label="Servizi">
        <div className="wrap">
          <SectionHead title={t.services_head.title} />
          <div className="svc-table">
            {ordered.map((s) => {
              const isOpen = open === s.n;
              const det = SVC_DETAILS[lang][s.n];
              return (
                <div className="svc-row" id={"svc-" + s.n} key={s.n} data-open={isOpen}
                     onClick={() => setOpen(isOpen ? null : s.n)} role="button" tabIndex={0}
                     onKeyDown={(e) => { if (e.key === "Enter") setOpen(isOpen ? null : s.n); }}>
                  <div className="svc-num">{!focused ? <b>{s.n}</b> : null}</div>
                  <div className="svc-head-cell">
                    <span className="svc-situation">{s.situation}</span>
                    <h3 className="svc-title">{s.t}</h3>
                  </div>
                  <p className="svc-body">{s.body}</p>
                  <PlatformWall service={s.n} />
                  <span className="svc-chevron" aria-hidden="true">+</span>
                  <div className="svc-detail">
                    <div className="svc-detail-inner">
                      <div />
                      <div className="svc-detail-block">
                        <h4>{lbl.d}</h4>
                        <ul>{det.deliver.map((d, i) => <li key={i}>{d}</li>)}</ul>
                      </div>
                      <div className="svc-detail-block">
                        <h4>{lbl.o}</h4>
                        <ul>{det.outcome.map((d, i) => <li key={i}>{d}</li>)}</ul>
                      </div>
                      <a className="svc-cta" href="#contatti"
                         onClick={(e) => { e.preventDefault(); e.stopPropagation(); requestCheck(s.checkTopic); }}>
                        {s.checkLabel} <span aria-hidden>→</span>
                      </a>
                    </div>
                  </div>
                </div>
              );
            })}
          </div>
        </div>
      </Section>
    </>
  );
}

// ---------- Pillars (sezione pinnata: "macchina della crescita" a 3 stadi) ----------
function clamp01(x) { return Math.max(0, Math.min(1, x)); }
function appear(p, start, span) { return clamp01((p - start) / span); }

// piccola icona utente che percorre una freccia
function TravelUser({ path, dur }) {
  return (
    <g>
      <circle r="8.5" fill="var(--accent)" />
      <circle cx="0" cy="-2.3" r="2.9" fill="#fff" />
      <path d="M-4.7 5.6 a4.7 4.7 0 0 1 9.4 0 Z" fill="#fff" />
      <animateMotion dur={dur} repeatCount="indefinite" path={path} />
    </g>
  );
}

function GrowthScene({ a, b, c, pulse, clients }) {
  const acc = "var(--accent)";
  const idle = "#cfcfcd";
  const dash = (p) => ({ strokeDasharray: 1, strokeDashoffset: 1 - clamp01(p) });
  // Scena 1: 4 facce che si costruiscono grandi al centro e si parcheggiano in fila
  const SLOTS = [[72, 62], [182, 62], [292, 62], [402, 62]];
  const BCX = 240, BCY = 192, SM = 0.46;
  const mkF = (i) => {
    const fl = clamp01((a - i * 0.25) / 0.25);
    const cp = clamp01((fl - 0.68) / 0.32);
    const s = 1 - cp * (1 - SM);
    const tcx = BCX + (SLOTS[i][0] - BCX) * cp, tcy = BCY + (SLOTS[i][1] - BCY) * cp;
    return { fl, cp, build: clamp01(fl / 0.68), opacity: clamp01(fl * 8), transform: `translate(${tcx - s * BCX} ${tcy - s * BCY}) scale(${s})` };
  };
  const F = [mkF(0), mkF(1), mkF(2), mkF(3)];
  const domain = "gptech.studio";
  const dchars = Math.round(clamp01((F[0].build - 0.15) / 0.28) * domain.length);
  const dtext = domain.slice(0, dchars);
  // Scena 2: 4 schermi (2 pc + 2 phone) che alimentano la presenza, in loop nel tempo (pulse)
  const COLS = [72, 182, 292, 402];
  const ADQ = ["agenzia siti web", "sviluppo sito web", "sito e-commerce", "web design"];
  const MAPQ = ["vicino a me", "studio web", "aperto ora", "servizi qui"];
  const adq = ADQ[pulse % ADQ.length];
  const mapq = MAPQ[Math.floor(pulse / 2) % MAPQ.length];
  const postIdx = Math.floor(pulse / 4);         // il post cambia ogni 4 tick
  const socFB = postIdx % 2 === 1;               // alterna Instagram / Facebook
  const likeBase = 21 + ((postIdx * 53 + 17) % 94); // ogni post parte da 21-114 like
  const likes = likeBase + (pulse % 4) * 6;      // e salgono mentre lo guardi
  const reviewIdx = Math.floor(pulse / 5);       // una recensione si "scrive" in 5 tick
  const revN = 41 + reviewIdx;
  const revStars = (reviewIdx % 3 === 0) ? 4 : 5;
  const typedLines = pulse % 5;                  // righe che compaiono = battitura
  // Scena 3: una sola label a sinistra dell'IA che cicla i compiti svolti
  const TASKS = [
    "Analisi keyword & audience",
    "Test delle campagne",
    "Tracking & performance",
    "Ottimizzazione del budget",
    "Analisi dei competitor",
    "Segmentazione del pubblico",
    "A/B test delle creatività",
    "Report automatici",
  ];
  const aiTask = TASKS[Math.floor(pulse / 2) % TASKS.length];
  // Scena 3: l'IA accelera i canali e converte utenti in clienti (verdi)
  const aiV = clamp01(c / 0.14);
  const aiHot = clamp01((c - 0.22) / 0.32);
  const travDur = (2.6 - aiHot * 1.4).toFixed(2) + "s";
  return (
    <svg className="growth-svg" viewBox="0 0 480 430" fill="none" role="img"
         aria-label="La macchina della crescita: presenza, acquisizione, automazioni AI">
      {/* ===== Scena 1 — Presenza online: 4 facce che si costruiscono e si allineano ===== */}
      {/* Faccia 0 — Sito web */}
      <g transform={F[0].transform} style={{ opacity: F[0].opacity }}>
        <rect x="142" y="117" width="196" height="150" rx="12" fill="var(--bg)" stroke={F[0].build > 0.5 ? acc : idle} strokeWidth="2" pathLength="1" style={{ ...dash(F[0].build), transition: "stroke .3s" }} />
        <g style={{ opacity: appear(F[0].build, 0.1, 0.14) }}>
          <circle cx="156" cy="133" r="2.6" fill={idle} /><circle cx="166" cy="133" r="2.6" fill={idle} /><circle cx="176" cy="133" r="2.6" fill={idle} />
          <rect x="196" y="127" width="98" height="12" rx="6" fill="#eceef1" />
          <line x1="142" y1="149" x2="338" y2="149" stroke={idle} strokeWidth="1" />
        </g>
        <text x="203" y="136.5" fontFamily="var(--ff-mono)" fontSize="8.5" fill="var(--muted)" style={{ opacity: appear(F[0].build, 0.14, 0.12) }}>{dtext}</text>
        <g style={{ opacity: appear(F[0].build, 0.32, 0.15) }}>
          <rect x="154" y="161" width="22" height="22" rx="6" fill={F[0].build > 0.45 ? acc : "#d7dbe0"} style={{ transition: "fill .3s" }} />
          <text x="165" y="176.5" textAnchor="middle" fontFamily="var(--ff-mono)" fontSize="8.5" fontWeight="700" fill="#fff" style={{ opacity: F[0].build > 0.45 ? 1 : 0 }}>GP</text>
          <rect x="250" y="169" width="18" height="5" rx="2.5" fill="#d7dbe0" /><rect x="274" y="169" width="18" height="5" rx="2.5" fill="#d7dbe0" /><rect x="298" y="169" width="18" height="5" rx="2.5" fill="#d7dbe0" />
        </g>
        <rect x="154" y="197" width="86" height="12" rx="3" fill="#2b2b2f" style={{ opacity: appear(F[0].build, 0.46, 0.16) }} />
        <rect x="154" y="215" width="66" height="7" rx="3.5" fill="#cfd3d8" style={{ opacity: appear(F[0].build, 0.54, 0.16) }} />
        <g style={{ opacity: appear(F[0].build, 0.62, 0.16) }}><rect x="154" y="228" width="54" height="16" rx="8" fill={acc} /><rect x="164" y="234.5" width="34" height="3" rx="1.5" fill="#fff" opacity="0.9" /></g>
        <g style={{ opacity: appear(F[0].build, 0.5, 0.2) }}><rect x="258" y="194" width="66" height="50" rx="6" fill="#f1f3f6" stroke={idle} strokeWidth="1.2" /><circle cx="275" cy="211" r="4.5" fill="#d7dbe0" /><path d="M262 240 L279 222 L291 232 L303 218 L318 228" fill="none" stroke="#cfd3d8" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" /></g>
      </g>
      {/* Faccia 1 — Social (profili reali) */}
      <g transform={F[1].transform} style={{ opacity: F[1].opacity }}>
        <rect x="142" y="117" width="196" height="150" rx="12" fill="var(--bg)" stroke={F[1].build > 0.5 ? acc : idle} strokeWidth="2" pathLength="1" style={{ ...dash(F[1].build), transition: "stroke .3s" }} />
        <text x="158" y="142" fontFamily="var(--ff-mono)" fontSize="9" fontWeight="500" fill="var(--muted)" style={{ opacity: appear(F[1].build, 0.1, 0.15) }}>I tuoi profili</text>
        {/* Instagram */}
        <g transform="translate(158 154)" style={{ opacity: appear(F[1].build, 0.25, 0.2) }}>
          <rect x="0" y="0" width="46" height="46" rx="13" fill="#fff" stroke="#E1306C" strokeWidth="2.6" />
          <circle cx="23" cy="23" r="10.5" fill="none" stroke="#E1306C" strokeWidth="2.6" />
          <circle cx="34.5" cy="11.5" r="2.6" fill="#E1306C" />
        </g>
        {/* Facebook */}
        <g transform="translate(217 154)" style={{ opacity: appear(F[1].build, 0.34, 0.2) }}>
          <circle cx="23" cy="23" r="23" fill="#1877F2" />
          <path d="M27 23h-3v10h-4V23h-2v-3.5h2v-2.2c0-2.6 1.1-4.1 4.1-4.1H29v3.5h-1.8c-1 0-1.2.4-1.2 1.3v1.5H29L28.5 23z" fill="#fff" />
        </g>
        {/* LinkedIn */}
        <g transform="translate(276 154)" style={{ opacity: appear(F[1].build, 0.43, 0.2) }}>
          <rect x="0" y="0" width="46" height="46" rx="9" fill="#0A66C2" />
          <text x="23" y="31" textAnchor="middle" fontFamily="var(--ff-sans)" fontSize="18" fontWeight="700" fill="#fff">in</text>
        </g>
        <text x="158" y="232" fontFamily="var(--ff-mono)" fontSize="8.5" fill="var(--muted)" style={{ opacity: appear(F[1].build, 0.55, 0.25) }}>1.2k follower totali</text>
      </g>
      {/* Faccia 2 — Mappa */}
      <g transform={F[2].transform} style={{ opacity: F[2].opacity }}>
        <rect x="142" y="117" width="196" height="150" rx="12" fill="var(--bg)" stroke={F[2].build > 0.5 ? acc : idle} strokeWidth="2" pathLength="1" style={{ ...dash(F[2].build), transition: "stroke .3s" }} />
        <rect x="144" y="119" width="192" height="146" rx="10" fill="#eef1f4" style={{ opacity: appear(F[2].build, 0.1, 0.2) }} />
        <rect x="150" y="126" width="46" height="34" rx="4" fill="#dcedd8" style={{ opacity: appear(F[2].build, 0.2, 0.25) }} />
        <rect x="288" y="212" width="44" height="46" rx="4" fill="#dcedd8" style={{ opacity: appear(F[2].build, 0.24, 0.25) }} />
        <g style={{ opacity: appear(F[2].build, 0.22, 0.3) }} stroke="#c9d0d8" strokeWidth="3" fill="none" strokeLinecap="round">
          <path d="M150 160 L250 205 L330 175" /><path d="M186 262 L226 190 L300 132" /><path d="M144 226 L336 246" />
        </g>
        <g style={{ opacity: appear(F[2].build, 0.5, 0.2), transform: `translateY(${(1 - appear(F[2].build, 0.5, 0.32)) * -14}px)`, transformBox: "fill-box", transformOrigin: "center" }}>
          <path d="M240 168 c-9 0 -16 7 -16 16 c0 12 16 28 16 28 c0 0 16 -16 16 -28 c0 -9 -7 -16 -16 -16 z" fill="#EA4335" />
          <circle cx="240" cy="184" r="5.5" fill="#fff" />
        </g>
      </g>
      {/* Faccia 3 — Scheda Google (My Business) */}
      <g transform={F[3].transform} style={{ opacity: F[3].opacity }}>
        <rect x="142" y="117" width="196" height="150" rx="12" fill="var(--bg)" stroke={F[3].build > 0.5 ? acc : idle} strokeWidth="2" pathLength="1" style={{ ...dash(F[3].build), transition: "stroke .3s" }} />
        {/* logo Google (G a 4 colori) */}
        <g transform="translate(154 128) scale(0.5)" style={{ opacity: appear(F[3].build, 0.14, 0.18) }}>
          <path fill="#4285F4" d="M45.12 24.5c0-1.56-.14-3.06-.4-4.5H24v8.51h11.84c-.51 2.75-2.06 5.08-4.39 6.64v5.52h7.11c4.16-3.83 6.56-9.47 6.56-16.17z" />
          <path fill="#34A853" d="M24 46c5.94 0 10.92-1.97 14.56-5.33l-7.11-5.52c-1.97 1.32-4.49 2.1-7.45 2.1-5.73 0-10.58-3.87-12.31-9.07H4.34v5.7C7.96 41.07 15.4 46 24 46z" />
          <path fill="#FBBC05" d="M11.69 28.18C11.25 26.86 11 25.45 11 24s.25-2.86.69-4.18v-5.7H4.34C2.85 17.09 2 20.45 2 24s.85 6.91 2.34 9.88l7.35-5.7z" />
          <path fill="#EA4335" d="M24 10.75c3.23 0 6.13 1.11 8.41 3.29l6.31-6.31C34.91 4.18 29.93 2 24 2 15.4 2 7.96 6.93 4.34 14.12l7.35 5.7c1.73-5.2 6.58-9.07 12.31-9.07z" />
        </g>
        <rect x="188" y="130" width="120" height="11" rx="3" fill="#2b2b2f" style={{ opacity: appear(F[3].build, 0.28, 0.18) }} />
        <rect x="188" y="147" width="72" height="6" rx="3" fill="#cfd3d8" style={{ opacity: appear(F[3].build, 0.34, 0.18) }} />
        <g style={{ opacity: appear(F[3].build, 0.46, 0.22) }}>
          <text x="154" y="180" fontFamily="var(--ff-mono)" fontSize="14" fill="#fbbc05" letterSpacing="1.5">★★★★★</text>
          <text x="248" y="179" fontFamily="var(--ff-mono)" fontSize="11" fontWeight="600" fill="var(--muted)">4,9</text>
          <text x="276" y="179" fontFamily="var(--ff-mono)" fontSize="9" fill="#b4b4b4">(128)</text>
        </g>
        <g style={{ opacity: appear(F[3].build, 0.6, 0.3) }}>
          <rect x="154" y="200" width="180" height="6" rx="3" fill="#e6e8eb" /><rect x="154" y="212" width="150" height="6" rx="3" fill="#e6e8eb" />
          <rect x="154" y="230" width="172" height="6" rx="3" fill="#e6e8eb" /><rect x="154" y="242" width="118" height="6" rx="3" fill="#e6e8eb" />
        </g>
      </g>
      {/* etichette parcheggiate */}
      <g>
        {["Sito web", "Social", "Geolocalizzazione", "Scheda Google"].map((lbl, i) => (
          <text key={i} x={SLOTS[i][0]} y="17" textAnchor="middle" fontFamily="var(--ff-mono)" fontSize="10" fontWeight="600" fill="var(--muted)"
                style={{ opacity: F[i].cp > 0.45 ? clamp01((F[i].cp - 0.45) / 0.4) : 0 }}>{lbl}</text>
        ))}
      </g>

      {/* ===== Scene 2+3 — 4 canali (2 pc + 2 phone) alimentano la presenza; l'IA accelera e converte ===== */}
      {b > 0.001 ? (
        <g>
          {/* ---- Ads PC -> Sito web ---- */}
          <g style={{ opacity: appear(b, 0, 0.16) }}>
            <rect x="14" y="162" width="116" height="130" rx="10" fill="var(--bg)" stroke={idle} strokeWidth="1.5" />
            <rect x="24" y="172" width="96" height="14" rx="7" fill="#f4f5f7" stroke={idle} strokeWidth="0.8" />
            <circle cx="33" cy="179" r="3" fill="none" stroke="var(--muted)" strokeWidth="1.2" />
            <text x="41" y="182" fontFamily="var(--ff-mono)" fontSize="7" fill="#3a3a3a">{adq}</text>
            <rect x="24" y="194" width="96" height="26" rx="5" strokeWidth="1.2" style={{ fill: "color-mix(in srgb, var(--accent) 9%, transparent)", stroke: acc }} />
            <text x="30" y="205" fontFamily="var(--ff-mono)" fontSize="6" fontWeight="700" fill="#0a7d33">Ann.</text>
            <text x="48" y="205" fontFamily="var(--ff-mono)" fontSize="6.5" fill={acc}>gptech.studio</text>
            <rect x="30" y="210" width="64" height="4" rx="2" fill={acc} />
            <rect x="24" y="230" width="58" height="4" rx="2" fill="#c7cdd4" /><rect x="24" y="238" width="90" height="3" rx="1.5" fill="#e6e8eb" />
            <rect x="24" y="250" width="50" height="4" rx="2" fill="#c7cdd4" /><rect x="24" y="258" width="82" height="3" rx="1.5" fill="#e6e8eb" />
            <g transform="translate(43 270)">
              <circle cx="4" cy="12" r="3" fill="#34A853" />
              <rect x="6" y="0" width="5" height="14" rx="2.5" fill="#FBBC04" transform="rotate(-28 8.5 3)" />
              <rect x="6" y="0" width="5" height="14" rx="2.5" fill="#4285F4" transform="rotate(28 8.5 3)" />
              <text x="18" y="11" fontFamily="var(--ff-mono)" fontSize="6.5" fill="var(--muted)">Google Ads</text>
            </g>
          </g>
          {/* ---- Social phone -> Social ---- */}
          <g style={{ opacity: appear(b, 0.06, 0.16) }}>
            <rect x="150" y="162" width="64" height="142" rx="12" fill="var(--bg)" stroke={idle} strokeWidth="1.5" />
            <rect x="172" y="168" width="20" height="2.5" rx="1.25" fill={idle} />
            {socFB ? (
              <g transform="translate(158 178)"><rect x="0" y="0" width="12" height="12" rx="3" fill="#1877F2" /><text x="6" y="10" textAnchor="middle" fontFamily="var(--ff-sans)" fontSize="10" fontWeight="700" fill="#fff">f</text></g>
            ) : (
              <g transform="translate(158 178)"><rect x="0" y="0" width="12" height="12" rx="4" fill="none" stroke="#E1306C" strokeWidth="1.2" /><circle cx="6" cy="6" r="2.6" fill="none" stroke="#E1306C" strokeWidth="1.2" /></g>
            )}
            <text x="206" y="187" textAnchor="end" fontFamily="var(--ff-mono)" fontSize="6.5" fill="var(--muted)">{socFB ? "Facebook" : "Instagram"}</text>
            <rect x="158" y="196" width="48" height="44" rx="4" fill={socFB ? "#e7eefc" : "#fbeaf1"} />
            {socFB ? (
              <g>
                <path d="M159 262 v-6 h2.6 v6 z M162.6 262 v-6.2 c1.5 -.4 2.4 -1.7 2.4 -3.3 c0 -.7 .7 -1.1 1.3 -.8 c.5 .3 .6 .9 .4 1.5 l-.5 1.5 h2.9 c.9 0 1.5 .8 1.3 1.6 l-.8 3.2 c-.2 .8 -.9 1.3 -1.6 1.3 z" fill="#1877F2" />
                <text x="177" y="259" fontFamily="var(--ff-mono)" fontSize="8" fontWeight="600" fill="#3a3a3a">{likes}</text>
                <text x="158" y="277" fontFamily="var(--ff-mono)" fontSize="7" fill="var(--muted)">like</text>
              </g>
            ) : (
              <g>
                <path d="M165 252 c-2.4 -2.4 -6 -1 -6 2.2 c0 3.4 6 6.6 6 6.6 c0 0 6 -3.2 6 -6.6 c0 -3.2 -3.6 -4.6 -6 -2.2 z" fill="#E1306C" />
                <text x="177" y="258" fontFamily="var(--ff-mono)" fontSize="8" fontWeight="600" fill="#3a3a3a">{likes}</text>
                <text x="158" y="277" fontFamily="var(--ff-mono)" fontSize="7" fill="var(--muted)">mi piace</text>
              </g>
            )}
          </g>
          {/* ---- Mappe phone -> Geolocalizzazione ---- */}
          <g style={{ opacity: appear(b, 0.12, 0.16) }}>
            <rect x="260" y="162" width="64" height="142" rx="12" fill="var(--bg)" stroke={idle} strokeWidth="1.5" />
            <rect x="282" y="168" width="20" height="2.5" rx="1.25" fill={idle} />
            <rect x="266" y="176" width="52" height="12" rx="6" fill="#f4f5f7" stroke={idle} strokeWidth="0.8" />
            <text x="271" y="184" fontFamily="var(--ff-mono)" fontSize="6.5" fill="#3a3a3a">{mapq}</text>
            <rect x="266" y="192" width="52" height="74" rx="5" fill="#eef1f4" />
            <g stroke="#d3d8de" strokeWidth="2" fill="none" strokeLinecap="round"><path d="M270 220 L296 236 L316 226" /><path d="M282 262 L296 226 L312 200" /></g>
            <path d="M292 214 c-5 0 -9 4 -9 9 c0 7 9 16 9 16 c0 0 9 -9 9 -16 c0 -5 -4 -9 -9 -9 z" fill="#EA4335" /><circle cx="292" cy="223" r="3" fill="#fff" />
            <text x="266" y="283" fontFamily="var(--ff-mono)" fontSize="7" fill="var(--muted)">vicino a te</text>
          </g>
          {/* ---- Recensioni PC -> Scheda Google ---- */}
          <g style={{ opacity: appear(b, 0.18, 0.16) }}>
            <rect x="344" y="162" width="116" height="130" rx="10" fill="var(--bg)" stroke={idle} strokeWidth="1.5" />
            <g transform="translate(354 172) scale(0.32)">
              <path fill="#4285F4" d="M45.12 24.5c0-1.56-.14-3.06-.4-4.5H24v8.51h11.84c-.51 2.75-2.06 5.08-4.39 6.64v5.52h7.11c4.16-3.83 6.56-9.47 6.56-16.17z" />
              <path fill="#34A853" d="M24 46c5.94 0 10.92-1.97 14.56-5.33l-7.11-5.52c-1.97 1.32-4.49 2.1-7.45 2.1-5.73 0-10.58-3.87-12.31-9.07H4.34v5.7C7.96 41.07 15.4 46 24 46z" />
              <path fill="#FBBC05" d="M11.69 28.18C11.25 26.86 11 25.45 11 24s.25-2.86.69-4.18v-5.7H4.34C2.85 17.09 2 20.45 2 24s.85 6.91 2.34 9.88l7.35-5.7z" />
              <path fill="#EA4335" d="M24 10.75c3.23 0 6.13 1.11 8.41 3.29l6.31-6.31C34.91 4.18 29.93 2 24 2 15.4 2 7.96 6.93 4.34 14.12l7.35 5.7c1.73-5.2 6.58-9.07 12.31-9.07z" />
            </g>
            <text x="372" y="182" fontFamily="var(--ff-mono)" fontSize="7.5" fill="#3a3a3a">Recensioni · {revN}</text>
            <text x="354" y="205" fontFamily="var(--ff-sans)" fontSize="15" letterSpacing="2">
              {[0, 1, 2, 3, 4].map((i) => <tspan key={i} fill={i < revStars ? "#fbbc05" : "#dfe2e6"}>★</tspan>)}
            </text>
            {/* la recensione si "scrive": righe grigie che compaiono una a una */}
            {[0, 1, 2, 3].map((i) => {
              const W = [96, 84, 90, 64][i];
              const w = i < typedLines ? W : (i === typedLines ? Math.round(W * 0.45) : 0);
              return w > 0 ? <rect key={i} x="354" y={216 + i * 12} width={w} height="4" rx="2" fill="#d6dade" /> : null;
            })}
            {typedLines < 4 ? <rect x={354 + Math.round([96, 84, 90, 64][typedLines] * 0.45) + 4} y={216 + typedLines * 12 - 3} width="1.4" height="9" fill={acc} className="gm-blink" /> : null}
          </g>

          {/* ---- Frecce dritte verso le schede + utenti (verdi = clienti con l'IA) ---- */}
          {COLS.map((cx, i) => {
            const vis = appear(b, 0.22 + i * 0.03, 0.15);
            const pth = `M ${cx} 156 L ${cx} 100`;
            return (
              <g key={i} style={{ opacity: vis }}>
                {/* bordo scheda che lampeggia all'arrivo dell'utente */}
                {vis > 0.6 ? <rect x={cx - 45} y="27" width="90" height="70" rx="6" fill="none" stroke={acc} strokeWidth="2" className="gm-cardhit" style={{ animationDuration: travDur, animationDelay: (i * 0.5).toFixed(2) + "s" }} /> : null}
                <path d={pth} fill="none" stroke={acc} strokeOpacity="0.35" strokeWidth="2" strokeLinecap="round" pathLength="1" style={dash(vis)} />
                <path d={`M${cx} 100 l-5 8 M${cx} 100 l5 8`} stroke={acc} strokeOpacity="0.35" strokeWidth="2" strokeLinecap="round" />
                {vis > 0.85 ? (
                  <g>
                    <circle r="7" fill={acc} /><circle cx="0" cy="-1.9" r="2.4" fill="#fff" /><path d="M-3.9 4.6 a3.9 3.9 0 0 1 7.8 0 Z" fill="#fff" />
                    <animateMotion dur={travDur} repeatCount="indefinite" path={pth} />
                  </g>
                ) : null}
                {aiHot > 0.15 ? (
                  <g>
                    <circle r="7" fill="#16a34a" /><circle cx="0" cy="-1.9" r="2.4" fill="#fff" /><path d="M-3.9 4.6 a3.9 3.9 0 0 1 7.8 0 Z" fill="#fff" />
                    <animateMotion dur={(2.1 - aiHot * 1.2).toFixed(2) + "s"} begin={(i * 0.35 + 0.5).toFixed(2) + "s"} repeatCount="indefinite" path={pth} />
                  </g>
                ) : null}
              </g>
            );
          })}

          {/* ---- Scena 3: l'IA collega i 4 canali e lavora sui compiti (label che cicla) ---- */}
          {aiV > 0.01 ? (
            <g style={{ opacity: aiV }}>
              {(() => {
                const green = "#16a34a";
                const on = aiHot > 0.1;
                const RX = 240, RY = 372;                 // centro robot/IA
                const SCR = [[72, 292], [182, 304], [292, 304], [402, 292]]; // fondo dei 4 schermi
                return (
                  <g>
                    {/* linee dall'IA ai 4 schermi + flusso di dati verso l'alto */}
                    {SCR.map(([sx, sy], i) => {
                      const d = `M ${RX} ${RY} L ${sx} ${sy}`;
                      return (
                        <g key={i}>
                          <path d={d} fill="none" stroke={green} strokeOpacity="0.3" strokeWidth="1.4" pathLength="1" style={dash(aiV)} />
                          {on ? (
                            <circle r="2.1" fill={green}>
                              <animateMotion dur={(1.6 - aiHot * 0.6).toFixed(2) + "s"} begin={(i * 0.3).toFixed(2) + "s"} keyPoints="1;0" keyTimes="0;1" repeatCount="indefinite" path={d} />
                            </circle>
                          ) : null}
                        </g>
                      );
                    })}
                    {/* una sola label a sinistra che cicla i compiti dell'IA */}
                    <g style={{ opacity: appear(aiV, 0.1, 0.4) }}>
                      <rect x="14" y="361" width="158" height="24" rx="12" fill="var(--bg)"
                            stroke={green} strokeOpacity={on ? 1 : 0.4} strokeWidth="1.4"
                            className={on ? "gm-taskglow" : ""} />
                      <circle cx="28" cy="373" r="3.2" fill={green} className={on ? "gm-blink" : ""} />
                      <text key={aiTask} x="39" y="376.5" fontFamily="var(--ff-mono)" fontSize="8" fill="var(--muted)">{aiTask}</text>
                      {/* collegamento label <-> IA, bidirezionale */}
                      <path d="M172 373 L 214 373" fill="none" stroke={green} strokeOpacity="0.35" strokeWidth="1.4" pathLength="1" style={dash(aiV)} />
                      {on ? (
                        <g>
                          <circle r="2.1" fill={green}><animateMotion dur={(1.4 - aiHot * 0.5).toFixed(2) + "s"} repeatCount="indefinite" path="M172 373 L 214 373" /></circle>
                          <circle r="2.1" fill="#22c55e"><animateMotion dur={(1.4 - aiHot * 0.5).toFixed(2) + "s"} begin="0.5s" keyPoints="1;0" keyTimes="0;1" repeatCount="indefinite" path="M172 373 L 214 373" /></circle>
                        </g>
                      ) : null}
                    </g>
                    {/* cervello / IA */}
                    <g transform="translate(240 371)">
                      <circle cx="0" cy="0" r="29" fill="color-mix(in srgb, #16a34a 10%, transparent)" className={aiHot > 0.2 ? "gm-twinkle" : ""} />
                      {/* sagoma del cervello */}
                      <path d="M0 -17 C -5 -21 -12 -20 -14 -14 C -21 -15 -24 -8 -19 -3 C -23 2 -19 10 -13 9 C -12 15 -4 16 0 12 C 4 16 12 15 13 9 C 19 10 23 2 19 -3 C 24 -8 21 -15 14 -14 C 12 -20 5 -21 0 -17 Z"
                            fill="color-mix(in srgb, #16a34a 14%, var(--bg))" stroke={green} strokeWidth="2" strokeLinejoin="round" />
                      {/* solco centrale + circonvoluzioni */}
                      <g fill="none" stroke={green} strokeWidth="1.4" strokeLinecap="round" strokeOpacity="0.85">
                        <path d="M0 -17 C 3 -11 -3 -7 0 -1 C 3 5 -3 9 0 12" />
                        <path d="M-14 -14 C -9 -13 -11 -7 -7 -5" />
                        <path d="M-19 -3 C -13 -4 -14 2 -9 4" />
                        <path d="M-13 9 C -10 6 -12 3 -8 1" />
                        <path d="M14 -14 C 9 -13 11 -7 7 -5" />
                        <path d="M19 -3 C 13 -4 14 2 9 4" />
                        <path d="M13 9 C 10 6 12 3 8 1" />
                      </g>
                      {/* sinapsi che pulsano quando l'IA è attiva */}
                      <circle cx="-8" cy="-4" r="1.8" fill={green} className={aiHot > 0.2 ? "gm-blink" : ""} />
                      <circle cx="9" cy="1" r="1.8" fill={green} className={aiHot > 0.2 ? "gm-blink" : ""} style={{ animationDelay: "0.5s" }} />
                    </g>
                    <text x="240" y="420" textAnchor="middle" fontFamily="var(--ff-mono)" fontSize="11" fontWeight="700" fill={green} letterSpacing="1.5">AI</text>
                  </g>
                );
              })()}
              {/* statistica clienti generati — in palette, pulita */}
              <g transform="translate(336 352)" style={{ opacity: clamp01((c - 0.2) / 0.15) }}>
                <rect x="0" y="0" width="130" height="50" rx="10" fill="var(--bg)" stroke={acc} strokeWidth="1.5" />
                <text x="14" y="19" fontFamily="var(--ff-mono)" fontSize="8" letterSpacing="0.1em" fill="var(--muted)">CLIENTI</text>
                <text x="14" y="41" fontFamily="var(--ff-sans)" fontSize="22" fontWeight="600" fill={acc}>+{clients}</text>
                <path d="M100 34 l8 -10 l5 5 l9 -12" stroke={acc} strokeWidth="2.2" fill="none" strokeLinecap="round" strokeLinejoin="round" />
                <path d="M118 17 l4 0 0 4" stroke={acc} strokeWidth="2.2" fill="none" strokeLinecap="round" strokeLinejoin="round" />
              </g>
            </g>
          ) : null}
        </g>
      ) : null}
    </svg>
  );
}

function PillarBars({ t }) {
  const ref = useRef(null);
  const [p, setP] = useState(0); // 0..1 avanzamento nella sezione pinnata
  const [done, setDone] = useState(false); // una volta completata, resta completata
  const [pulse, setPulse] = useState(0); // tempo: cicla i contenuti dei 4 schermi
  const [clients, setClients] = useState(0);
  const cRef = useRef(0);
  // Auto-play: l'animazione parte da sola quando la sezione entra in vista
  // (una volta sola), avanzando p 0->1 a velocita' costante. Niente scrub da scroll.
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const DUR = 7000; // ms: durata dell'intera animazione (3 scene)
    const reduce = window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
    let raf = 0, startTs = 0, started = false;
    const tick = (ts) => {
      if (!startTs) startTs = ts;
      const v = clamp01((ts - startTs) / DUR);
      setP(v);
      if (v >= 1) { setDone(true); return; } // completata: resta completata (latch)
      raf = requestAnimationFrame(tick);
    };
    const io = new IntersectionObserver((entries) => {
      if (started || !entries[0].isIntersecting) return;
      started = true;
      io.disconnect();
      if (reduce) { setP(1); setDone(true); return; } // accessibilita': stato finale, senza animare
      raf = requestAnimationFrame(tick);
    }, { threshold: 0.35 });
    io.observe(el);
    return () => { io.disconnect(); cancelAnimationFrame(raf); };
  }, []);
  useEffect(() => {
    let id, alive = true;
    const loop = () => {
      if (!alive) return;
      setPulse((x) => x + 1);
      if (cRef.current > 0.4) setClients((x) => x + (cRef.current > 0.7 ? 2 : 1));
      const period = Math.max(430, 1500 - cRef.current * 1050); // l'IA accelera i cambi
      id = setTimeout(loop, period);
    };
    id = setTimeout(loop, 1400);
    return () => { alive = false; clearTimeout(id); };
  }, []);
  // Scena 1 (Presenza) 0-50% · Scena 2 (4 canali) 50-75% · Scena 3 (IA) 75-100%
  // se completata, resta completata anche scorrendo verso l'alto
  const pEff = done ? 1 : p;
  const W0 = 0.5, W1 = 0.25;
  const A = clamp01(pEff / (W0 * 0.92));
  const B = clamp01((pEff - W0) / (W1 * 0.92));
  const C = clamp01((pEff - W0 - W1) / ((1 - W0 - W1) * 0.92));
  cRef.current = C;
  const locals = [A, B, C];
  const items = t.pillars || [];
  return (
    <section className="pillars-pin" ref={ref} aria-label={t.pillars_head ? t.pillars_head.title : "Pillars"}>
      <div className="pillars-sticky">
        <div className="wrap pillars-stage">
          <div className="pillars-left">
            <div className="pillars-head">
              {t.pillars_head && t.pillars_head.kicker ? <span className="pillars-kicker">{t.pillars_head.kicker}</span> : null}
              {t.pillars_head ? <h2 className="pillars-title">{t.pillars_head.title}</h2> : null}
            </div>
            <div className="pillars-list">
              {items.map((it, i) => {
                const lv = locals[i] != null ? locals[i] : 0;
                const state = lv >= 0.985 ? "done" : lv > 0.02 ? "active" : "idle";
                return (
                  <div className="pillar-item" data-state={state} key={i}>
                    <span className="pillar-index">{String(i + 1).padStart(2, "0")}</span>
                    <div className="pillar-text">
                      <span className="pillar-label">{it.label}</span>
                      <span className="pillar-note">{it.note}</span>
                    </div>
                    <div className="pillar-uline"><span style={{ width: (lv * 100) + "%" }} /></div>
                  </div>
                );
              })}
            </div>
          </div>
          <div className="pillars-right">
            <GrowthScene a={A} b={B} c={C} pulse={pulse} clients={clients} />
          </div>
        </div>
      </div>
    </section>
  );
}

// ---------- Routing (instradamento per situazione, pinned allo scroll) ----------
function Routing({ t, goToService }) {
  const sectionRef = useRef(null);
  const [progress, setProgress] = useState(0);
  useEffect(() => {
    const onScroll = () => {
      const el = sectionRef.current;
      if (!el) return;
      const total = el.offsetHeight - window.innerHeight;
      if (total <= 0) { setProgress(0); return; }
      const p = Math.max(0, Math.min(1, -el.getBoundingClientRect().top / total));
      setProgress(p);
    };
    window.addEventListener("scroll", onScroll, { passive: true });
    onScroll();
    return () => window.removeEventListener("scroll", onScroll);
  }, []);
  const n = t.routing.length;
  // n+1 fasi: una per card, piu' una finale "nessuna selezione" cosi' l'ultima
  // card non resta evidenziata quando si esce dalla sezione.
  const raw = Math.floor(progress * (n + 1) * 0.999);
  const active = raw >= n ? -1 : Math.max(0, raw);
  const dotActive = active === -1 ? n - 1 : active;
  return (
    <section ref={sectionRef} id="instradamento" className="routing-pin" data-cursor-label="Da dove parti">
      <div className="routing-sticky">
        <div className="wrap routing-pin-inner">
          <div className="routing-pin-head">
            <h2 className="section-title">{t.routing_head.title}</h2>
          </div>
          <div className="route-cards">
            {t.routing.map((c, i) => (
              <button className="route-card" key={c.id} data-active={i === active}
                      onClick={() => goToService(c.target)}>
                <span className="route-card-hint">{c.hint}</span>
                <span className="route-card-situation">{c.situation}</span>
                <span className="route-card-go">{t.routing_cta} <span className="route-card-arrow" aria-hidden="true">→</span></span>
              </button>
            ))}
          </div>
          <div className="routing-foot">
            <div className="routing-dots" aria-hidden="true">
              {t.routing.map((c, i) => <i key={c.id} data-on={i <= dotActive} />)}
            </div>
          </div>
        </div>
      </div>
    </section>
  );
}

// ---------- Check gratuito (conversione principale) ----------
function Check({ t, requestCheck }) {
  return (
    <Section id="check" label="Check gratuito">
      <div className="wrap">
        <SectionHead title={t.check_head.title} />
        <p className="check-lead">{t.check_lead}</p>
        <div className="check-grid">
          <div className="check-lenses">
            {t.check_lenses.map((l, i) => (
              <div className="check-lens" key={i}>
                <span className="check-lens-svc">{l.svc}</span>
                <span className="check-lens-look">{l.look}</span>
              </div>
            ))}
          </div>
          <div className="check-aside">
            <div className="check-deliver">
              <h4>{t.check_deliver_title}</h4>
              <ul>{t.check_deliver.map((d, i) => <li key={i}>{d}</li>)}</ul>
            </div>
            <dl className="check-terms">
              {t.check_terms.map((x, i) => (
                <div className="check-term" key={i}>
                  <dt>{x.k}</dt>
                  <dd>{x.v}</dd>
                </div>
              ))}
            </dl>
            <a className="btn btn-ink check-cta" href="#contatti"
               onClick={(e) => { e.preventDefault(); requestCheck(""); }}>
              {t.check_cta} <span aria-hidden>→</span>
            </a>
          </div>
        </div>
      </div>
    </Section>
  );
}

// ---------- Manifesto pinned ----------
function Manifesto({ lang }) {
  const sectionRef = useRef(null);
  const [progress, setProgress] = useState(0); // 0..1 within sticky range
  const STATEMENTS = lang === "it" ? [
    <>Capiamo il tuo <span className="ital">business</span>, studiamo la <span className="ital">concorrenza</span>, troviamo i tuoi <span className="accent">clienti</span>.</>,
    <>Aumenta la <span className="ital">produttività</span> nell'era dell'<span className="accent">intelligenza artificiale</span>.</>,
    <>Uno <span className="ital">studio</span> al tuo fianco, non un semplice <span className="accent">fornitore</span>.</>,
  ] : [
    <>We learn your <span className="ital">business</span>, study the <span className="ital">competition</span>, find your <span className="accent">customers</span>.</>,
    <>Boost <span className="ital">productivity</span> in the age of <span className="accent">artificial intelligence</span>.</>,
    <>A <span className="ital">studio</span> by your side, not just a <span className="accent">vendor</span>.</>,
  ];
  useEffect(() => {
    const onScroll = () => {
      const el = sectionRef.current;
      if (!el) return;
      const r = el.getBoundingClientRect();
      const total = el.offsetHeight - window.innerHeight;
      if (total <= 0) return;
      const p = Math.max(0, Math.min(1, -r.top / total));
      setProgress(p);
    };
    window.addEventListener("scroll", onScroll, { passive: true });
    onScroll();
    return () => window.removeEventListener("scroll", onScroll);
  }, []);
  const idx = Math.min(STATEMENTS.length - 1, Math.floor(progress * STATEMENTS.length * 0.999));
  // horizontal bars filling progressively — una per frase
  const BARS = STATEMENTS.length;
  return (
    <section ref={sectionRef} className="manifesto" data-cursor-label="Manifesto" style={{ height: STATEMENTS.length * 130 + "vh" }}>
      <div className="manifesto-inner">
        <div className="manifesto-mid">
          <h2 className="manifesto-text">
            {STATEMENTS.map((s, i) => (
              <span key={i} className="item" data-on={i === idx}>{s}</span>
            ))}
          </h2>
          <div className="manifesto-bars" aria-hidden="true">
            {Array.from({ length: BARS }).map((_, i) => {
              const start = i / BARS;
              const w = Math.max(0, Math.min(1, (progress - start) * BARS)) * 100;
              return <div className="bar" key={i} style={{ "--w": w + "%" }} />;
            })}
          </div>
        </div>
      </div>
    </section>
  );
}

// ---------- Studio ----------
function Studio({ t }) {
  return (
    <Section id="studio" label="Studio">
      <div className="wrap">
        <SectionHead title={t.about_head.title} />
        <div className="studio-grid">
          <div className="studio-text">
            <p>{t.about.p1}</p>
            <p>{t.about.p2}</p>
            {t.about.p3 ? <p>{t.about.p3}</p> : null}
          </div>
        </div>
        <div className="metrics studio-metrics">
          {t.metrics.map((m, i) => (
            <div className="metric" key={i}>
              <div className="metric-num"><CountUp raw={m.num} /><sup>{m.sup}</sup></div>
              <div className="metric-foot">{m.label}</div>
              {m.src ? <div className="aside-metric-src">{m.src}</div> : null}
            </div>
          ))}
        </div>
      </div>
    </Section>
  );
}

// ---------- Process ----------
function Process({ t, lang }) {
  const phaseLbl = lang === "en" ? "Phase" : "Fase";
  const nowLbl = lang === "en" ? "Now" : "Ora";
  const ref = useRef(null);
  const [progress, setProgress] = useState(0);
  const [thresholds, setThresholds] = useState([]);
  useEffect(() => {
    const measure = () => {
      const el = ref.current;
      if (!el) return;
      const H = el.scrollHeight || el.offsetHeight || 1;
      const steps = Array.from(el.querySelectorAll(".proc-step"));
      setThresholds(steps.map((s) => (s.offsetTop + s.offsetHeight / 2) / H));
    };
    const onScroll = () => {
      const el = ref.current;
      if (!el) return;
      const r = el.getBoundingClientRect();
      const vh = window.innerHeight;
      const p = (vh * 0.85 - r.top) / (r.height || 1);
      setProgress(Math.max(0, Math.min(1, p)));
    };
    measure();
    onScroll();
    window.addEventListener("resize", measure);
    window.addEventListener("scroll", onScroll, { passive: true });
    return () => { window.removeEventListener("resize", measure); window.removeEventListener("scroll", onScroll); };
  }, []);
  return (
    <>
      <Section id="approccio" label="Approccio">
        <div className="wrap">
          <SectionHead title={t.approach_head.title} />
          <div className="process" ref={ref}>
            <div className="proc-rail" aria-hidden="true">
              <span className="proc-rail-fill" style={{ height: (progress * 100) + "%" }} />
            </div>
            {t.approach.map((s, i) => {
              const last = i === t.approach.length - 1;
              const reached = progress >= (thresholds[i] != null ? thresholds[i] : (i + 0.5) / t.approach.length);
              return (
                <div className={"proc-step" + (last ? " future" : "")} key={s.n}>
                  <span className="proc-node" data-reached={reached} aria-hidden="true"><ProcIcon step={s.n} /></span>
                  <div className="proc-head">
                    <ProcIcon step={s.n} />
                    <span className="proc-tag">{last ? nowLbl : `${phaseLbl} ${s.n}`}</span>
                  </div>
                  <h4 className="proc-title">{s.t}</h4>
                  <p className="proc-body">{s.body}</p>
                </div>
              );
            })}
          </div>
        </div>
      </Section>
    </>
  );
}

// ---------- Animated counter ----------
function CountUp({ raw }) {
  // raw like "+212", "<1.4", "97"
  const [ref, inView] = useInView({ threshold: 0.5 });
  const [shown, setShown] = useState(raw);
  const startedRef = useRef(false);
  useEffect(() => {
    if (!inView || startedRef.current) return;
    startedRef.current = true;
    const m = raw.match(/^([+<>]?)(-?\d+(?:\.\d+)?)$/);
    if (!m) { setShown(raw); return; }
    const prefix = m[1] || "";
    const target = parseFloat(m[2]);
    const decimals = (m[2].split(".")[1] || "").length;
    const duration = 1100;
    const start = performance.now();
    const tick = (now) => {
      const t = Math.min(1, (now - start) / duration);
      const eased = 1 - Math.pow(1 - t, 3);
      const v = target * eased;
      setShown(prefix + v.toFixed(decimals));
      if (t < 1) requestAnimationFrame(tick);
      else setShown(raw);
    };
    requestAnimationFrame(tick);
  }, [inView, raw]);
  return <span ref={ref}>{shown}</span>;
}

function Metrics({ t }) {
  return (
    <>
      <Section id="numeri" label="Numeri">
        <div className="wrap">
          <SectionHead title={t.metrics_head.title} />
          <div className="metrics">
            {t.metrics.map((m, i) => (
              <div className="metric" key={i}>
                <div className="metric-num"><CountUp raw={m.num} /><sup>{m.sup}</sup></div>
                <div className="metric-foot">{m.label}</div>
              </div>
            ))}
          </div>
        </div>
      </Section>
    </>
  );
}

// ---------- Lead form ----------
// Incolla qui l'URL del Web App Google Apps Script per salvare gli invii nel
// tuo Google Sheet. Se vuoto, il modulo apre una email precompilata (fallback).
const LEAD_ENDPOINT = "https://script.google.com/macros/s/AKfycbwCMYgHbpGCwXx7oX5v5eq_UmwlsQGu8KnvLuBoaOHcnDqGQKUf1--oMKDsaDKrvZMu/exec";

function LeadForm({ t, lang, email }) {
  const f = t.form;
  const fl = t.check_flow;
  const questions = fl.questions;
  const total = questions.length;

  const [step, setStep] = useState(0); // 0..total-1 = domande; total = risultato + contatti
  const [answers, setAnswers] = useState({});
  const [name, setName] = useState("");
  const [mail, setMail] = useState("");
  const [company, setCompany] = useState("");
  const [website, setWebsite] = useState("");
  const [sector, setSector] = useState("");
  const [hp, setHp] = useState(""); // honeypot: invisibile agli umani, i bot lo compilano
  const [consent, setConsent] = useState(false);
  const [error, setError] = useState("");
  const [sending, setSending] = useState(false);
  const [sent, setSent] = useState(false);

  const atResult = step >= total;

  const chooseSingle = (q, opt) => {
    setAnswers((a) => ({ ...a, [q.key]: opt.label }));
    setError("");
    setTimeout(() => setStep((s) => Math.min(total, s + 1)), 340); // avanza da solo, dopo la conferma
  };
  const toggleMulti = (q, opt) => setAnswers((a) => {
    const arr = Array.isArray(a[q.key]) ? a[q.key] : [];
    return { ...a, [q.key]: arr.includes(opt.label) ? arr.filter((x) => x !== opt.label) : [...arr, opt.label] };
  });

  const recommended = useMemo(() => {
    const score = {};
    questions.forEach((q) => {
      const v = answers[q.key];
      const chosen = Array.isArray(v) ? v : (v ? [v] : []);
      chosen.forEach((lbl) => {
        const opt = q.options.find((o) => o.label === lbl);
        (opt && opt.tags ? opt.tags : []).forEach((tg) => { score[tg] = (score[tg] || 0) + 1; });
      });
    });
    let order = Object.keys(score).sort((a, b) => score[b] - score[a] || a.localeCompare(b));
    if (!order.length) order = ["02", "01"];
    return order;
  }, [answers]);

  const recoNames = () => recommended.map((n) => (t.services.find((s) => s.n === n) || {}).t).filter(Boolean);
  const answersText = () => questions.map((q) => {
    const v = answers[q.key];
    const val = Array.isArray(v) ? v.join(", ") : v;
    return val ? `${q.q} → ${val}` : null;
  }).filter(Boolean).join(" | ");

  const buildMailto = () => {
    const L = lang === "it";
    const subject = L ? "Check gratuito" : "Free check";
    const lines = [
      `${L ? "Nome" : "Name"}: ${name}`,
      `Email: ${mail}`,
      company ? `${L ? "Azienda" : "Company"}: ${company}` : null,
      website ? `${L ? "Sito" : "Website"}: ${website}` : null,
      sector ? `${L ? "Settore" : "Sector"}: ${sector}` : null,
      `${L ? "Consigliato" : "Recommended"}: ${recoNames().join(", ")}`,
      `${L ? "Risposte" : "Answers"}: ${answersText()}`,
    ].filter((l) => l !== null);
    return `mailto:${email}?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(lines.join("\n"))}`;
  };

  const submit = async () => {
    if (!name.trim() || !/^\S+@\S+\.\S+$/.test(mail) || !consent) { setError(f.required); return; }
    if (hp) { setSent(true); return; } // bot: finto successo, nessun invio
    setError(""); setSending(true);
    if (!LEAD_ENDPOINT) {
      window.location.href = buildMailto();
      setSent(true); setSending(false); return;
    }
    const buildFd = () => {
      const fd = new FormData();
      const data = { recommended: recoNames().join(", "), answers: answersText(), name, email: mail, company, website, sector, hp, lang, source: location.href };
      Object.entries(data).forEach(([k, v]) => fd.append(k, v));
      return fd;
    };
    // Invio robusto: fino a 3 tentativi leggendo la conferma del server ({ok:true}).
    // "keepalive" fa completare la richiesta anche se l'utente chiude la pagina.
    // Se la risposta non è leggibile (CORS/proxy), ultimo invio "cieco" come prima.
    // Mai finto successo: se non parte, l'utente vede l'errore e i dati restano.
    const wait = (ms) => new Promise((r) => setTimeout(r, ms));
    let confirmed = false, refused = false, unreadable = false;
    for (let attempt = 0; attempt < 3 && !confirmed && !refused; attempt++) {
      if (attempt) await wait(700 * attempt);
      try {
        const res = await fetch(LEAD_ENDPOINT, { method: "POST", body: buildFd(), keepalive: true });
        const j = await res.json().catch(() => null);
        if (j && j.ok) confirmed = true;
        else if (j && j.ok === false) refused = true; // il server ha risposto: salvataggio fallito
        else unreadable = true;
      } catch (err) { unreadable = true; }
    }
    if (!confirmed && !refused && unreadable) {
      try {
        await fetch(LEAD_ENDPOINT, { method: "POST", mode: "no-cors", body: buildFd(), keepalive: true });
        confirmed = true; // rete ok ma risposta opaca: comportamento pre-esistente
      } catch (err) {}
    }
    if (confirmed) setSent(true);
    else setError(f.send_error || f.required);
    setSending(false);
  };

  const back = () => { setError(""); setStep((s) => Math.max(0, s - 1)); };
  const nextMulti = () => { setError(""); setStep((s) => Math.min(total, s + 1)); };

  // Tastiera: tasti 1-N per scegliere, Invio per avanzare (solo sulle domande)
  useEffect(() => {
    if (atResult) return;
    const q = questions[step];
    const onKey = (e) => {
      const tag = e.target && e.target.tagName;
      if (tag === "INPUT" || tag === "TEXTAREA") return;
      const n = parseInt(e.key, 10);
      if (n >= 1 && n <= q.options.length) {
        e.preventDefault();
        const opt = q.options[n - 1];
        q.type === "multi" ? toggleMulti(q, opt) : chooseSingle(q, opt);
      } else if (e.key === "Enter" && q.type === "multi") {
        const v = answers[q.key];
        if (Array.isArray(v) && v.length) nextMulti();
      }
    };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [step, atResult, answers]);

  if (sent) {
    return (
      <div className="lead-success">
        <h3>{f.success_title}</h3>
        <p>{f.success_body}</p>
      </div>
    );
  }

  const progressPct = Math.round((Math.min(step, total) / total) * 100);

  return (
    <div className="lead-flow">
      <div className="q-progress" aria-hidden="true"><span style={{ width: progressPct + "%" }} /></div>

      {!atResult && (() => {
        const q = questions[step];
        const v = answers[q.key];
        const isMulti = q.type === "multi";
        return (
          <div className="q-screen" key={q.key}>
            <div className="q-count">{fl.count} {step + 1} / {total}</div>
            <h3 className="q-title">{q.q}</h3>
            {(q.hint || (step === 0 ? fl.intro_note : null)) ? <p className="q-hint">{q.hint || fl.intro_note}</p> : null}
            <div className="q-options">
              {q.options.map((opt, i) => {
                const on = isMulti ? (Array.isArray(v) && v.includes(opt.label)) : v === opt.label;
                return (
                  <button type="button" key={opt.label} className="q-option" data-on={on}
                          onClick={() => isMulti ? toggleMulti(q, opt) : chooseSingle(q, opt)}>
                    <span className="q-option-key" aria-hidden="true">{i + 1}</span>
                    <span className="q-option-label">{opt.label}</span>
                    <span className="q-option-mark" aria-hidden="true">{on ? "✓" : (isMulti ? "+" : "")}</span>
                  </button>
                );
              })}
            </div>
            <div className="flow-actions">
              {step > 0 ? <button type="button" className="btn btn-ghost" onClick={back}>← {fl.back}</button> : <span />}
              {isMulti
                ? <button type="button" className="btn btn-ink" onClick={nextMulti} disabled={!(Array.isArray(v) && v.length)}>{fl.next} <span aria-hidden>→</span></button>
                : <span />}
            </div>
          </div>
        );
      })()}

      {atResult && (
        <form className="q-result" onSubmit={(e) => { e.preventDefault(); submit(); }} noValidate>
          <div className="q-count">{fl.result_step}</div>
          <h3 className="q-title">{fl.result_title}</h3>
          <p className="q-hint">{fl.result_sub}</p>
          <div className="reco-primary">
            <span className="reco-kicker">{fl.result_primary}</span>
            <p className="reco-primary-text">{fl.recommend[recommended[0]]}</p>
          </div>
          {recommended.length > 1 ? (
            <div className="reco-more">
              <span className="lead-label">{fl.result_more}</span>
              <div className="reco-list">
                {recommended.slice(1).map((n) => (
                  <div className="reco-item" key={n}><span className="reco-dot" aria-hidden="true" />{fl.recommend[n]}</div>
                ))}
              </div>
            </div>
          ) : null}
          <div className="reco-contact">
            <span className="lead-label">{fl.contact_intro}</span>
            <div className="lead-row">
              <input className="lead-input" placeholder={f.name} value={name} onChange={(e) => setName(e.target.value)} autoComplete="name" />
              <input className="lead-input" type="email" placeholder={f.email} value={mail} onChange={(e) => setMail(e.target.value)} autoComplete="email" />
            </div>
            <div className="lead-row">
              <input className="lead-input" placeholder={f.company} value={company} onChange={(e) => setCompany(e.target.value)} autoComplete="organization" />
              <input className="lead-input" placeholder={f.website} value={website} onChange={(e) => setWebsite(e.target.value)} autoComplete="url" inputMode="url" />
            </div>
            <input className="lead-input" placeholder={f.sector} value={sector} onChange={(e) => setSector(e.target.value)} />
            {/* honeypot: nome volutamente senza significato, così l'autocompilazione
                del browser non lo riempie mai (solo i bot che compilano tutto) */}
            <div className="lead-hp" aria-hidden="true">
              <input type="text" name="gp_xf_note" tabIndex={-1} autoComplete="off" value={hp} onChange={(e) => setHp(e.target.value)} />
            </div>
            <p className="lead-optnote">{f.optional_note}</p>
            <label className="lead-consent">
              <input type="checkbox" checked={consent} onChange={(e) => setConsent(e.target.checked)} />
              <span>{f.consent}</span>
            </label>
            {f.privacy_link ? <a className="lead-privacy" href="#/privacy">{f.privacy_link} ↗</a> : null}
          </div>
          {error ? <p className="lead-error">{error}</p> : null}
          <div className="flow-actions">
            <button type="button" className="btn btn-ghost" onClick={back}>← {fl.back}</button>
            <button type="submit" className="btn btn-ink" disabled={sending}>{sending ? f.sending : f.submit} <span aria-hidden>→</span></button>
          </div>
        </form>
      )}

      <a className="lead-alt" href={buildMailto()}>{f.email_alt} <span className="lead-alt-addr">{email}</span></a>
      <p className="contact-promise">{t.contact.promise}</p>
    </div>
  );
}

// ---------- Contact ----------
function Contact({ t, lang, requestCheck }) {
  const email = t.contact.meta.email;
  const headlineMailto = `mailto:${email}?subject=${encodeURIComponent(lang === "it" ? "Check gratuito" : "Free check")}`;
  return (
    <section className="contact" id="contatti" data-cursor-label="Contatti">
      <div className="wrap">
        <div className="contact-eyebrow">
          <span>{email}</span>
        </div>
        <div className="contact-headline">
          <h2>
            {lang === "it" ? <>Richiedi il tuo<br /><span className="ital">check</span> <span className="underline">gratuito</span>.</>
                            : <>Get your<br /><span className="ital">free</span> <span className="underline">check</span>.</>}
          </h2>
          <button className="contact-mark-link" onClick={() => requestCheck("")} aria-label={t.contact.cta_primary}>
            <img className="contact-mark" src="uploads/logogptechbianco.png?v=2" alt="" aria-hidden="true" />
          </button>
        </div>
        <div className="contact-cta-row">
          <button className="btn btn-ink" onClick={() => requestCheck("")}>{t.contact.cta_primary} <span aria-hidden>→</span></button>
          <a className="contact-email" href={headlineMailto}>{email}</a>
        </div>
        <p className="contact-promise">{t.contact.promise}</p>
      </div>
    </section>
  );
}

// ---------- Check modal (popup a tutto schermo, focus-trapped) ----------
function CheckModal({ t, lang, onClose }) {
  const ref = useRef(null);
  useEffect(() => {
    const prevFocus = document.activeElement;
    const root = ref.current;
    if (root) root.focus();
    const sel = 'a[href], button:not([disabled]), input:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
    const onKey = (e) => {
      if (e.key === "Escape") { onClose(); return; }
      if (e.key === "Tab" && root) {
        const f = Array.from(root.querySelectorAll(sel)).filter((el) => el.offsetParent !== null);
        if (!f.length) return;
        const first = f[0], last = f[f.length - 1];
        if (e.shiftKey && document.activeElement === first) { e.preventDefault(); last.focus(); }
        else if (!e.shiftKey && document.activeElement === last) { e.preventDefault(); first.focus(); }
      }
    };
    document.addEventListener("keydown", onKey);
    return () => {
      document.removeEventListener("keydown", onKey);
      if (prevFocus && prevFocus.focus) prevFocus.focus();
    };
  }, [onClose]);
  return (
    <div className="check-modal" role="dialog" aria-modal="true" tabIndex={-1} ref={ref}
         aria-label={lang === "it" ? "Check gratuito" : "Free check"}>
      <div className="check-modal-bar">
        <span className="check-modal-tag">{lang === "it" ? "Check gratuito" : "Free check"}</span>
        <button className="check-modal-close" onClick={onClose} aria-label={lang === "it" ? "Chiudi" : "Close"}>×</button>
      </div>
      <div className="check-modal-scroll">
        <div className="check-modal-inner">
          <LeadForm t={t} lang={lang} email={t.contact.meta.email} />
        </div>
      </div>
    </div>
  );
}

function Footer({ t, go, onOpenConsent }) {
  return (
    <footer className="footer">
      <div className="wrap footer-inner">
        <a className="footer-brand" href="#/home" aria-label="GP Tech Studio">
          <img src="uploads/logogptechorizzontalebianco.png?v=2" alt="GP Tech Studio" />
        </a>
        <div className="footer-meta">
          <span>{t.footer.copyright}</span>
          <div className="footer-links">
            {t.footer.links.map((l) => (
              <a
                key={l.label}
                href={l.href}
                {...(l.external ? { target: "_blank", rel: "noreferrer noopener" } : {})}
              >
                {l.label} ↗
              </a>
            ))}
            <a href="#/privacy" onClick={(e) => { if (go) { e.preventDefault(); go("privacy"); } }}>{t.footer.privacy}</a>
            {GA4_ID && onOpenConsent ? (
              <button type="button" className="footer-linkbtn" onClick={onOpenConsent}>{t.footer.cookie}</button>
            ) : null}
          </div>
        </div>
      </div>
    </footer>
  );
}

// ---------- Tweaks ----------
function GPTweaks({ tweaks, setTweak }) {
  return (
    <TweaksPanel title="Tweaks">
      <TweakSection title="Accent">
        <TweakColor label="Colore d'accento" value={tweaks.accent} options={ACCENT_OPTIONS} onChange={(v) => setTweak("accent", v)} />
      </TweakSection>
      <TweakSection title="Hero">
        <TweakRadio label="Animazione titolo" value={tweaks.heroAnim}
          options={[{ value: "blur", label: "Blur" }, { value: "type", label: "Typewriter" }]}
          onChange={(v) => setTweak("heroAnim", v)} />
      </TweakSection>
      <TweakSection title="Type">
        <TweakRadio label="Font system" value={tweaks.fontPair}
          options={[{ value: "geist", label: "Geist" }, { value: "inter", label: "Inter T" }, { value: "helvetica", label: "Helvetica" }]}
          onChange={(v) => setTweak("fontPair", v)} />
      </TweakSection>
      <TweakSection title="Theme">
        <TweakToggle label="Dark mode" value={tweaks.dark} onChange={(v) => setTweak("dark", v)} />
        <TweakRadio label="Lingua iniziale" value={tweaks.lang}
          options={[{ value: "it", label: "IT" }, { value: "en", label: "EN" }]}
          onChange={(v) => setTweak("lang", v)} />
      </TweakSection>
    </TweaksPanel>
  );
}

// ---------- App ----------
// ---------- Analytics (GA4) + consenso cookie ----------
// Incolla qui l'ID della proprietà Google Analytics 4 (formato G-XXXXXXXXXX).
// Finché è vuoto, nessuno script di analytics viene caricato e il banner non appare.
const GA4_ID = "G-L5KT4N9S4R";
// Google Ads: conversione "Visualizzazione di pagina" (sparata al caricamento, dopo il consenso).
const ADS_ID = "AW-18315782519";
const ADS_CONVERSION_PAGEVIEW = "AW-18315782519/HHFKCP_A1s4cEPfS0p1E";
const CONSENT_KEY = "gptech_consent"; // "granted" | "denied"
let gaLoaded = false;
function loadGA4() {
  if (gaLoaded || !GA4_ID) return;
  gaLoaded = true;
  window.dataLayer = window.dataLayer || [];
  window.gtag = function () { window.dataLayer.push(arguments); };
  window.gtag("consent", "default", { ad_storage: "denied", ad_user_data: "denied", ad_personalization: "denied", analytics_storage: "denied" });
  window.gtag("consent", "update", { analytics_storage: "granted" });
  const s = document.createElement("script");
  s.async = true;
  s.src = "https://www.googletagmanager.com/gtag/js?id=" + GA4_ID;
  document.head.appendChild(s);
  window.gtag("js", new Date());
  window.gtag("config", GA4_ID, { anonymize_ip: true });
  window.gtag("config", ADS_ID);
  window.gtag("event", "conversion", {
    send_to: ADS_CONVERSION_PAGEVIEW,
    value: 1.0,
    currency: "EUR",
  });
}
function readConsent() { try { return localStorage.getItem(CONSENT_KEY); } catch (e) { return null; } }
function writeConsent(v) { try { localStorage.setItem(CONSENT_KEY, v); } catch (e) {} }

function CookieBanner({ t, onAccept, onReject }) {
  const c = t.cookie;
  return (
    <div className="cookie-banner" role="dialog" aria-live="polite" aria-label={c.more}>
      <div className="cookie-inner">
        <p className="cookie-text">{c.text} <a href="#/privacy">{c.more} ↗</a></p>
        <div className="cookie-actions">
          <button type="button" className="btn btn-ghost cookie-btn" onClick={onReject}>{c.reject}</button>
          <button type="button" className="btn btn-ink cookie-btn" onClick={onAccept}>{c.accept}</button>
        </div>
      </div>
    </div>
  );
}

function LegalPage({ t, go }) {
  const l = t.legal;
  return (
    <section className="legal" data-cursor-label="Privacy">
      <div className="wrap legal-wrap">
        <button type="button" className="legal-back" onClick={() => go("home")}>← {l.back}</button>
        <h1 className="legal-title">{l.title}</h1>
        <p className="legal-updated">{l.updated}</p>
        <p className="legal-intro">{l.intro}</p>
        {l.sections.map((s, i) => (
          <div className="legal-section" key={i}>
            <h2 className="legal-h">{s.h}</h2>
            <p className="legal-body">{s.body}</p>
          </div>
        ))}
      </div>
    </section>
  );
}

function App() {
  const [tweaks, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const [route, go] = useHashRoute();
  const [lang, setLang] = useState(tweaks.lang || "it");
  const [atTop, setAtTop] = useState(true);
  const [atContact, setAtContact] = useState(false);
  const [checkTopic, setCheckTopic] = useState("");
  const [checkOpen, setCheckOpen] = useState(false);
  const [openService, setOpenService] = useState(null);
  const [focusedService, setFocusedService] = useState(null);
  const [consentChoice, setConsentChoice] = useState(readConsent);
  const [consentOpen, setConsentOpen] = useState(false);
  useEffect(() => { setLang(tweaks.lang || "it"); }, [tweaks.lang]);
  useEffect(() => { if (consentChoice === "granted") loadGA4(); }, [consentChoice]);
  const acceptConsent = useCallback(() => { writeConsent("granted"); setConsentChoice("granted"); setConsentOpen(false); }, []);
  const rejectConsent = useCallback(() => { writeConsent("denied"); setConsentChoice("denied"); setConsentOpen(false); }, []);
  useEffect(() => {
    const onScroll = () => {
      const top = window.scrollY < 80;
      setAtTop(top);
      document.body.toggleAttribute("data-at-top", top);
      const contactEl = document.getElementById("contatti");
      if (contactEl) {
        const r = contactEl.getBoundingClientRect();
        setAtContact(r.top < window.innerHeight * 0.6 && r.bottom > 0);
      } else {
        setAtContact(false);
      }
    };
    window.addEventListener("scroll", onScroll, { passive: true });
    onScroll();
    return () => window.removeEventListener("scroll", onScroll);
  }, []);

  useEffect(() => {
    const r = document.documentElement;
    r.style.setProperty("--accent", tweaks.accent);
    const pair = FONT_PAIRS[tweaks.fontPair] || FONT_PAIRS.geist;
    r.style.setProperty("--ff-sans", pair.sans);
    r.style.setProperty("--ff-mono", pair.mono);
    r.style.setProperty("--ff-serif", pair.serif);
    r.setAttribute("data-theme", tweaks.dark ? "dark" : "light");
  }, [tweaks]);

  const t = useMemo(() => COPY[lang] || COPY.it, [lang]);

  useEffect(() => {
    window.scrollTo({ top: 0, behavior: "instant" });
    if (route === "privacy") setCheckOpen(false);
  }, [route]);

  const requestCheck = useCallback((topic) => {
    setCheckTopic(topic || "");
    setCheckOpen(true);
  }, []);
  const closeCheck = useCallback(() => setCheckOpen(false), []);
  useEffect(() => {
    document.body.style.overflow = checkOpen ? "hidden" : "";
    document.body.toggleAttribute("data-check-open", checkOpen);
    return () => { document.body.style.overflow = ""; document.body.removeAttribute("data-check-open"); };
  }, [checkOpen]);
  const goToService = useCallback((n) => {
    setFocusedService(n);
    setOpenService(n);
    setTimeout(() => {
      document.getElementById("servizi")?.scrollIntoView({ behavior: "smooth", block: "start" });
      const row = document.getElementById("svc-" + n);
      if (row) {
        row.classList.remove("svc-arrive");
        void row.offsetWidth; // reflow so the animation can replay
        row.classList.add("svc-arrive");
        setTimeout(() => row.classList.remove("svc-arrive"), 1500);
      }
    }, 90);
  }, []);

  return (
    <>
      <CrosshairCursor />
      <ScrollProgress />
      <FloatingNav lang={lang} setLang={setLang} t={t} requestCheck={requestCheck} />
      <main>
        {route === "privacy" ? (
          <LegalPage t={t} go={go} />
        ) : (
          <>
            <Hero t={t} lang={lang} heroAnim={tweaks.heroAnim} requestCheck={requestCheck} />
            <PillarBars t={t} />
            <Routing t={t} goToService={goToService} />
            <Services t={t} lang={lang} open={openService} setOpen={setOpenService} focused={focusedService} requestCheck={requestCheck} />
            <Manifesto lang={lang} />
            <Check t={t} requestCheck={requestCheck} />
            <Studio t={t} lang={lang} />
            <Process t={t} lang={lang} />
            <Contact t={t} lang={lang} requestCheck={requestCheck} />
          </>
        )}
      </main>
      <Footer t={t} go={go} onOpenConsent={() => setConsentOpen(true)} />
      <GPTweaks tweaks={tweaks} setTweak={setTweak} />
      {checkOpen ? <CheckModal t={t} lang={lang} checkTopic={checkTopic} onClose={closeCheck} /> : null}
      {GA4_ID && (consentChoice == null || consentOpen) ? <CookieBanner t={t} onAccept={acceptConsent} onReject={rejectConsent} /> : null}
    </>
  );
}

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