// ==== Easter eggs: Terminal, AI chat, DeepRacer ======================

// ---------- Terminal ----------
const TERM_BANNER = [
  "agent-os :: terminal v0.26",
  "type 'help' to start. 'whoami', 'cat experience.md', 'ls projects', 'open <id>', 'racer', 'clear', 'exit'",
];

function Terminal({ onClose }) {
  const [lines, setLines] = useState(TERM_BANNER.map(t => ({ kind: "out", t })));
  const [input, setInput] = useState("");
  const [history, setHistory] = useState([]);
  const [hIdx, setHIdx] = useState(-1);
  const inputRef = useRef(null);
  const bodyRef = useRef(null);

  useEffect(() => { inputRef.current && inputRef.current.focus(); }, []);
  useEffect(() => { if (bodyRef.current) bodyRef.current.scrollTop = bodyRef.current.scrollHeight; }, [lines]);

  function out(t, kind = "out") { setLines(prev => [...prev, { kind, t }]); }

  function run(cmd) {
    const c = cmd.trim();
    setLines(prev => [...prev, { kind: "prompt", t: c }]);
    if (!c) return;
    setHistory(prev => [...prev, c]); setHIdx(-1);

    const [head, ...rest] = c.toLowerCase().split(/\s+/);

    if (head === "help") {
      out("commands:");
      out("  whoami              · short intro");
      out("  cat experience.md   · long form experience");
      out("  ls projects | papers | talks | awards | skills");
      out("  open <id>           · open project (mantis, aegis, agentbridge, …)");
      out("  metrics             · live numbers");
      out("  racer               · launch deepracer easter egg");
      out("  chat                · open the AI agent");
      out("  contact             · how to reach me");
      out("  clear · exit");
      return;
    }
    if (head === "clear") { setLines(TERM_BANNER.map(t => ({ kind: "out", t }))); return; }
    if (head === "exit") { onClose && onClose(); return; }
    if (head === "whoami") {
      out("ayush kumar · agentic ai × security engineer");
      out("lead architect — agentic ai & security platforms @ eli lilly");
      out("formerly: founding eng @ superagi (21k★) · samsung research intern");
      out("based: bangalore · open to relocation");
      return;
    }
    if (head === "cat" && rest[0] === "experience.md") {
      out("# experience", "ok");
      out("");
      out("## eli lilly — lead architect, agentic ai & security (jul 2023 – now)");
      out("- four-tier multi-agent platform: claude agent sdk + langgraph + cortex");
      out("- 800+ tickets/mo autonomously resolved · mean resolution 45m → 4m (-88%)");
      out("- enterprise mcp gateway: 8 servers, 130+ tools, 7-layer defense");
      out("- authored: agentic identity (nhi) framework · cyberway connector standard");
      out("- presented to leadership; approved & adopted org-wide");
      out("");
      out("## superagi — founding engineer (apr 2023 – jul 2023)");
      out("- pioneered superagi: 21k+ github stars, 15k+ developers");
      out("- backend agent orchestration · tool integration · memory");
      out("- supercoder (5k+ devs) · contlo.ai chatgpt plugin (20k+ users)");
      out("");
      out("## samsung research — ai r&d intern (may 2021 – nov 2021)");
      out("- ml algorithm for 5g mec server placement & task scheduling");
      out("- 30+ servers → 11 · certificate of excellence (best project)");
      out("- published research paper on the algorithm mechanism");
      return;
    }
    if (head === "ls") {
      const what = rest[0];
      if (what === "projects" || !what) {
        AYUSH.PROJECTS.forEach(p => out(`  ${p.id.padEnd(12)} · ${p.name.padEnd(22)} · ${p.sub}`));
        return;
      }
      if (what === "papers")  { AYUSH.PAPERS.forEach((p,i)=>out(`  [${i}] ${p.yr}  ${p.title} · ${p.venue}`)); return; }
      if (what === "talks")   { AYUSH.TALKS.forEach((t,i)=>out(`  [${i}] ${t.yr}  ${t.title} · ${t.venue}`)); return; }
      if (what === "awards")  { AYUSH.AWARDS.forEach((a,i)=>out(`  [${i}] ${a.yr}  ${a.name} · ${a.org}`)); return; }
      if (what === "skills")  { AYUSH.SKILLS.forEach(g=>{ out(`  [${g.group}]`); out("    " + g.items.join(" · ")); }); return; }
      out("unknown list. try: projects, papers, talks, awards, skills", "err"); return;
    }
    if (head === "open") {
      const id = rest[0];
      const p = AYUSH.PROJECTS.find(x => x.id === id);
      if (p) {
        out(`opening ${p.name}…`, "ok");
        if (p.id === "mantis") { onClose && onClose(); scrollToId("mantis"); }
        else if (p.link) { window.open(p.link, "_blank"); }
        else { onClose && onClose(); scrollToId("projects"); }
        return;
      }
      out("project not found. ls projects", "err"); return;
    }
    if (head === "metrics") {
      AYUSH.NUMBERS.forEach(n => out(`  ${n.v.padEnd(8)}  ${n.l}`));
      return;
    }
    if (head === "racer") {
      out("launching deepracer…", "ok");
      onClose && onClose();
      window.dispatchEvent(new CustomEvent("ayush:open-racer"));
      return;
    }
    if (head === "chat") {
      out("opening agent chat…", "ok");
      onClose && onClose();
      window.dispatchEvent(new CustomEvent("ayush:open-chat"));
      return;
    }
    if (head === "contact") {
      out("email · " + AYUSH.PROFILE.email);
      out("phone · " + AYUSH.PROFILE.phone);
      out("github · " + AYUSH.PROFILE.links.github);
      out("linkedin · " + AYUSH.PROFILE.links.linkedin);
      return;
    }
    if (head === "sudo") { out("nope. lethal-trifecta separation forbids it.", "err"); return; }
    if (head === "rm" && rest.join(" ") === "-rf /") { out("attempt logged. analyst notified.", "err"); return; }
    out(`command not found: ${c}`, "err");
  }

  function onKey(e) {
    if (e.key === "Enter") { run(input); setInput(""); }
    else if (e.key === "ArrowUp") {
      if (!history.length) return;
      const i = hIdx < 0 ? history.length - 1 : Math.max(0, hIdx - 1);
      setHIdx(i); setInput(history[i] || "");
      e.preventDefault();
    } else if (e.key === "ArrowDown") {
      if (!history.length) return;
      const i = hIdx < 0 ? -1 : Math.min(history.length, hIdx + 1);
      setHIdx(i); setInput(i >= history.length ? "" : (history[i] || ""));
      e.preventDefault();
    } else if (e.key === "Escape") { onClose && onClose(); }
  }

  return (
    <div id="terminal" role="dialog" aria-label="agent-os terminal">
      <div className="term-head">
        <div className="dots"><span/><span/><span/></div>
        <span className="title">agent-os :: terminal</span>
        <span className="close" onClick={onClose}>✕</span>
      </div>
      <div className="term-body" ref={bodyRef}>
        {lines.map((l, i) => (
          <div key={i} className={"term-line " + l.kind}>{l.t}</div>
        ))}
      </div>
      <div className="term-input">
        <input ref={inputRef} value={input} onChange={e => setInput(e.target.value)} onKeyDown={onKey} placeholder="type a command…" autoFocus />
      </div>
    </div>
  );
}

// ---------- AI Chat ----------
const SYSTEM_PROMPT = `You are an agent representing AYUSH KUMAR's portfolio. Be concise, technical, confident, and a touch warm — like a senior engineer at the bar after a conference.
Cite project IDs as [P:mantis], [P:aegis], [P:agentbridge], [P:cyberway], [P:superagi], [P:mec] where natural.
Never invent facts. If unsure, say "Ayush hasn't published that publicly — happy to put you in touch."

Profile facts:
- Lead Architect — Agentic AI & Security Platforms @ Eli Lilly (Jul 2023 – present). Bangalore. Open to relocation.
- Built a four-tier multi-agent platform (Claude Agent SDK + LangGraph + Cortex) — 800+ tickets/month autonomously resolved across 25 platform services, MRT cut 88% (45m→4m), $1.30–3.20/ticket, SOX/GxP compliant, Graph RAG cut per-ticket tokens 85%, Temporal saga-pattern rollback.
- Enterprise MCP gateway on AWS ECS Fargate: 8 servers, 130+ AI-callable security tools, 7-layer defense (Cloudflare→WAF→ALB→VPC→OAuth 2.1 Agentic Auth→3-tier RBAC→tool-level safety gates), per-session credential isolation. Org-wide standard.
- Authored the Agentic Identity (NHI) Framework — 3-category actor model (human/workload/agent), per-agent Entra Workload Identities, 15-min JIT credentials, "Lethal Trifecta Separation" (no agent simultaneously holds untrusted input + creds + mutation).
- AI-powered QC analytics for ServiceNow/XSOAR — 10K+ endpoints, NLP autonomous categorization, SLA forecasting, priority-mismatch detection; manual triage effort cut 95%.
- Platform IaC — Terraform for Cloudflare/Zscaler/Splunk with GitHub Actions OIDC. Adopted org-wide.
- Splunk: 31 high-priority data onboardings, AWS security data lake federation.

Agentic security frameworks (designs & audits against — fluent, not name-dropping):
- OWASP Top 10 for Agentic Applications (ASI01–ASI10, Dec 2025) — the autonomy-era successor to the LLM Top 10. Maps each agentic threat (goal hijacking, tool misuse, identity & privilege abuse, memory/context poisoning, privilege escalation, cascading failures, inter-agent/A2A exploitation, human-trust manipulation, rogue/untraceable agents, insecure orchestration & supply chain) to a control running in the enterprise MCP gateway, MANTIS, AEGIS or Agent Forensics.
- CSA MAESTRO — 7-layer threat modeling for multi-agent systems. MITRE ATLAS — adversarial-ML TTP matrix, basis for MANTIS detection-as-code. NIST AI RMF + AI 100-2e2025 adversarial-ML taxonomy — governance & control selection. EU AI Act high-risk audit controls — what the Agent Forensics work maps onto.
- Real threat classes this defends against: indirect prompt-injection exfiltration, A2A "agent session smuggling" (Unit 42, 2025), memory poisoning, agent-driven destructive actions.

Papers:
- "Tamper-Evident ≠ Trustworthy: Forensically Sound Attribution of Autonomous-Agent Actions" (2026) — Halpern–Pearl causality, EU AI Act mapping, released testbed. DOI 10.5281/zenodo.20698154.
- "Autonomous Adversarial Threat Detection Agent" (2025) — BSides Bangalore, NullCon AI Paper of the Year. DOI 10.5281/zenodo.19161532.
- "Optimal 5G Mobile Edge Server Placement & Task Scheduling" (2021) — Samsung Research, Best Project.

Earlier:
- Founding Engineer @ SuperAGI (Apr–Jul 2023). SuperAGI grew to 21K+ GitHub stars & 15K+ developers. Architected Supercoder (5K+ devs). Built ChatGPT-plugin agent layer for Contlo.ai (20K+ users).
- AI R&D Intern @ Samsung Research (May–Nov 2021). 5G MEC server placement & task scheduling. Cut servers 30+→11. Best Project. Published research paper.

Projects:
- MANTIS [P:mantis] — open-source autonomous SOC. 55K LOC, 45 modules, 2.1K tests, 130+ API endpoints. 12-engine parallel detection (ViT, BERT, transformer log anomaly, DGA, UEBA, Sigma) with weighted confidence fusion. ReAct agent with 15 tools and multi-LLM consensus. RL response (PPO/DQN). Detection-as-Code w/ analyst feedback loop. Knowledge graph + PageRank risk propagation. Federated TI (differential privacy + HMAC). AI self-defense (prompt-injection guard, model-integrity verification). Presented BSides Bangalore 2025 — NullCon AI Paper of the Year. DOI 10.5281/zenodo.19161532. GitHub: COLONAYUSH/Project-MANTIS.
- AEGIS [P:aegis] — "Wiz for AI Agents". OSS production runtime agent observability — agent behavioral fingerprinting w/ anomaly detection, NHI credential lifecycle monitoring + privilege drift alerts, multi-agent trust protocol w/ cryptographic attestation + delegation chain verification, agent decision flight recorder for full-trace forensics & replay, OWASP Top 10 for Agentic Applications continuous compliance scoring. Python + Rust + MCP + OTel + gRPC.
- AgentBridge [P:agentbridge] — OpenAI Agents SDK × MCP reference. Eval harness, OTel observability, guardrails, per-session credential isolation. Authoring technical blog series on canonical patterns.
- Agent Forensics [P:agent-forensics] — forensically sound attribution of autonomous-agent actions. Halpern–Pearl actual causality over a structural causal model of the agent; exogenous anchors (provenance, authenticated delegation, internal-state probes) that break the anti-forensics impossibility. Maps onto EU AI Act audit controls. The testbed + mechanism behind the 2026 paper "Tamper-Evident ≠ Trustworthy." GitHub: COLONAYUSH/agent-forensics. DOI 10.5281/zenodo.20698154.
- CyberWay [P:cyberway] — enterprise development standard for security-first AI connectors (MCP, Claude Agent SDK skills, RAG, browser agents). Tool-level RBAC, HITL gateways, prompt-injection defenses, NHI lifecycle, SOX audit logging. Adopted org-wide.
- SuperAGI [P:superagi] — 21K★ open agent framework.
- MEC Server Placement [P:mec] — 5G optimization at Samsung.

Education: BMS College of Engineering, BE Computer Science, CGPA 9.2/10 (2019–2023).

Awards: Rising Star @ Lilly (among 10K+), NullCon AI Paper of the Year 2025, LiFT Scholar (Linux Foundation — 50 globally), AWS DeepRacer 23rd global, HPAIR India rep (Harvard 2020), Open Source Summit Scholar, TechBharat finalist.

Contact: ayushkaps9462@gmail.com · +91-7091956935 · github.com/COLONAYUSH · linkedin.com/in/colonayush.

Keep responses under 6 sentences unless the user asks for deep detail.`;

// Get a reply from the agent. In production (Vercel) this hits the
// /api/chat serverless function backed by your Ollama Cloud key. Inside
// this design preview there's no backend, so it falls back to the built-in
// window.claude.complete. If neither is available, it throws → email msg.
async function getAgentReply(history) {
  // 1) production backend (Ollama Cloud via /api/chat)
  try {
    const r = await fetch("/api/chat", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ system: SYSTEM_PROMPT, messages: history }),
    });
    if (r.ok) {
      const data = await r.json();
      if (data && data.reply && data.reply.trim()) return data.reply.trim();
    }
  } catch (e) { /* no backend here — fall through */ }

  // 2) in-canvas fallback (this design preview)
  if (window.claude && window.claude.complete) {
    return await window.claude.complete({
      messages: [
        { role: "user", content: SYSTEM_PROMPT + "\n\n---\nConversation so far:\n" + history.map(m => (m.role === "user" ? "User: " : "Agent: ") + m.content).join("\n") + "\n\nReply as the Agent:" }
      ]
    });
  }
  throw new Error("no agent backend available");
}

function Chat({ onClose }) {
  const [msgs, setMsgs] = useState([
    { role: "assistant", text: "Hey — I'm the agent representing Ayush's portfolio. Ask me anything about his work, projects, papers, or how to reach him." }
  ]);
  const [input, setInput] = useState("");
  const [typing, setTyping] = useState(false);
  const bodyRef = useRef(null);

  useEffect(() => { if (bodyRef.current) bodyRef.current.scrollTop = bodyRef.current.scrollHeight; }, [msgs, typing]);

  async function send(text) {
    const q = (text ?? input).trim();
    if (!q) return;
    setInput("");
    setMsgs(prev => [...prev, { role: "user", text: q }]);
    setTyping(true);
    try {
      const history = [...msgs, { role: "user", text: q }].map(m => ({ role: m.role, content: m.text }));
      const reply = await getAgentReply(history);
      setMsgs(prev => [...prev, { role: "assistant", text: reply }]);
    } catch (e) {
      setMsgs(prev => [...prev, { role: "assistant", text: "Hmm, that signal got dropped. Email ayushkaps9462@gmail.com — Ayush answers fast." }]);
    } finally { setTyping(false); }
  }

  // Render light markdown: **bold**, *italic*, `code`, [text](url) links,
  // and [P:project] citations. Splits into paragraphs + bullet lines.
  function renderInline(text, kp) {
    const out = [];
    const re = /(\[P:[a-z-]+\])|(\[[^\]]+\]\([^)]+\))|(\*\*[^*]+\*\*)|(\*[^*]+\*)|(`[^`]+`)/g;
    let last = 0, m, i = 0;
    while ((m = re.exec(text)) !== null) {
      if (m.index > last) out.push(<span key={kp + "t" + i++}>{text.slice(last, m.index)}</span>);
      const seg = m[0];
      if (m[1]) {
        const id = seg.slice(3, -1);
        const proj = AYUSH.PROJECTS.find(p => p.id === id);
        out.push(proj
          ? <a key={kp + "c" + i++} className="cite" style={{cursor:"pointer"}} onClick={() => { onClose && onClose(); scrollToId(proj.id === "mantis" ? "mantis" : "projects"); }}>↪ {proj.name}</a>
          : <span key={kp + "c" + i++}>{seg}</span>);
      } else if (m[2]) {
        const mm = seg.match(/^\[([^\]]+)\]\(([^)]+)\)$/);
        out.push(<a key={kp + "l" + i++} href={mm[2]} target="_blank" rel="noreferrer">{mm[1]}</a>);
      } else if (m[3]) {
        out.push(<strong key={kp + "b" + i++}>{seg.slice(2, -2)}</strong>);
      } else if (m[4]) {
        out.push(<em key={kp + "i" + i++}>{seg.slice(1, -1)}</em>);
      } else if (m[5]) {
        out.push(<code key={kp + "k" + i++}>{seg.slice(1, -1)}</code>);
      }
      last = re.lastIndex;
    }
    if (last < text.length) out.push(<span key={kp + "t" + i++}>{text.slice(last)}</span>);
    return out;
  }

  function render(text) {
    const paras = text.trim().split(/\n{2,}/);
    return paras.map((para, pi) => {
      const lines = para.split("\n");
      const isList = lines.every(l => /^\s*[-*•]\s+/.test(l));
      if (isList) {
        return (
          <ul key={"u" + pi} className="chat-ul">
            {lines.map((l, li) => <li key={li}>{renderInline(l.replace(/^\s*[-*•]\s+/, ""), pi + "-" + li + "-")}</li>)}
          </ul>
        );
      }
      return (
        <p key={"p" + pi} className="chat-p">
          {lines.map((l, li) => {
            const clean = l.replace(/^#{1,6}\s+/, "");
            const heading = clean !== l;
            const content = renderInline(clean, pi + "-" + li + "-");
            return (
              <React.Fragment key={li}>
                {li > 0 && <br />}
                {heading ? <strong>{content}</strong> : content}
              </React.Fragment>
            );
          })}
        </p>
      );
    });
  }

  const suggested = [
    "Tell me about MANTIS",
    "What's the Lethal Trifecta?",
    "How does Agent Forensics work?",
    "Which security frameworks do you build against?",
    "How do I hire Ayush?",
  ];

  return (
    <div id="chat-pane" role="dialog" aria-label="ayush.ai">
      <div className="head">
        <div className="av"/>
        <div>
          <div className="name">ayush · agent</div>
          <div className="sub"><span className="dot"/> grounded on Ayush’s work · live</div>
        </div>
        <button className="close" onClick={onClose}>✕</button>
      </div>
      <div className="body" ref={bodyRef}>
        {msgs.map((m, i) => (
          <div key={i} className={"msg " + m.role}>
            <div className="bubble">{render(m.text)}</div>
          </div>
        ))}
        {typing && <div className="msg assistant"><div className="bubble"><span className="dot" style={{marginRight:6}}/> thinking…</div></div>}
      </div>
      <div className="suggest">
        {suggested.map((s, i) => <button key={i} onClick={() => send(s)}>{s}</button>)}
      </div>
      <div className="input">
        <input value={input} onChange={e => setInput(e.target.value)} placeholder="ask anything…" onKeyDown={e => { if (e.key === "Enter") send(); }} />
        <button onClick={() => send()}>send</button>
      </div>
    </div>
  );
}

// ---------- DeepRacer 3D track ----------
function DeepRacer({ onClose }) {
  const wrap = useRef(null);
  useEffect(() => {
    if (!window.THREE) return;
    const el = wrap.current;
    const canvas = document.createElement("canvas");
    canvas.style.cssText = "position:absolute;inset:0;width:100%;height:100%";
    el.appendChild(canvas);

    const renderer = new THREE.WebGLRenderer({ canvas, antialias: true });
    renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
    renderer.setSize(window.innerWidth, window.innerHeight, false);
    renderer.setClearColor(0x06080d, 1);

    const scene = new THREE.Scene();
    scene.fog = new THREE.Fog(0x06080d, 30, 220);
    const camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 800);

    // Track: a CatmullRom curve, drawn as a flat ribbon
    const pts = [];
    for (let i = 0; i < 20; i++) {
      const a = (i / 20) * Math.PI * 2;
      const r = 60 + Math.sin(a * 3) * 14;
      pts.push(new THREE.Vector3(Math.cos(a) * r, 0, Math.sin(a) * r));
    }
    const curve = new THREE.CatmullRomCurve3(pts, true);

    // Ribbon geometry
    const segs = 600;
    const ribbonW = 10;
    const positions = [];
    const indices = [];
    for (let i = 0; i <= segs; i++) {
      const t = i / segs;
      const p = curve.getPointAt(t);
      const tan = curve.getTangentAt(t);
      const n = new THREE.Vector3(-tan.z, 0, tan.x).normalize();
      const a = p.clone().add(n.clone().multiplyScalar(ribbonW));
      const b = p.clone().add(n.clone().multiplyScalar(-ribbonW));
      positions.push(a.x, 0.0, a.z, b.x, 0.0, b.z);
      if (i < segs) {
        const k = i * 2;
        indices.push(k, k + 1, k + 2, k + 2, k + 1, k + 3);
      }
    }
    const trackGeom = new THREE.BufferGeometry();
    trackGeom.setAttribute("position", new THREE.BufferAttribute(new Float32Array(positions), 3));
    trackGeom.setIndex(indices);
    trackGeom.computeVertexNormals();
    const track = new THREE.Mesh(trackGeom, new THREE.MeshBasicMaterial({ color: 0x161b27 }));
    scene.add(track);

    // Centerline mint
    const linePts = [];
    for (let i = 0; i <= 200; i++) linePts.push(curve.getPointAt(i / 200));
    const lineGeo = new THREE.BufferGeometry().setFromPoints(linePts);
    const line = new THREE.LineLoop(lineGeo, new THREE.LineBasicMaterial({ color: 0x5cf0d8 }));
    line.position.y = 0.01;
    scene.add(line);

    // Edges (white-ish)
    const edgeGeoL = new THREE.BufferGeometry().setFromPoints(linePts.map((p, i) => {
      const t = i / 200;
      const tan = curve.getTangentAt(t);
      const n = new THREE.Vector3(-tan.z, 0, tan.x).normalize();
      return p.clone().add(n.multiplyScalar(ribbonW));
    }));
    const edgeGeoR = new THREE.BufferGeometry().setFromPoints(linePts.map((p, i) => {
      const t = i / 200;
      const tan = curve.getTangentAt(t);
      const n = new THREE.Vector3(-tan.z, 0, tan.x).normalize();
      return p.clone().add(n.multiplyScalar(-ribbonW));
    }));
    scene.add(new THREE.LineLoop(edgeGeoL, new THREE.LineBasicMaterial({ color: 0xECEAE2, transparent: true, opacity: 0.4 })));
    scene.add(new THREE.LineLoop(edgeGeoR, new THREE.LineBasicMaterial({ color: 0xECEAE2, transparent: true, opacity: 0.4 })));

    // Car: a glowing chip
    const car = new THREE.Mesh(new THREE.BoxGeometry(2.6, 0.9, 1.4), new THREE.MeshBasicMaterial({ color: 0xf0b85c }));
    scene.add(car);
    const carHalo = new THREE.PointLight(0xf0b85c, 1.0, 12);
    scene.add(carHalo);

    // Ground starfield
    const N = 1200;
    const sp = new Float32Array(N * 3);
    for (let i = 0; i < N; i++) {
      sp[i*3]   = (Math.random() - 0.5) * 600;
      sp[i*3+1] = -5 + Math.random() * 0.5;
      sp[i*3+2] = (Math.random() - 0.5) * 600;
    }
    const stars = new THREE.Points(
      new THREE.BufferGeometry().setAttribute("position", new THREE.BufferAttribute(sp, 3)),
      new THREE.PointsMaterial({ color: 0x5cf0d8, size: 0.3, transparent: true, opacity: 0.4 })
    );
    scene.add(stars);

    let t0 = performance.now();
    let raf;
    function onResize() {
      renderer.setSize(window.innerWidth, window.innerHeight, false);
      camera.aspect = window.innerWidth / window.innerHeight;
      camera.updateProjectionMatrix();
    }
    window.addEventListener("resize", onResize);

    function loop() {
      const elapsed = (performance.now() - t0) / 1000;
      const u = (elapsed * 0.07) % 1;
      const p = curve.getPointAt(u);
      const tan = curve.getTangentAt(u);
      car.position.copy(p).add(new THREE.Vector3(0, 0.5, 0));
      car.lookAt(p.clone().add(tan));
      carHalo.position.copy(car.position);

      // chase cam
      const camTarget = p.clone().sub(tan.clone().multiplyScalar(10)).add(new THREE.Vector3(0, 4.5, 0));
      camera.position.lerp(camTarget, 0.08);
      camera.lookAt(p);

      renderer.render(scene, camera);
      raf = requestAnimationFrame(loop);
    }
    loop();

    function onKey(e) { if (e.key === "Escape") onClose && onClose(); }
    window.addEventListener("keydown", onKey);

    return () => {
      cancelAnimationFrame(raf);
      window.removeEventListener("resize", onResize);
      window.removeEventListener("keydown", onKey);
      renderer.dispose();
      el.removeChild(canvas);
    };
  }, []);

  return (
    <div id="racer" role="dialog" aria-label="deepracer">
      <div ref={wrap} style={{position:"absolute", inset: 0}}/>
      <div className="racer-hud">
        <div>
          <h3>deepracer · #23 globally</h3>
          <div className="mono" style={{marginTop: 4}}>aws deepracer · 2022 · reinforcement learning</div>
          <div className="mono" style={{marginTop: 2, color:"var(--ink-dim)"}}>esc · exit</div>
        </div>
        <span className="exit" onClick={onClose}>exit ✕</span>
      </div>
    </div>
  );
}

// ---------- Easter Egg coordinator (wraps everything) ----------
function EasterEggs() {
  const [termOpen, setTermOpen] = useState(false);
  const [chatOpen, setChatOpen] = useState(false);
  const [racerOpen, setRacerOpen] = useState(false);
  const [cmdkOpen, setCmdkOpen] = useState(false);
  const konamiRef = useRef([]);
  const KONAMI = ["arrowup","arrowup","arrowdown","arrowdown","arrowleft","arrowright","arrowleft","arrowright","b","a"];

  useEffect(() => {
    function onKey(e) {
      // Cmd+K / Ctrl+K → command palette
      if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "k") {
        e.preventDefault();
        setCmdkOpen(o => !o);
        return;
      }
      // tilde / backtick toggles terminal
      if ((e.key === "`" || e.key === "~") && !chatOpen && !racerOpen) {
        e.preventDefault();
        setTermOpen(t => !t);
        return;
      }
      // Konami sequence
      const k = e.key.toLowerCase();
      konamiRef.current = [...konamiRef.current, k].slice(-KONAMI.length);
      if (konamiRef.current.join(",") === KONAMI.join(",")) {
        setRacerOpen(true);
        konamiRef.current = [];
      }
    }
    function openTerm() { setTermOpen(true); }
    function openChat() { setChatOpen(true); }
    function openRacer() { setRacerOpen(true); }
    function openCmdk() { setCmdkOpen(true); }
    window.addEventListener("keydown", onKey);
    window.addEventListener("ayush:open-terminal", openTerm);
    window.addEventListener("ayush:open-chat", openChat);
    window.addEventListener("ayush:open-racer", openRacer);
    window.addEventListener("ayush:open-cmdk", openCmdk);
    return () => {
      window.removeEventListener("keydown", onKey);
      window.removeEventListener("ayush:open-terminal", openTerm);
      window.removeEventListener("ayush:open-chat", openChat);
      window.removeEventListener("ayush:open-racer", openRacer);
      window.removeEventListener("ayush:open-cmdk", openCmdk);
    };
  }, [chatOpen, racerOpen]);

  return (
    <>
      {!chatOpen && !termOpen && !cmdkOpen && (
        <button className="chat-fab" onClick={() => setChatOpen(true)} aria-label="open agent chat">
          <span className="glyph"/> ask the agent
        </button>
      )}
      {termOpen && <Terminal onClose={() => setTermOpen(false)} />}
      {chatOpen && <Chat onClose={() => setChatOpen(false)} />}
      {racerOpen && <DeepRacer onClose={() => setRacerOpen(false)} />}
      {cmdkOpen && <CommandPalette onClose={() => setCmdkOpen(false)} />}
    </>
  );
}

Object.assign(window, { Terminal, Chat, DeepRacer, EasterEggs });
