// i18n + Logo + Motion utilities
const { createContext, useContext, useState, useEffect, useRef, useMemo, useCallback } = React;

// ---- i18n ----
// Default site language is Portuguese (BR). EN and ES provided as alternates.
const STRINGS = {
  pt: {
    dir: "ltr",
    nav: { home: "Início", platform: "Ecossistema", capital: "BLU Fund", cities: "Smart City", manifesto: "Manifesto", console: "Console", access: "Investir" },
    brand_sub: "ECOSSISTEMA URBANO",
    network: "Rede", stack: "Verticais", live_pilot: "AO VIVO · Tallinn",
    hero_eyebrow: "BlockUrb OÜ · Tallinn, Estônia · Desde 2024",
    hero_title: ["Urbanização", "descentralizada,", { em: "operada como software." }],
    hero_p: "A BlockUrb OÜ é a holding e núcleo de governança de um ecossistema que reúne empresas, produtos e tecnologias dedicados à transformação do setor urbanístico — convergindo blockchain, tokenização, smart city, IA generativa e urbanização descentralizada sob uma mesma estrutura.",
    explore: "Explorar o ecossistema", read_manifesto: "Ler o manifesto",
    cities_online: "verticais ativas", active_modules: "empresas/produtos", parcels_indexed: "lotes mapeados",
    tvl: "tvl tokenizado", permit_time: "tempo médio de licença",
    section02: "AS VERTICAIS", section02_title: ["Sete empresas e produtos.", { em: "Uma única arquitetura." }],
    section02_sub: "Cada vertical opera de forma independente, mas se alimenta das demais — gerando uma cadeia inédita: terra, capital, tecnologia, criação, educação e impacto social conectados.",
    read_module: "Ver vertical ↗",
  },
  en: {
    dir: "ltr",
    nav: { home: "Home", platform: "Ecosystem", capital: "BLU Fund", cities: "Smart City", manifesto: "Manifesto", console: "Console", access: "Invest" },
    brand_sub: "URBAN ECOSYSTEM",
    network: "Network", stack: "Verticals", live_pilot: "LIVE · Tallinn",
    hero_eyebrow: "BlockUrb OÜ · Tallinn, Estonia · Est. 2024",
    hero_title: ["Decentralized", "urbanization,", { em: "operated as software." }],
    hero_p: "BlockUrb OÜ is the holding company and governance core of an ecosystem that unites companies, products and technologies dedicated to transforming the urban-development sector — converging blockchain, tokenization, smart city, generative AI and decentralized urbanization under a single structure.",
    explore: "Explore the ecosystem", read_manifesto: "Read the manifesto",
    cities_online: "active verticals", active_modules: "companies/products", parcels_indexed: "parcels mapped",
    tvl: "tokenized tvl", permit_time: "avg. permit time",
    section02: "THE VERTICALS", section02_title: ["Seven companies and products.", { em: "One single architecture." }],
    section02_sub: "Each vertical operates independently but feeds the others — generating a chain unseen in the sector: land, capital, technology, creation, education and social impact connected.",
    read_module: "Open vertical ↗",
  },
  es: {
    dir: "ltr",
    nav: { home: "Inicio", platform: "Ecosistema", capital: "BLU Fund", cities: "Smart City", manifesto: "Manifiesto", console: "Consola", access: "Invertir" },
    brand_sub: "ECOSISTEMA URBANO",
    network: "Red", stack: "Verticales", live_pilot: "EN VIVO · Tallinn",
    hero_eyebrow: "BlockUrb OÜ · Tallin, Estonia · Desde 2024",
    hero_title: ["Urbanización", "descentralizada,", { em: "operada como software." }],
    hero_p: "BlockUrb OÜ es la holding y núcleo de gobernanza de un ecosistema que reúne empresas, productos y tecnologías dedicados a transformar el sector urbanístico — convergiendo blockchain, tokenización, smart city, IA generativa y urbanización descentralizada bajo una misma estructura.",
    explore: "Explorar el ecosistema", read_manifesto: "Leer el manifiesto",
    cities_online: "verticales activas", active_modules: "empresas/productos", parcels_indexed: "lotes mapeados",
    tvl: "tvl tokenizado", permit_time: "tiempo medio de licencia",
    section02: "LAS VERTICALES", section02_title: ["Siete empresas y productos.", { em: "Una única arquitectura." }],
    section02_sub: "Cada vertical opera de forma independiente pero se alimenta de las demás — generando una cadena inédita en el sector: tierra, capital, tecnología, creación, educación e impacto social conectados.",
    read_module: "Abrir vertical ↗",
  },
};

// Estonian (mirrors English copy — runtime translator can refine if claude.complete is available)
STRINGS.et = {
  dir: "ltr",
  nav: { home: "Avaleht", platform: "Ökosüsteem", capital: "BLU Fund", cities: "Smart City", manifesto: "Manifest", console: "Konsool", access: "Investeeri" },
  brand_sub: "LINNAKESKKOND",
  network: "Võrk", stack: "Vertikaalid", live_pilot: "OTSE · Tallinn",
  hero_eyebrow: "BlockUrb OÜ · Tallinn, Eesti · alates 2024",
  hero_title: ["Detsentraliseeritud", "linnaplaneerimine,", { em: "juhitud nagu tarkvara." }],
  hero_p: "BlockUrb OÜ on holding ja juhtimiskeskus ökosüsteemile, mis ühendab ettevõtteid, tooteid ja tehnoloogiaid linnaarengu sektori muutmiseks — ühendades plokiaheltehnoloogiat, tokeniseerimist, smart city’t, generatiivset tehisintellekti ja detsentraliseeritud linnaarengut ühe struktuuri alla.",
  explore: "Avasta ökosüsteemi", read_manifesto: "Loe manifesti",
  cities_online: "akiivseid vertikaale", active_modules: "ettevõtted/tooted", parcels_indexed: "krundid kaardistatud",
  tvl: "tokeniseeritud TVL", permit_time: "keskmine loaaeg",
  section02: "VERTIKAALID", section02_title: ["Seitse ettevõtet ja toodet.", { em: "Üks arhitektuur." }],
  section02_sub: "Iga vertikaal toimib sõltumatult, kuid toitub teistest — luues sektoris ennenägematu ahela: maa, kapital, tehnoloogia, looming, haridus ja sotsiaalne mõju ühendatud.",
  read_module: "Ava vertikaal ↗",
};

// Mandarin Chinese (Simplified)
STRINGS.zh = {
  dir: "ltr",
  nav: { home: "首页", platform: "生态系统", capital: "BLU Fund", cities: "智慧城市", manifesto: "宣言", console: "控制台", access: "投资" },
  brand_sub: "城市生态系统",
  network: "网络", stack: "垂直领域", live_pilot: "直播 · 塔林",
  hero_eyebrow: "BlockUrb OÜ · 爱沙尼亚塔林 · 自 2024 年",
  hero_title: ["去中心化的", "城市化，", { em: "像软件一样运行。" }],
  hero_p: "BlockUrb OÜ 是一个生态系统的控股公司与治理核心，汇聚致力于变革城市开发领域的企业、产品与技术——在同一架构下融合区块链、通证化、智慧城市、生成式人工智能与去中心化城市化。",
  explore: "探索生态系统", read_manifesto: "阅读宣言",
  cities_online: "活跃垂直领域", active_modules: "企业/产品", parcels_indexed: "已测绘地块",
  tvl: "通证化 TVL", permit_time: "平均审批时长",
  section02: "垂直领域", section02_title: ["七家企业与产品。", { em: "一套统一架构。" }],
  section02_sub: "每个垂直领域独立运营，又彼此供给——形成业内前所未有的链条：土地、资本、技术、创意、教育与社会影响力相互连接。",
  read_module: "查看垂直领域 ↗",
};

// English fallback default for components that still expect t.section03..section05
["pt","en","es","et","zh"].forEach(l => {
  STRINGS[l].section03 = STRINGS[l].section02; STRINGS[l].section03_title = ""; STRINGS[l].section03_sub = "";
  STRINGS[l].section04 = ""; STRINGS[l].section04_title = "";
  STRINGS[l].section05 = ""; STRINGS[l].section05_title = ""; STRINGS[l].section05_sub = "";
  STRINGS[l].schedule = STRINGS[l].explore; STRINGS[l].see_live = STRINGS[l].read_module;
});

const TX = {}; // disabled — content is authored directly in target language


// Per-language inline translator. Content is authored PT (key) + EN (2nd arg).
// Other languages come either inline (es=3rd, et=4th, zh=5th — used for JSX-fragment
// and array copy that can't be dictionary-keyed) or via window.BU_DICT[pt][lang]
// (used for all plain-string copy, so page files stay tr(pt,en)).
// pt → pt; es/et/zh → inline || BU_DICT[pt][lang] || en || pt; en (and any other) → en || pt.
function makeTr(lang) {
  return function tr(pt, en, es, et, zh) {
    if (lang === "pt") return pt;
    if (lang === "es" || lang === "et" || lang === "zh") {
      const inline = { es, et, zh }[lang];
      if (inline != null) return inline;
      const D = (typeof window !== "undefined" && window.BU_DICT) || {};
      if (typeof pt === "string" && D[pt] && D[pt][lang] != null) return D[pt][lang];
      return en != null ? en : pt;
    }
    return en != null ? en : pt;
  };
}

const I18nContext = createContext({ lang: "en", t: STRINGS.en, setLang: () => {}, tr: makeTr("en") });

function tx(text, lang) { return text; }

// ---- Runtime translator ----
// Walks the DOM, batches all visible text nodes, sends them to Claude,
// replaces in place, caches per-lang in localStorage. Stores the original PT text on
// each node (data attribute via a WeakMap) so switching back is instant.
const LANG_NAMES = { pt: "Portuguese", en: "English", es: "Spanish", et: "Estonian", ar: "Arabic", zh: "Mandarin Chinese" };
const LANG_DIR = { pt: "ltr", en: "ltr", es: "ltr", et: "ltr", ar: "rtl", zh: "ltr" };

const __originalText = new WeakMap();
const __cacheKey = (lang) => `blockurb_tx_${lang}`;
function __loadCache(lang) {
  try { return JSON.parse(localStorage.getItem(__cacheKey(lang)) || "{}"); } catch { return {}; }
}
function __saveCache(lang, cache) {
  try { localStorage.setItem(__cacheKey(lang), JSON.stringify(cache)); } catch {}
}

function __collectTextNodes() {
  const SKIP_TAGS = new Set(["SCRIPT","STYLE","NOSCRIPT","CODE","PRE"]);
  const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT, {
    acceptNode(n) {
      if (!n.parentElement) return NodeFilter.FILTER_REJECT;
      if (SKIP_TAGS.has(n.parentElement.tagName)) return NodeFilter.FILTER_REJECT;
      if (n.parentElement.closest("[data-no-translate]")) return NodeFilter.FILTER_REJECT;
      const txt = n.nodeValue || "";
      if (!txt.trim()) return NodeFilter.FILTER_REJECT;
      // Skip pure numbers / mono codes / very short tokens that are likely brand or coords
      if (/^[\s\d.,:;%+\-/×·°€$£¥{}()\[\]]+$/.test(txt)) return NodeFilter.FILTER_REJECT;
      return NodeFilter.FILTER_ACCEPT;
    }
  });
  const nodes = [];
  let n; while ((n = walker.nextNode())) nodes.push(n);
  return nodes;
}

function __withTimeout(promise, ms) {
  return Promise.race([
    promise,
    new Promise((_, rej) => setTimeout(() => rej(new Error("timeout")), ms)),
  ]);
}

async function __translateBatch(strings, lang) {
  const target = LANG_NAMES[lang];
  if (!target || lang === "en") return strings;
  if (!window.claude || typeof window.claude.complete !== "function") return strings;
  const numbered = strings.map((s, i) => `${i+1}. ${s.replace(/\n/g, " ⏎ ")}`).join("\n");
  const prompt = `You are a professional translator for an architecture / urbanism / web3 brand called BlockUrb.
Translate the following ${strings.length} numbered English strings into ${target}.

RULES:
- Keep brand names exactly as-is: BlockUrb, Blockurb, BLU, BLU Fund, Urblock, BluArq.AI, BlockUrb School, BlockUrb Smart City, Instituto BlockUrb, BlockUrb Franchise, Magnatas do Lote, BlockUrb Gamer, Tallinn, OÜ.
- Keep technical terms accurate: blockchain, tokenization/tokenização, smart city, DAO, MiCA, RRT, ITBI, IPTU, P2P, B2G, ESG, KYC, KYB, VASP.
- Preserve all leading/trailing whitespace and the ⏎ markers.
- Output ONLY the translated strings, one per line, in the same numbered format. NO commentary, NO header, NO quotes.
- Match the tone: editorial, restrained, professional.

Strings:
${numbered}`;
  let resp;
  try { resp = await __withTimeout(window.claude.complete(prompt), 25000); } catch (e) { console.warn("[i18n] batch failed:", e.message); return strings; }
  const lines = (resp || "").split("\n").map(l => l.trim()).filter(Boolean);
  const out = strings.slice();
  for (const line of lines) {
    const m = line.match(/^(\d+)[.)\]]?\s*(.*)$/s);
    if (!m) continue;
    const idx = parseInt(m[1], 10) - 1;
    if (idx >= 0 && idx < out.length) out[idx] = m[2].replace(/⏎/g, "\n");
  }
  return out;
}

// Translation progress signal — components can subscribe via window.__buTxProgress
function __setProgress(state) {
  window.__buTxState = state;
  window.dispatchEvent(new CustomEvent("bu:tx", { detail: state }));
}

async function applyTranslation(lang) {
  // Runtime DOM translator is disabled: it depended on window.claude.complete()
  // which only exists inside the Claude artifact preview, not on the live site.
  // All copy is now authored per-language in React via the tr() helper + STRINGS.
  __setProgress({ active: false });
  return;
  // eslint-disable-next-line no-unreachable
  // Snapshot original text once
  const nodes = __collectTextNodes();
  for (const n of nodes) {
    if (!__originalText.has(n)) __originalText.set(n, n.nodeValue);
  }
  if (lang === "en") {
    for (const n of nodes) {
      const orig = __originalText.get(n);
      if (orig != null && n.nodeValue !== orig) n.nodeValue = orig;
    }
    return;
  }
  const cache = __loadCache(lang);
  const todo = [];
  for (const n of nodes) {
    const orig = __originalText.get(n);
    if (orig == null) continue;
    const key = orig.trim();
    if (cache[key]) {
      const lead = orig.match(/^\s*/)[0];
      const tail = orig.match(/\s*$/)[0];
      n.nodeValue = lead + cache[key] + tail;
    } else {
      todo.push({ node: n, orig, key });
    }
  }
  // Batch in chunks of 14 strings, run 3 in parallel
  const CHUNK = 14;
  const batches = [];
  for (let i = 0; i < todo.length; i += CHUNK) batches.push(todo.slice(i, i + CHUNK));
  if (batches.length === 0) { __setProgress({ active: false }); return; }
  __setProgress({ active: true, lang, total: batches.length, done: 0 });

  const PARALLEL = 3;
  let done = 0;
  for (let i = 0; i < batches.length; i += PARALLEL) {
    const slice = batches.slice(i, i + PARALLEL);
    await Promise.all(slice.map(async (chunk) => {
      const inputs = chunk.map(c => c.key);
      const outputs = await __translateBatch(inputs, lang);
      chunk.forEach((c, j) => {
        const tx = outputs[j];
        if (typeof tx === "string" && tx.trim() && tx !== c.key) {
          cache[c.key] = tx;
          const lead = c.orig.match(/^\s*/)[0];
          const tail = c.orig.match(/\s*$/)[0];
          c.node.nodeValue = lead + tx + tail;
        }
      });
      done++;
      __setProgress({ active: true, lang, total: batches.length, done });
      __saveCache(lang, cache);
    }));
  }
  __setProgress({ active: false });
}

function TranslationOverlay() {
  const [state, setState] = useState({ active: false });
  useEffect(() => {
    const h = (e) => setState(e.detail || { active: false });
    window.addEventListener("bu:tx", h);
    return () => window.removeEventListener("bu:tx", h);
  }, []);
  if (!state.active) return null;
  const pct = state.total ? Math.round((state.done / state.total) * 100) : 0;
  const langName = LANG_NAMES[state.lang] || state.lang;
  return (
    <div data-no-translate style={{
      position:"fixed", bottom: 24, insetInlineEnd: 24, zIndex: 9999,
      background:"var(--bg-elev)", color:"var(--ink)",
      border:"1px solid var(--line-strong)",
      padding:"14px 18px", minWidth: 240,
      fontFamily:"var(--font-mono)", fontSize: 12,
      boxShadow:"0 12px 32px -12px rgba(0,0,0,0.35)",
      animation:"menuPop 0.22s cubic-bezier(.2,.7,.2,1)",
      direction:"ltr", textAlign:"left",
    }}>
      <div style={{display:"flex", alignItems:"center", gap: 10, marginBottom: 10}}>
        <span className="lang-spinner" aria-hidden="true"></span>
        <span style={{flex:1}}>Translating to <strong>{langName}</strong>…</span>
        <span style={{opacity:0.6, fontVariantNumeric:"tabular-nums"}}>{pct}%</span>
      </div>
      <div style={{height:2, background:"color-mix(in srgb, var(--ink) 12%, transparent)", overflow:"hidden"}}>
        <div style={{height:"100%", width:`${pct}%`, background:"var(--accent-ink)", transition:"width 0.4s ease"}}/>
      </div>
      <div style={{marginTop: 8, opacity:0.55, fontSize: 10}}>{state.done}/{state.total} chunks · cached after first run</div>
    </div>
  );
}

function I18nProvider({ children }) {
  const [lang, setLang] = useState(() => {
    const SUPPORTED = ["en", "pt", "es", "et", "zh"];
    // Default pt-BR: SEO/meta e público-alvo (BR/PY) são portugueses; coerente com <html lang="pt-BR">.
    try {
      const stored = localStorage.getItem("blockurb_lang");
      return SUPPORTED.includes(stored) ? stored : "pt";
    } catch { return "pt"; }
  });
  const [translating, setTranslating] = useState(false);
  useEffect(() => {
    try { localStorage.setItem("blockurb_lang", lang); } catch {}
    const dir = LANG_DIR[lang] || "ltr";
    document.documentElement.setAttribute("lang", lang);
    document.documentElement.setAttribute("dir", dir);
  }, [lang]);
  // Run translator after each render cycle (so newly mounted React content gets translated)
  useEffect(() => {
    let cancelled = false;
    const run = async () => {
      setTranslating(true);
      // Let React commit before walking DOM
      await new Promise(r => requestAnimationFrame(() => requestAnimationFrame(r)));
      if (cancelled) return;
      await applyTranslation(lang);
      if (!cancelled) setTranslating(false);
    };
    run();
    return () => { cancelled = true; };
  }, [lang]);
  const t = STRINGS[lang] || STRINGS.en;
  const tr = makeTr(lang);
  return <I18nContext.Provider value={{ lang, t, setLang, translating, tr }}>{children}</I18nContext.Provider>;
}
function useT() { return useContext(I18nContext); }
function useAutoTranslate() { /* runtime-translator handles this */ }

function renderTitle(parts) {
  if (typeof parts === "string") return parts;
  return parts.map((p, i) =>
    typeof p === "string"
      ? <React.Fragment key={i}>{p}{i < parts.length - 1 && <br/>}</React.Fragment>
      : <em key={i}>{p.em}</em>
  );
}

// ---- Logo ----
function Logo({ size = 28, withWord = true, color }) {
  return (
    <span style={{display:"inline-flex", alignItems:"center", gap: 10}}>
      <span style={{
        width: size, height: size, flexShrink: 0,
        backgroundImage: "url('logo-coin.png')",
        backgroundSize: "contain", backgroundRepeat:"no-repeat", backgroundPosition:"center",
        filter: "drop-shadow(0 2px 8px rgba(74,124,89,0.25))",
      }} aria-label="BlockUrb"/>
      {withWord && (
        <span style={{display:"inline-flex", alignItems:"baseline", gap: 8}}>
          <span style={{fontFamily:"var(--font-mono)", fontSize: 13, fontWeight: 600, letterSpacing:"0.02em", color: color || "currentColor"}}>BlockUrb</span>
          <span style={{fontFamily:"var(--font-mono)", fontSize: 10, fontWeight: 400, letterSpacing:"0.14em", color: "var(--ink-muted)", textTransform:"uppercase"}}>OÜ</span>
        </span>
      )}
    </span>
  );
}

// ---- Lang switcher ----
function LangSwitcher() {
  const { lang, setLang } = useT();
  const [open, setOpen] = useState(false);
  const ref = useRef(null);
  useEffect(() => {
    const onDown = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    window.addEventListener("mousedown", onDown);
    return () => window.removeEventListener("mousedown", onDown);
  }, []);
  const { translating } = useT();
  const langs = [
    ["en","EN","English"],
    ["pt","PT","Português"],
    ["es","ES","Español"],
    ["et","ET","Eesti"],
    ["zh","ZH","中文"],
  ];
  return (
    <div ref={ref} style={{position:"relative"}}>
      <button className="lang-btn" onClick={(e) => { e.stopPropagation(); setOpen(o => !o); }} data-no-translate>
        {translating ? <span className="lang-spinner" aria-hidden="true"></span> : null}
        {langs.find(l => l[0]===lang)?.[1] || "EN"} <span style={{opacity:0.5}}>▾</span>
      </button>
      {open && (
        <div className="lang-menu">
          {langs.map(([code, short, name]) => (
            <button key={code} type="button" className={`lang-opt ${lang===code?"active":""}`} onMouseDown={(e) => { e.preventDefault(); e.stopPropagation(); setLang(code); setOpen(false); }} onClick={(e) => { e.preventDefault(); e.stopPropagation(); setLang(code); setOpen(false); }}>
              <span style={{fontWeight:600, minWidth: 24}}>{short}</span>
              <span style={{opacity:0.7}}>{name}</span>
            </button>
          ))}
        </div>
      )}
    </div>
  );
}

// ---- Motion / animation primitives ----
function Reveal({ as = "div", children, delay = 0, className, style, ...rest }) {
  const Tag = as;
  const ref = useRef(null);
  const [shown, setShown] = useState(false);
  useEffect(() => {
    const el = ref.current; if (!el) return;
    // Fallback: if element is already on screen at mount, reveal immediately.
    const r = el.getBoundingClientRect();
    const inView = r.top < window.innerHeight && r.bottom > 0;
    if (inView) {
      const id = setTimeout(() => setShown(true), delay);
      return () => clearTimeout(id);
    }
    const io = new IntersectionObserver(([e]) => { if (e.isIntersecting) { setTimeout(() => setShown(true), delay); io.disconnect(); } }, { threshold: 0.05 });
    io.observe(el);
    // Safety net: ensure visibility after 1.5s no matter what.
    const fallback = setTimeout(() => setShown(true), 1500 + delay);
    return () => { io.disconnect(); clearTimeout(fallback); };
  }, [delay]);
  return <Tag ref={ref} className={className} style={{
    ...style,
    opacity: shown ? 1 : 0,
    transform: shown ? "translateY(0)" : "translateY(14px)",
    transition: "opacity 0.7s cubic-bezier(.2,.7,.2,1), transform 0.7s cubic-bezier(.2,.7,.2,1)",
  }} {...rest}>{children}</Tag>;
}

function SplitReveal({ text, lineDelay = 80 }) {
  const lines = Array.isArray(text) ? text : [text];
  return (
    <span style={{display:"block"}}>
      {lines.map((line, i) => (
        <span key={i} style={{display:"block", overflow:"hidden"}}>
          <Reveal as="span" delay={i * lineDelay} style={{display:"block"}}>
            {typeof line === "string" ? line : <em>{line.em}</em>}
          </Reveal>
        </span>
      ))}
    </span>
  );
}

function CountUp({ to, decimals = 0 }) {
  const [n, setN] = useState(0);
  const ref = useRef(null);
  useEffect(() => {
    const el = ref.current; if (!el) return;
    const io = new IntersectionObserver(([e]) => {
      if (e.isIntersecting) {
        const start = performance.now(), dur = 1400;
        const tick = (now) => {
          const t = Math.min(1, (now - start) / dur);
          const eased = 1 - Math.pow(1 - t, 3);
          setN(eased * to);
          if (t < 1) requestAnimationFrame(tick);
        };
        requestAnimationFrame(tick);
        io.disconnect();
      }
    }, { threshold: 0.4 });
    io.observe(el);
    return () => io.disconnect();
  }, [to]);
  return <span ref={ref}>{decimals ? n.toFixed(decimals) : Math.round(n).toLocaleString()}</span>;
}

function Magnetic({ children }) {
  const ref = useRef(null);
  const onMove = (e) => {
    const el = ref.current; if (!el) return;
    const r = el.getBoundingClientRect();
    const x = (e.clientX - r.left - r.width/2) * 0.18;
    const y = (e.clientY - r.top - r.height/2) * 0.18;
    el.style.transform = `translate(${x}px, ${y}px)`;
  };
  const onLeave = () => { if (ref.current) ref.current.style.transform = "translate(0,0)"; };
  return <span ref={ref} onMouseMove={onMove} onMouseLeave={onLeave} style={{display:"inline-block", transition:"transform 0.2s cubic-bezier(.2,.7,.2,1)"}}>{children}</span>;
}

function Parallax({ children, speed = 0.1, style }) {
  const ref = useRef(null);
  useEffect(() => {
    const onScroll = () => {
      const el = ref.current; if (!el) return;
      const r = el.getBoundingClientRect();
      const center = r.top + r.height/2 - window.innerHeight/2;
      el.style.transform = `translateY(${-center * speed}px)`;
    };
    window.addEventListener("scroll", onScroll, { passive: true });
    onScroll();
    return () => window.removeEventListener("scroll", onScroll);
  }, [speed]);
  return <div ref={ref} style={{...style, willChange:"transform"}}>{children}</div>;
}

function MarqueeStrip({ words }) {
  return (
    <div style={{
      borderTop:"1px solid var(--line)", borderBottom:"1px solid var(--line)",
      padding:"40px 0", overflow:"hidden", background:"var(--paper)",
    }}>
      <div style={{display:"flex", gap: 80, animation:"marquee 40s linear infinite", whiteSpace:"nowrap", width:"max-content"}}>
        {[...words, ...words, ...words].map((w, i) => (
          <span key={i} style={{fontFamily:"var(--font-display)", fontSize: 64, color:"var(--ink)", display:"inline-flex", alignItems:"center", gap: 80}}>
            {w}<span style={{color:"var(--accent-ink)", fontStyle:"italic"}}>·</span>
          </span>
        ))}
      </div>
    </div>
  );
}

Object.assign(window, { I18nContext, I18nProvider, useT, useAutoTranslate, STRINGS, TX, tx, renderTitle, Logo, LangSwitcher, TranslationOverlay, Reveal, SplitReveal, CountUp, Magnetic, Parallax, MarqueeStrip });
