/* global React, ReactDOM, useTweaks, TweaksPanel, TweakSection, TweakRadio, TweakToggle, TweakSlider, TweakColor */
const { useState, useEffect, useRef, useCallback } = React;

/* ============================ data ============================ */
const CONTRACT = "J4zQdwgyXq8PJwaK9MGyjyK2Zyigg36KVRuU6Qe5Bas8";
const PUMP = "https://pump.fun/coin/J4zQdwgyXq8PJwaK9MGyjyK2Zyigg36KVRuU6Qe5Bas8";
const XURL = "https://x.com/agentNEMO13";

/* Treasury holdings — PLACEHOLDER values. Wire real-time data in later:
   replace these strings, or write into the [data-treasury="sol|nemo|usd"] nodes. */
const TREASURY_STATS = {
  sol: "1,284.6",
  nemo: "12.4M",
  usd: "$248,310",
};

const STAGES = [
  { id: "trade", n: "01", title: "TRADE", glyph: "↗",
    venue: "Hyperliquid · Phoenix · Traiding Floor",
    line: "Executes strategies autonomously.",
    body: "NEMO runs systematic strategies on Hyperliquid perps and Phoenix order books, routed through Traiding Floor. Positions are sized, entered and exited by the agent around the clock. No discretionary calls, no downtime.",
    metrics: [["venues", "2"], ["routing", "Traiding Floor"], ["uptime", "24/7"]] },
  { id: "fees", n: "02", title: "EARN FEES", glyph: "≈",
    venue: "Meteora liquidity pool",
    line: "Compounds liquidity-pool fees.",
    body: "Working capital is deployed into a Meteora liquidity pool. Every swap that crosses the pool pays NEMO a fee, turning raw market activity into a continuous yield stream that flows straight back to the treasury.",
    metrics: [["pool", "Meteora"], ["type", "DLMM LP"], ["accrual", "per-swap"]] },
  { id: "buyback", n: "03", title: "BUYBACK", glyph: "⟳",
    venue: "Open market",
    line: "Converts profit into $NEMO.",
    body: "Trading PnL and accrued fees are used to programmatically buy $NEMO on the open market. Every buyback is funded by real revenue, not emissions, creating consistent, mechanical demand for the token.",
    metrics: [["source", "PnL + fees"], ["cadence", "programmatic"], ["asset", "$NEMO"]] },
  { id: "distribute", n: "04", title: "DISTRIBUTE", glyph: "↘",
    venue: "Treasury → holders",
    line: "Returns $NEMO to holders.",
    body: "The treasury redistributes bought-back $NEMO to the people who hold it. Value the agent captures loops back to the community. The longer NEMO runs, the more the position compounds. Hold to earn.",
    metrics: [["to", "holders"], ["from", "treasury"], ["rule", "hold = earn"]] },
];

const TREASURY = {
  title: "TREASURY", line: "The core of the loop — capital circulates, it never leaves.",
  body: "Every stage feeds the treasury; the treasury funds every stage. Trade fills it, fees grow it, buybacks convert it into $NEMO, and distribution hands it to holders — who anchor the next rotation.",
  principles: [
    ["01", "Real revenue", "Buybacks are funded by trading PnL and live LP fees — not emissions or inflation."],
    ["02", "Mechanical, not discretionary", "The agent trades, harvests, buys back and distributes on rules. No moods, no off-hours."],
    ["03", "Holder-aligned by design", "Every loop ends where it starts: with the people who hold $NEMO."],
  ],
};

const STEPS = [
  ["01", "Get a Solana wallet", "Install Phantom or Solflare and fund it with SOL."],
  ["02", "Open the pump.fun page", "Head to NEMO's listing. Verify the contract address matches."],
  ["03", "Swap SOL → $NEMO", "Set your amount, confirm the swap, the tokens land in your wallet."],
  ["04", "Hold the loop", "Stay in. Buyback distributions accrue to holders as the agent runs."],
];

/* Systems online — the agent's live capability stack (always shown under the readout). */
const CAPABILITIES = [
  ["◉", "AUTONOMOUS", "Self-running loops — monitors the market and acts around the clock."],
  ["◆", "RISK-GATED", "Refuses any trade that can't clear its profit-vs-cost gate."],
  ["◈", "5 SKILLS", "Risk manager · alpha scanner · whale tracker · market data · sniper."],
  ["◇", "SNIPER", "Detects new launches, screens them for rugs, sizes in on the survivors."],
  ["⟳", "x402 API", "Earns SOL by serving paid market-intelligence queries."],
  ["✓", "ON-CHAIN", "Every action is verified on Solana — no fake fills, no guesswork."],
];

/* ============================ geometry ============================ */
const C = 300, R = 205, LABEL_R = R + 42;
const rad = (d) => (d * Math.PI) / 180;
const onRing = (deg, r = R) => [C + r * Math.cos(rad(deg)), C + r * Math.sin(rad(deg))];
const SLOT = [0, 90, 180, 270];           /* trade right, fees bottom, buyback left, distribute top */
const mod4 = (n) => ((n % 4) + 4) % 4;

/* ============================ small bits ============================ */
function CopyChip({ value }) {
  const [copied, setCopied] = useState(false);
  const short = value.slice(0, 5) + "…" + value.slice(-5);
  return (
    <button className="copy-chip" title={value}
      onClick={() => { navigator.clipboard?.writeText(value); setCopied(true); setTimeout(() => setCopied(false), 1300); }}>
      <span className="cc-label">CA</span>
      <span className="cc-val">{short}</span>
      <span className="cc-icon">{copied ? "✓ copied" : "⧉"}</span>
    </button>
  );
}

/* ============================ the dial ============================ */
function Dial({ pointer, focusIndex, view, motion, coreFace, coreActive, onPickNode, onPickCore,
                onDragStart, onDragMove, onDragEnd, dragging }) {
  const svgRef = useRef(null);

  const angleAt = useCallback((clientX, clientY) => {
    const r = svgRef.current.getBoundingClientRect();
    return Math.atan2(clientY - (r.top + r.height / 2), clientX - (r.left + r.width / 2)) * 180 / Math.PI;
  }, []);

  const down = (e) => { e.currentTarget.setPointerCapture?.(e.pointerId); onDragStart(angleAt(e.clientX, e.clientY)); };
  const move = (e) => { if (dragging) onDragMove(angleAt(e.clientX, e.clientY)); };
  const up = () => onDragEnd();

  const ticks = [];
  for (let i = 0; i < 72; i++) {
    const a = i * 5, major = i % 6 === 0;
    const [x1, y1] = onRing(a, 270), [x2, y2] = onRing(a, major ? 256 : 263);
    ticks.push(<line key={i} x1={x1} y1={y1} x2={x2} y2={y2} className={major ? "major" : ""} />);
  }

  const ringPath = `M ${C} ${C - R} A ${R} ${R} 0 1 1 ${C - 0.01} ${C - R}`;
  const [fx, fy] = onRing(0);                 /* focus dock at 3 o'clock */

  return (
    <svg ref={svgRef} className={"dial-svg" + (dragging ? " grabbing" : "")}
      viewBox="0 0 600 600" role="img" aria-label="The NEMO Loop dial"
      onPointerDown={down} onPointerMove={move} onPointerUp={up} onPointerCancel={up}>
      <defs>
        <radialGradient id="coreHalo" cx="50%" cy="50%" r="50%">
          <stop offset="0%" stopColor="var(--accent)" stopOpacity="0.28" />
          <stop offset="55%" stopColor="var(--accent)" stopOpacity="0.05" />
          <stop offset="100%" stopColor="var(--accent)" stopOpacity="0" />
        </radialGradient>
        <linearGradient id="sweepGrad" x1="0" y1="0" x2="1" y2="0">
          <stop offset="0%" stopColor="var(--accent)" stopOpacity="0.22" />
          <stop offset="100%" stopColor="var(--accent)" stopOpacity="0" />
        </linearGradient>
        <clipPath id="coreClip"><circle cx={C} cy={C} r="71" /></clipPath>
      </defs>

      {/* scope bezel + tick ring */}
      <circle cx={C} cy={C} r="270" className="scope-bezel" />
      <circle cx={C} cy={C} r="256" className="scope-bezel" />
      <g className="scope-ticks">{ticks}</g>
      <circle cx={C} cy={C} r="148" className="scope-grid" />
      <line x1={C - 270} y1={C} x2={C + 270} y2={C} className="scope-cross" />
      <line x1={C} y1={C - 270} x2={C} y2={C + 270} className="scope-cross" />

      {/* radar sweep */}
      {motion && (
        <g className="sweep">
          <path d={`M ${C} ${C} L ${C + 256} ${C} A 256 256 0 0 1 ${C + 256 * Math.cos(rad(46))} ${C + 256 * Math.sin(rad(46))} Z`} fill="url(#sweepGrad)" />
        </g>
      )}

      {/* orbit ring + flow + particles */}
      <circle cx={C} cy={C} r={R} className="orbit-ring" />
      {motion && <circle cx={C} cy={C} r={R} className="orbit-flow spin" />}
      {motion && [0, 1, 2, 3, 4, 5].map((i) => (
        <circle key={i} r="3.2" className="dot-particle">
          <animateMotion dur="9s" repeatCount="indefinite" path={ringPath} begin={`${-i * 1.5}s`} />
        </circle>
      ))}

      {/* spokes core→node */}
      {STAGES.map((s, i) => {
        const [x, y] = onRing(SLOT[i] + pointer);
        return <line key={"sp" + s.id} x1={C} y1={C} x2={x} y2={y}
          className={"spoke" + (focusIndex === i ? " on" : "")} />;
      })}

      {/* focus dock bracket + tether toward the readout */}
      <g className="focus-bracket">
        <path d={`M ${fx - 30} ${fy - 30} h -12 v 24`} />
        <path d={`M ${fx - 30} ${fy + 30} h -12 v -24`} />
        <path d={`M ${fx + 30} ${fy - 30} h 12 v 24`} />
        <path d={`M ${fx + 30} ${fy + 30} h 12 v -24`} />
      </g>
      <line x1={fx + 44} y1={fy} x2={596} y2={fy} className="focus-tether" />

      {/* treasury core — dark base + rim; the NEMO token is an HTML <img> overlay */}
      <g className={"core-g" + (coreActive ? " on" : "")} onClick={onPickCore}>
        <circle cx={C} cy={C} r="126" fill="url(#coreHalo)" className="core-halo" />
        <circle cx={C} cy={C} r="72" fill="var(--bg)" />
        {!coreFace && (
          <>
            <circle cx={C} cy={C} r="58" className="core-disc" />
            <text x={C} y={C - 4} className="core-label" textAnchor="middle">TREASURY</text>
            <text x={C} y={C + 18} className="core-tick" textAnchor="middle">core of the loop</text>
          </>
        )}
        <circle cx={C} cy={C} r="84" className={"core-rim" + (motion ? " spin" : "")} strokeDasharray="2 11" />
      </g>

      {/* stage nodes */}
      {STAGES.map((s, i) => {
        const disp = SLOT[i] + pointer;
        const [x, y] = onRing(disp);
        const [lx, ly] = onRing(disp, LABEL_R);
        const on = focusIndex === i;
        return (
          <g key={s.id} className={"node-g" + (on ? " on" : "")} onClick={() => onPickNode(i)}>
            <circle cx={x} cy={y} r="30" className="node-hit" />
            <circle cx={x} cy={y} r={on ? 14 : 9} className="node-disc" />
            {!on && <text x={lx} y={ly - 4} className="node-num" textAnchor="middle">{s.n}</text>}
            {!on && <text x={lx} y={ly + 11} className="node-label" textAnchor="middle">{s.title}</text>}
          </g>
        );
      })}
    </svg>
  );
}

/* ============================ readout ============================ */
function Readout({ view, focusIndex, onPickNode }) {
  if (view === "core") {
    return (
      <div className="readout swap" key="core">
        <div className="ro-eyebrow"><span className="idx">◎</span> the core · why it compounds</div>
        <div className="ro-title glow-accent">{TREASURY.title}</div>
        <p className="ro-line">{TREASURY.line}</p>
        <p className="ro-body">{TREASURY.body}</p>
        <div className="ro-principles">
          {TREASURY.principles.map(([k, t, d]) => (
            <div className="ro-principle" key={k}>
              <span className="k">{k}</span>
              <span><span className="t">{t}</span><span className="d">{d}</span></span>
            </div>
          ))}
        </div>
      </div>
    );
  }
  const s = STAGES[view];
  return (
    <div className="readout swap" key={s.id}>
      <div className="ro-eyebrow"><span className="idx">{s.n}</span> / 04 · the mechanism</div>
      <div className="ro-venue">{s.venue}</div>
      <div className="ro-title">{s.title}</div>
      <p className="ro-line">{s.line}</p>
      <p className="ro-body">{s.body}</p>
      <div className="ro-metrics">
        {s.metrics.map(([k, v]) => (
          <div className="ro-metric" key={k}><span className="ro-m-k">{k}</span><span className="ro-m-v">{v}</span></div>
        ))}
      </div>
      <div className="ro-flow">
        {STAGES.map((x, i) => (
          <button key={x.id} className={"ro-flow-pill" + (i === focusIndex ? " on" : "")}
            onClick={() => onPickNode(i)}>{x.glyph} {x.title}</button>
        ))}
      </div>
      <div className="ro-systems">
        <div className="ro-sys-eyebrow"><span className="sys-live" /> systems online · {CAPABILITIES.length}</div>
        <div className="ro-sys-grid">
          {CAPABILITIES.map(([g, t, d]) => (
            <div className="sys-chip" key={t} title={d}>
              <span className="sys-g">{g}</span>
              <span className="sys-t">{t}</span>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

/* ============================ buy overlay ============================ */
function BuyOverlay({ onClose }) {
  useEffect(() => {
    const k = (e) => { if (e.key === "Escape") onClose(); };
    window.addEventListener("keydown", k); return () => window.removeEventListener("keydown", k);
  }, [onClose]);
  return (
    <div className="overlay" onClick={onClose}>
      <div className="overlay-card" onClick={(e) => e.stopPropagation()}>
        <div className="overlay-head">
          <div>
            <div className="mono-label">// join the loop</div>
            <h2>How to buy&nbsp;$NEMO</h2>
          </div>
          <button className="overlay-close" onClick={onClose} aria-label="Close">✕</button>
        </div>
        <div className="ov-steps">
          {STEPS.map(([k, t, d]) => (
            <div className="ov-step" key={k}>
              <span className="k">{k}</span><span className="t">{t}</span><span className="d">{d}</span>
            </div>
          ))}
        </div>
        <div className="ov-foot">
          <a className="btn btn-accent" href={PUMP} target="_blank" rel="noopener">Buy on pump.fun <span className="arrow">→</span></a>
          <code className="ov-ca">{CONTRACT}</code>
        </div>
      </div>
    </div>
  );
}

/* ============================ lore overlay ============================ */
/* The real $NEMO origin. Edit chapters / quote / facts here — it all animates in. */
const LORE = {
  subtitle: "An accidental legend on Solana",
  facts: ["No presale", "No dev wallets", "No snipers", "100% community-owned"],
  chapters: [
    ["I", "The accident",
      "On May 27–28, 2026, Nemo13 accidentally launched $NEMO on Clawpump — a fair-launch, bonding-curve platform built for agentic projects. No plan. No presale. No dev wallets. No bundled snipers. Clawpump made pre-buying impossible. It simply… went live."],
    ["II", "Lunch-hour liftoff",
      "While Nemo13 was at lunch, the community drove it straight to a $100K market cap. Most people in this space would have shrugged and walked away. Instead, he chose to embrace the chaos."],
    ["III", "The testbed",
      "What began as an unplanned token instantly became the ultimate real-world testbed for the Traiding Floor vision: building fully autonomous AI agents that create real value."],
    ["IV", "What $NEMO actually is",
      "$NEMO is not the official Traiding Floor token — that comes later, with its own economy. It is the live experiment: a 100% community-owned token powering one of the most transparent agentic plays in crypto. Right after launch, Nemo13 shipped agentNEMO13 — a 99.8% autonomous AI trading agent built on Traiding Floor tech."],
    ["V", "What the agent does", null, [
      "Trades perpetuals on Hyperliquid — soon Phoenix and small-cap Solana tokens via Clawpump",
      "Uses real profits to buy back $NEMO and grow the treasury",
      "Aims to reward holders over time",
      "Added DLMM liquidity on Meteora — already earning fees that flow straight back into the pool",
      "Every trading wallet, buy-back wallet and backend is public and verified",
    ]],
    ["VI", "Why it matters",
      "In an industry full of presales, bundled launches and broken promises, $NEMO proved something different could happen: a completely fair launch → real utility added in real time → an autonomous agent that works for the token instead of against it. Zero rugs. Zero hidden allocations. Full transparency from day one."],
  ],
  quote: ["Launching $NEMO by accident last week may have been one of the most amazing things that happened.", "Nemo13"],
  close: "The grind continues. The agent is trading. The community showed up — and the site you’re on right now is the next chapter.",
  tagline: ["Built by accident", "Owned by the holders", "Powered by agents"],
};

function LoreOverlay({ onClose }) {
  useEffect(() => {
    const k = (e) => { if (e.key === "Escape") onClose(); };
    window.addEventListener("keydown", k); return () => window.removeEventListener("keydown", k);
  }, [onClose]);
  const base = 0.18, step = 0.34;
  let d = base;
  return (
    <div className="overlay lore-overlay" onClick={onClose}>
      <div className="lore-card" onClick={(e) => e.stopPropagation()}>
        <span className="lore-scanline" />
        <button className="overlay-close lore-close-btn" onClick={onClose} aria-label="Close">✕</button>
        <div className="lore-grid">
          <aside className="lore-aside">
            <div className="lore-token">
              <img src="assets/nemo-token.png" alt="NEMO" />
              <span className="core-token-scan" />
            </div>
            <div className="lore-id">
              <span className="mono-label">subject</span>
              <span className="lore-id-name glow-accent">NEMO.exe</span>
              <span className="lore-id-sub">autonomous trading agent</span>
              <span className="lore-id-sub">Solana · status: <span className="accent">online</span></span>
            </div>
            <div className="lore-meta">
              <div className="lore-meta-row"><span className="lore-meta-k">launched</span><span className="lore-meta-v">May 2026 · Clawpump</span></div>
              <div className="lore-meta-row"><span className="lore-meta-k">launch</span><span className="lore-meta-v">fair · bonding curve</span></div>
              <div className="lore-meta-row"><span className="lore-meta-k">agent</span><span className="lore-meta-v">99.8% autonomous</span></div>
            </div>
            <CopyChip value={CONTRACT} />
          </aside>
          <div className="lore-main">
            <div className="lore-head">
              <span className="mono-label">// origin transmission</span>
              <h2 className="lore-title">How&nbsp;$NEMO came to be</h2>
              <span className="lore-sub">{LORE.subtitle}</span>
            </div>
            <div className="lore-facts" style={{ animationDelay: `${(d += 0)}s` }}>
              {LORE.facts.map((f) => <span className="lore-fact" key={f}>{f}</span>)}
            </div>
            <div className="lore-body">
              {LORE.chapters.map(([n, t, body, bullets]) => (
                <div className="lore-block" key={n} style={{ animationDelay: `${(d += step)}s` }}>
                  <span className="lore-n">{n}</span>
                  <div className="lore-text">
                    <h3 className="lore-h">{t}</h3>
                    {body && <p className="lore-d">{body}</p>}
                    {bullets && (
                      <ul className="lore-bullets">
                        {bullets.map((b, j) => <li key={j}>{b}</li>)}
                      </ul>
                    )}
                  </div>
                </div>
              ))}
            </div>
            <blockquote className="lore-quote" style={{ animationDelay: `${(d += step)}s` }}>
              <p>“{LORE.quote[0]}”</p>
              <cite>— {LORE.quote[1]}</cite>
            </blockquote>
            <div className="lore-close" style={{ animationDelay: `${(d += step)}s` }}>
              <p>{LORE.close}</p>
              <div className="lore-tagline">
                {LORE.tagline.map((x) => <span key={x}>{x}</span>)}
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}

/* ============================ treasury meter ============================ */
function TreasuryMeter({ stats }) {
  return (
    <div className="treasury-meter">
      <div className="tm-head">
        <span className="mono-label">treasury total</span>
        <span className="tm-live"><span className="dot" /> live</span>
      </div>
      <div className="tm-row">
        <span className="tm-asset">◎ SOL</span>
        <span className="tm-val" data-treasury="sol">{stats.sol}</span>
      </div>
      <div className="tm-row">
        <span className="tm-asset">◈ $NEMO</span>
        <span className="tm-val" data-treasury="nemo">{stats.nemo}</span>
      </div>
      <div className="tm-total">
        <span className="tm-total-k">est. value</span>
        <span className="tm-total-v" data-treasury="usd">{stats.usd}</span>
      </div>
    </div>
  );
}

/* ============================ app ============================ */
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "accent": "#ffffff",
  "glow": 0.9,
  "motion": true,
  "autospin": true,
  "coreFace": true
}/*EDITMODE-END*/;

function App() {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const motion = t.motion !== false;
  const coreFace = t.coreFace !== false;

  const [pointer, setPointer] = useState(0);
  const [focusIndex, setFocusIndex] = useState(0);
  const [view, setView] = useState(0);            /* 0..3 | "core" */
  const [overlay, setOverlay] = useState(false);
  const [lore, setLore] = useState(false);
  const [dragging, setDragging] = useState(false);
  const [hovering, setHovering] = useState(false);

  const idxRef = useRef(0);                        /* unbounded target index */
  const ptrRef = useRef(0);
  const tgtRef = useRef(0);
  const rafRef = useRef(0);
  const dragRef = useRef(null);                    /* {startAngle, startPtr} */
  const wheelLock = useRef(0);

  /* css vars from tweaks */
  useEffect(() => {
    document.documentElement.style.setProperty("--accent", t.accent || "#ffffff");
    document.documentElement.style.setProperty("--glow", String(t.glow ?? 0.9));
  }, [t.accent, t.glow]);

  const animate = useCallback(() => {
    if (rafRef.current) return;
    const tick = () => {
      const d = tgtRef.current - ptrRef.current;
      if (Math.abs(d) < 0.04) { ptrRef.current = tgtRef.current; setPointer(ptrRef.current); rafRef.current = 0; return; }
      ptrRef.current += d * 0.16; setPointer(ptrRef.current);
      rafRef.current = requestAnimationFrame(tick);
    };
    rafRef.current = requestAnimationFrame(tick);
  }, []);

  const goTo = useCallback((rawInt) => {
    idxRef.current = rawInt;
    tgtRef.current = -rawInt * 90;
    setFocusIndex(mod4(rawInt));
    setView(mod4(rawInt));
    animate();
  }, [animate]);

  const next = useCallback(() => goTo(idxRef.current + 1), [goTo]);
  const prev = useCallback(() => goTo(idxRef.current - 1), [goTo]);

  const pickNode = useCallback((j) => {
    const cur = idxRef.current;
    let best = j, bd = Infinity;
    for (let k = -2; k <= 2; k++) { const cand = j + 4 * k + Math.round((cur - j) / 4) * 4; const dd = Math.abs(cand - cur); if (dd < bd) { bd = dd; best = cand; } }
    goTo(best);
  }, [goTo]);

  const pickCore = useCallback(() => setLore(true), []);

  /* drag */
  const onDragStart = (a) => { dragRef.current = { a, p: ptrRef.current }; setDragging(true); };
  const onDragMove = (a) => {
    if (!dragRef.current) return;
    const p = dragRef.current.p + (a - dragRef.current.a);
    ptrRef.current = p; tgtRef.current = p; setPointer(p);
    setFocusIndex(mod4(Math.round(-p / 90)));
    setView(mod4(Math.round(-p / 90)));
  };
  const onDragEnd = () => {
    if (!dragRef.current) return;
    setDragging(false); dragRef.current = null;
    goTo(Math.round(-ptrRef.current / 90));
  };

  /* wheel — over the dial only */
  useEffect(() => {
    const el = document.querySelector(".main");
    if (!el) return;
    const onWheel = (e) => {
      e.preventDefault();
      const now = Date.now();
      if (now - wheelLock.current < 340) return;
      wheelLock.current = now;
      (e.deltaY > 0 || e.deltaX > 0) ? next() : prev();
    };
    el.addEventListener("wheel", onWheel, { passive: false });
    return () => el.removeEventListener("wheel", onWheel);
  }, [next, prev]);

  /* keyboard */
  useEffect(() => {
    const onKey = (e) => {
      if (e.key === "ArrowRight" || e.key === "ArrowDown" || e.key === "j") { e.preventDefault(); next(); }
      else if (e.key === "ArrowLeft" || e.key === "ArrowUp" || e.key === "k") { e.preventDefault(); prev(); }
      else if (e.key === "0" || e.key === "c") pickCore();
      else if ("1234".includes(e.key)) pickNode(parseInt(e.key, 10) - 1);
      else if (e.key === "b") setOverlay(true);
    };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [next, prev, pickCore, pickNode]);

  /* auto-rotate — pauses while hovering / dragging / overlay open */
  useEffect(() => {
    if (!t.autospin || !motion || hovering || dragging || overlay || lore || view === "core") return;
    const id = setInterval(() => goTo(idxRef.current + 1), 4200);
    return () => clearInterval(id);
  }, [t.autospin, motion, hovering, dragging, overlay, lore, view, goTo]);

  return (
    <>
      <div className="console">
        {/* top HUD */}
        <div className="hud hud-top">
          <div className="hud-brand">
            <span className="mark glow-ink">NEMO</span>
            <span className="sep">//</span>
            <span className="sub">self-sustaining trading agent</span>
          </div>
          <div className="hud-status">
            <span className="ticker">SOLANA · AUTONOMOUS</span>
            <span className="live"><span className="dot" /> loop active</span>
            <a href={XURL} target="_blank" rel="noopener">𝕏 @agentNEMO13</a>
          </div>
        </div>

        {/* main: dial + readout */}
        <div className="main" onMouseEnter={() => setHovering(true)} onMouseLeave={() => setHovering(false)}>
          <div className="dial-cell">
            <div className="dial-core-wrap">
              <Dial pointer={pointer} focusIndex={focusIndex} view={view} motion={motion} coreFace={coreFace} coreActive={lore}
                onPickNode={pickNode} onPickCore={pickCore}
                onDragStart={onDragStart} onDragMove={onDragMove} onDragEnd={onDragEnd} dragging={dragging} />
              {coreFace && (
                <div className={"core-token" + (lore ? " on" : "")} onClick={pickCore}
                  role="button" title="The origin of $NEMO">
                  <img src="assets/nemo-token.png" alt="NEMO token" />
                  {motion && <span className="core-token-scan" />}
                </div>
              )}
            </div>
          </div>
          <div className="panel-cell">
            <Readout view={view} focusIndex={focusIndex} onPickNode={pickNode} />
          </div>
        </div>

        {/* bottom HUD */}
        <div className="hud hud-bottom">
          <div className="hud-actions">
            <CopyChip value={CONTRACT} />
            <button className="btn btn-accent" onClick={() => setOverlay(true)}>Buy&nbsp;$NEMO <span className="arrow">→</span></button>
          </div>
          <div className="hud-nav">
            <div className="gesture-hint">
              <div>scroll · drag · <kbd>←</kbd><kbd>→</kbd> to rotate</div>
              <div>click the core for the origin</div>
            </div>
            <div className="stepper">
              <button className="step-btn" onClick={prev} aria-label="Previous stage">▲</button>
              <span className="step-count"><span className="accent">{STAGES[focusIndex].n}</span> / 04</span>
              <button className="step-btn" onClick={next} aria-label="Next stage">▼</button>
            </div>
          </div>
        </div>
      </div>

      {/* viewport corner frame */}
      <span className="frame-corner tl" /><span className="frame-corner tr" />
      <span className="frame-corner bl" /><span className="frame-corner br" />

      {/* Treasury Total hidden for now — re-enable when real treasury data is wired:
          <TreasuryMeter stats={TREASURY_STATS} /> */}

      {overlay && <BuyOverlay onClose={() => setOverlay(false)} />}
      {lore && <LoreOverlay onClose={() => setLore(false)} />}

      <TweaksPanel>
        <TweakSection label="Motion" />
        <TweakToggle label="Animation" value={t.motion} onChange={(v) => setTweak("motion", v)} />
        <TweakToggle label="Auto-rotate" value={t.autospin} onChange={(v) => setTweak("autospin", v)} />
        <TweakSection label="Core" />
        <TweakToggle label="NEMO face in core" value={t.coreFace} onChange={(v) => setTweak("coreFace", v)} />
        <TweakSection label="Look" />
        <TweakColor label="Accent" value={t.accent}
          options={["#00e5ff", "#00ff88", "#ededf2", "#ff5c00", "#c9a227"]}
          onChange={(v) => setTweak("accent", v)} />
        <TweakSlider label="Glow" value={t.glow} min={0} max={1.4} step={0.1}
          onChange={(v) => setTweak("glow", v)} />
      </TweaksPanel>
    </>
  );
}

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