// main.jsx — RUNTIME · Intiser Ahmed
// On-device inference console portfolio

const { useState, useMemo, useEffect, useRef } = React;

/* ───── Icons ───── */
const I = {
  arrow: (s=14) => <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"><path d="M5 12h14M13 5l7 7-7 7"/></svg>,
  ext:   (s=12) => <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M7 17L17 7M9 7h8v8"/></svg>,
  plus:  (s=14) => <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><path d="M12 5v14M5 12h14"/></svg>,
  dl:    (s=13) => <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M12 3v12m0 0l-4-4m4 4l4-4M5 21h14"/></svg>,
  list:  (s=12) => <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><path d="M4 6h16M4 12h16M4 18h16"/></svg>,
  grid:  (s=12) => <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><rect x="4" y="4" width="7" height="7"/><rect x="13" y="4" width="7" height="7"/><rect x="4" y="13" width="7" height="7"/><rect x="13" y="13" width="7" height="7"/></svg>,
  img:   (s=26) => <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.3"><rect x="3" y="5" width="18" height="14"/><circle cx="9" cy="11" r="1.5"/><path d="M21 17l-5-5-9 9"/></svg>,
  gh:    (s=15) => <svg width={s} height={s} viewBox="0 0 24 24" fill="currentColor"><path d="M12 2a10 10 0 00-3.16 19.49c.5.09.68-.22.68-.48v-1.7c-2.78.6-3.37-1.34-3.37-1.34-.45-1.16-1.11-1.47-1.11-1.47-.91-.62.07-.6.07-.6 1 .07 1.53 1.03 1.53 1.03.89 1.52 2.34 1.08 2.91.83.09-.65.35-1.08.63-1.33-2.22-.25-4.56-1.11-4.56-4.95 0-1.09.39-1.98 1.03-2.68-.1-.25-.45-1.27.1-2.65 0 0 .84-.27 2.75 1.02a9.5 9.5 0 015 0c1.91-1.29 2.75-1.02 2.75-1.02.55 1.38.2 2.4.1 2.65.64.7 1.03 1.59 1.03 2.68 0 3.85-2.34 4.7-4.57 4.94.36.31.68.92.68 1.85v2.74c0 .27.18.58.69.48A10 10 0 0012 2z"/></svg>,
  li:    (s=15) => <svg width={s} height={s} viewBox="0 0 24 24" fill="currentColor"><path d="M19 0H5a5 5 0 00-5 5v14a5 5 0 005 5h14a5 5 0 005-5V5a5 5 0 00-5-5zM8 19H5V8h3v11zM6.5 6.7a1.75 1.75 0 110-3.5 1.75 1.75 0 010 3.5zM20 19h-3v-5.6c0-1.4-.5-2.4-1.8-2.4-1 0-1.6.7-1.9 1.4-.1.2-.1.6-.1.9V19h-3V8h3v1.3c.4-.6 1.1-1.5 2.7-1.5 2 0 3.5 1.3 3.5 4V19z"/></svg>,
  mail:  (s=14) => <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"><rect x="3" y="5" width="18" height="14"/><path d="M3 7l9 6 9-6"/></svg>,
};

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "mode": "paper",
  "signal": "#c8f24e",
  "density": "comfortable",
  "view": "gallery"
}/*EDITMODE-END*/;

const SIGNALS = ["#c8f24e", "#3fd9d2", "#ff8a3d", "#e85aa0"];

const statusCls = (s) => "s-" + s;

/* reveal-on-scroll hook */
function useReveal() {
  useEffect(() => {
    const els = document.querySelectorAll("[data-reveal]");
    const io = new IntersectionObserver((ents) => {
      ents.forEach(e => { if (e.isIntersecting) { e.target.classList.add("in"); io.unobserve(e.target); } });
    }, { threshold: 0.12 });
    els.forEach(el => io.observe(el));
    return () => io.disconnect();
  }, []);
}

/* ───── Status bar ───── */
function StatusBar() {
  const [clock, setClock] = useState("");
  useEffect(() => {
    const tick = () => {
      const d = new Date();
      const t = d.toLocaleTimeString("en-US", { hour12: false, timeZone: "America/New_York" });
      setClock(t + " EST");
    };
    tick(); const id = setInterval(tick, 1000); return () => clearInterval(id);
  }, []);
  return (
    <div className="statusbar">
      <div className="statusbar-in">
        <div className="sb-left">
          <a href="#top" className="sb-logo">
            <span className="sb-glyph">IA</span>
            <span><b>INTISER&nbsp;AHMED</b> <span>// runtime</span></span>
          </a>
        </div>
        <div className="sb-center">
          <span className="sb-status"><span className="led pulse"></span>STATUS: AVAILABLE FOR HIRE</span>
          <span style={{color:"var(--muted-2)"}}>·</span>
          <span className="sb-clock">{clock}</span>
        </div>
        <div className="sb-right">
          <a href="#units">Work</a>
          <a href="#timeline">Career</a>
          <a href="#contact">Contact</a>
          <a href="IntiserAhmed.pdf" target="_blank" rel="noreferrer" className="sb-resume">{I.dl(12)} Résumé</a>
        </div>
      </div>
    </div>
  );
}

/* ───── Graph background ───── */
function GraphBg() {
  const ref = useRef(null);
  useEffect(() => {
    const el = ref.current;
    if (!el || typeof ForceGraph3D === 'undefined') return;

    const sig = () =>
      getComputedStyle(document.documentElement).getPropertyValue('--signal').trim() || '#c8f24e';

    // LEFT side — Mobile (iOS, Android, Flutter, macOS) on a left arc
    // RIGHT side — AI/Backend/Cloud/Web/Security on a right arc
    const MOBILE = [
      { label: "iOS",     subs: ["GCD · KVO · Memory Mgmt", "Metal GPU · Kernel Opt.", "Core Data Concurrency"] },
      { label: "Android", subs: ["Kotlin Coroutines · Flow", "Jetpack Compose", "Kotlin Autofill Service"] },
      { label: "Flutter", subs: ["Dart Isolates", "Platform Channels · FFI", "Riverpod · Isar"] },
      { label: "macOS",   subs: ["AppKit · NSWindow Layers", "SwiftData · Spotlight", "Menu-bar Agent · FSEvents"] },
    ];
    const AI = [
      { label: "AI · ML",       subs: ["CoreML · ONNX Conversion", "On-device LLM Inference", "RAG · Fine-tuning"] },
      { label: "Backend",       subs: ["Go · gRPC · Protobuf", "Postgres · CQRS · DynamoDB", "REST · WebSockets · OAuth2"] },
      { label: "Cloud · Infra", subs: ["AWS · DynamoDB · Lambda", "Supabase · Postgres · RLS", "Cloudflare R2 · CDN · Edge Fn"] },
      { label: "Web",           subs: ["Next.js · React · TypeScript", "FastAPI · Node · WebSockets", "CI/CD · Xcode Cloud · Fastlane"] },
      { label: "Security",      subs: ["E2EE · AtSign Protocol", "WebRTC · Opus Codec", "Zero-Knowledge · OAuth2 · Autofill"] },
      { label: "AI Edge Compute",  subs: ["llama.cpp · GGUF Q4_K_M", "Metal MPS · Vulkan Compute", "INT4 Quant · KV-cache · Speculative Decode"] },
    ];

    const CY   = 85;
    const rx   = 90;
    const ry   = 62;
    const rz   = 55;
    const tilt = Math.PI / 5;
    const Y_FLOOR = 38;

    const allNodes = [];
    const allLinks = [];
    let id = 0;

    function placeGroup(stacks, aStart, aEnd) {
      const ids = [];
      stacks.forEach((stack, i) => {
        const a  = aStart + (i / Math.max(stacks.length - 1, 1)) * (aEnd - aStart);
        const px = rx * Math.cos(a);
        const py = CY + ry * Math.sin(a) * Math.cos(tilt);
        const pz =      rz * Math.sin(a) * Math.sin(tilt);

        const parentId = id++;
        ids.push(parentId);
        allNodes.push({ id: parentId, label: stack.label, val: 10, isParent: true, fx: px, fy: py, fz: pz });

        const outX = px, outY = py - CY, outZ = pz;
        const len  = Math.sqrt(outX**2 + outY**2 + outZ**2) || 1;
        const nx = outX/len, ny = outY/len, nz = outZ/len;
        const perpX = -nz, perpZ = nx;

        const subFan = [
          { d: 26, p:  0,  u: 18 },
          { d: 24, p:  20, u: -7 },
          { d: 24, p: -20, u: -7 },
        ];
        stack.subs.forEach((sub, j) => {
          const off = subFan[j];
          const subId = id++;
          allNodes.push({
            id: subId, label: sub, val: 2, isParent: false,
            fx: px + nx * off.d + perpX * off.p,
            fy: Math.max(Y_FLOOR, py + ny * off.d + off.u),
            fz: pz + nz * off.d + perpZ * off.p,
          });
          allLinks.push({ source: parentId, target: subId });
        });
      });
      // connect adjacent parents within the group
      ids.forEach((pid, i) => {
        if (i < ids.length - 1) allLinks.push({ source: pid, target: ids[i + 1] });
      });
      return ids;
    }

    // Left arc: mobile nodes span a = 0.75π → 1.25π (left half)
    const leftIds  = placeGroup(MOBILE, Math.PI * 0.75, Math.PI * 1.25);
    // Right arc: AI nodes span a = -0.35π → 0.35π (right half)
    const rightIds = placeGroup(AI,     -Math.PI * 0.35, Math.PI * 0.35);

    // Bridge the two groups at top and bottom to frame the photo
    allLinks.push({ source: leftIds[0],                  target: rightIds[rightIds.length - 1] });
    allLinks.push({ source: leftIds[leftIds.length - 1], target: rightIds[0] });

    // Reveal sequence: parent then its 3 subs, then next parent, etc.
    // allNodes is already ordered [parent0, sub0a, sub0b, sub0c, parent1, …]
    const w = el.offsetWidth  || window.innerWidth;
    const h = el.offsetHeight || window.innerHeight * 0.88;

    const Graph = new ForceGraph3D(el)
      .backgroundColor('rgba(0,0,0,0)')
      .showNavInfo(false)
      .enableNodeDrag(false)
      .enableNavigationControls(true)
      .nodeVal(n => n.val)
      .nodeLabel(n => n.label)
      .nodeColor(() => sig())
      .nodeOpacity(0.9)
      .nodeRelSize(3.5)
      .linkColor(() => sig())
      .linkOpacity(0.2)
      .linkWidth(0.8)
      .d3AlphaDecay(0.018)
      .d3VelocityDecay(0.35)
      .width(w)
      .height(h)
      .cameraPosition({ x: 0, y: 20, z: 230 }, { x: 0, y: CY, z: 0 })
      .graphData({ nodes: [], links: [] });

    const controls = Graph.controls();
    if (controls) {
      controls.enableZoom = false;
      controls.enablePan  = false;
      controls.target.set(0, CY, 0);
      controls.update();
    }

    // Intercept wheel before OrbitControls — forward to page scroll
    const canvas = Graph.renderer().domElement;
    const onWheel = (e) => { e.stopImmediatePropagation(); window.scrollBy(0, e.deltaY); };
    canvas.addEventListener('wheel', onWheel, { capture: true });

    // ── 2D label overlay ──
    // Project each node's 3D world position → 2D screen coords each frame
    // and draw text directly on a canvas overlay. No external library needed.
    const lc = document.createElement('canvas');
    lc.style.cssText = 'position:absolute;inset:0;pointer-events:none;z-index:2;';
    el.appendChild(lc);
    const lx = lc.getContext('2d');
    const dpr = Math.min(window.devicePixelRatio || 1, 2);

    function resizeLc() {
      lc.width  = el.offsetWidth  * dpr;
      lc.height = el.offsetHeight * dpr;
      lc.style.width  = el.offsetWidth  + 'px';
      lc.style.height = el.offsetHeight + 'px';
      lx.setTransform(dpr, 0, 0, dpr, 0, 0);
    }
    resizeLc();

    // Replicates THREE.Vector3.applyMatrix4 + .project(camera) without needing THREE global
    function projectNode(wx, wy, wz) {
      const camera = Graph.camera();
      const cw = el.offsetWidth, ch = el.offsetHeight;
      function applyM4(x, y, z, m) {
        const e = m.elements;
        const w = 1 / (e[3]*x + e[7]*y + e[11]*z + e[15]);
        return [(e[0]*x+e[4]*y+e[8]*z+e[12])*w, (e[1]*x+e[5]*y+e[9]*z+e[13])*w, (e[2]*x+e[6]*y+e[10]*z+e[14])*w];
      }
      let [x,y,z] = applyM4(wx, wy, wz, camera.matrixWorldInverse);
      [x,y,z]     = applyM4(x,  y,  z,  camera.projectionMatrix);
      return { sx: (x+1)/2*cw, sy: (-y+1)/2*ch, behind: z > 1 };
    }

    // Read both ink and signal once per frame so they respond to theme changes
    const css = () => getComputedStyle(document.documentElement);
    const ink    = () => css().getPropertyValue('--ink').trim()   || '#141611';
    const ink2   = () => css().getPropertyValue('--ink-2').trim() || '#36392f';
    const bgCol  = () => css().getPropertyValue('--bg').trim()    || '#e9e7df';

    let labelAlpha = 0;
    let labelRaf;
    function drawLabels() {
      const cw = el.offsetWidth, ch = el.offsetHeight;
      lx.clearRect(0, 0, cw, ch);
      Graph.graphData().nodes.forEach(n => {
        const { sx, sy, behind } = projectNode(n.x ?? n.fx ?? 0, n.y ?? n.fy ?? 0, n.z ?? n.fz ?? 0);
        if (behind || sx < 0 || sx > cw || sy < 0 || sy > ch) return;
        const fs = n.isParent ? 11 : 7;
        lx.font = `${n.isParent ? '600' : '400'} ${fs}px "Geist Mono",monospace`;
        lx.textAlign = 'center';
        lx.textBaseline = 'middle';
        // pill bg for legibility over the 3D scene
        const tw = lx.measureText(n.label).width;
        lx.globalAlpha = 0.72 * labelAlpha;
        lx.fillStyle = bgCol();
        lx.fillRect(sx - tw/2 - 5, sy - fs/2 - 3, tw + 10, fs + 6);
        lx.globalAlpha = (n.isParent ? 1 : 0.85) * labelAlpha;
        lx.fillStyle = n.isParent ? ink() : ink2();
        lx.fillText(n.label, sx, sy);
      });
      labelRaf = requestAnimationFrame(drawLabels);
    }
    drawLabels();

    // All nodes start at the ring centre and burst outward to their
    // fx/fy/fz positions — the force simulation drives the flight.
    // Seed each node near the centre so motion is visible.
    const launchNodes = allNodes.map(n => ({
      ...n,
      x: (Math.random() - 0.5) * 8,
      y: CY + (Math.random() - 0.5) * 8,
      z: (Math.random() - 0.5) * 8,
    }));

    Graph.nodeOpacity(0);
    labelAlpha = 0;
    Graph.graphData({ nodes: launchNodes, links: allLinks });

    // Fade in while nodes are in flight
    let t = 0;
    const fadeIn = setInterval(() => {
      t += 16;
      const p = Math.min(1, t / 600);
      const eased = 1 - Math.pow(1 - p, 3);
      Graph.nodeOpacity(eased * 0.9);
      labelAlpha = eased;
      if (p >= 1) clearInterval(fadeIn);
    }, 16);

    // Auto-orbit pivoting around the photo
    let angle = 0, orbiting = true;
    const orbit = setInterval(() => {
      if (!orbiting) return;
      angle += Math.PI / 550;
      Graph.cameraPosition(
        { x: 38 * Math.sin(angle), y: 18 * Math.sin(angle * 0.4), z: 230 },
        { x: 0, y: CY, z: 0 }, 0
      );
    }, 16);

    // Float: nudge each parent node with a unique sine wave so the
    // graph breathes and feels 3D even when the camera isn't moving.
    let floatT = 0;
    const floatNodes = allNodes.filter(n => n.isParent);
    const floatPhases = floatNodes.map((_, i) => (i / floatNodes.length) * Math.PI * 2);
    const floatInterval = setInterval(() => {
      floatT += 0.032;
      // Mutate positions directly — never call graphData() setter here,
      // that restarts the simulation every tick and causes label flicker.
      floatNodes.forEach((fn, idx) => {
        const ph = floatPhases[idx];
        fn.x = fn.fx + Math.sin(floatT + ph) * 5.5;
        fn.y = fn.fy + Math.cos(floatT * 0.7 + ph) * 4.2;
        fn.z = fn.fz + Math.sin(floatT * 0.5 + ph + 1) * 5.0;
      });
    }, 32);

    const onDown = () => { orbiting = false; };
    const onUp   = () => { orbiting = true;  };
    el.addEventListener('pointerdown', onDown);
    el.addEventListener('pointerup',   onUp);

    const onResize = () => { Graph.width(el.offsetWidth).height(el.offsetHeight); resizeLc(); };
    window.addEventListener('resize', onResize);

    return () => {
      clearInterval(fadeIn);
      clearInterval(orbit);
      clearInterval(floatInterval);
      cancelAnimationFrame(labelRaf);
      el.removeEventListener('pointerdown', onDown);
      el.removeEventListener('pointerup',   onUp);
      window.removeEventListener('resize', onResize);
      if (canvas) canvas.removeEventListener('wheel', onWheel, { capture: true });
      try { Graph._destructor && Graph._destructor(); } catch (_) {}
      el.innerHTML = '';
    };
  }, []);

  return <div ref={ref} className="graph-bg" aria-hidden="true" />;
}

/* ───── Hero ───── */
const HERO_STATS = [
  { v: "13+", l: "Apps shipped" },
  { v: "5 yrs", l: "Experience" },
  { v: "40+", l: "Countries" },
  { v: "90%+", l: "Enterprise accuracy" },
];

function Hero() {
  return (
    <section className="hero-section" id="top">
      <GraphBg />
      <div className="wrap hero-inner" data-reveal>
        <div className="hero-photo">
          <img src="photo.png" alt="Intiser Ahmed" />
        </div>
        <div className="hero-badges">
          <div className="hero-badge">
            <span className="led pulse" style={{"--c":"var(--led-live)"}}></span>
            Available for senior iOS / mobile roles
          </div>
          <div className="hero-badge hero-badge--claude">
            <span className="claude-glyph">✦</span>
            Claude Native
          </div>
          <div className="hero-badge hero-badge--antigravity">
            <span className="antigravity-glyph">⬡</span>
            Antigravity Native
          </div>
        </div>
        <h1 className="hero-h">
          I ship native apps that run their own <span className="mark">intelligence</span> on-device.
        </h1>
        <p className="hero-sub">
          Senior mobile &amp; AI engineer. iOS, Android, Flutter and macOS — with on-device LLMs, RAG and edge ML built in. From a clinician-owned medical scribe to enterprise automation at Continental. Privacy-first, production-grade.
        </p>
        <div className="hero-stats">
          {HERO_STATS.map((s, i) => (
            <div key={s.l} className="hero-stat">
              {i > 0 && <span className="hero-stat-div"></span>}
              <span className="hero-stat-v">{s.v}</span>
              <span className="hero-stat-l">{s.l}</span>
            </div>
          ))}
        </div>
        <div className="hero-cta">
          <a href="#featured" className="btn btn-primary">See my work {I.arrow(14)}</a>
          <a href="mailto:intiser4@gmail.com?subject=Hi%20Intiser" className="btn btn-ghost">{I.mail(13)} intiser4@gmail.com</a>
        </div>
      </div>
    </section>
  );
}

/* ───── Gazey Talk — gaze/mesh viz ───── */
function VizGazeyTalk() {
  const dotRef  = useRef(null);
  const glowRef = useRef(null);

  // Precompute face mesh lines (clipped to ellipse cx=160 cy=95 rx=64 ry=74)
  const meshH = [], meshV = [];
  for (let i = 0; i < 13; i++) {
    const y = 21 + i * 13;
    const dy = y - 95;
    if (Math.abs(dy) >= 74) continue;
    const hw = 64 * Math.sqrt(1 - (dy * dy) / (74 * 74));
    meshH.push([160 - hw, y, 160 + hw, y]);
  }
  for (let j = 0; j < 11; j++) {
    const x = 109 + j * 12;
    const dx = x - 160;
    if (Math.abs(dx) >= 64) continue;
    const hh = 74 * Math.sqrt(1 - (dx * dx) / (64 * 64));
    meshV.push([x, 95 - hh, x, 95 + hh]);
  }

  useEffect(() => {
    let t = 0, raf, running = true;
    const reduce = matchMedia("(prefers-reduced-motion: reduce)").matches;
    function tick() {
      if (!running) return;
      if (!reduce) {
        t += 0.018;
        const gx = (135 + Math.sin(t * 1.1) * 5).toFixed(2);
        const gy = (86 + Math.cos(t * 0.85) * 2.8).toFixed(2);
        dotRef.current?.setAttribute("cx", gx);
        dotRef.current?.setAttribute("cy", gy);
        glowRef.current?.setAttribute("cx", gx);
        glowRef.current?.setAttribute("cy", gy);
      }
      raf = requestAnimationFrame(tick);
    }
    raf = requestAnimationFrame(tick);
    return () => { running = false; cancelAnimationFrame(raf); };
  }, []);

  return (
    <svg viewBox="0 0 320 200" style={{width:"100%",height:"100%",display:"block"}} xmlns="http://www.w3.org/2000/svg">
      <defs>
        <radialGradient id="gtGl" cx="50%" cy="50%" r="50%">
          <stop offset="0%" stopColor="var(--signal)" stopOpacity="0.3"/>
          <stop offset="100%" stopColor="var(--signal)" stopOpacity="0"/>
        </radialGradient>
        <style>{`@keyframes gtBeam{from{stroke-dashoffset:18}to{stroke-dashoffset:0}}`}</style>
      </defs>

      {/* Face mesh grid */}
      {meshH.map(([x1,y1,x2,y2],i) => <line key={"h"+i} x1={x1} y1={y1} x2={x2} y2={y2} stroke="var(--signal)" strokeWidth="0.5" opacity="0.16"/>)}
      {meshV.map(([x1,y1,x2,y2],i) => <line key={"v"+i} x1={x1} y1={y1} x2={x2} y2={y2} stroke="var(--signal)" strokeWidth="0.5" opacity="0.16"/>)}

      {/* Face oval */}
      <ellipse cx="160" cy="95" rx="64" ry="74" fill="none" stroke="var(--signal)" strokeWidth="1.2" opacity="0.45"/>

      {/* Detection corner brackets */}
      <path d="M90 14 L77 14 L77 26" fill="none" stroke="var(--signal)" strokeWidth="1.6" opacity="0.8"/>
      <path d="M230 14 L243 14 L243 26" fill="none" stroke="var(--signal)" strokeWidth="1.6" opacity="0.8"/>
      <path d="M90 176 L77 176 L77 164" fill="none" stroke="var(--signal)" strokeWidth="1.6" opacity="0.8"/>
      <path d="M230 176 L243 176 L243 164" fill="none" stroke="var(--signal)" strokeWidth="1.6" opacity="0.8"/>

      {/* Eye outlines */}
      <ellipse cx="135" cy="86" rx="11.5" ry="6.5" fill="none" stroke="var(--signal)" strokeWidth="1.1" opacity="0.7"/>
      <ellipse cx="185" cy="86" rx="11.5" ry="6.5" fill="none" stroke="var(--signal)" strokeWidth="1.1" opacity="0.7"/>

      {/* IR scan beams from left eye */}
      <line x1="123" y1="86" x2="18" y2="55" stroke="var(--signal)" strokeWidth="0.9" strokeDasharray="4 5" opacity="0.45" style={{animation:"gtBeam 1.4s linear infinite"}}/>
      <line x1="123" y1="86" x2="18" y2="86" stroke="var(--signal)" strokeWidth="0.9" strokeDasharray="4 5" opacity="0.28" style={{animation:"gtBeam 1.4s linear infinite .5s"}}/>
      <line x1="123" y1="86" x2="18" y2="117" stroke="var(--signal)" strokeWidth="0.9" strokeDasharray="4 5" opacity="0.45" style={{animation:"gtBeam 1.4s linear infinite 1s"}}/>

      {/* Gaze tracking dot */}
      <circle ref={glowRef} cx="135" cy="86" r="13" fill="url(#gtGl)"/>
      <circle ref={dotRef}  cx="135" cy="86" r="4"  fill="var(--signal)" opacity="0.95"/>

      {/* Bottom status strip */}
      <rect x="0" y="185" width="320" height="15" fill="var(--bg)" opacity="0.65"/>
      <text x="10"  y="195" fontFamily="'Geist Mono',monospace" fontSize="8" fill="var(--signal)" opacity="0.8">GAZE · GESTURE</text>
      <text x="160" y="195" fontFamily="'Geist Mono',monospace" fontSize="8" fill="var(--muted)" opacity="0.65" textAnchor="middle">AI PERSONALIZED</text>
      <text x="310" y="195" fontFamily="'Geist Mono',monospace" fontSize="8" fill="var(--muted)" opacity="0.65" textAnchor="end">0 BYTES EGRESS</text>
    </svg>
  );
}

const FEAT_VIZ = { gazey: VizGazeyTalk };

/* ───── Featured projects ───── */
const FEATURED_IDS = ["gazey", "pallinhealth", "continental"];

function Featured() {
  const featured = window.PROJECTS.filter(p => FEATURED_IDS.includes(p.id));
  return (
    <section className="wrap section" id="featured">
      <div className="sec-head" data-reveal>
        <div>
          <div className="sec-label">Selected work</div>
          <h2 className="sec-title">Featured <em>projects.</em></h2>
        </div>
        <a href="#units" className="btn btn-ghost">All 15 projects {I.arrow(14)}</a>
      </div>
      <div className="feat-grid" data-reveal>
        {featured.map(p => (
          <div className="feat-card" key={p.id}>
            <div className="feat-shot">
              {FEAT_VIZ[p.id]
                ? React.createElement(FEAT_VIZ[p.id])
                : p.image
                  ? <img src={p.image} alt={p.name} />
                  : <div className="u-shot-ph">{I.img(26)}{p.type}</div>}
              <span className={`badge ${statusCls(p.status)}`}><span className="led"></span>{p.statusLabel}</span>
            </div>
            <div className="feat-body">
              <div className="feat-nm">{p.name}</div>
              <div className="feat-sb">{p.sub}</div>
              <div className="u-impact"><div className="k">▸ Impact</div><div className="v">{p.impact}</div></div>
              <div className="feat-tech">
                {p.tech.slice(0,4).map(t => <span key={t} className="stk">{t}</span>)}
                {p.tech.length > 4 && <span className="stk" style={{opacity:0.5}}>+{p.tech.length-4}</span>}
              </div>
              {p.links.filter(l => l.primary).map(l => (
                <a key={l.label} href={l.url} target="_blank" rel="noreferrer" className="btn btn-primary" style={{marginTop:12}}>{l.label} {I.ext(12)}</a>
              ))}
            </div>
          </div>
        ))}
      </div>
    </section>
  );
}

/* ───── Build matrix ───── */
function BuildMatrix() {
  const P = window.PROJECTS;
  const c = (d) => P.filter(p => p.domains.includes(d)).length;
  const cols = [
    { n: "01", h: "Native mobile", p: "iOS, Android & Flutter apps with tactile UX. SwiftUI, Compose, Kotlin, Dart — wherever the platform reads best.", c: c("mobile") },
    { n: "02", h: "Desktop apps", p: "macOS utilities & menu-bar tools. AppKit + SwiftUI hybrids built for power users.", c: c("desktop") },
    { n: "03", h: "AI · Edge ML", p: "On-device LLMs, RAG and quantized pipelines. Private by construction, fast by design.", c: c("ai") + c("edge") },
    { n: "04", h: "Orchestration", p: "Multi-region backends, IoT pipelines & enterprise automation handling real throughput.", c: c("orchestration") },
  ];
  return (
    <section className="wrap section" id="build">
      <div className="sec-head" data-reveal>
        <div>
          <div className="sec-tag"><span className="n">[01]</span><span className="rule"></span><span className="l">Capabilities</span></div>
          <h2 className="sec-title">Four practices, <em>one continuous craft.</em></h2>
        </div>
        <p className="sec-note">Most units live at the intersection — a mobile app that runs its own model, an enterprise system that talks to firmware. Filter the work below by any practice.</p>
      </div>
      <div className="matrix" data-reveal>
        {cols.map(col => (
          <div className="mx" key={col.n}>
            <div className="mx-top"><span className="mx-n">{col.n}</span><span className="led" style={{"--c":"var(--signal)"}}></span></div>
            <div className="mx-h">{col.h}</div>
            <div className="mx-p">{col.p}</div>
            <div className="mx-c"><b>{String(col.c).padStart(2,"0")}</b> units</div>
          </div>
        ))}
      </div>
    </section>
  );
}

/* ───── Unit row ───── */
function Unit({ p, i, open, onToggle }) {
  return (
    <article className="unit" data-open={open}>
      <button className="unit-head" onClick={onToggle} aria-expanded={open}>
        <div className="u-idx">
          <span className="led" style={{"--c":`var(--led-${p.status==="shipped"?"live":p.status==="production"?"prod":p.status})`}}></span>
          {String(i+1).padStart(2,"0")}
          <span className="yr">{p.year}</span>
        </div>
        <div className="u-name">
          <div className="nm">{p.name}</div>
          <div className="sb">{p.sub}</div>
        </div>
        <div className="u-class">
          <span className="ct">Class</span>
          {p.type}
        </div>
        <div className="u-kpi">
          <span className="kt">Signal</span>
          <span className="kv">{p.kpi.split("\n").map((l,idx)=><span key={idx}>{l}<br/></span>)}</span>
        </div>
        <div className="u-exp">{I.plus(14)}</div>
      </button>
      {open && <UnitDetail p={p} />}
    </article>
  );
}

function UnitDetail({ p }) {
  return (
    <div className="u-detail">
      <div className="u-shot">
        <span className="u-shot-corner">{p.statusLabel}</span>
        {p.image
          ? <img src={p.image} alt={p.name} />
          : <div className="u-shot-ph">{I.img(26)}{p.type}<br/>visual pending</div>}
      </div>
      <div className="u-body">
        <div className="u-spec">
          <div><div className="k">Classification</div><div className="v">{p.type}</div></div>
          <div><div className="k">Build</div><div className="v">{p.role} · {p.year}</div></div>
        </div>
        <div className="u-section-lbl">What it does</div>
        <div className="u-desc">{p.desc}</div>
        <div className="u-section-lbl">Approach</div>
        <div className="u-approach">{p.approach}</div>
        <div className="u-impact"><div className="k">▸ Metrics</div><div className="v">{p.impact}</div></div>
        <div className="u-section-lbl">Stack</div>
        <div className="u-stack">{p.tech.map(t => <span key={t} className="stk">{t}</span>)}</div>
        <div className="u-links">
          {p.links.map(l => (
            <a key={l.label} href={l.url} target="_blank" rel="noreferrer" className={`btn ${l.primary ? "btn-primary" : "btn-ghost"}`}>{l.label} {I.ext(12)}</a>
          ))}
          <a href={`project.html?id=${p.id}`} className="btn btn-ghost" style={{fontSize:11,padding:'3px 10px'}}>Open page {I.ext(11)}</a>
        </div>
      </div>
    </div>
  );
}

/* ───── Video lightbox ───── */
/* ───── Card fullscreen modal ───── */
function CardModal({ p, onClose }) {
  const vidRef = useRef(null);
  useEffect(() => {
    const onKey = (e) => { if (e.key === 'Escape') onClose(); };
    window.addEventListener('keydown', onKey);
    if (vidRef.current) vidRef.current.play();
    document.body.style.overflow = 'hidden';
    return () => { window.removeEventListener('keydown', onKey); document.body.style.overflow = ''; };
  }, []);
  return (
    <div className="cm-overlay" onClick={onClose}>
      <div className="cm-box" onClick={e => e.stopPropagation()}>
        <button className="cm-close" onClick={onClose}>✕</button>
        <div className="cm-inner">
          {/* Left: media */}
          <div className="cm-media">
            {p.video
              ? <video ref={vidRef} src={p.video} controls autoPlay playsInline loop className="cm-video" />
              : p.image
                ? <img src={p.image} alt={p.name} className="cm-img" />
                : <div className="u-shot-ph cm-ph">{I.img(40)}{p.type}</div>
            }
          </div>
          {/* Right: detail */}
          <div className="cm-detail">
            <div className="cm-head">
              <div>
                <div className="cm-nm">{p.name}</div>
                <div className="cm-sb">{p.sub}</div>
              </div>
              <span className={`badge ${statusCls(p.status)}`}><span className="led"></span>{p.statusLabel}</span>
            </div>
            <div className="cm-meta">
              <span className="cm-pill">{p.type}</span>
              <span className="cm-pill">{p.role} · {p.year}</span>
            </div>
            <div className="cm-section">What it does</div>
            <p className="cm-text">{p.desc}</p>
            <div className="cm-section">Approach</div>
            <p className="cm-text">{p.approach}</p>
            <div className="u-impact" style={{margin:'16px 0'}}><div className="k">▸ Impact</div><div className="v">{p.impact}</div></div>
            <div className="cm-section">Stack</div>
            <div className="cm-stack">{p.tech.map(t => <span key={t} className="stk">{t}</span>)}</div>
            {p.links.length > 0 && (
              <div className="cm-links">
                {p.links.map(l => (
                  <a key={l.label} href={l.url} target="_blank" rel="noreferrer" className={`btn ${l.primary ? 'btn-primary' : 'btn-ghost'}`}>{l.label} {I.ext(12)}</a>
                ))}
              </div>
            )}
          </div>
        </div>
      </div>
    </div>
  );
}

/* ───── Gallery card ───── */
function GCard({ p }) {
  const [expanded, setExpanded] = useState(false);
  const [hovered, setHovered] = useState(false);
  const [slide, setSlide] = useState(0);
  const [modal, setModal] = useState(false); // kept but unused — card navigates to project page
  const vidRef = useRef(null);

  const images = p.images || (p.image ? [p.image] : []);
  const hasSlides = images.length > 1;

  useEffect(() => {
    if (!hovered || !hasSlides) return;
    const id = setInterval(() => setSlide(s => (s + 1) % images.length), 1400);
    return () => clearInterval(id);
  }, [hovered, hasSlides]);

  const onEnter = () => {
    setHovered(true);
    if (vidRef.current) { vidRef.current.currentTime = 0; vidRef.current.play(); }
  };
  const onLeave = () => {
    setHovered(false);
    setSlide(0);
    if (vidRef.current) { vidRef.current.pause(); vidRef.current.currentTime = 0; }
  };

  return (
    <article className="gcard" onMouseEnter={onEnter} onMouseLeave={onLeave} onClick={() => window.location.href = p.page || `project.html?id=${p.id}`} style={{cursor:'pointer'}}>
      <div className={`gcard-shot${p.video ? ' has-video' : ''}`}>
        {p.video
          ? <video ref={vidRef} src={p.video} muted playsInline loop preload="metadata" />
          : images.length > 0
            ? <img src={images[slide]} alt={p.name} />
            : <div className="u-shot-ph">{I.img(26)}{p.type}</div>
        }
        {p.video && <div className="gcard-play">▶</div>}
        {hasSlides && (
          <div className="gcard-slide-dots">
            {images.map((_, i) => <span key={i} {...(i === slide ? {"data-active": ""} : {})} />)}
          </div>
        )}
      </div>
      <div className="gcard-body">
        <div className="gcard-top">
          <div>
            <div className="gcard-nm">{p.name}</div>
            <div className="gcard-sb">{p.sub}</div>
          </div>
          <span className={`badge ${statusCls(p.status)}`}><span className="led"></span>{p.statusLabel}</span>
        </div>
        <div className="gcard-desc" {...(expanded ? {"data-expanded": ""} : {})}>{p.desc}</div>
        <button className="gcard-expand" onClick={e => { e.stopPropagation(); setExpanded(x => !x); }}>{expanded ? "▴ less" : "▾ more"}</button>
        <div className="u-impact"><div className="k">▸ Impact</div><div className="v">{p.impact}</div></div>
        <div className="gcard-foot">
          {p.tech.slice(0,7).map(t => <span key={t} className="stk">{t}</span>)}
          {p.tech.length > 7 && <span className="stk" style={{opacity:0.55}}>+{p.tech.length-7}</span>}
        </div>
        {p.links.length > 0 && (
          <div className="u-links" style={{marginTop:4}}>
            {p.links.map(l => <a key={l.label} href={l.url} target="_blank" rel="noreferrer" className={`btn ${l.primary?"btn-primary":"btn-ghost"}`}>{l.label} {I.ext(12)}</a>)}
          </div>
        )}
      </div>
    </article>
  );
}

/* ───── Work section ───── */
function Work({ view, setView }) {
  const P = window.PROJECTS, D = window.DOMAINS;
  const [filter, setFilter] = useState("all");
  const [openId, setOpenId] = useState(P[0].id);

  const counts = useMemo(() => {
    const m = { all: P.length };
    D.forEach(d => { if (d.id !== "all") m[d.id] = P.filter(p => p.domains.includes(d.id)).length; });
    return m;
  }, []);
  const list = useMemo(() => filter === "all" ? P : P.filter(p => p.domains.includes(filter)), [filter]);

  return (
    <section className="wrap section" id="units">
      <div className="sec-head" data-reveal>
        <div>
          <div className="sec-label">All projects</div>
          <h2 className="sec-title">{list.length} {filter==="all" ? "projects" : `of ${P.length}`}, <em>filterable.</em></h2>
        </div>
        <p className="sec-note">Switch to list view to expand any row for the full description, impact and links.</p>
      </div>

      <div className="channels" data-reveal>
        <span className="ch-lbl">Filter by</span>
        {D.map(d => (
          <button key={d.id} className="chip" data-on={filter===d.id} onClick={() => setFilter(d.id)}>
            {d.label}<span className="ct">{String(counts[d.id]).padStart(2,"0")}</span>
          </button>
        ))}
        <span className="ch-spacer"></span>
        <div className="vtoggle">
          <button data-on={view==="list"} onClick={()=>setView("list")}>{I.list(12)} List</button>
          <button data-on={view==="gallery"} onClick={()=>setView("gallery")}>{I.grid(12)} Grid</button>
        </div>
      </div>

      {view === "list" ? (
        <div className="units" data-reveal>
          {list.map((p,i) => <Unit key={p.id} p={p} i={i} open={openId===p.id} onToggle={()=>setOpenId(openId===p.id?null:p.id)} />)}
        </div>
      ) : (
        <div className="gallery" data-reveal>
          {list.map(p => <GCard key={p.id} p={p} />)}
        </div>
      )}
    </section>
  );
}

/* ───── Subsystems / skills ───── */
function Subs() {
  return (
    <section className="wrap section" id="subs">
      <div className="sec-head" data-reveal>
        <div>
          <div className="sec-label">Skills</div>
          <h2 className="sec-title">The <em>tools</em> behind the work.</h2>
        </div>
        <p className="sec-note">Native-first where it matters, cross-platform where it ships faster.</p>
      </div>
      <div className="subs" data-reveal>
        {window.SKILLS.map(col => (
          <div className="sub" key={col.h}>
            <div className="sub-h">{col.h}</div>
            <ul>{col.items.map(it => <li key={it.t} className={it.lead?"lead":""}>{it.t}</li>)}</ul>
          </div>
        ))}
      </div>
    </section>
  );
}

/* ───── Career Timeline ───── */
function TimelineRow({ item, open, onToggle }) {
  const isCurrent = item.status === "current";
  return (
    <article className={"tl-entry" + (isCurrent ? " current" : "") + (open ? " open" : "")}>
      <div className="tl-left">
        <div className="tl-year">{item.year.replace("—", "–")}</div>
        <div className="tl-n">{item.n}</div>
      </div>
      <div className="tl-node">
        <div className="tl-dot">
          {isCurrent && <span className="led pulse" style={{"--c":"var(--signal)"}} />}
        </div>
      </div>
      <div className="tl-card">
        <button className="tl-toggle" onClick={onToggle} aria-expanded={open}>
          <div className="tl-top">
            <div className="tl-role">
              <div className="tl-role-nm">{item.role}</div>
              <div className="tl-role-co">{item.company}</div>
            </div>
            <div className="tl-card-meta">
              <span className="tl-type-badge">{item.type}</span>
              <span className="tl-expander">{I.plus(12)}</span>
            </div>
          </div>
          {!open && <div className="tl-preview">{item.achievements[0]}</div>}
        </button>
        {open && (
          <div className="tl-detail">
            <ul className="tl-achiev">
              {item.achievements.map((a, i) => (
                <li key={i}><span className="tl-bullet">▸</span>{a}</li>
              ))}
            </ul>
          </div>
        )}
      </div>
    </article>
  );
}

function Timeline() {
  const [openN, setOpenN] = useState("01");
  return (
    <section className="wrap section" id="timeline">
      <div className="sec-head" data-reveal>
        <div>
          <div className="sec-label">Experience</div>
          <h2 className="sec-title">Roles that <em>shaped the work.</em></h2>
        </div>
        <p className="sec-note">Five roles across founding, enterprise, and product engineering. Expand any for key wins.</p>
      </div>
      <div className="tl-track" data-reveal>
        {window.TIMELINE.map(item => (
          <TimelineRow
            key={item.n}
            item={item}
            open={openN === item.n}
            onToggle={() => setOpenN(openN === item.n ? null : item.n)}
          />
        ))}
      </div>
    </section>
  );
}

/* ───── Process ───── */
function Process() {
  return (
    <section className="wrap section" id="process">
      <div className="sec-head" data-reveal>
        <div>
          <div className="sec-label">How I work</div>
          <h2 className="sec-title">Four phases, <em>zero chaos.</em></h2>
        </div>
        <p className="sec-note">A practiced loop for shipping AI-first and mobile products without drama — from alignment to live.</p>
      </div>
      <div className="proc-grid" data-reveal>
        {window.PROCESS.map(p => (
          <div className="proc" key={p.n}>
            <div className="proc-top">
              <span className="proc-n">{p.n}</span>
              <span className="led" style={{"--c":"var(--signal)"}}></span>
            </div>
            <div className="proc-phase">{p.phase}</div>
            <div className="proc-desc">{p.desc}</div>
            <div className="proc-section">
              <div className="proc-lbl">Activities</div>
              <ul>{p.activities.map(a => <li key={a}>{a}</li>)}</ul>
            </div>
            <div className="proc-section">
              <div className="proc-lbl">Deliverables</div>
              <ul>{p.deliverables.map(d => <li key={d}>{d}</li>)}</ul>
            </div>
            <div className="proc-tools">
              {p.tools.map(t => <span key={t} className="stk">{t}</span>)}
            </div>
          </div>
        ))}
      </div>
    </section>
  );
}

/* ───── Testimonials / Refs ───── */
function Refs() {
  return (
    <section className="wrap section" id="refs">
      <div className="sec-head" data-reveal>
        <div>
          <div className="sec-label">Recommendations</div>
          <h2 className="sec-title">People who <em>shipped with me.</em></h2>
        </div>
        <p className="sec-note">Verified LinkedIn recommendations from managers, teammates, and mentors at Continental AG.</p>
      </div>
      <div className="refs" data-reveal>
        {window.TESTIMONIALS.map(t => (
          <div className="ref" key={t.name}>
            <div className="ref-head">
              <div className="ref-av">{t.initials}</div>
              <div className="ref-info">
                <div className="ref-nm">{t.name}</div>
                <div className="ref-ti">{t.title}</div>
                <div className="ref-meta">
                  <span className="ref-rel">{t.relationship}</span>
                  <span className="ref-sep">·</span>
                  <span>{t.company}</span>
                  <span className="ref-sep">·</span>
                  <span>{t.date}</span>
                </div>
              </div>
            </div>
            <div className="ref-q">
              <span className="ref-ql">❝</span>
              {t.text}
            </div>
            <div className="ref-foot">
              <span className="ref-li">LI · Verified on LinkedIn</span>
            </div>
          </div>
        ))}
      </div>
    </section>
  );
}

/* ───── Contact ───── */
function Contact() {
  return (
    <section className="wrap contact" id="contact">
      <div className="contact-grid" data-reveal>
        <div>
          <div className="sec-label">Contact</div>
          <h2 className="contact-h">Building something<br/>calm, fast &amp; <em>private?</em></h2>
          <p className="contact-p">Open to senior mobile, AI-mobile and applied-ML roles — plus high-trust contract work where on-device AI, privacy or platform craft matters.</p>
          <div className="contact-cta">
            <a href="mailto:intiser4@gmail.com?subject=Hi%20Intiser" className="btn btn-primary">{I.mail(13)} intiser4@gmail.com</a>
            <a href="IntiserAhmed.pdf" target="_blank" rel="noreferrer" className="btn btn-ghost">{I.dl(13)} Résumé</a>
          </div>
        </div>
        <div className="terminal">
          <div className="term-head">
            <span className="dots"><i style={{background:"var(--led-beta)"}}></i><i style={{background:"var(--led-live)"}}></i><i style={{background:"var(--led-prod)"}}></i></span>
            <span className="t">channels.sh</span>
          </div>
          <div className="term-body">
            <a className="tlink" href="https://github.com/Intiserahmed" target="_blank" rel="noreferrer">
              <span className="l"><span className="ic">{I.gh(14)}</span><span><span className="k">GitHub</span><br/><span className="v">github.com/Intiserahmed</span></span></span>{I.arrow(14)}
            </a>
            <a className="tlink" href="https://linkedin.com/in/intiser-ahmed" target="_blank" rel="noreferrer">
              <span className="l"><span className="ic">{I.li(14)}</span><span><span className="k">LinkedIn</span><br/><span className="v">/in/intiser-ahmed</span></span></span>{I.arrow(14)}
            </a>
            <a className="tlink" href="mailto:intiser4@gmail.com">
              <span className="l"><span className="ic">{I.mail(13)}</span><span><span className="k">Email</span><br/><span className="v">intiser4@gmail.com</span></span></span>{I.arrow(14)}
            </a>
            <div className="term-prompt">$ ready to deploy<span className="cur"></span></div>
          </div>
        </div>
      </div>
      <footer>
        <span className="lbl">© 2026 INTISER AHMED · BUILT CALMLY</span>
        <span className="lbl">RUNTIME v2 · LAST SHIP: FOCUSBAR AI</span>
      </footer>
    </section>
  );
}

/* ───── Edge Compute / Inference Field ───── */
function InferenceField() {
  const ref = useRef(null);
  useEffect(() => {
    const canvas = ref.current; if (!canvas) return;
    const ctx = canvas.getContext("2d");
    let raf, t = 0, running = true;
    const reduce = matchMedia("(prefers-reduced-motion: reduce)").matches;
    let rect = { width: 300, height: 240 };
    function resize() {
      rect = canvas.getBoundingClientRect();
      const dpr = Math.min(window.devicePixelRatio || 1, 2);
      canvas.width = Math.max(1, rect.width * dpr);
      canvas.height = Math.max(1, rect.height * dpr);
      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
    }
    resize();
    window.addEventListener("resize", resize);
    const hexRgb = (h) => { h = (h || "#c8f24e").replace("#", "").trim(); if (h.length === 3) h = h.split("").map(c => c + c).join(""); const n = parseInt(h, 16); return [(n >> 16) & 255, (n >> 8) & 255, n & 255]; };
    const cell = 13, gap = 5, step = cell + gap;
    function draw() {
      const w = rect.width, ht = rect.height;
      ctx.clearRect(0, 0, w, ht);
      const sig = getComputedStyle(document.documentElement).getPropertyValue("--signal");
      const [cr, cg, cb] = hexRgb(sig);
      const cols = Math.ceil(w / step), rows = Math.ceil(ht / step);
      const band = (t * 0.10) % (cols + 16) - 4;
      for (let y = 0; y < rows; y++) {
        for (let x = 0; x < cols; x++) {
          const wave = Math.sin(x * 0.55 - t * 0.05) * Math.cos(y * 0.5 + t * 0.03);
          let a = 0.05 + Math.max(0, wave) * 0.11;
          const d = Math.abs(x - band);
          if (d < 3.2) a += (1 - d / 3.2) * 0.6;
          ctx.fillStyle = `rgba(${cr},${cg},${cb},${a.toFixed(3)})`;
          ctx.fillRect(x * step, y * step, cell, cell);
        }
      }
      if (!reduce) t += 1;
      if (running) raf = requestAnimationFrame(draw);
    }
    draw();
    return () => { running = false; cancelAnimationFrame(raf); window.removeEventListener("resize", resize); };
  }, []);
  return <canvas ref={ref} className="ifield-canvas"></canvas>;
}

function EdgeReadout() {
  const [tok, setTok] = useState(48);
  useEffect(() => {
    const reduce = matchMedia("(prefers-reduced-motion: reduce)").matches;
    if (reduce) return;
    const id = setInterval(() => setTok(44 + Math.floor(Math.random() * 9)), 900);
    return () => clearInterval(id);
  }, []);
  return (
    <div className="ifield-read">
      <div><div className="rk">Throughput</div><div className="rv">{tok}<small>tok/s</small></div></div>
      <div><div className="rk">Quant</div><div className="rv">Q4<small>K_M</small></div></div>
      <div><div className="rk">Footprint</div><div className="rv">1.1<small>GB</small></div></div>
      <div><div className="rk">Egress</div><div className="rv">0<small>bytes</small></div></div>
    </div>
  );
}

const EDGE_TABS = {
  mobile: {
    label: "Mobile",
    run: "CoreML · llama-3.2-3b · on-device",
    readout: { throughput: "48 tok/s", quant: "Q4_K_M", footprint: "1.1 GB", egress: "0 bytes" },
    backends: [
      { name: "Apple Neural Engine", sub: "16-core ANE · Core ML compute units", tag: "iOS · macOS", c: "var(--led-live)" },
      { name: "Metal / MPS",         sub: "Metal Performance Shaders, GPU kernels", tag: "Apple GPU",   c: "var(--led-live)" },
      { name: "Vulkan",              sub: "Cross-vendor compute, SPIR-V kernels",   tag: "Android",     c: "var(--led-prod)" },
      { name: "Core ML / ONNX",      sub: "Model conversion + runtime export",      tag: "Toolchain",   c: "var(--led-beta)" },
    ],
    tech: ["CoreML", "SwiftUI AI APIs", "Metal MPS", "GGUF Q4_K_M", "llama.cpp", "ONNX Runtime", "Vulkan compute", "INT4 quant"],
  },
  ai: {
    label: "AI Inference",
    run: "llama-3.2-3b · GGUF · speculative decode",
    readout: { throughput: "52 tok/s", quant: "Q4_K_M", footprint: "1.1 GB", egress: "0 bytes" },
    backends: [
      { name: "llama.cpp / GGUF",  sub: "Quantized inference, KV-cache, batching",   tag: "Portable",  c: "var(--led-live)" },
      { name: "Speculative Decode",sub: "Draft + verify for 1.6× speedup",           tag: "Optimisation", c: "var(--led-live)" },
      { name: "RAG Pipeline",      sub: "Embedded vector store, no cloud round-trip", tag: "Retrieval", c: "var(--led-beta)" },
      { name: "Fine-tuning / LoRA",sub: "Adapter layers, on-device personalisation", tag: "Training",   c: "var(--led-beta)" },
    ],
    tech: ["llama.cpp", "GGUF Q4_K_M", "KV-cache", "Speculative decoding", "RAG", "LoRA", "ONNX Runtime", "INT8 / INT4 quant"],
  },
  iot: {
    label: "IoT",
    run: "ESP32 · MicroPython · MQTT · FastAPI",
    readout: { throughput: "realtime", quant: "INT8", footprint: "320 KB", egress: "MQTT" },
    backends: [
      { name: "ESP32 / MicroPython", sub: "Firmware, ADC sampling, MQTT publish",   tag: "Firmware",  c: "var(--led-live)" },
      { name: "WebGPU / WGSL",       sub: "Compute shaders in the browser",         tag: "Web",       c: "var(--led-prod)" },
      { name: "FastAPI + SSE",        sub: "Streaming anomaly alerts to Flutter app", tag: "Backend",  c: "var(--led-prod)" },
      { name: "TFLite / ONNX",        sub: "Lightweight ML on constrained hardware", tag: "Inference", c: "var(--led-beta)" },
    ],
    tech: ["MicroPython", "MQTT", "ESP32", "FastAPI", "SSE", "TFLite", "WebGPU / WGSL", "SPIR-V", "Flutter"],
  },
};

/* ── ARC Memory Graph visualization ── */
function VizARC() {
  const [step, setStep] = useState(0);
  // Step durations (ms): blank → VC → VM+e12 → CL+e13 → e31 → hold → cycle! → hold → fix → hold → deinit → hold
  const DURS = [500, 700, 600, 600, 500, 1500, 500, 1800, 500, 1800, 600, 1600];
  useEffect(() => {
    const tid = setTimeout(() => setStep(s => (s+1) % DURS.length), DURS[step]);
    return () => clearTimeout(tid);
  }, [step]);

  // Derive display state from step number
  const showVC  = step >= 1;
  const showVM  = step >= 2;
  const showCL  = step >= 3;
  const showE12 = step >= 2; // VC → VM
  const showE13 = step >= 3; // VC → CL
  const showE31 = step >= 4; // CL → VC (the cycle edge)
  const isCycle = step >= 6 && step < 8;
  const isFix   = step >= 8;
  const isDeinit = step >= 10;

  const VC=[80,26], VM=[18,156], CL=[142,156];
  const ep = (a,b,r=21) => {
    const dx=b[0]-a[0],dy=b[1]-a[1],d=Math.hypot(dx,dy),nx=dx/d,ny=dy/d;
    return {x1:a[0]+nx*r,y1:a[1]+ny*r,x2:b[0]-nx*(r+5),y2:b[1]-ny*(r+5)};
  };
  const e12=ep(VC,VM), e13=ep(VC,CL), e31=ep(CL,VC);
  const cycleClr = isCycle?"#ff5f57":isFix?"var(--signal)":"var(--muted)";
  const msg = isDeinit?"✓ deinit — memory freed"
    :isFix?"[weak self] → cycle broken"
    :isCycle?"⚠ retain cycle detected"
    :showE31?"ARC object graph"
    :"building graph…";
  const msgC = (isDeinit||isFix)?"var(--signal)":isCycle?"#ff5f57":"var(--muted)";

  return (
    <svg width="160" height="194" viewBox="0 0 160 194" fontFamily="var(--mono)">
      <defs>
        {[["arcN","var(--muted)"],["arcR","#ff5f57"],["arcG","var(--signal)"]].map(([id,c])=>(
          <marker key={id} id={id} markerWidth="5" markerHeight="5" refX="4" refY="2.5" orient="auto">
            <path d="M0,0 L5,2.5 L0,5Z" fill={c}/>
          </marker>
        ))}
      </defs>
      {/* VC → VM */}
      <line {...e12} stroke="var(--muted)" strokeWidth="1" markerEnd="url(#arcN)"
        style={{opacity:showE12?1:0,transition:"opacity 0.4s"}}/>
      <text x={(e12.x1+e12.x2)/2-12} y={(e12.y1+e12.y2)/2-4}
        fill="var(--muted)" fontSize="5.5"
        style={{opacity:showE12?1:0,transition:"opacity 0.4s"}}>strong</text>
      {/* VC → CL */}
      <line {...e13} stroke="var(--muted)" strokeWidth="1" markerEnd="url(#arcN)"
        style={{opacity:showE13?1:0,transition:"opacity 0.4s"}}/>
      <text x={(e13.x1+e13.x2)/2+10} y={(e13.y1+e13.y2)/2-4}
        fill="var(--muted)" fontSize="5.5"
        style={{opacity:showE13?1:0,transition:"opacity 0.4s"}}>strong</text>
      {/* CL → VC (retain cycle / fix) */}
      <line {...e31} stroke={cycleClr} strokeWidth={isCycle?1.8:1}
        {...(isFix?{strokeDasharray:"4 3"}:{})}
        markerEnd={`url(#${isCycle?"arcR":isFix?"arcG":"arcN"})`}
        style={{opacity:showE31?1:0,transition:"opacity 0.4s,stroke 0.4s"}}/>
      <text x="116" y="88" textAnchor="middle" fill={cycleClr} fontSize="5.5"
        style={{opacity:showE31?1:0,transition:"fill 0.4s,opacity 0.4s"}}>
        {isFix?"[weak self]":"strong"}
      </text>
      {/* Nodes */}
      {[
        {pos:VC,lbl:"ViewController",show:showVC,faded:false},
        {pos:VM,lbl:"ViewModel",     show:showVM,faded:false},
        {pos:CL,lbl:"Closure",       show:showCL,faded:isDeinit},
      ].map(({pos,lbl,show,faded})=>{
        const isCL=lbl==="Closure";
        const ringClr=isCL&&isCycle?"#ff5f57":isCL&&isFix&&!isDeinit?"var(--signal)":"var(--line)";
        const words=lbl.match(/[A-Z][a-z]*/g)||[lbl];
        return (
          <g key={lbl} style={{opacity:show?(faded?0.12:1):0,transition:"opacity 0.5s"}}>
            <circle cx={pos[0]} cy={pos[1]} r="22" fill="var(--bg-2)"
              stroke={ringClr} strokeWidth="1.2" style={{transition:"stroke 0.4s"}}/>
            <text x={pos[0]} y={pos[1]-3} textAnchor="middle" fill="var(--ink-2)" fontSize="6.5">{words[0]}</text>
            <text x={pos[0]} y={pos[1]+7} textAnchor="middle" fill="var(--muted)" fontSize="5.5">{words.slice(1).join("")}</text>
          </g>
        );
      })}
      <text x="80" y="188" textAnchor="middle" fill={msgC} fontSize="7"
        style={{transition:"fill 0.4s"}}>{msg}</text>
    </svg>
  );
}

/* ── Swift Concurrency Tree visualization ── */
function VizConcurrency() {
  const [step, setStep] = useState(0);
  // Step durations: blank→boundary→root→a→b→TaskGroup→hold→running→hold→cancel→cancelled→hold→recover→hold
  const DURS = [400,700,700,600,600,600,1000,400,1400,500,700,1600,500,1200];
  useEffect(() => {
    const tid = setTimeout(() => setStep(s=>(s+1)%DURS.length), DURS[step]);
    return () => clearTimeout(tid);
  }, [step]);

  const showBound = step >= 1;
  const showRoot  = step >= 2;
  const showA     = step >= 3;
  const showB     = step >= 4;
  const showG     = step >= 5;
  const running   = step >= 7 && step < 9;
  const cancelling = step === 9;
  const cancelled  = step >= 10 && step < 12;
  const done       = step >= 12;

  const ROOT=[80,26], A=[24,100], B=[80,100], GRP=[136,100];
  const edg = (a,b,r=20) => {
    const dx=b[0]-a[0],dy=b[1]-a[1],d=Math.hypot(dx,dy),nx=dx/d,ny=dy/d;
    return {x1:a[0]+nx*r,y1:a[1]+ny*r,x2:b[0]-nx*(r+4),y2:b[1]-ny*(r+4)};
  };
  const eA=edg(ROOT,A), eB=edg(ROOT,B), eG=edg(ROOT,GRP);

  const nodeClr = (id) => {
    if (cancelled||cancelling) return id==="root"?"var(--signal)":"#ff5f57";
    if (running||done) return "var(--signal)";
    return "var(--muted)";
  };
  const edgeClr = (show) => {
    if (!show) return "transparent";
    if (cancelled||cancelling) return "#ff5f57";
    if (running||done) return "var(--signal)";
    return "var(--line)";
  };

  const msg = done?"✓ cancellation handled"
    :cancelled?"✗ all children cancelled"
    :cancelling?"cancel token sent…"
    :running?"structured concurrency"
    :showG?"task tree built"
    :"spawning tasks…";
  const msgC = done?"var(--signal)":cancelled||cancelling?"#ff5f57":"var(--muted)";

  const TASKS = [
    {id:"a",pos:A,lbl:"async let a",show:showA},
    {id:"b",pos:B,lbl:"async let b",show:showB},
    {id:"g",pos:GRP,lbl:"TaskGroup",show:showG},
  ];

  return (
    <svg width="160" height="148" viewBox="0 0 160 148" fontFamily="var(--mono)">
      {/* @MainActor boundary */}
      <line x1="4" y1="54" x2="156" y2="54"
        stroke="var(--muted)" strokeWidth="0.8" strokeDasharray="4 3"
        style={{opacity:showBound?0.5:0,transition:"opacity 0.5s"}}/>
      <text x="6" y="50" fill="var(--signal)" fontSize="5.5" letterSpacing="0.3"
        style={{opacity:showBound?0.9:0,transition:"opacity 0.5s"}}>@MainActor</text>
      <text x="6" y="64" fill="var(--muted)" fontSize="5" letterSpacing="0.3"
        style={{opacity:showBound?0.6:0,transition:"opacity 0.5s"}}>background executor</text>
      {/* Edges — appear with their target node, change color with state */}
      {[{e:eA,show:showA},{e:eB,show:showB},{e:eG,show:showG}].map(({e,show},i)=>(
        <line key={i} {...e} stroke={edgeClr(show)} strokeWidth="1"
          style={{opacity:show?1:0,transition:"opacity 0.4s,stroke 0.4s"}}/>
      ))}
      {/* Root node */}
      <g style={{opacity:showRoot?1:0,transition:"opacity 0.5s"}}>
        <rect x={ROOT[0]-27} y={ROOT[1]-13} width="54" height="26" rx="6"
          fill="var(--bg-2)" stroke={nodeClr("root")} strokeWidth="1.3"
          style={{transition:"stroke 0.4s"}}/>
        <text x={ROOT[0]} y={ROOT[1]+2} textAnchor="middle" dominantBaseline="middle"
          fill={nodeClr("root")} fontSize="6.5" fontWeight="600"
          style={{transition:"fill 0.4s"}}>Task { }</text>
      </g>
      {/* Child nodes */}
      {TASKS.map(({id,pos,lbl,show})=>(
        <g key={id} style={{opacity:show?1:0,transition:"opacity 0.5s"}}>
          <rect x={pos[0]-27} y={pos[1]-13} width="54" height="26" rx="5"
            fill="var(--bg-2)" stroke={nodeClr(id)} strokeWidth="1.1"
            style={{transition:"stroke 0.4s"}}/>
          <text x={pos[0]} y={pos[1]+2} textAnchor="middle" dominantBaseline="middle"
            fill={nodeClr(id)} fontSize="5.5" style={{transition:"fill 0.4s"}}>{lbl}</text>
        </g>
      ))}
      <text x="80" y="142" textAnchor="middle" fill={msgC} fontSize="7"
        style={{transition:"fill 0.4s"}}>{msg}</text>
    </svg>
  );
}

/* ── RunLoop Cycle visualization ── */
function VizRunLoop() {
  const dotRef  = useRef(null);
  const glowRef = useRef(null);
  const lblRefs = useRef([]);

  const R=48, CX=80, CY=80;
  const PHASES = ["Input Sources","Timers","Observers","CADisplayLink","Render"];
  const phaseAngles = PHASES.map((_,i) => -Math.PI/2 + i*(Math.PI*2/5));

  useEffect(() => {
    let angle = -Math.PI/2, raf, running = true;
    const speed = 0.014;
    function tick() {
      if (!running) return;
      angle += speed;
      const x = CX + R*Math.cos(angle);
      const y = CY + R*Math.sin(angle);
      if (dotRef.current)  { dotRef.current.setAttribute('cx',x);  dotRef.current.setAttribute('cy',y); }
      if (glowRef.current) { glowRef.current.setAttribute('cx',x); glowRef.current.setAttribute('cy',y); }
      // Light up active phase label
      const norm = ((angle+Math.PI/2)%(Math.PI*2)+Math.PI*2)%(Math.PI*2);
      const active = Math.floor(norm/(Math.PI*2/5)) % 5;
      lblRefs.current.forEach((el,i) => {
        if (!el) return;
        el.style.fill = i===active ? 'var(--signal)' : 'var(--muted)';
        el.style.fontWeight = i===active ? '600' : '400';
      });
      raf = requestAnimationFrame(tick);
    }
    raf = requestAnimationFrame(tick);
    return () => { running=false; cancelAnimationFrame(raf); };
  }, []);

  return (
    <svg width="160" height="168" viewBox="0 0 160 168" fontFamily="var(--mono)">
      {/* Orbit ring */}
      <circle cx={CX} cy={CY} r={R} fill="none" stroke="var(--line)" strokeWidth="1.2" strokeDasharray="3 3"/>
      {/* Phase stop-dots + labels */}
      {PHASES.map((p,i) => {
        const a=phaseAngles[i];
        const px=CX+R*Math.cos(a), py=CY+R*Math.sin(a);
        const lx=CX+(R+20)*Math.cos(a), ly=CY+(R+20)*Math.sin(a);
        return (
          <g key={p}>
            <circle cx={px} cy={py} r="2.5" fill="var(--muted)" opacity="0.4"/>
            <text ref={el=>lblRefs.current[i]=el}
              x={lx} y={ly} textAnchor="middle" dominantBaseline="middle"
              fill="var(--muted)" fontSize="5.5">{p}</text>
          </g>
        );
      })}
      {/* Center label */}
      <text x={CX} y={CY-7} textAnchor="middle" fill="var(--ink-2)" fontSize="10" fontWeight="700" letterSpacing="-0.5">120fps</text>
      <text x={CX} y={CY+7} textAnchor="middle" fill="var(--muted)" fontSize="6.5">8.3ms budget</text>
      {/* Orbiting dot (glow + core) — updated via ref, no re-render */}
      <circle ref={glowRef} cx={CX} cy={CY-R} r="9" fill="var(--signal)" opacity="0.15"/>
      <circle ref={dotRef}  cx={CX} cy={CY-R} r="4.5" fill="var(--signal)"/>
      <text x={CX} y="162" textAnchor="middle" fill="var(--muted)" fontSize="7">iOS RunLoop · main thread</text>
    </svg>
  );
}

/* ── MVVM Architecture visualization ── */
function VizMVVM() {
  const [step, setStep] = useState(0);
  // blank→Model→ViewModel+edges12→View+edges23→hold→user-action→VM-process→fetch→data→@Published→re-render→hold
  const DURS = [400,600,650,650,900,400,500,500,500,500,500,1300];
  useEffect(() => {
    const tid = setTimeout(() => setStep(s=>(s+1)%DURS.length), DURS[step]);
    return () => clearTimeout(tid);
  }, [step]);

  const showModel  = step>=1;
  const showVM     = step>=2;
  const showEdge12 = step>=2;
  const showView   = step>=3;
  const showEdge23 = step>=3;
  const fs = step-5; // flow step 0-5 after hold

  const nodeClr = (id) => {
    if (step<5) return "var(--muted)";
    if (id==="view"  && (fs===0||fs===5)) return "var(--signal)";
    if (id==="vm"    && (fs===1||fs===4)) return "var(--signal)";
    if (id==="model" && (fs===2||fs===3)) return "var(--signal)";
    return "var(--muted)";
  };
  const isActive = (name) => {
    if (step<5) return false;
    return (name==="v2vm"&&fs===0)||(name==="vm2m"&&fs===2)||
           (name==="m2vm"&&fs===3)||(name==="vm2v"&&fs===4);
  };

  const MY=22, VMY=76, VY=130;
  const BH=13; // box half-height
  const g1y1=MY+BH+3, g1y2=VMY-BH-3; // gap between Model and VM
  const g2y1=VMY+BH+3, g2y2=VY-BH-3; // gap between VM and View
  const xR=84, xL=76; // right and left arrow columns

  // Inline arrow renderer
  const Arr = ({x,y1,y2,up,name,label,side}) => {
    const c=isActive(name)?"var(--signal)":"var(--line)";
    const tipY=up?y1:y2, tailY=up?y2:y1, off=up?7:-7;
    const pts=`${x},${tipY} ${x-3},${tipY+off} ${x+3},${tipY+off}`;
    const mid=(y1+y2)/2, lx=side==="r"?x+4:x-4;
    return (
      <g style={{transition:"fill 0.35s,stroke 0.35s"}}>
        <line x1={x} y1={tailY} x2={x} y2={tipY+off} stroke={c} strokeWidth="0.9"/>
        <polygon points={pts} style={{fill:c,transition:"fill 0.35s"}}/>
        <text x={lx} y={mid+2} textAnchor={side==="r"?"start":"end"}
          fill={c} fontSize="5" style={{transition:"fill 0.35s"}}>{label}</text>
      </g>
    );
  };

  const Box = ({cy,lbl,sub,show,id,hw=38}) => (
    <g style={{opacity:show?1:0,transition:"opacity 0.5s"}}>
      <rect x={80-hw} y={cy-BH} width={hw*2} height={BH*2} rx="5"
        fill="var(--bg-2)" stroke={nodeClr(id)} strokeWidth="1.2" style={{transition:"stroke 0.35s"}}/>
      <text x="80" y={cy-2} textAnchor="middle" fill={nodeClr(id)}
        fontSize="7" fontWeight="600" style={{transition:"fill 0.35s"}}>{lbl}</text>
      <text x="80" y={cy+7} textAnchor="middle" fill="var(--muted)" fontSize="4.8">{sub}</text>
    </g>
  );

  const msg = step<4?"MVVM architecture"
    :fs===0?"user action → ViewModel"
    :fs===1?"ViewModel processes intent"
    :fs===2?"fetch() → Model"
    :fs===3?"data ← Model"
    :fs===4?"@Published → View"
    :fs===5?"View re-renders ✓"
    :"MVVM data flow";
  const msgC = (fs>=5&&step>=5)?"var(--signal)":step>=5?"var(--signal)":"var(--muted)";

  return (
    <svg width="160" height="166" viewBox="0 0 160 166" fontFamily="var(--mono)">
      <Box cy={MY}  lbl="Model"     sub="Codable · Repository"          show={showModel} id="model" hw={38}/>
      <Box cy={VMY} lbl="ViewModel" sub="@Published · ObservableObject"  show={showVM}   id="vm"    hw={46}/>
      <Box cy={VY}  lbl="View"      sub="SwiftUI · Bindings"             show={showView} id="view"  hw={38}/>
      <g style={{opacity:showEdge12?1:0,transition:"opacity 0.5s"}}>
        <Arr x={xR} y1={g1y1} y2={g1y2} up={false} name="m2vm" label="data"    side="r"/>
        <Arr x={xL} y1={g1y1} y2={g1y2} up={true}  name="vm2m" label="fetch()" side="l"/>
      </g>
      <g style={{opacity:showEdge23?1:0,transition:"opacity 0.5s"}}>
        <Arr x={xR} y1={g2y1} y2={g2y2} up={false} name="vm2v" label="@Published" side="r"/>
        <Arr x={xL} y1={g2y1} y2={g2y2} up={true}  name="v2vm" label="intent"     side="l"/>
      </g>
      <text x="80" y="160" textAnchor="middle" fill={msgC} fontSize="7"
        style={{transition:"fill 0.35s"}}>{msg}</text>
    </svg>
  );
}

const IPH_CONCEPTS = [
  { id:"arc",  tag:"ARC · Memory",         title:"Retain Cycle → Fix",    Viz: VizARC },
  { id:"conc", tag:"Swift Concurrency",     title:"Structured Task Tree",  Viz: VizConcurrency },
  { id:"rl",   tag:"RunLoop · Main Thread", title:"120fps Render Cycle",   Viz: VizRunLoop },
  { id:"mvvm", tag:"Architecture",          title:"MVVM Data Flow",        Viz: VizMVVM },
];

function IPhoneMock() {
  const [idx, setIdx] = useState(0);
  useEffect(() => {
    const id = setInterval(() => setIdx(i => (i+1) % IPH_CONCEPTS.length), 12000);
    return () => clearInterval(id);
  }, []);
  const c = IPH_CONCEPTS[idx];
  return (
    <div className="iph-wrap">
      <div className="iph-frame">
        <div className="iph-mono">
          <div className="iph-di"></div>
          <div className="iph-bar">
            <span className="iph-time">9:41</span>
            <div className="iph-bar-r">
              <svg width="15" height="11" viewBox="0 0 17 12" fill="currentColor" opacity="0.6"><rect x="0" y="4" width="3" height="8" rx="0.5" opacity="0.4"/><rect x="4.5" y="2.5" width="3" height="9.5" rx="0.5" opacity="0.6"/><rect x="9" y="0.5" width="3" height="11.5" rx="0.5"/><rect x="13.5" y="0" width="3" height="12" rx="0.5"/></svg>
              <span className="led pulse" style={{"--c":"var(--signal)"}}></span>
            </div>
          </div>
          {/* Concept header */}
          <div className="iph-chead">
            <div className="iph-ctag">{c.tag}</div>
            <div className="iph-ctitle">{c.title}</div>
          </div>
          {/* Visualization */}
          <div className="iph-viz">
            <c.Viz />
          </div>
          {/* Dots nav */}
          <div className="iph-dots">
            {IPH_CONCEPTS.map((_,i) => (
              <button key={i} className={"iph-dot"+(i===idx?" on":"")} onClick={()=>setIdx(i)} />
            ))}
          </div>
          <div className="iph-home"></div>
        </div>
      </div>
    </div>
  );
}

function EdgeCompute() {
  const [tab, setTab] = useState("mobile");
  const T = EDGE_TABS[tab];
  return (
    <section className="wrap section" id="edge">
      <div className="sec-head" data-reveal>
        <div>
          <div className="sec-label">Edge compute</div>
          <h2 className="sec-title">Models that run <em>where the data lives.</em></h2>
        </div>
        <p className="sec-note">Same engine philosophy — quantized, GPU-accelerated, private — deployed across mobile, pure AI inference, and embedded IoT.</p>
      </div>

      <div className="edge-tabs" data-reveal>
        {Object.entries(EDGE_TABS).map(([key, t]) => (
          <button key={key} className="edge-tab" data-on={tab === key} onClick={() => setTab(key)}>
            {t.label}
          </button>
        ))}
      </div>

      <div className="edge" data-reveal>
        {tab === "mobile" ? (
          <IPhoneMock />
        ) : (
          <div className="ifield">
            <div className="ifield-head">
              <span className="t"><span className="led pulse" style={{"--c":"var(--signal)"}}></span>INFERENCE&nbsp;FIELD</span>
              <span className="run">● {T.run}</span>
            </div>
            <InferenceField />
            <div className="ifield-read">
              <div><div className="rk">Throughput</div><div className="rv">{T.readout.throughput}</div></div>
              <div><div className="rk">Quant</div><div className="rv">{T.readout.quant}</div></div>
              <div><div className="rk">Footprint</div><div className="rv">{T.readout.footprint}</div></div>
              <div><div className="rk">Egress</div><div className="rv">{T.readout.egress}</div></div>
            </div>
          </div>
        )}

        <div className="backends">
          {T.backends.map(b => (
            <div className="bk" key={b.name}>
              <div className="bk-top">
                <span className="led" style={{"--c": b.c}}></span>
                <span className="bk-tag">{b.tag}</span>
              </div>
              <div className="bk-name">{b.name}</div>
              <div className="bk-sub">{b.sub}</div>
            </div>
          ))}
        </div>
      </div>

      <div className="edge-depth" data-reveal>
        <div className="gk">Toolchain · {T.label}</div>
        <div className="gv">{T.tech.map(t => <span key={t} className="stk">{t}</span>)}</div>
      </div>
    </section>
  );
}

/* ── Local model chat FAB ── */
function ChatFAB() {
  const [open, setOpen]   = useState(false);
  const [input, setInput] = useState("");
  const [busy, setBusy]   = useState(false);
  const [msgs, setMsgs]   = useState([]);
  const [llm, setLlm]     = useState({ status: "idle", progress: 0 });
  const bottomRef = useRef(null);

  // Sync with window.__wllama status
  useEffect(() => {
    const w = window.__wllama;
    if (!w) return;
    setLlm({ status: w.status, progress: w.progress });
    const unsub = w.subscribe(s => setLlm({ ...s }));
    return unsub;
  }, []);

  useEffect(() => {
    bottomRef.current?.scrollIntoView({ behavior: "smooth" });
  }, [msgs]);

  const buildSystemPrompt = (userText) => {
    const P = window.PROJECTS || [];
    const low = userText.toLowerCase();

    // Detect which projects are relevant
    const projectKeys = { gazey:"gazey", pallinhealth:"pallin", focusbar:"focus", continental:"continental", voiceping:"voice|walkie", realtor:"realtor|real.state", piperportal:"piper", tips:"tips", meetingcrm:"meeting|crm" };
    const relevant = P.filter(p => { const rx = projectKeys[p.id]; return rx && new RegExp(rx).test(low); });
    const useProjects = relevant.length > 0 ? relevant : P;

    const projectSummary = useProjects.map(p =>
      `[${p.name}] ${p.sub}. ${p.desc} Impact: ${p.impact}`
    ).join("\n\n");

    const base = `You are an AI assistant on Intiser Ahmed's portfolio. Answer in 2-3 sentences, stay on-topic.\n\nIntiser Ahmed — senior mobile + AI engineer, 4 years, 13 shipped apps. Available for senior iOS/AI roles globally. Contact: intiser4@gmail.com\n\nProjects:\n${projectSummary}`;
    return base;
  };

  const send = async (text) => {
    text = text.trim();
    if (!text || busy || llm.status !== "ready") return;
    setInput("");
    const history = [...msgs, { role: "user", text }];
    setMsgs([...history, { role: "ai", text: "" }]);
    setBusy(true);
    try {
      const oai = history.map(m => ({ role: m.role === "ai" ? "assistant" : "user", content: m.text }));
      const sysPrompt = buildSystemPrompt(text);
      await window.__wllama.chat(oai, (token) => {
        setMsgs(m => { const n=[...m]; n[n.length-1]={ role:"ai", text: n[n.length-1].text + token }; return n; });
      }, sysPrompt);
      // If stream ended with nothing visible (all think tokens filtered), show fallback
      setMsgs(m => {
        const last = m[m.length-1];
        if (last?.role === "ai" && !last.text.trim()) {
          const n=[...m]; n[n.length-1]={ role:"ai", text:"I couldn't generate a response — try again." }; return n;
        }
        return m;
      });
    } catch(e) {
      setMsgs(m => { const n=[...m]; n[n.length-1]={ role:"ai", text:"Something went wrong — try again." }; return n; });
    } finally {
      setBusy(false);
    }
  };

  const CHIPS = ["What's Intiser's iOS depth?", "Tell me about on-device AI", "Is he available for hire?"];
  const { status, progress } = llm;
  const isReady = status === "ready";

  return (
    <>
      {open && (
        <div className="chat-panel">
          {/* Header */}
          <div className="chat-hd">
            <div className="chat-hd-l">
              <span className={"led" + (isReady ? " pulse" : "")} style={{"--c": isReady ? "var(--signal)" : "var(--muted)"}}></span>
              <span className="chat-nm">Ask Intiser</span>
              <span className="chat-sub">
                {status === "idle"        && "Qwen3-0.6B · not loaded"}
                {status === "downloading" && `Downloading ${progress}%`}
                {status === "loading"     && "Initializing…"}
                {status === "ready"       && "Qwen3-0.6B · on-device"}
                {status === "error"       && "Load failed"}
              </span>
            </div>
            <button className="chat-x" onClick={() => setOpen(false)}>
              <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5"><path d="M18 6L6 18M6 6l12 12"/></svg>
            </button>
          </div>

          {/* Pre-load prompt */}
          {(status === "idle" || status === "error") && (
            <div className="chat-boot">
              <p className="chat-boot-msg">
                {status === "error" ? "Failed to load model." : "Real LLM. Runs entirely in your browser."}
              </p>
              <p className="chat-boot-sub">One-time ~400MB download, cached after that.</p>
              <button className="btn btn-primary chat-boot-btn" onClick={() => window.__wllama?.load()}>
                {status === "error" ? "Retry" : "Load model"} {I.dl(13)}
              </button>
            </div>
          )}

          {/* Download / init progress */}
          {(status === "downloading" || status === "loading") && (
            <div className="chat-boot">
              <p className="chat-boot-msg">{status === "downloading" ? "Downloading model…" : "Initializing WASM runtime…"}</p>
              <div className="chat-prog">
                <div className="chat-prog-fill" style={{
                  width: status === "loading" ? "100%" : progress + "%",
                  animation: status === "loading" ? "chatProgPulse 1.2s ease-in-out infinite" : "none"
                }}></div>
              </div>
              {status === "downloading" && <p className="chat-boot-sub">{progress}% — cached after this</p>}
            </div>
          )}

          {/* Chat UI */}
          {isReady && (
            <>
              <div className="chat-msgs">
                {msgs.length === 0 && (
                  <div className="chat-msg ai">Hi — I'm Qwen3-0.6B running entirely on your device. Nothing leaves here. Ask me anything about Intiser's work.</div>
                )}
                {msgs.map((m, i) => (
                  <div key={i} className={"chat-msg " + m.role}>
                    {m.text}
                    {m.role === "ai" && i === msgs.length-1 && busy && <span className="chat-cur">▌</span>}
                  </div>
                ))}
                <div ref={bottomRef}/>
              </div>
              {msgs.length === 0 && (
                <div className="chat-chips">
                  {CHIPS.map(c => <button key={c} className="chat-chip" onClick={() => send(c)}>{c}</button>)}
                </div>
              )}
              <div className="chat-input-row">
                <input className="chat-input" value={input}
                  onChange={e => setInput(e.target.value)}
                  onKeyDown={e => e.key === "Enter" && send(input)}
                  placeholder="Ask about Intiser's work…"
                  disabled={busy} autoFocus/>
                <button className="chat-send" onClick={() => send(input)} disabled={busy || !input.trim()}>
                  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M22 2L11 13M22 2L15 22l-4-9-9-4 20-7z"/></svg>
                </button>
              </div>
            </>
          )}

          <div className="chat-egress">⊗ 0 bytes egress · model runs locally</div>
        </div>
      )}
      <button className="chat-fab" onClick={() => setOpen(o => !o)} title="Chat with Ask Intiser">
        {open
          ? <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5"><path d="M18 6L6 18M6 6l12 12"/></svg>
          : <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M21 15a2 2 0 01-2 2H7l-4 4V5a2 2 0 012-2h14a2 2 0 012 2z"/></svg>
        }
      </button>
    </>
  );
}

function App() {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const [view, setView] = useState(t.view);
  useEffect(() => { setView(t.view); }, [t.view]);
  useEffect(() => {
    document.documentElement.setAttribute("data-mode", t.mode);
    document.documentElement.style.setProperty("--signal", t.signal);
    document.documentElement.style.setProperty("--row-pad", t.density === "compact" ? "15px" : "24px");
  }, [t.mode, t.signal, t.density]);
  useReveal();

  return (
    <div className="shell">
      <StatusBar />
      <main>
        <Hero />
        <EdgeCompute />
        <Featured />
        <Work view={view} setView={setView} />
        <Timeline />
        <Subs />
        <Refs />
        <Contact />
      </main>

      <ChatFAB />
      <TweaksPanel>
        <TweakSection label="Surface" />
        <TweakRadio label="Mode" value={t.mode} options={["console", "paper"]} onChange={(v)=>setTweak("mode", v)} />
        <TweakColor label="Signal color" value={t.signal} options={SIGNALS} onChange={(v)=>setTweak("signal", v)} />
        <TweakSection label="Unit list" />
        <TweakRadio label="Density" value={t.density} options={["compact", "comfortable"]} onChange={(v)=>setTweak("density", v)} />
        <TweakRadio label="Default view" value={t.view} options={["list", "gallery"]} onChange={(v)=>setTweak("view", v)} />
      </TweaksPanel>
    </div>
  );
}

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