// Shared hooks and small components used across the three variations.

const { useState, useEffect, useRef, useMemo, useCallback, createContext, useContext } = React;

// ─── Typewriter terminal ────────────────────────────────────────────────────
function useTypewriterLines(lines, opts = {}) {
  const speed = opts.speed ?? 18;
  const pause = opts.pause ?? 280;
  const [rendered, setRendered] = useState([]);
  const [cursorOn, setCursorOn] = useState(true);

  useEffect(() => {
    let cancelled = false;
    let timeouts = [];
    setRendered([]);
    async function run() {
      const out = [];
      for (let i = 0; i < lines.length; i++) {
        const ln = lines[i];
        if (cancelled) return;
        if (ln.o !== undefined) {
          // output line — stream char by char fast
          const text = ln.o;
          let cur = "";
          for (let c = 0; c < text.length; c++) {
            if (cancelled) return;
            cur += text[c];
            await new Promise((r) => timeouts.push(setTimeout(r, 6)));
            if (!cancelled) setRendered([...out, { kind: "o", text: cur }]);
          }
          out.push({ kind: "o", text });
        } else {
          // command line: prompt + typed command
          const prompt = ln.p ?? "$ ";
          const cmd = ln.c ?? "";
          await new Promise((r) => timeouts.push(setTimeout(r, pause)));
          let cur = "";
          for (let c = 0; c < cmd.length; c++) {
            if (cancelled) return;
            cur += cmd[c];
            await new Promise((r) => timeouts.push(setTimeout(r, speed)));
            if (!cancelled) setRendered([...out, { kind: "c", prompt, text: cur }]);
          }
          out.push({ kind: "c", prompt, text: cmd });
        }
      }
    }
    run();
    return () => {
      cancelled = true;
      timeouts.forEach(clearTimeout);
    };
  }, [JSON.stringify(lines), speed, pause]);

  useEffect(() => {
    const id = setInterval(() => setCursorOn((c) => !c), 530);
    return () => clearInterval(id);
  }, []);

  return { rendered, cursorOn };
}

// ─── Reveal-on-scroll wrapper ───────────────────────────────────────────────
function Reveal({ children, delay = 0, y = 14, className = "", as: Tag = "div", style = {} }) {
  const ref = useRef(null);
  const [shown, setShown] = useState(false);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const io = new IntersectionObserver(
      (entries) => {
        entries.forEach((e) => {
          if (e.isIntersecting) {
            setTimeout(() => setShown(true), delay);
            io.disconnect();
          }
        });
      },
      { threshold: 0.08, rootMargin: "0px 0px -40px 0px" }
    );
    io.observe(el);
    return () => io.disconnect();
  }, [delay]);
  return (
    <Tag
      ref={ref}
      className={className}
      style={{
        opacity: shown ? 1 : 0,
        transform: shown ? "translateY(0)" : `translateY(${y}px)`,
        transition: "opacity .7s cubic-bezier(.2,.7,.2,1), transform .7s cubic-bezier(.2,.7,.2,1)",
        ...style,
      }}
    >
      {children}
    </Tag>
  );
}

// ─── Custom cursor (terminal block) ─────────────────────────────────────────
function CustomCursor({ accent = "oklch(0.78 0.19 145)", enabled = true }) {
  const [pos, setPos] = useState({ x: -100, y: -100 });
  const [hover, setHover] = useState(false);
  const [visible, setVisible] = useState(false);
  useEffect(() => {
    if (!enabled) return;
    const move = (e) => {
      setPos({ x: e.clientX, y: e.clientY });
      setVisible(true);
      const t = e.target;
      const interactive = t && (t.closest("a, button, [data-cursor='hover'], input, textarea"));
      setHover(!!interactive);
    };
    const leave = () => setVisible(false);
    window.addEventListener("mousemove", move);
    window.addEventListener("mouseleave", leave);
    return () => {
      window.removeEventListener("mousemove", move);
      window.removeEventListener("mouseleave", leave);
    };
  }, [enabled]);
  if (!enabled) return null;
  return (
    <>
      <div
        aria-hidden="true"
        style={{
          position: "fixed",
          left: 0, top: 0,
          transform: `translate(${pos.x - 6}px, ${pos.y - 8}px) scale(${hover ? 1.6 : 1})`,
          width: 12, height: 16,
          background: accent,
          mixBlendMode: "difference",
          pointerEvents: "none",
          zIndex: 99998,
          opacity: visible ? 0.95 : 0,
          transition: "transform .12s cubic-bezier(.2,.7,.2,1), opacity .2s",
        }}
      />
      <div
        aria-hidden="true"
        style={{
          position: "fixed",
          left: 0, top: 0,
          transform: `translate(${pos.x - 1}px, ${pos.y - 1}px)`,
          width: 2, height: 2,
          background: accent,
          borderRadius: "50%",
          pointerEvents: "none",
          zIndex: 99999,
          opacity: visible ? 1 : 0,
          transition: "opacity .2s",
        }}
      />
    </>
  );
}

// ─── Photo placeholder/real ────────────────────────────────────────────────
function ProfilePhoto({ src = "assets/maxwell.png", size = 120, accent, square = false, ring = true }) {
  return (
    <div
      style={{
        width: size, height: size,
        borderRadius: square ? 4 : "50%",
        overflow: "hidden",
        position: "relative",
        flexShrink: 0,
        boxShadow: ring ? `0 0 0 1px var(--border), 0 0 0 ${Math.max(2, size * 0.04)}px ${accent}22` : "none",
        background: "var(--surface-2)",
      }}
    >
      <img src={src} alt="Maxwell Mezadre" style={{ width: "100%", height: "100%", objectFit: "cover", display: "block" }} />
    </div>
  );
}

// ─── Demo com respostas pré-definidas (sem API) ─────────────────────────────
const CANNED = {
  en: {
    fixed: {
      "What's Maxwell's strongest stack?": "PHP 8.x / Laravel 12 + Vue 3 / TypeScript is where I'm most battle-tested — 6+ years, platforms serving 3M+ users with zero critical incidents. PostgreSQL, Redis, Kafka/RabbitMQ and Docker round out the core.",
      "How does he use AI in production?": "Daily workflow: Claude Code for PR acceleration (~30% faster), custom MCP servers to automate repetitive tasks, RAG pipelines for document-heavy features, and autonomous agents for code review and test generation. It's tooling, not theatre.",
      "Why hire a senior PHP dev in 2026?": "Because most products still run on PHP, and running it well — Clean Architecture, zero-downtime migrations, distributed messaging — is a rare skill. I also ship AI-native features on top of that foundation, so you get both.",
    },
    fallback: [
      "That's a good question. I've shipped systems at this scale before — happy to walk through specifics on a call.",
      "Clean Architecture + SOLID + TDD is the baseline. Everything else (AI, messaging, cloud) plugs in on top of that foundation.",
      "I work async, ship daily commits, and communicate in plain English. US/Canada timezone overlap is easy from GMT-3.",
      "Available immediately for senior FullStack contracts — PJ/Contractor, 100% remote, flexible hours.",
      "My SDD workflow means specs drive tests, tests drive code. Fewer surprises, more predictable delivery.",
      "6+ years, 3M+ users, 700+ tenants, 0 critical post-release incidents in 2025. The numbers speak for themselves.",
      "Laravel 12, PHP 8.5, Vue 3, TypeScript, PostgreSQL, Redis, Kafka, RabbitMQ, Docker, AWS, GCP, Anthropic — all in production, not just on a resume.",
      "I'd love to chat. Fastest way: maxwellmezadre@gmail.com — I reply within 24h.",
    ],
  },
  pt: {
    fixed: {
      "Qual a stack mais forte do Maxwell?": "PHP 8.x / Laravel 12 + Vue 3 / TypeScript é onde tenho mais batalha — 6+ anos, plataformas com 3M+ usuários e zero incidentes críticos. PostgreSQL, Redis, Kafka/RabbitMQ e Docker completam o core.",
      "Como ele usa IA em produção?": "Workflow diário: Claude Code pra acelerar PRs (~30% mais rápido), servidores MCP customizados pra automatizar tarefas repetitivas, pipelines RAG pra features pesadas em documentos, e agentes autônomos pra code review e geração de testes. É tooling, não teatro.",
      "Por que contratar um PHP sênior em 2026?": "Porque a maioria dos produtos ainda roda em PHP, e rodar bem — Clean Architecture, migrações zero-downtime, mensageria distribuída — é skill rara. Eu também shipto features AI-native em cima dessa base, então você leva os dois.",
    },
    fallback: [
      "Boa pergunta. Já entreguei sistemas nessa escala antes — posso detalhar numa call.",
      "Clean Architecture + SOLID + TDD é o baseline. Todo o resto (IA, mensageria, cloud) pluga em cima dessa fundação.",
      "Trabalho async, commito diariamente e me comunico em inglês com times US/Canadá. Timezone GMT-3 tem bastante overlap.",
      "Disponível imediatamente pra contratos FullStack sênior — PJ, 100% remoto, horário flexível.",
      "Meu workflow SDD significa que specs geram testes, testes geram código. Menos surpresas, entrega mais previsível.",
      "6+ anos, 3M+ usuários, 700+ tenants, 0 incidentes críticos pós-release em 2025. Os números falam por si.",
      "Laravel 12, PHP 8.5, Vue 3, TypeScript, PostgreSQL, Redis, Kafka, RabbitMQ, Docker, AWS, GCP, Anthropic — tudo em produção, não só no currículo.",
      "Adoro conversar. Forma mais rápida: maxwellmezadre@gmail.com — respondo em até 24h.",
    ],
  },
};

function ClaudeDemo({ copy, accent, dense = false, lang = "en", onCmd = null }) {
  const [messages, setMessages] = useState([]);
  const [input, setInput] = useState("");
  const [busy, setBusy] = useState(false);
  const scrollRef = useRef(null);
  const canned = CANNED[lang] || CANNED.en;

  useEffect(() => {
    if (scrollRef.current) scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
  }, [messages, busy]);

  const send = (q) => {
    const question = (q ?? input).trim();
    if (!question || busy) return;
    setInput("");

    // Intercept /commands — reply in chat with relevant content
    if (question.startsWith("/")) {
      const cmd = question.slice(1).toLowerCase().trim();
      const cmdReplies = {
        en: {
          about: "I'm Maxwell Mezadre — Senior FullStack Developer based in Rio Grande do Sul, Brazil (GMT-3). 6+ years shipping production systems for US, Canada and Brazil clients. I specialize in PHP/Laravel, Vue 3, distributed systems and AI-native engineering. Available immediately, 100% remote, PJ/Contractor.",
          stack: "Primary: PHP 8.x · Laravel 12 · Livewire 3 · Vue 3 · TypeScript · Tailwind · PostgreSQL · Redis · Kafka · RabbitMQ · Docker · AWS · GCP · Anthropic · MCP. Also comfortable with: Node.js · React · MySQL · MongoDB · Azure · Pest · PHPUnit · Elixir · Go.",
          contact: "📧 maxwellmezadre@gmail.com · GitHub: github.com/maxwellmezadre · LinkedIn: linkedin.com/in/maxwell-mezadre · Available for PJ contracts, replies within 24h.",
          experience: "Sep 2025–Now: Senior FullStack @ US/Canada Consultancy. Jan 2023–Sep 2025: Senior PHP @ Brazilian Real Estate SaaS (3M+ users, 700+ agencies). Nov 2021–Dec 2022: Mid PHP @ same platform. Sep 2019–Nov 2021: Junior PHP + RPA @ Real Estate & Financial Cooperative.",
          projects: "01 AI Chargeback Engine · 02 Multi-Currency CRM · 03 Healthcare CRM SaaS · 04 Real Estate Platform (3M+ users) · 05 PHP 5.6→8.x Migration (zero downtime, -60% bugs) · 06 AI Agent + RAG Pipeline.",
          matrix: "↑↑↓↓←→←→BA — you found it. Activating matrix mode…",
          ai: "I use Claude Code, custom MCP servers, RAG pipelines and autonomous agents daily. ~30% faster PR throughput. AI isn't decoration — it's my actual workflow. SDD: specs → tests → code, all accelerated by AI tooling.",
          sdd: "Spec-Driven Development: every feature starts from a written spec → derived contracts (OpenAPI) → tests generated from acceptance criteria → implementation against those tests. Result: predictable delivery, full coverage, no surprises.",
        },
        pt: {
          about: "Sou Maxwell Mezadre — Desenvolvedor FullStack Sênior baseado no RS, Brasil (GMT-3). 6+ anos entregando sistemas em produção para clientes US, Canadá e Brasil. Especialista em PHP/Laravel, Vue 3, sistemas distribuídos e engenharia AI-native. Disponível imediatamente, 100% remoto, PJ.",
          stack: "Primário: PHP 8.x · Laravel 12 · Livewire 3 · Vue 3 · TypeScript · Tailwind · PostgreSQL · Redis · Kafka · RabbitMQ · Docker · AWS · GCP · Anthropic · MCP. Também: Node.js · React · MySQL · MongoDB · Azure · Pest · PHPUnit · Elixir · Go.",
          contact: "📧 maxwellmezadre@gmail.com · GitHub: github.com/maxwellmezadre · LinkedIn: linkedin.com/in/maxwell-mezadre · Disponível para contratos PJ, resposta em até 24h.",
          experience: "Set 2025–Atual: FullStack Sênior @ Consultoria US/Canadá. Jan 2023–Set 2025: PHP Sênior @ SaaS Imobiliária (3M+ usuários, 700+ imobiliárias). Nov 2021–Dez 2022: PHP Pleno @ mesma plataforma. Set 2019–Nov 2021: PHP Jr + RPA @ Imobiliária & Cooperativa Financeira.",
          projects: "01 Motor IA de Chargeback · 02 CRM Multi-Moeda · 03 CRM SaaS de Saúde · 04 Plataforma Imobiliária (3M+ usuários) · 05 Migração PHP 5.6→8.x (zero downtime, -60% bugs) · 06 Agente IA + Pipeline RAG.",
          matrix: "↑↑↓↓←→←→BA — você achou. Ativando modo matrix…",
          ai: "Uso Claude Code, servidores MCP customizados, pipelines RAG e agentes autônomos diariamente. ~30% mais rápido nos PRs. IA não é decoração — é meu workflow real. SDD: specs → testes → código, tudo acelerado por IA.",
          sdd: "Spec-Driven Development: toda feature começa com uma spec escrita → contratos derivados (OpenAPI) → testes gerados das ACs → implementação contra esses testes. Resultado: entrega previsível, cobertura total, sem surpresas.",
        },
      };
      const replies = cmdReplies[lang] || cmdReplies.en;
      const reply = replies[cmd] ?? (lang === "pt"
        ? `Comando não encontrado. Tente /about, /stack, /contact, /experience, /projects, /ai, /sdd, /matrix.`
        : `Command not found. Try /about, /stack, /contact, /experience, /projects, /ai, /sdd, /matrix.`);
      setMessages((m) => [
        ...m,
        { role: "user", content: question },
        { role: "assistant", content: reply },
      ]);
      if (cmd === "matrix" && onCmd) setTimeout(() => onCmd("matrix"), 800);
      return;
    }

    setMessages((m) => [...m, { role: "user", content: question }]);
    setBusy(true);
    // Simulate a short typing delay
    const delay = 480 + Math.random() * 400;
    setTimeout(() => {
      const fixed = canned.fixed[question];
      const reply = fixed ?? canned.fallback[Math.floor(Math.random() * canned.fallback.length)];
      setMessages((m) => [...m, { role: "assistant", content: reply }]);
      setBusy(false);
    }, delay);
  };

  return (
    <div style={{
      border: "1px solid var(--border)",
      borderRadius: 8,
      background: "var(--surface)",
      padding: dense ? 14 : 18,
      display: "flex", flexDirection: "column",
      fontFamily: "var(--mono)",
      fontSize: dense ? 12 : 13,
      minHeight: dense ? 320 : 380,
    }}>
      <div style={{ color: "var(--fg-dim)", marginBottom: 10, display: "flex", justifyContent: "space-between" }}>
        <span>{copy.ai_demo_title}</span>
        <span style={{ display: "flex", alignItems: "center", gap: 6 }}>
          <span style={{ width: 6, height: 6, borderRadius: "50%", background: accent, boxShadow: `0 0 8px ${accent}` }} />
          ready
        </span>
      </div>
      <div ref={scrollRef} style={{ flex: 1, overflowY: "auto", display: "flex", flexDirection: "column", gap: 10, marginBottom: 10, maxHeight: 280, paddingRight: 4 }}>
        {messages.length === 0 && (
          <div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
            {copy.ai_demo_examples.map((ex, i) => (
              <button key={i} onClick={() => send(ex)} data-cursor="hover"
                style={{
                  textAlign: "left", padding: "8px 10px",
                  border: "1px solid var(--border)", borderRadius: 6,
                  background: "transparent", color: "var(--fg-dim)",
                  fontFamily: "inherit", fontSize: "inherit",
                  cursor: "pointer", transition: "all .15s",
                }}
                onMouseEnter={(e) => { e.currentTarget.style.borderColor = accent; e.currentTarget.style.color = "var(--fg)"; }}
                onMouseLeave={(e) => { e.currentTarget.style.borderColor = "var(--border)"; e.currentTarget.style.color = "var(--fg-dim)"; }}
              >
                <span style={{ color: accent, marginRight: 6 }}>›</span>{ex}
              </button>
            ))}
          </div>
        )}
        {messages.map((m, i) => (
          <div key={i} style={{ lineHeight: 1.55 }}>
            <span style={{ color: m.role === "user" ? accent : "var(--fg-dim)", marginRight: 6 }}>
              {m.role === "user" ? "›" : "λ"}
            </span>
            <span style={{ color: "var(--fg)" }}>{m.content}</span>
          </div>
        ))}
        {busy && (
          <div style={{ color: "var(--fg-dim)" }}>
            <span style={{ color: accent, marginRight: 6 }}>λ</span>
            <span className="typing-dots">thinking</span>
          </div>
        )}
      </div>
      <form onSubmit={(e) => { e.preventDefault(); send(); }}
        style={{ display: "flex", gap: 8, borderTop: "1px solid var(--border)", paddingTop: 10 }}>
        <span style={{ color: accent }}>›</span>
        <input
          value={input}
          onChange={(e) => setInput(e.target.value)}
          placeholder={copy.ai_demo_placeholder}
          style={{
            flex: 1, background: "transparent", border: 0, outline: "none",
            color: "var(--fg)", fontFamily: "inherit", fontSize: "inherit",
          }}
        />
        <button type="submit" disabled={busy} data-cursor="hover"
          style={{
            background: "transparent", border: 0, color: accent,
            fontFamily: "inherit", fontSize: "inherit",
            cursor: "pointer", opacity: busy ? 0.4 : 1,
          }}
        >
          {copy.ai_demo_send} ↵
        </button>
      </form>
    </div>
  );
}

// ─── Command palette (/) ────────────────────────────────────────────────────
function CommandPalette({ open, setOpen, accent, lang, onAction }) {
  const [q, setQ] = useState("");
  const [idx, setIdx] = useState(0);
  const inputRef = useRef(null);

  const cmds = useMemo(() => ([
    { key: "about", label: lang === "pt" ? "Ir para Sobre" : "Go to About", action: () => onAction("scroll", "about") },
    { key: "stack", label: lang === "pt" ? "Ir para Stack" : "Go to Stack", action: () => onAction("scroll", "stack") },
    { key: "experience", label: lang === "pt" ? "Ir para Experiência" : "Go to Experience", action: () => onAction("scroll", "experience") },
    { key: "projects", label: lang === "pt" ? "Ir para Projetos" : "Go to Projects", action: () => onAction("scroll", "projects") },
    { key: "ai", label: lang === "pt" ? "Demo de IA ao vivo" : "Live AI demo", action: () => onAction("scroll", "ai") },
    { key: "contact", label: lang === "pt" ? "Ir para Contato" : "Go to Contact", action: () => onAction("scroll", "contact") },
    { key: "email", label: "maxwellmezadre@gmail.com", action: () => { window.location.href = "mailto:maxwellmezadre@gmail.com"; } },
    { key: "github", label: "github.com/maxwellmezadre", action: () => window.open("https://github.com/maxwellmezadre", "_blank") },
    { key: "linkedin", label: "linkedin.com/in/maxwell-mezadre", action: () => window.open("https://linkedin.com/in/maxwell-mezadre", "_blank") },
    { key: "matrix", label: lang === "pt" ? "Modo matrix (easter egg)" : "Matrix mode (easter egg)", action: () => onAction("matrix") },
    { key: "theme", label: lang === "pt" ? "Alternar tema claro/escuro" : "Toggle light/dark theme", action: () => onAction("theme") },
    { key: "lang", label: lang === "pt" ? "Mudar para English" : "Switch to Portuguese", action: () => onAction("lang") },
  ]), [lang, onAction]);

  const filtered = useMemo(() => {
    const s = q.replace(/^\//, "").toLowerCase().trim();
    if (!s) return cmds;
    return cmds.filter((c) => c.key.includes(s) || c.label.toLowerCase().includes(s));
  }, [q, cmds]);

  useEffect(() => { if (open) { setQ(""); setIdx(0); setTimeout(() => inputRef.current?.focus(), 50); } }, [open]);
  useEffect(() => { setIdx(0); }, [q]);

  const onKey = (e) => {
    if (e.key === "Escape") { setOpen(false); }
    else if (e.key === "ArrowDown") { e.preventDefault(); setIdx((i) => Math.min(i + 1, filtered.length - 1)); }
    else if (e.key === "ArrowUp") { e.preventDefault(); setIdx((i) => Math.max(i - 1, 0)); }
    else if (e.key === "Enter") { e.preventDefault(); const c = filtered[idx]; if (c) { c.action(); setOpen(false); } }
  };

  if (!open) return null;
  return (
    <div onClick={() => setOpen(false)}
      style={{ position: "fixed", inset: 0, background: "rgba(0,0,0,.5)", zIndex: 99990, backdropFilter: "blur(4px)", display: "flex", alignItems: "flex-start", justifyContent: "center", paddingTop: "16vh" }}>
      <div onClick={(e) => e.stopPropagation()}
        style={{ width: 480, maxWidth: "92vw", background: "var(--surface)", border: "1px solid var(--border)", borderRadius: 8, fontFamily: "var(--mono)", fontSize: 13, overflow: "hidden", boxShadow: "0 24px 60px rgba(0,0,0,.6)" }}>
        <div style={{ display: "flex", alignItems: "center", gap: 10, padding: "12px 14px", borderBottom: "1px solid var(--border)" }}>
          <span style={{ color: accent }}>/</span>
          <input ref={inputRef} value={q} onChange={(e) => setQ(e.target.value)} onKeyDown={onKey}
            placeholder={lang === "pt" ? "Digite um comando…" : "Type a command…"}
            style={{ flex: 1, background: "transparent", border: 0, outline: "none", color: "var(--fg)", fontFamily: "inherit", fontSize: "inherit" }} />
          <span style={{ color: "var(--fg-dim)", fontSize: 11 }}>esc</span>
        </div>
        <div style={{ maxHeight: 320, overflowY: "auto", padding: 6 }}>
          {filtered.length === 0 && <div style={{ padding: 14, color: "var(--fg-dim)" }}>—</div>}
          {filtered.map((c, i) => (
            <div key={c.key} onMouseEnter={() => setIdx(i)} onClick={() => { c.action(); setOpen(false); }}
              style={{ padding: "9px 12px", borderRadius: 4, cursor: "pointer",
                background: i === idx ? "var(--surface-2)" : "transparent",
                color: i === idx ? "var(--fg)" : "var(--fg-dim)",
                display: "flex", justifyContent: "space-between", gap: 12 }}>
              <span>/<span style={{ color: i === idx ? accent : "var(--fg)" }}>{c.key}</span></span>
              <span style={{ fontSize: 11, color: "var(--fg-dim)" }}>{c.label}</span>
            </div>
          ))}
        </div>
        <div style={{ padding: "8px 14px", borderTop: "1px solid var(--border)", color: "var(--fg-dim)", fontSize: 10, display: "flex", gap: 14 }}>
          <span>↑↓ navigate</span><span>↵ select</span><span>esc close</span>
        </div>
      </div>
    </div>
  );
}

// ─── Matrix rain easter egg ─────────────────────────────────────────────────
function MatrixRain({ active, onClose, accent }) {
  const canvasRef = useRef(null);
  useEffect(() => {
    if (!active) return;
    const canvas = canvasRef.current;
    const ctx = canvas.getContext("2d");
    const resize = () => { canvas.width = window.innerWidth; canvas.height = window.innerHeight; };
    resize();
    window.addEventListener("resize", resize);
    const chars = "0101$~#@λ→•マツMAXWELLMEZADRELARAVELVUE".split("");
    const fontSize = 14;
    const cols = Math.floor(canvas.width / fontSize);
    const drops = Array(cols).fill(1);
    let raf;
    const draw = () => {
      ctx.fillStyle = "rgba(0,0,0,0.06)";
      ctx.fillRect(0, 0, canvas.width, canvas.height);
      ctx.fillStyle = accent;
      ctx.font = `${fontSize}px ui-monospace, monospace`;
      for (let i = 0; i < drops.length; i++) {
        const text = chars[Math.floor(Math.random() * chars.length)];
        ctx.fillText(text, i * fontSize, drops[i] * fontSize);
        if (drops[i] * fontSize > canvas.height && Math.random() > 0.975) drops[i] = 0;
        drops[i]++;
      }
      raf = requestAnimationFrame(draw);
    };
    draw();
    return () => { window.removeEventListener("resize", resize); cancelAnimationFrame(raf); };
  }, [active, accent]);
  if (!active) return null;
  return (
    <div style={{ position: "fixed", inset: 0, zIndex: 99995, background: "#000" }}>
      <canvas ref={canvasRef} style={{ display: "block" }} />
      <button onClick={onClose} style={{
        position: "absolute", top: 20, right: 20, padding: "8px 14px",
        background: "transparent", color: accent, border: `1px solid ${accent}`,
        fontFamily: "ui-monospace, monospace", fontSize: 12, cursor: "pointer", borderRadius: 4
      }}>esc to exit</button>
    </div>
  );
}

// ─── Konami toast ───────────────────────────────────────────────────────────
function useKonami(onTrigger) {
  useEffect(() => {
    const seq = ["ArrowUp","ArrowUp","ArrowDown","ArrowDown","ArrowLeft","ArrowRight","ArrowLeft","ArrowRight","KeyB","KeyA"];
    let pos = 0;
    const handler = (e) => {
      if (e.code === seq[pos]) { pos++; if (pos === seq.length) { onTrigger(); pos = 0; } }
      else { pos = e.code === seq[0] ? 1 : 0; }
    };
    window.addEventListener("keydown", handler);
    return () => window.removeEventListener("keydown", handler);
  }, [onTrigger]);
}

function Toast({ message, accent, onClose }) {
  useEffect(() => {
    if (!message) return;
    const t = setTimeout(onClose, 4500);
    return () => clearTimeout(t);
  }, [message, onClose]);
  if (!message) return null;
  return (
    <div style={{
      position: "fixed", left: "50%", bottom: 32, transform: "translateX(-50%)",
      padding: "14px 20px", background: "var(--surface)", color: "var(--fg)",
      border: `1px solid ${accent}`, boxShadow: `0 0 0 1px ${accent}33, 0 12px 40px rgba(0,0,0,.4)`,
      fontFamily: "var(--mono)", fontSize: 13, borderRadius: 6, zIndex: 99996,
      animation: "toastIn .4s cubic-bezier(.2,.7,.2,1)"
    }}>
      <span style={{ color: accent, marginRight: 8 }}>★</span>{message}
    </div>
  );
}

// ─── Contact form ───────────────────────────────────────────────────────────
function ContactForm({ copy, accent, dense }) {
  const f = copy.contact_form;
  const [fields, setFields] = useState({ name: "", email: "", company: "", message: "" });
  const [sent, setSent] = useState(false);
  const [sending, setSending] = useState(false);

  const inp = (key) => ({
    value: fields[key],
    onChange: (e) => setFields(p => ({ ...p, [key]: e.target.value })),
    placeholder: f[key],
    style: {
      width: "100%", background: "var(--surface-2)", border: "1px solid var(--border)",
      color: "var(--fg)", borderRadius: 6, padding: "11px 14px",
      fontFamily: "var(--mono)", fontSize: 13, outline: "none",
      transition: "border-color .15s", boxSizing: "border-box",
    }
  });

  const handleSubmit = async (e) => {
    e.preventDefault();
    setSending(true);
    try {
      const res = await fetch("https://formspree.io/f/xaqvbgrq", {
        method: "POST",
        headers: { "Accept": "application/json", "Content-Type": "application/json" },
        body: JSON.stringify(fields),
      });
      if (res.ok) setSent(true);
    } finally {
      setSending(false);
    }
  };

  if (sent) return (
    <div style={{ marginTop: 24, padding: "18px 20px", background: "var(--surface-2)", border: `1px solid ${accent}`, borderRadius: 8, fontFamily: "var(--mono)", fontSize: 13, color: accent }}>
      ✓ {f.sent}
    </div>
  );

  return (
    <form onSubmit={handleSubmit} style={{ marginTop: 24, display: "flex", flexDirection: "column", gap: 12 }}>
      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }}>
        <input {...inp("name")} required />
        <input {...inp("email")} type="email" required />
      </div>
      <input {...inp("company")} />
      <textarea {...inp("message")} required rows={5} style={{ ...inp("message").style, resize: "vertical" }} />
      <button type="submit" disabled={sending} style={{
        alignSelf: "flex-start", padding: "12px 24px", background: accent, color: "#0a0a0a",
        border: "none", borderRadius: 6, fontFamily: "var(--mono)", fontSize: 13, fontWeight: 600,
        cursor: "pointer", opacity: sending ? 0.7 : 1, transition: "opacity .15s",
      }}>
        {sending ? "..." : f.send}
      </button>
    </form>
  );
}

// ─── Section heading helpers ────────────────────────────────────────────────
function SectionLabel({ num, label, accent }) {
  return (
    <div style={{
      display: "flex", alignItems: "center", gap: 12,
      fontFamily: "var(--mono)", fontSize: 11,
      color: "var(--fg-dim)", textTransform: "uppercase", letterSpacing: ".18em",
      marginBottom: 24
    }}>
      <span style={{ color: accent }}>{num}</span>
      <span>{label}</span>
      <span style={{ flex: 1, height: 1, background: "var(--border)" }} />
    </div>
  );
}

// Expose
Object.assign(window, {
  useTypewriterLines, Reveal, CustomCursor, ProfilePhoto, ClaudeDemo,
  CommandPalette, MatrixRain, useKonami, Toast, SectionLabel, ContactForm
});
