// ============================================================
// Paper Trail v3 — scroll-driven single scene
// ============================================================
// One sticky stage. Progress (0 → 1) derived from scroll within
// a tall track. All elements interpolate continuously.
//
// Checkpoints along progress axis:
//   0.00  chaos        — 9 tool cards scattered
//   0.14  centralize   — cards converge into Control (retro terminal)
//   0.32  tether       — user tethers a mission to Control
//   0.45  plan         — CoS branches from Control; mission brief builds
//   0.63  work         — 3 specialist branches + tool leaves; scans happen
//   0.83  reassembly   — outputs flow up the tree
//   0.95  delivered    — final artifact at the root
// ============================================================

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

// ---- math helpers ----------------------------------------------
const clamp = (v, a = 0, b = 1) => Math.max(a, Math.min(b, v));
const lerp = (a, b, t) => a + (b - a) * t;
const ease = (t) => t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2; // in-out quad
// progress sub-range: 0 outside [a,b], eased 0→1 inside
const range = (p, a, b) => ease(clamp((p - a) / (b - a)));

// ---- data ------------------------------------------------------

// 3 tools per agent (rebalanced for equal tool chains)
// laneIdx = order within the agent's vertical chain (0 = top leaf, 2 = bottom)
const SCATTER = [
  // Agent 0 · Trail (CMO · narrative)
  { id: "s1", tool: "slack",   label: "Dana · \"need Acme proposal Thu\"",   x: 8,  y: 14, agent: 0, laneIdx: 0 },
  { id: "s2", tool: "gmail",   label: "Ravi @ Acme · 22 msgs",                x: 78, y: 10, agent: 0, laneIdx: 1 },
  { id: "s3", tool: "zoom",    label: "Discovery call · 48min transcript",    x: 18, y: 66, agent: 0, laneIdx: 2 },
  // Agent 1 · Ledger (CRO · pricing)
  { id: "s4", tool: "hubspot", label: "Deal: Acme · $240k ARR",               x: 70, y: 58, agent: 1, laneIdx: 0 },
  { id: "s5", tool: "drive",   label: "pricing_q4_2025.xlsx",                 x: 6,  y: 38, agent: 1, laneIdx: 1 },
  { id: "s6", tool: "notion",  label: "Brand voice · v4",                     x: 84, y: 36, agent: 1, laneIdx: 2 },
  // Agent 2 · Chart (Insights · rollout)
  { id: "s7", tool: "linear",  label: "ENG-482 · rollout blockers",           x: 44, y: 6,  agent: 2, laneIdx: 0 },
  { id: "s8", tool: "granola", label: "Follow-up notes · 3 meetings",         x: 56, y: 78, agent: 2, laneIdx: 1 },
  { id: "s9", tool: "jira",    label: "PROC-19 · MSA review",                 x: 32, y: 86, agent: 2, laneIdx: 2 },
];

const SPECIALISTS = [
  { id: "cmo",       name: "Trail",  role: "CMO",      task: "Exec summary", color: "#ffc880", llm: "Claude 4.5", lane: 0, output: "narrative_v1.md" },
  { id: "cro",       name: "Ledger", role: "CRO",      task: "Pricing",      color: "#7dd3c0", llm: "GPT-5",      lane: 1, output: "pricing_tiers.md" },
  { id: "insights",  name: "Chart",  role: "Insights", task: "Rollout",      color: "#c9a0dc", llm: "Gemini 2.5", lane: 2, output: "rollout_plan.md" },
];

// ---- tool icons -------------------------------------------------

function ToolMark({ tool, size = 18, color = "#ffc880" }) {
  const common = { width: size, height: size, viewBox: "0 0 24 24", fill: color };
  switch (tool) {
    case "slack":   return <svg {...common}><path d="M5 15.2a2.5 2.5 0 1 1-2.5-2.5H5zM6.3 15.2a2.5 2.5 0 1 1 5 0v6.3a2.5 2.5 0 1 1-5 0zM8.8 5a2.5 2.5 0 1 1-2.5-2.5V5zM8.8 6.3a2.5 2.5 0 1 1 0 5H2.5a2.5 2.5 0 1 1 0-5zM19 8.8A2.5 2.5 0 1 1 21.5 6.3V8.8zM17.7 8.8a2.5 2.5 0 1 1-5 0V2.5a2.5 2.5 0 1 1 5 0zM15.2 19a2.5 2.5 0 1 1 2.5 2.5V19zM15.2 17.7a2.5 2.5 0 1 1 0-5h6.3a2.5 2.5 0 1 1 0 5z"/></svg>;
    case "gmail":   return <svg {...common} fill="none" stroke={color} strokeWidth="1.5"><path d="M2 6v13h4V11l6 4.5L18 11v8h4V6l-10 7.2z" fill={color}/></svg>;
    case "zoom":    return <svg {...common} fill="none"><rect x="1" y="6" width="15" height="12" rx="2" fill={color}/><path d="M17 9l6-3v12l-6-3z" fill={color}/></svg>;
    case "hubspot": return <svg {...common}><circle cx="18" cy="12" r="3" fill="none" stroke={color} strokeWidth="1.5"/><circle cx="6" cy="18" r="2.5" fill={color}/><circle cx="6" cy="6" r="2" fill={color}/><path stroke={color} strokeWidth="1.5" d="M8 6l7 6M8 18l7-6M18 9V3"/></svg>;
    case "notion":  return <svg {...common} fill="none" stroke={color} strokeWidth="1.5"><rect x="4" y="3" width="16" height="18" rx="1"/><path d="M8 8l8 11V6"/></svg>;
    case "drive":   return <svg {...common}><path d="M7 3h10l5 9-3 6-5-9h-7l5 9H2z"/></svg>;
    case "linear":  return <svg {...common} fill="none" stroke={color} strokeWidth="1.5"><path d="M4 12a8 8 0 0 1 8-8M4 16a12 12 0 0 1 4-4M4 20a16 16 0 0 1 8-8M8 20a12 12 0 0 1 4-4M12 20a8 8 0 0 1 8-8"/></svg>;
    case "granola": return <svg {...common} fill="none" stroke={color} strokeWidth="1.5"><circle cx="12" cy="12" r="8"/><path d="M8 11h8M8 15h5"/></svg>;
    case "jira":    return <svg {...common}><path d="M11.5 11.5H0a5 5 0 0 0 5 5h2v2a5 5 0 0 0 5 5V12.5a1 1 0 0 0-1-1zM17 5.7H6a5 5 0 0 0 5 5h2v2a5 5 0 0 0 5 5V6.7a1 1 0 0 0-1-1zM22.5 0h-11a5 5 0 0 0 5 5h2v2a5 5 0 0 0 5 5V1a1 1 0 0 0-1-1z"/></svg>;
    default:        return <svg {...common}><rect x="4" y="4" width="16" height="16" rx="2"/></svg>;
  }
}

// ---- hook: scroll progress for a track -------------------------

function useScrollProgress(ref) {
  const [p, setP] = useState(0);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    let raf = 0;
    const update = () => {
      raf = 0;
      const rect = el.getBoundingClientRect();
      const vh = window.innerHeight;
      const total = rect.height - vh;
      const scrolled = -rect.top;
      setP(clamp(scrolled / total));
    };
    const onScroll = () => { if (!raf) raf = requestAnimationFrame(update); };
    update();
    window.addEventListener("scroll", onScroll, { passive: true });
    window.addEventListener("resize", onScroll);
    return () => {
      window.removeEventListener("scroll", onScroll);
      window.removeEventListener("resize", onScroll);
      if (raf) cancelAnimationFrame(raf);
    };
  }, [ref]);
  return p;
}

// ---- beats config ---------------------------------------------

const BEATS = [
  { at: 0.02, k: "01", t: "Today",      label: "Scattered chaos"          },
  { at: 0.10, k: "02", t: "Control",    label: "Signals centralized"      },
  { at: 0.22, k: "03", t: "Tether",     label: "You \u2192 Control"       },
  { at: 0.36, k: "04", t: "Dispatch",   label: "Control \u2192 Brief"     },
  { at: 0.46, k: "05", t: "Plan",       label: "Brief builds"             },
  { at: 0.66, k: "06", t: "Fan-out",    label: "Tree fans out"            },
  { at: 0.74, k: "07", t: "Work",       label: "Tools scanning"           },
  { at: 0.92, k: "08", t: "Shipped",    label: "Delivered"                },
];

function findBeat(p) {
  let cur = BEATS[0];
  for (const b of BEATS) if (p >= b.at) cur = b;
  return cur;
}

// ============================================================
// Main component
// ============================================================

// Embedded = true when mounted inside the landing page (no internal chrome; landing supplies its own TopNav + SectionTag).
// Embedded = false (default) when served as the standalone Paper Trail v3 page.
function PaperTrailV3({ embedded = false, sectionNum = "CTRL·05" } = {}) {
  const trackRef = useRef(null);
  const p = useScrollProgress(trackRef);

  // sub-progresses — tether + work both stretched; scene is 900vh (see track height)
  const pCentralize = range(p, 0.04, 0.14);
  const pControl    = range(p, 0.08, 0.22);
  const pTether     = range(p, 0.20, 0.36);
  const pDispatch   = range(p, 0.34, 0.46);
  const pMission    = range(p, 0.44, 0.66);
  const pBriefShift = range(p, 0.64, 0.72);
  const pAgents     = range(p, 0.70, 0.78);
  const pWork       = range(p, 0.74, 0.92);
  const pMerge      = range(p, 0.90, 0.96);
  const pDeliver    = range(p, 0.94, 1.00);

  const beat = findBeat(p);

  // Chrome heights — standalone fixed bar is 180px; embedded depends on viewport width
  // (landing TopNav + sticky intro bar).  Tailwind responsive classes below handle mobile vs desktop.

  const stage = (
    <section ref={trackRef} style={{ height: "900vh" }} className="relative">
      {embedded ? (
        // Embedded: responsive offset via Tailwind — mobile has taller stacked intro bar
        <div className="sticky overflow-hidden top-[168px] md:top-[176px] h-[calc(100vh-168px)] md:h-[calc(100vh-176px)]">
          <Stage
            p={p}
            pCentralize={pCentralize}
            pControl={pControl}
            pTether={pTether}
            pDispatch={pDispatch}
            pMission={pMission}
            pBriefShift={pBriefShift}
            pAgents={pAgents}
            pWork={pWork}
            pMerge={pMerge}
            pDeliver={pDeliver}
            beat={beat}
          />
        </div>
      ) : (
        // Standalone: fixed 180px offset
        <div className="sticky overflow-hidden" style={{ top: 180, height: "calc(100vh - 180px)" }}>
          <Stage
            p={p}
            pCentralize={pCentralize}
            pControl={pControl}
            pTether={pTether}
            pDispatch={pDispatch}
            pMission={pMission}
            pBriefShift={pBriefShift}
            pAgents={pAgents}
            pWork={pWork}
            pMerge={pMerge}
            pDeliver={pDeliver}
            beat={beat}
          />
        </div>
      )}
    </section>
  );

  // === EMBEDDED — renders inside the landing page ===
  if (embedded) {
    return (
      <>
        {/* Sticky section intro — sits just below landing's TopNav; stays in frame while track scrolls */}
        <div className="sticky z-40 bg-surface/95 backdrop-blur-md border-y border-outline-variant/15" style={{ top: 64 }}>
          <div className="max-w-screen-2xl mx-auto px-4 sm:px-6 md:px-8 py-3 md:py-4">
            <div className="grid lg:grid-cols-12 gap-4 lg:gap-6 items-end">
              <div className="lg:col-span-5">
                <div className="font-label text-[10px] text-primary tracking-[0.3em] uppercase mb-1.5 md:mb-2">◉ {sectionNum} · Paper Trail</div>
                <h2 className="font-headline text-lg sm:text-xl md:text-3xl font-bold tracking-[-0.02em] leading-[1.15] md:leading-[1.1]">
                  From scattered chaos to one finished <span className="text-primary italic">deliverable.</span>
                </h2>
              </div>
              <div className="lg:col-span-7 hidden md:block">
                <p className="text-on-surface-variant text-sm md:text-[15px] leading-relaxed">
                  Control pulls every signal from every tool into one surface. Your Chief of Staff builds the mission and delegates to specialist agents who read docs, ingest data, query your knowledge base, and call the right LLMs. One clean artifact comes back.
                </p>
              </div>
            </div>
          </div>
        </div>
        {stage}
      </>
    );
  }

  // === STANDALONE — served at /Paper Trail v3.html ===
  return (
    <div className="min-h-screen">
      {/* Fixed top bar — pinned through the whole scroll */}
      <div className="fixed top-0 left-0 right-0 z-50 bg-surface/95 backdrop-blur-md border-b border-outline-variant/20">
        <header className="h-12 flex items-center border-b border-outline-variant/10">
          <div className="max-w-screen-2xl mx-auto w-full px-8 flex items-baseline justify-between gap-6">
            <div className="flex items-baseline gap-4">
              <span className="font-label text-[10px] text-primary tracking-[0.3em]">◉ CTRL·08</span>
              <h1 className="font-headline text-lg font-bold">How Control gets work done</h1>
            </div>
          </div>
        </header>
        <div className="max-w-screen-2xl mx-auto w-full px-8 py-4">
          <div className="grid lg:grid-cols-12 gap-6 items-start">
            <div className="lg:col-span-5">
              <h2 className="font-headline text-2xl md:text-3xl font-bold tracking-[-0.02em] leading-[1.1]">
                From scattered chaos to one finished <span className="text-primary italic">deliverable.</span>
              </h2>
            </div>
            <div className="lg:col-span-7">
              <p className="text-on-surface-variant text-sm md:text-[15px] leading-relaxed">
                Control pulls every signal from every tool into one surface. Your Chief of Staff builds the mission and delegates to specialist agents who read docs, ingest data, query your knowledge base, and call the right LLMs. One clean artifact comes back.
              </p>
            </div>
          </div>
        </div>
      </div>

      <div className="h-[180px]" />
      {stage}
    </div>
  );
}

// ============================================================
// The stage — everything lives here, styled by scroll progress
// ============================================================

function Stage({ p, pCentralize, pControl, pTether, pDispatch, pMission, pBriefShift, pAgents, pWork, pMerge, pDeliver, beat }) {
  return (
    <div className="relative w-full h-full grid-bg">
      {/* Beat indicator (top-left) */}
      <div className="absolute top-6 left-6 z-40 flex items-center gap-3 bg-surface-container-lowest/80 backdrop-blur-md px-3 py-2 border border-outline-variant/30">
        <span className="font-label text-[9px] text-primary tracking-[0.3em]">◉</span>
        <span className="font-label text-[9px] text-outline tracking-[0.3em] uppercase">{beat.k} · {beat.t}</span>
        <span className="w-px h-3 bg-outline-variant/40" />
        <span className="font-headline text-sm font-bold">{beat.label}</span>
      </div>

      {/* Progress rail (top-right) */}
      <div className="absolute top-6 right-6 z-40 flex items-center gap-2 bg-surface-container-lowest/80 backdrop-blur-md px-3 py-2 border border-outline-variant/30">
        <span className="font-label text-[9px] text-outline tracking-widest">{String(Math.round(p * 100)).padStart(3, "0")}%</span>
        <div className="w-32 h-1 bg-outline-variant/20 relative">
          <div className="absolute inset-y-0 left-0 bg-primary" style={{ width: `${p * 100}%` }} />
          {BEATS.map((b, i) => (
            <div key={i} className="absolute top-0 bottom-0 w-px bg-outline-variant/50" style={{ left: `${b.at * 100}%` }} />
          ))}
        </div>
      </div>

      {/* === CONTROL HALO (soft glow behind the monitor) === */}
      <ControlRing p={pControl} pBriefShift={pBriefShift} />

      {/* === TREE CONNECTORS — dashed tree rooted at shifted Mission Brief === */}
      <TreeConnectors pBriefShift={pBriefShift} pAgents={pAgents} pWork={pWork} pMerge={pMerge} pDeliver={pDeliver} />

      {/* === INTER-TOOL DASHED FLOWS inside each lane === */}
      <ToolFlows pWork={pWork} pMerge={pMerge} />

      {/* === TOOL CARDS — 9 persistent elements that transform === */}
      {SCATTER.map((s) => (
        <ToolCard key={s.id} data={s} pCentralize={pCentralize} pAgents={pAgents} pWork={pWork} pMerge={pMerge} pDeliver={pDeliver} />
      ))}

      {/* === USER (left) === */}
      <UserNode pTether={pTether} />

      {/* === USER → CONTROL TETHER (dashed flow line with traveling message) === */}
      <TetherLine p={pTether} />

      {/* === CONTROL MONITOR (retro terminal, center) === */}
      <ControlMonitor pCentralize={pCentralize} pControl={pControl} pTether={pTether} pMission={pMission} pBriefShift={pBriefShift} pAgents={pAgents} pMerge={pMerge} pDeliver={pDeliver} />

      {/* === CONTROL → BRIEF LINK (dispatch + live composing) === */}
      <ControlToBriefLink pDispatch={pDispatch} pMission={pMission} pBriefShift={pBriefShift} />

      {/* === MISSION BRIEF — builds on right, then travels to top-middle as tree root === */}
      <MissionBrief pMission={pMission} pBriefShift={pBriefShift} pMerge={pMerge} pDeliver={pDeliver} />

      {/* === AGENT BRANCHES — specialists as tree leaves === */}
      <AgentLanes pAgents={pAgents} pWork={pWork} pMerge={pMerge} />

      {/* === BRIEF → OUTCOME LINK (dashed line between brief and final proposal) === */}
      <BriefToOutcomeLink pMerge={pMerge} pDeliver={pDeliver} />

      {/* === FINAL DOCUMENT — built from checked tools flying up === */}
      <FinalDoc pMerge={pMerge} pDeliver={pDeliver} />
    </div>
  );
}

// ============================================================
// CONTROL RING — the platform surface that lights up
// ============================================================

function ControlRing({ p, pBriefShift = 0 }) {
  // p: 0 invisible → 1 visible
  // Fade out as brief takes over as tree root
  const fade = 1 - pBriefShift;
  const o = p * 0.9 * fade;
  if (o < 0.02) return null;
  return (
    <div className="absolute inset-0 flex items-center justify-center pointer-events-none">
      <div style={{
        width: lerp(200, 440, p),
        height: lerp(200, 440, p),
        borderRadius: "50%",
        background: "radial-gradient(circle, rgba(255,200,128,0.18) 0%, rgba(255,200,128,0.06) 45%, transparent 70%)",
        opacity: o,
        transition: "opacity 0.15s",
      }} />
    </div>
  );
}

// ============================================================
// TOOL CARD — persistent element
// Positions over time:
//   p=0:   scattered at (s.x, s.y)
//   centralize 1: collapsed to ring near center (angle from index)
//   agents 1: moved to assigned agent's lane (top input slot)
//   work done: dimmed / ✓
// ============================================================

function ToolCard({ data, pCentralize, pAgents, pWork, pMerge, pDeliver }) {
  // scattered position (percentages)
  const scatterPos = { x: data.x, y: data.y };

  // ring position — original layout: full 360° orbit around Control at radius 14
  const idx = parseInt(data.id.slice(1), 10) - 1;
  const angle = (idx / 9) * Math.PI * 2 - Math.PI / 2;
  const ringR = 14;
  const ringPos = { x: 50 + Math.cos(angle) * ringR, y: 50 + Math.sin(angle) * ringR };

  // agent lane position — x matches LANE_X (20/50/80), y stacks by laneIdx below each specialist
  const laneX = [20, 50, 80][data.agent];
  const laneY = 56 + data.laneIdx * 12; // 56, 68, 80 — matches ToolFlows segments
  const lanePos = { x: laneX, y: laneY };

  // collect destination: roll up INTO the brief at top-middle
  const collectPos = { x: 50, y: 14 };
  const collectStart = data.laneIdx * 0.12 + data.agent * 0.04;    // bottom tools fly first
  const collectP     = clamp((pMerge - collectStart) / 0.45);

  // interpolate: scattered → ring (pCentralize), ring → lane (pAgents), lane → collect (pMerge)
  const pos1x = lerp(scatterPos.x, ringPos.x, pCentralize);
  const pos1y = lerp(scatterPos.y, ringPos.y, pCentralize);
  const pos2x = lerp(pos1x, lanePos.x, pAgents);
  const pos2y = lerp(pos1y, lanePos.y, pAgents);
  const x = lerp(pos2x, collectPos.x, collectP);
  const y = lerp(pos2y, collectPos.y, collectP);

  const size = lerp(210, 44, pCentralize);
  const showFullCard = pCentralize < 0.5;

  // tool is "being read" during its scan window; "consumed" after
  const scanStart    = 0.08 + data.agent * 0.10 + data.laneIdx * 0.10;
  const scanEnd      = scanStart + 0.28;   // wider scan window (was 0.18)
  const isBeingRead  = pWork > scanStart && pWork < scanEnd;
  const consumed     = pWork > scanEnd;

  // Full fade as it reaches the brief. After pDeliver the card is gone.
  const collectFade = collectP > 0.55 ? lerp(1, 0, (collectP - 0.55) / 0.45) : 1;
  const postDeliver = pDeliver > 0.2 ? 0 : 1;

  const opacity = (consumed && collectP < 0.02 ? 0.55 : 1) * collectFade * postDeliver;
  if (opacity < 0.02) return null;

  return (
    <div
      className="absolute"
      style={{
        left: `${x}%`,
        top: `${y}%`,
        width: showFullCard ? size : 44,
        transform: "translate(-50%, -50%)",
        opacity,
        zIndex: showFullCard ? 10 : 15,
      }}>
      {showFullCard ? (
        <div className="bg-surface-container border border-outline-variant/30 p-3 flex items-start gap-3 shadow-xl">
          <div className="w-7 h-7 bg-surface-container-high flex items-center justify-center shrink-0">
            <ToolMark tool={data.tool} size={14} />
          </div>
          <div className="min-w-0 flex-1">
            <div className="font-label text-[8px] text-outline tracking-[0.2em] uppercase mb-1">{data.tool}</div>
            <div className="text-[11px] text-on-surface leading-tight">{data.label}</div>
          </div>
          <div className="w-1.5 h-1.5 bg-primary rounded-full shrink-0 mt-1" style={{ animation: "dash 2s linear infinite" }} />
        </div>
      ) : (
        <div className="w-11 h-11 bg-surface-container border flex items-center justify-center relative"
             style={{ borderColor: isBeingRead ? "#ffc880" : "rgba(255,200,128,0.4)" }}>
          <ToolMark tool={data.tool} size={16} />
          {isBeingRead && (
            <div className="absolute inset-0 overflow-hidden pointer-events-none">
              <div className="absolute inset-y-0 w-1/2 bg-gradient-to-r from-transparent via-primary/30 to-transparent"
                   style={{ animation: "sweep 2.8s linear infinite" }} />
            </div>
          )}
          {consumed && (
            <div className="absolute -top-1 -right-1 w-3 h-3 bg-secondary rounded-full flex items-center justify-center">
              <span className="text-[8px] text-[#0b3a30] font-bold">✓</span>
            </div>
          )}
        </div>
      )}
    </div>
  );
}

// ============================================================
// USER NODE
// ============================================================

function UserNode({ pTether }) {
  // Appears as tether phase starts on left
  const fadeIn  = clamp(pTether * 2);
  const x       = lerp(-20, 15, fadeIn);
  // Fully fade out once tether is done — no ghost behind
  const fadeOut = clamp((pTether - 0.85) / 0.15);
  const opacity = fadeIn * (1 - fadeOut);
  if (opacity < 0.02) return null;
  return (
    <div className="absolute top-1/2 z-30" style={{
      left: `${x}%`,
      transform: "translate(-50%, -50%)",
      opacity,
    }}>
      <div className="text-center">
        <div className="w-16 h-16 mx-auto mb-2 rounded-full bg-surface-container border border-outline-variant/40 flex items-center justify-center">
          <span className="material-symbols-outlined text-on-surface-variant" style={{ fontSize: 30 }}>person</span>
        </div>
        <div className="font-label text-[9px] text-outline tracking-[0.3em] uppercase">You</div>
      </div>
    </div>
  );
}

// ============================================================
// TETHER LINE — dashed link from user → Control
// Draws in progressively; dashes flow continuously along the line
// to suggest a message being sent. No floating bubbles.
// ============================================================

function TetherLine({ p }) {
  // Stage 1 of the two-stage message flow: User → Control only.
  // The message types out on the wire, travels to Control, and is absorbed there.
  // (Control then dispatches a NEW signal to the Brief — that's ControlToBriefLink.)
  const drawIn  = clamp(p / 0.22);
  const fadeOut = clamp((p - 0.90) / 0.10);
  const lineOpacity = clamp(p * 3) * (1 - fadeOut);
  if (p < 0.01) return null;

  // Anchor points in viewport %
  const x1 = 17, y1 = 50;     // user port
  const x2 = 43, y2 = 50;     // control port
  const midx = (x1 + x2) / 2;

  // Typewriter (slower — spread across more of the phase)
  const MESSAGE = "Proposal for Acme — pricing + rollout + exec summary. Due Thu.";
  const typeP = clamp((p - 0.15) / 0.40);   // starts later, takes longer
  const shownChars = Math.floor(MESSAGE.length * typeP);
  const typed = MESSAGE.slice(0, shownChars);
  const showCursor = typeP > 0 && typeP < 1;

  // Bubble position: holds at midpoint while typing → travels to Control → absorbed (fades) AT Control
  const travelToCtrl = clamp((p - 0.60) / 0.25);    // mid → control
  const absorbP      = clamp((p - 0.80) / 0.18);    // fades into Control
  const bubbleX = lerp(midx, x2, travelToCtrl);
  const bubbleY = y1;
  const bubbleScale = lerp(1, 0.4, absorbP);        // shrinks as it enters Control
  const bubbleOpacity = clamp(p * 3) * (1 - absorbP);

  const arrived = travelToCtrl > 0.8;

  // Control receipt pulse — ring that appears when bubble reaches Control
  const receiptPulse = travelToCtrl > 0.85 && absorbP < 0.9;

  return (
    <div className="absolute inset-0 z-[5] pointer-events-none">
      <svg className="absolute inset-0 w-full h-full" style={{ opacity: lineOpacity }} viewBox="0 0 100 100" preserveAspectRatio="none">
        {/* Base line */}
        <line
          x1={x1} y1={y1}
          x2={lerp(x1, x2, drawIn)} y2={y2}
          stroke="#ffc880" strokeOpacity="0.35" strokeWidth="1"
          vectorEffect="non-scaling-stroke"
        />
        {/* Flowing dashes — non-scaling keeps dashes square at any aspect ratio */}
        {drawIn >= 0.95 && (
          <line
            x1={x1} y1={y1} x2={x2} y2={y2}
            stroke="#ffc880" strokeOpacity="0.95" strokeWidth="2"
            strokeDasharray="2 6"
            strokeLinecap="round"
            vectorEffect="non-scaling-stroke"
            style={{ animation: "dash 1.4s linear infinite", filter: "drop-shadow(0 0 2px rgba(255,200,128,0.7))" }}
          />
        )}
        {/* Absorbed-at-Control burst (stays circular because it's at a single point) */}
        {receiptPulse && (
          <circle
            cx={x2} cy={y2}
            r={lerp(0.8, 2.5, absorbP)}
            fill="none"
            stroke="#ffc880"
            strokeOpacity={1 - absorbP}
            strokeWidth="2"
            vectorEffect="non-scaling-stroke"
          />
        )}
      </svg>
      {/* port ticks — rendered as DOM dots so they stay perfectly round */}
      {drawIn > 0.05 && (
        <>
          <div className="absolute w-2 h-2 rounded-full bg-primary" style={{
            left: `${x1}%`, top: `${y1}%`, transform: "translate(-50%, -50%)",
          }} />
          <div className="absolute w-2 h-2 rounded-full bg-primary" style={{
            left: `${lerp(x1, x2, drawIn)}%`, top: `${y2}%`, transform: "translate(-50%, -50%)",
          }} />
        </>
      )}

      {/* Message bubble — types on wire, travels to Control, absorbed */}
      {bubbleOpacity > 0.02 && drawIn > 0.1 && (
        <div className="absolute" style={{
          left: `${bubbleX}%`,
          top: `${bubbleY}%`,
          transform: `translate(-50%, -140%) scale(${bubbleScale})`,
          transformOrigin: "center bottom",
          opacity: bubbleOpacity,
          maxWidth: "32vw",
        }}>
          <div className="font-label text-[8px] text-primary tracking-[0.32em] uppercase mb-1 text-center">
            ◉ mission · {arrived ? "absorbed" : "transmitting"}
          </div>
          <div className="bg-surface-container-lowest/80 backdrop-blur-sm border border-primary/50 px-3 py-2 font-label text-[11px] text-on-surface leading-snug shadow-[0_0_16px_rgba(255,200,128,0.3)]">
            <span className="text-outline">&gt;</span> <span className="font-headline">{typed}</span>
            {showCursor && <span className="inline-block w-[6px] h-[11px] bg-primary ml-0.5 align-middle animate-pulse" />}
          </div>
          <svg className="block mx-auto" width="10" height="6" viewBox="0 0 10 6"><path d="M0 0 L5 6 L10 0 Z" fill="#ffc880" opacity="0.7" /></svg>
        </div>
      )}

      {/* tx / rx labels */}
      {lineOpacity > 0.02 && (
        <>
          <div className="absolute" style={{ left: `${x1}%`, top: `${y1}%`, transform: "translate(-50%, 28px)", opacity: lineOpacity }}>
            <span className="font-label text-[9px] text-primary tracking-[0.3em] uppercase whitespace-nowrap">◉ tx</span>
          </div>
          <div className="absolute" style={{ left: `${x2}%`, top: `${y2}%`, transform: "translate(-50%, 28px)", opacity: lineOpacity * clamp((p - 0.55) / 0.2) }}>
            <span className="font-label text-[9px] text-secondary tracking-[0.3em] uppercase whitespace-nowrap">◉ rx</span>
          </div>
        </>
      )}
    </div>
  );
}

// ============================================================
// BRIEF → OUTCOME LINK — dashed line from the brief at top-middle
// down to the final proposal as it emerges in the center.  Appears
// as the outcome materializes (late merge through deliver).
// ============================================================

function BriefToOutcomeLink({ pMerge, pDeliver }) {
  const drawIn = clamp((pMerge - 0.45) / 0.4);
  const opacity = drawIn;
  if (opacity < 0.02) return null;

  // Brief bottom ≈ (50, 20); outcome top ≈ (50, 46)
  const x  = 50;
  const y1 = 20;
  const y2 = 46;

  return (
    <div className="absolute inset-0 z-[5] pointer-events-none" style={{ opacity }}>
      <svg className="absolute inset-0 w-full h-full" viewBox="0 0 100 100" preserveAspectRatio="none">
        {/* Base line — draws in progressively */}
        <line x1={x} y1={y1} x2={x} y2={lerp(y1, y2, drawIn)}
              stroke="#ffc880" strokeOpacity="0.3" strokeWidth="1"
              vectorEffect="non-scaling-stroke" />
        {/* Flowing dashes (downward — brief → outcome production) */}
        {drawIn >= 0.95 && (
          <line x1={x} y1={y1} x2={x} y2={y2}
                stroke="#ffc880" strokeOpacity="0.9" strokeWidth="2"
                strokeDasharray="2 6"
                strokeLinecap="round"
                vectorEffect="non-scaling-stroke"
                style={{
                  animation: "dash 1.3s linear infinite",
                  filter: "drop-shadow(0 0 2px rgba(255,200,128,0.6))",
                }} />
        )}
      </svg>
      {/* Ports as DOM dots (stay round) */}
      <div className="absolute w-2 h-2 rounded-full bg-primary" style={{
        left: `${x}%`, top: `${y1}%`, transform: "translate(-50%, -50%)",
      }} />
      <div className="absolute w-2 h-2 rounded-full bg-primary" style={{
        left: `${x}%`, top: `${lerp(y1, y2, drawIn)}%`, transform: "translate(-50%, -50%)",
      }} />
      {/* inline label on the link */}
      <div className="absolute" style={{
        left: `${x}%`,
        top: `${(y1 + y2) / 2}%`,
        transform: "translate(8px, -50%)",
      }}>
        <span className="font-label text-[8px] text-primary tracking-[0.32em] uppercase whitespace-nowrap">
          ◉ {pDeliver > 0.2 ? "delivered" : "producing"}
        </span>
      </div>
    </div>
  );
}

// ============================================================
// CONTROL → BRIEF LINK — dashed line from Control monitor to
// the Mission Brief panel during the Plan phase, showing Control
// is actively composing the brief.  Fades as the brief moves
// to top-middle (briefShift).
// ============================================================

function ControlToBriefLink({ pDispatch, pMission, pBriefShift }) {
  // Two roles:
  //   1. During pDispatch — a packet emerges from Control and travels to Brief (stage 2 of the message)
  //   2. During pMission — the dashed link stays live (Control composing the brief)
  // Fades out during pBriefShift.
  const drawIn  = clamp(pDispatch / 0.25);                                    // line draws as dispatch starts
  const fadeOut = pBriefShift;
  const opacity = Math.max(drawIn, clamp(pMission * 1.5)) * (1 - fadeOut);
  if (opacity < 0.02) return null;

  // Coordinates
  const x1 = 57, y1 = 50;
  const x2 = 72, y2 = 50;

  // Dispatch packet travels x1 → x2 during pDispatch
  const packetP   = clamp(pDispatch);
  const packetX   = lerp(x1, x2, packetP);
  const showPacket = packetP > 0.05 && packetP < 0.98;
  const arrived   = packetP >= 0.95;

  return (
    <div className="absolute inset-0 z-[5] pointer-events-none" style={{ opacity }}>
      <svg className="absolute inset-0 w-full h-full" viewBox="0 0 100 100" preserveAspectRatio="none">
        {/* Base line — draws in during dispatch */}
        <line x1={x1} y1={y1} x2={lerp(x1, x2, drawIn)} y2={y2}
              stroke="#ffc880" strokeOpacity="0.3" strokeWidth="1"
              vectorEffect="non-scaling-stroke" />
        {/* Flowing dashes — non-scaling keeps dashes round at any aspect ratio */}
        {drawIn >= 0.85 && (
          <line
            x1={x1} y1={y1} x2={x2} y2={y2}
            stroke="#ffc880" strokeOpacity="0.9" strokeWidth="2"
            strokeDasharray="2 6"
            strokeLinecap="round"
            vectorEffect="non-scaling-stroke"
            style={{ animation: "dash 1.2s linear infinite", filter: "drop-shadow(0 0 2px rgba(255,200,128,0.6))" }}
          />
        )}
        {/* Arrival burst at Brief end (single-point circle — stays round) */}
        {arrived && (
          <circle cx={x2} cy={y2} r={lerp(0.8, 2.2, packetP)}
                  fill="none" stroke="#ffc880" strokeOpacity={1 - packetP} strokeWidth="2"
                  vectorEffect="non-scaling-stroke" />
        )}
      </svg>
      {/* Dispatch packet as DOM dot (shown only while actively traveling) */}
      {showPacket && (
        <div className="absolute w-3 h-3 rounded-full bg-primary shadow-[0_0_10px_rgba(255,200,128,0.95)]" style={{
          left: `${packetX}%`, top: `${y2}%`, transform: "translate(-50%, -50%)",
        }}>
          <div className="absolute inset-[3px] rounded-full bg-surface" />
        </div>
      )}

      {/* inline label — below the line */}
      <div className="absolute" style={{
        left: `${(x1 + x2) / 2}%`,
        top: `${y1}%`,
        transform: "translate(-50%, 12px)",
      }}>
        <span className="font-label text-[8px] text-primary tracking-[0.32em] uppercase whitespace-nowrap">
          ◉ {pMission > 0.1 ? "composing brief" : "dispatching"}
        </span>
      </div>
    </div>
  );
}

// ============================================================
// CONTROL MONITOR — the retro terminal icon that replaces the orb
// Materializes during centralize, stays visible as tree root
// ============================================================

function ControlMonitor({ pCentralize, pControl, pTether, pMission, pBriefShift, pAgents, pMerge, pDeliver }) {
  const appear = pCentralize;
  if (appear < 0.02) return null;

  // Control stays at center through Plan; fades + shrinks as Mission Brief takes over as tree root
  const briefFade  = pBriefShift;
  const y          = 50;
  const baseSize   = 180;
  const size       = baseSize * lerp(0.55, 1, appear) * lerp(1, 0.55, briefFade);

  // Terminal "power" pulses harder during tether + mission phases
  const powerBoost = Math.max(pControl, pTether, pMission * 0.4);
  const glow       = lerp(0.15, 0.55, powerBoost);

  // Scanline sweeps faster during mission phase
  const scanSpeed  = lerp(4, 1.4, clamp(pMission * 1.5));

  // Screen label reflects current beat
  const screenLabel =
    pMission > 0.2 ? "PLANNING"
    : pTether > 0.3 ? "MISSION RX"
    : "READY";

  const opacity = appear * (1 - briefFade);
  if (opacity < 0.02) return null;

  return (
    <div className="absolute left-1/2 z-30" style={{
      top: `${y}%`,
      transform: "translate(-50%, -50%)",
      opacity,
    }}>
      <div className="relative" style={{ width: size, height: size * 0.78 }}>
        {/* Halo behind */}
        <div className="absolute -inset-4 rounded-full pointer-events-none" style={{
          background: `radial-gradient(circle, rgba(255,200,128,${glow}) 0%, transparent 65%)`,
        }} />

        {/* Retro terminal monitor (matches Control Labs logo mark) */}
        <svg viewBox="0 0 60 44" width={size} height={size * 0.78} fill="none"
             stroke="#ffc880" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"
             style={{ filter: `drop-shadow(0 0 ${lerp(6, 18, powerBoost)}px rgba(255,200,128,${glow + 0.2}))` }}>
          {/* Monitor bezel */}
          <rect x="4" y="3" width="52" height="32" rx="2.5" fill="#0d1117" />
          {/* Stand */}
          <path d="M20 39 L40 39" />
          <path d="M24 35 L24 39 M36 35 L36 39" />
          {/* Saturn (the "planet") inside the screen */}
          <ellipse cx="27" cy="19" rx="8" ry="2.6" transform="rotate(-18 27 19)" strokeWidth="1.8" />
          <circle cx="27" cy="19" r="4.2" fill="#ffc880" stroke="none" />
          {/* Stars */}
          <circle cx="13" cy="11" r="0.6" fill="#ffc880" stroke="none" opacity="0.8" />
          <circle cx="17" cy="25" r="0.5" fill="#ffc880" stroke="none" opacity="0.6" />
          <circle cx="38" cy="12" r="0.6" fill="#ffc880" stroke="none" opacity="0.8" />
          {/* Indicator lights on right */}
          <circle cx="46" cy="13" r="1.3" fill="#ffc880" stroke="none" />
          <circle cx="46" cy="19" r="1.3" fill="#ffc880" stroke="none" opacity={0.5 + 0.5 * powerBoost} />
          <circle cx="46" cy="25" r="1.3" fill="#ffc880" stroke="none" opacity={0.5 + 0.5 * powerBoost} />
        </svg>

        {/* CRT scanline sweep (only visible once Control is "on") */}
        <div className="absolute pointer-events-none overflow-hidden" style={{
          left:   `${(4 / 60) * 100}%`,
          top:    `${(3 / 44) * 100 * (size * 0.78) / (size * 0.78)}%`,
          width:  `${(52 / 60) * 100}%`,
          height: `${(32 / 44) * 100 * 0.78}%`,
          opacity: appear * 0.7,
        }}>
          <div className="absolute inset-0" style={{
            background: "linear-gradient(180deg, transparent 0%, rgba(255,200,128,0.12) 48%, rgba(255,200,128,0.35) 50%, rgba(255,200,128,0.12) 52%, transparent 100%)",
            animation: `sweep ${scanSpeed}s linear infinite`,
            transform: "rotate(90deg) scaleX(1.4)",
            transformOrigin: "center",
          }} />
        </div>

        {/* Screen-status label (terminal text floating just below the monitor) */}
        {size > 80 && (
          <div className="absolute left-1/2 -translate-x-1/2 whitespace-nowrap" style={{ top: "100%", marginTop: 8 }}>
            <span className="font-label text-[10px] tracking-[0.35em] text-primary uppercase">
              ◉ Control · <span className="text-on-surface">{screenLabel}</span>
            </span>
          </div>
        )}
      </div>
    </div>
  );
}

// helper: are we actively in the working phase?
function pWorkish(pAgents, pMerge) {
  return pAgents > 0.4 && pMerge < 0.4;
}

// ============================================================
// MISSION BRIEF — appears in mission phase · SLOWED with more substeps
// Reads as if CoS is thinking out loud before spawning branches.
// ============================================================

function MissionBrief({ pMission, pBriefShift, pMerge, pDeliver }) {
  const opacity = clamp(pMission * 1.8);
  if (opacity < 0.02) return null;

  // Check thresholds along pMission (0 → 1)
  const checks = [
    { at: 0.08, t: "Parse intent · classify DEAL_CLOSE",  sub: "priority · high · due Thu 18:00" },
    { at: 0.22, t: "Scope the graph · 14 sources",        sub: "across 6 tools · permissions scoped" },
    { at: 0.38, t: "Index context · brand voice v4",      sub: "pricing_q4_2025.xlsx · 180 slack msgs" },
    { at: 0.54, t: "Decompose · 3 parallel workstreams",  sub: "exec summary · pricing · rollout" },
    { at: 0.70, t: "Assemble team · 3 specialists",       sub: "Trail · Ledger · Chart" },
    { at: 0.86, t: "Budget + HITL · 2 checkpoints",       sub: "42m target · $1–5 model spend" },
  ];

  // Position: right-center when building, travels to top-middle once complete
  const x = lerp(82, 50, pBriefShift);   // right side → horizontal middle
  const y = lerp(50, 14, pBriefShift);   // center-vertical → near top
  const briefW = lerp(340, 520, pBriefShift);  // compact → wide compact bar

  // Checks fade out during the shift (they've been read/processed by that point)
  const checksOpacity = Math.max(0, 1 - pBriefShift * 1.3);

  return (
    <div className="absolute z-30" style={{
      left: `${x}%`,
      top: `${y}%`,
      transform: "translate(-50%, -50%)",
      opacity,
      width: briefW,
      maxWidth: "92vw",
    }}>
      {/* Header tag */}
      <div className="font-label text-[9px] text-outline tracking-[0.3em] uppercase mb-2 flex items-center gap-2">
        <span className="w-1.5 h-1.5 bg-primary rounded-full animate-pulse" />
        <span>◉ mission · {pBriefShift > 0.5 ? "live" : "brief"}</span>
      </div>

      {/* Intent card — always visible, shape subtly changes after shift */}
      <div className="bg-surface-container-lowest backdrop-blur border-l-2 border-primary p-3 sm:p-4 shadow-[0_0_24px_rgba(255,200,128,0.12)]">
        <div className="font-label text-[8px] text-primary tracking-[0.3em] uppercase mb-1.5">
          {pBriefShift > 0.5 ? "◉ running · dispatched" : "inbound · from you"}
        </div>
        <div className="text-[12px] text-on-surface leading-relaxed font-headline italic">
          "Proposal for Acme — pricing + rollout + exec summary. Due Thu 18:00."
        </div>
      </div>

      {/* 6 checks — visible only during plan phase */}
      {checksOpacity > 0.02 && (
        <div className="mt-3 space-y-1" style={{ opacity: checksOpacity }}>
          {checks.map((c, i) => {
            const done = pMission > c.at;
            const working = !done && pMission > c.at - 0.06;
            return (
              <div key={i} className={`p-2 border text-[11px] transition-colors bg-surface-container-lowest ${
                done
                  ? "border-secondary/40 text-on-surface"
                  : working
                  ? "border-primary/40 text-on-surface"
                  : "border-outline-variant/20 text-outline"
              }`}>
                <div className="flex items-center gap-2">
                  <div className={`w-4 h-4 flex items-center justify-center font-label text-[8px] font-bold shrink-0 ${
                    done
                      ? "bg-secondary text-[#0b3a30]"
                      : working
                      ? "bg-primary text-on-primary"
                      : "bg-surface-container-high text-outline"
                  }`}>
                    {done ? "✓" : working ? "…" : String(i + 1).padStart(2, "0")}
                  </div>
                  <span className="font-headline text-[11px]">{c.t}</span>
                </div>
                <div className="font-label text-[9px] text-outline tracking-wider uppercase mt-1 ml-6 leading-tight">
                  {c.sub}
                </div>
              </div>
            );
          })}
        </div>
      )}

      {/* Footer stats — appear after shift, sit inside the top-middle bar */}
      {pBriefShift > 0.4 && (
        <div className="mt-2 flex items-center justify-between gap-4 text-[9px] font-label tracking-[0.24em] uppercase text-outline" style={{ opacity: clamp((pBriefShift - 0.4) / 0.4) }}>
          <span>◉ 3 specialists</span>
          <span>9 sources</span>
          <span>2 HITL checkpoints</span>
          <span className="text-secondary">live</span>
        </div>
      )}
    </div>
  );
}

// ============================================================
// TREE CONNECTORS — dashed tree rooted at the shifted Mission Brief
//   - Brief (top-middle) → 3 specialists (branch out) during pAgents
//   - Each specialist → its first tool leaf during pWork
//   - (inter-tool chains handled separately by <ToolFlows />)
// ============================================================

function TreeConnectors({ pBriefShift, pAgents, pWork, pMerge, pDeliver }) {
  // Whole tree fades as we merge/deliver
  const treeFade = clamp(1 - pMerge * 1.2) * (1 - clamp(pDeliver * 1.5));
  if (treeFade < 0.02) return null;
  // Brief sits at (50, 14) compact; root anchor is just below it
  const root   = { x: 50, y: 19 };
  // specialists (must match LANE_X / LANE_Y)
  const lanes  = [{ x: 20, y: 38 }, { x: 50, y: 38 }, { x: 80, y: 38 }];
  // first tool y in each lane (matches ToolCard laneY logic below)
  const firstToolY = 56;

  const drawBranches = clamp(pAgents * 1.2);
  const drawToTools  = clamp((pWork - 0.05) / 0.4);

  if (pBriefShift < 0.3 && drawBranches < 0.02) return null;

  return (
    <>
    <svg className="absolute inset-0 w-full h-full z-[5] pointer-events-none" viewBox="0 0 100 100" preserveAspectRatio="none" style={{ opacity: treeFade }}>
      {/* 1. Brief → 3 specialists (dashed, branch out staggered) */}
      {drawBranches > 0.02 && lanes.map((lane, i) => {
        const staggered = clamp((drawBranches - i * 0.15) / 0.55);
        if (staggered < 0.02) return null;
        const midY = (root.y + lane.y) / 2;
        return (
          <path
            key={`branch-${i}`}
            d={`M ${root.x} ${root.y} Q ${root.x} ${midY}, ${lerp(root.x, lane.x, staggered)} ${lerp(root.y, lane.y - 3, staggered)}`}
            fill="none"
            stroke="#ffc880"
            strokeOpacity={0.55}
            strokeWidth="2"
            strokeDasharray="2 6"
            strokeLinecap="round"
            vectorEffect="non-scaling-stroke"
            style={{
              animation: drawBranches > 0.9 ? "dash 1.6s linear infinite" : "none",
              filter: "drop-shadow(0 0 2px rgba(255,200,128,0.35))",
            }}
          />
        );
      })}

      {/* 2. Each specialist → its first tool leaf (short dashed stem) */}
      {drawToTools > 0.02 && lanes.map((lane, i) => {
        const staggered = clamp((drawToTools - i * 0.1) / 0.55);
        if (staggered < 0.02) return null;
        return (
          <line
            key={`stem-${i}`}
            x1={lane.x} y1={lane.y + 4}
            x2={lane.x} y2={lerp(lane.y + 4, firstToolY - 4, staggered)}
            stroke="#ffc880" strokeOpacity="0.45" strokeWidth="1.5"
            strokeDasharray="2 5"
            strokeLinecap="round"
            vectorEffect="non-scaling-stroke"
          />
        );
      })}
    </svg>
    {/* Merge-phase aggregate pulses — DOM dots so they stay round */}
    {pMerge > 0.02 && lanes.map((lane, i) => (
      <div key={`merge-pulse-${i}`} className="absolute w-1.5 h-1.5 rounded-full bg-secondary shadow-[0_0_6px_rgba(125,211,192,0.8)] z-[5] pointer-events-none" style={{
        left: `${lerp(lane.x, root.x, pMerge)}%`,
        top:  `${lerp(lane.y - 3, root.y, pMerge)}%`,
        transform: "translate(-50%, -50%)",
        opacity: pMerge * treeFade,
      }} />
    ))}
    </>
  );
}

// ============================================================
// TOOL FLOWS — dashed-dot connectors between consecutive tools
// in a specialist's chain (laneIdx 0 → 1 → 2). Animate during work.
// ============================================================

function ToolFlows({ pWork, pMerge }) {
  // Suppress during merge — tools are flying up, not chain-flowing
  if (pWork < 0.05 || pMerge > 0.4) return null;

  // For each agent lane, draw dashed-dot lines between laneIdx 0↔1 and 1↔2
  const laneXs   = [20, 50, 80];
  const toolYs   = [56, 68, 80];       // must match ToolCard laneY math
  const segments = [];
  laneXs.forEach((x, lane) => {
    for (let i = 0; i < toolYs.length - 1; i++) {
      // each segment starts after its upstream tool is "being read"
      const activate = clamp((pWork - 0.12 - i * 0.18 - lane * 0.06) / 0.35);
      if (activate < 0.02) continue;
      segments.push({ key: `${lane}-${i}`, x, y1: toolYs[i] + 4, y2: toolYs[i + 1] - 4, alpha: activate });
    }
  });
  if (!segments.length) return null;

  return (
    <svg className="absolute inset-0 w-full h-full z-[5] pointer-events-none" viewBox="0 0 100 100" preserveAspectRatio="none">
      {segments.map((s) => (
        <line
          key={s.key}
          x1={s.x} y1={s.y1}
          x2={s.x} y2={s.y2}
          stroke="#ffc880"
          strokeOpacity={0.7 * s.alpha}
          strokeWidth="1.8"
          strokeDasharray="2 5"
          strokeLinecap="round"
          vectorEffect="non-scaling-stroke"
          style={{
            animation: "dash 1.1s linear infinite",
            filter: "drop-shadow(0 0 2px rgba(255,200,128,0.6))",
          }}
        />
      ))}
    </svg>
  );
}

// ============================================================
// AGENT BRANCHES — three specialist leaves, positioned as tree branches
// rooted under the CoS node
// ============================================================

// lane anchor x% coords — MUST match TreeConnectors `lanes` array
const LANE_X = [20, 50, 80];
const LANE_Y = 42;

function AgentLanes({ pAgents, pWork, pMerge }) {
  if (pAgents < 0.02) return null;
  return (
    <>
      {SPECIALISTS.map((a, i) => (
        <AgentLaneCard key={a.id} agent={a} pAgents={pAgents} pWork={pWork} pMerge={pMerge} lane={i} />
      ))}
    </>
  );
}

function AgentLaneCard({ agent, pAgents, pWork, pMerge, lane }) {
  // Stagger each lane's entrance along pAgents
  const laneEntrance = clamp((pAgents - lane * 0.12) / 0.55);
  // Each lane's internal work staggers along pWork
  const laneStart    = lane * 0.08;
  const laneWork     = clamp((pWork - laneStart) / 0.7);
  const hasOutput    = laneWork > 0.78;

  // During merge, the specialist itself rolls up INTO the brief at top-middle
  const mergeStart   = 0.35 + lane * 0.05;       // starts AFTER the tools below it have started flying
  const mergeP       = clamp((pMerge - mergeStart) / 0.55);
  const x            = lerp(LANE_X[lane], 50, mergeP);
  const baseY        = LANE_Y + lerp(10, 0, laneEntrance);
  const y            = lerp(baseY, 14, mergeP);
  const scale        = lerp(1, 0.35, mergeP);
  const opacity      = laneEntrance * (1 - mergeP);

  if (opacity < 0.02) return null;

  return (
    <div className="absolute z-20 pointer-events-none" style={{
      left: `${x}%`,
      top:  `${y}%`,
      transform: `translate(-50%, -50%) scale(${scale})`,
      transformOrigin: "center center",
      opacity,
      width: "min(22vw, 280px)",
    }}>
      <div className="bg-surface-container-lowest/85 backdrop-blur border border-outline-variant/30 shadow-xl">
      <div className="p-3 border-b border-outline-variant/25 flex items-center gap-3">
        <div className="relative w-9 h-9 shrink-0">
          <div className="absolute inset-0 rounded-full" style={{
            background: `radial-gradient(circle, ${agent.color}aa 0%, transparent 70%)`,
            animation: "orb-breathe 2.5s ease-in-out infinite",
          }} />
          <div className="absolute inset-1 rounded-full border flex items-center justify-center bg-surface-container-lowest"
               style={{ borderColor: agent.color }}>
            <span className="font-headline text-xs font-bold" style={{ color: agent.color }}>{agent.name[0]}</span>
          </div>
        </div>
        <div className="min-w-0 flex-1">
          <div className="font-label text-[8px] tracking-[0.25em] uppercase" style={{ color: agent.color }}>{agent.role}</div>
          <div className="font-headline text-sm font-bold">{agent.name}</div>
          <div className="font-label text-[9px] text-outline truncate">{agent.task}</div>
        </div>
      </div>

      {/* LLM call indicator */}
      <div className="p-3 border-b border-outline-variant/25">
        <div className="font-label text-[8px] text-outline tracking-[0.25em] uppercase mb-2">◉ llm · reasoning</div>
        <div className={`border p-2 flex items-center gap-2 transition-colors ${
          laneWork > 0.3 ? "border-primary/40 bg-primary/5" : "border-outline-variant/20 bg-surface-container-lowest/40"
        }`}>
          <span className="material-symbols-outlined text-primary" style={{ fontSize: 14 }}>auto_awesome</span>
          <div className="font-label text-[10px] text-on-surface">{agent.llm}</div>
          {laneWork > 0.3 && laneWork < 0.85 && (
            <div className="ml-auto flex gap-1">
              {[0, 1, 2].map(i => (
                <div key={i} className="w-1 h-1 bg-primary rounded-full" style={{
                  animation: `orb-breathe 0.8s ease-in-out ${i * 0.15}s infinite`,
                }} />
              ))}
            </div>
          )}
          {laneWork > 0.85 && <span className="ml-auto text-secondary font-label text-[9px]">✓</span>}
        </div>
      </div>

      <div className="p-3">
        <div className="font-label text-[8px] text-outline tracking-[0.25em] uppercase mb-2">◉ output</div>
        {hasOutput ? (
          <div className="border bg-secondary/10 border-secondary/40 p-2 flex items-center gap-2">
            <span className="material-symbols-outlined text-secondary" style={{ fontSize: 14 }}>description</span>
            <div className="font-headline text-[11px] font-semibold text-on-surface truncate">{agent.output}</div>
            <span className="ml-auto font-label text-[9px] text-secondary">✓</span>
          </div>
        ) : (
          <div className="border border-dashed border-outline-variant/30 p-2 text-[10px] text-outline italic">
            {laneWork > 0.2 ? "drafting…" : "queued"}
          </div>
        )}
      </div>
      </div>
    </div>
  );
}

// ============================================================
// FINAL DOC — emerges from the Control monitor (tree root),
// slides down to center of stage as it solidifies
// ============================================================

function FinalDoc({ pMerge, pDeliver }) {
  // The outcome — emerges at center of stage (independent of the brief at top)
  // Only appears late in merge and solidifies during deliver
  const emerge = clamp((pMerge - 0.45) / 0.55);
  if (emerge < 0.02) return null;

  const delivered = pDeliver > 0.3;
  // Standalone outcome position — centered, with the brief at top (y=14) and this below (y=52)
  const y = 52;
  const scale = lerp(0.45, 1, emerge);

  return (
    <div className="absolute left-1/2 z-35"
         style={{
           top: `${y}%`,
           transform: `translate(-50%, -50%) scale(${scale})`,
           transformOrigin: "center center",
           opacity: emerge,
         }}>
      <div className="doc-paper p-5 w-[300px] relative">
        {delivered && (
          <div className="absolute -top-3 -right-3 bg-secondary text-[#0b3a30] font-label text-[9px] tracking-[0.25em] uppercase font-bold px-3 py-1.5"
               style={{ opacity: pDeliver }}>
            ✓ delivered
          </div>
        )}
        <div className="font-headline text-[13px] font-bold text-[#1a1612] leading-tight mb-2">Proposal — Acme × Parallel</div>
        <div className="font-label text-[8px] text-[#8a7d65] tracking-wider uppercase mb-3">
          {delivered ? "v1 · final · 6pp" : "assembling…"}
        </div>
        <div className="space-y-1.5 text-[9px] leading-snug text-[#1a1612]">
          {[
            { label: "Context",  color: "#ffc880", body: "Acme reached out Mon ref. rollout", at: 0.1 },
            { label: "Pricing",  color: "#7dd3c0", body: "Tier 2 · $240k ARR · 5 seats",      at: 0.3 },
            { label: "Rollout",  color: "#c9a0dc", body: "W1–2 setup · W3–6 pilot",           at: 0.5 },
          ].map((sec, i) => {
            const visible = pMerge > sec.at;
            return (
              <div key={i} style={{ opacity: visible ? 1 : 0.2, transition: "opacity 0.3s" }}>
                <div className="font-bold text-[10px]">{sec.label}</div>
                <div style={{ background: sec.color + "40", padding: "1px 4px" }}>
                  {sec.body}
                </div>
              </div>
            );
          })}
        </div>
        {delivered && (
          <div className="absolute bottom-3 right-3 font-label text-[7px] text-[#8a7d65]">14 cites</div>
        )}
      </div>

      {/* Delivery note below */}
      {delivered && (
        <div className="mt-4 text-center font-label text-[9px] text-outline tracking-widest uppercase"
             style={{ opacity: pDeliver }}>
          via Gmail · procurement@acme · Hubspot → Proposal Sent
        </div>
      )}
    </div>
  );
}

Object.assign(window, { PaperTrailV3 });
