/*
  ArchitectOrbit — single-Architect homepage diagram (v60.1).

  Redesign goals:
    • Eliminate the dizzying full-ring rotation (previous version's
      orbit-counter spinning text was nauseating).
    • Promote the Architect node to a hexagon that matches the logo,
      with subtle inner motion suggesting "machinery at work".
    • Show the LLM as a first-class participant — Architect leans on
      it, the diagram should make that obvious.
    • Tools stay STATIC — no orbiting. A subtle "in use" highlight
      cycles through them one at a time so the eye sees activity
      without having to chase a moving target.
    • Subagents render as small hexagons beneath Architect, fading in
      and out on a calm cadence.
    • Substrate hex-mesh remains the backdrop, anchored at Architect.

  All animation is slow (3–8s cycles) and additive — not perpetual
  rotation. Total motion budget kept low so the page reads as a
  diagram first, animation second.
*/

const ORBIT_W = 820;
const ORBIT_H = 680;
const ARCHITECT = { cx: 410, cy: 360, r: 70 };       // hex circumradius
// LLM lives just outside the hex's upper-right corner, attached by a
// short dashed tether — visually reads as "Architect consults the LLM"
// rather than "another agent floating overhead". Small enough to stay
// subordinate to the Architect node.
const LLM       = { cx: 410 + 86, cy: 360 - 72, r: 24 };
const SUBSTRATE = { cx: 410, cy: 600, r: 26, label: 'autoarchitect-code', short: 'AC' };

const TOOL_HIGHLIGHT_INTERVAL_MS = 1800;
const SUBAGENT_INTERVAL_MS       = 4500;
const SUBAGENT_LIFETIME_MS       = 6000;
const SUBAGENT_MAX_VISIBLE       = 2;

// Representative tool families, expanded to show the platform's range:
// file ops, shell, web (search + fetch), build/deploy, runtime hooks
// (useApi/useConnection), LLM-as-tool, archetypes, agents, kanban,
// connections/cron. Each chip carries a SHORT readable label that fits
// inside the chip body, plus its longer canonical name as the tooltip.
const ORBIT_TOOLS = [
  { name: 'read_file',       label: 'read' },
  { name: 'write_file',      label: 'write' },
  { name: 'edit_file',       label: 'edit' },
  { name: 'bash',            label: 'bash' },
  { name: 'grep',            label: 'grep' },
  { name: 'web_search',      label: 'web search' },
  { name: 'web_fetch',       label: 'web fetch' },
  { name: 'useApi',          label: 'useApi' },
  { name: 'useConnection',   label: 'useConnection' },
  { name: 'llm_extract',     label: 'extract' },
  { name: 'find_archetype',  label: 'archetype' },
  { name: 'deploy_workspace',label: 'deploy' },
  { name: 'run_smoke',       label: 'smoke' },
  { name: 'visual_check',    label: 'visual' },
  { name: 'spawn_agent',     label: 'subagent' },
  { name: 'schedule_action', label: 'cron' },
];

// Moat capabilities — the four distinct platform layers that wrap
// Architect. Larger hexagons than tool chips, placed beyond the tool
// ring at cardinal positions. Each is its own first-class system, not
// a tool: connections persist credentials, plugins inject runtime
// code, cron fires on schedule, subagents are long-lived peers. The
// presence of these four is what makes AutoArchitect a platform vs.
// "another app generator" — visible right on the homepage diagram.
const MOAT_RING_R = 290;
const MOAT_HEX_R  = 32;
// Placed at the four quadrant corners so we don't fight the LLM badge
// (upper-right of Architect) or the substrate node (below). Top-left
// gets Connections (the most-asked-about layer); top-right Plugins
// (visually closest to Architect's "what gets installed" surface); the
// bottom row gets Cron + Subagents (runtime-time concerns).
const MOAT_NODES = [
  { id: 'connections', label: 'Connections', sub: 'oauth · vault',   angleDeg: -135, accent: 'var(--accent)' },
  { id: 'plugins',     label: 'Plugins',     sub: 'pre-vetted',      angleDeg:  -45, accent: 'var(--accent-2)' },
  { id: 'subagents',   label: 'Subagents',   sub: 'parallel',        angleDeg:  135, accent: 'var(--accent-2)' },
  { id: 'cron',        label: 'Cron',        sub: 'scheduled',       angleDeg:   45, accent: 'var(--accent-3)' },
];

function moatNodePositions(architectCx, architectCy) {
  return MOAT_NODES.map((n) => {
    const a = (n.angleDeg * Math.PI) / 180;
    return {
      ...n,
      x: architectCx + MOAT_RING_R * Math.cos(a),
      y: architectCy + MOAT_RING_R * Math.sin(a),
    };
  });
}

/* ── Substrate hex mesh (unchanged) ──────────────────────────────── */
const MESH_HEX_SIZE   = 16;
const MESH_FADE_INNER = 90;
const MESH_FADE_OUTER = 380;
const MESH_MAX_OP     = 0.16;
const MESH_ACTIVE_FRACTION = 0.05;

function buildMesh(width, height, ax, ay) {
  const s = MESH_HEX_SIZE;
  const w = Math.sqrt(3) * s;
  const h = 1.5 * s;
  const cells = [];
  let row = 0;
  for (let cy = -s; cy <= height + s; cy += h, row++) {
    const offset = (row % 2 === 0) ? 0 : w / 2;
    for (let cx = -s + offset; cx <= width + s; cx += w) {
      const dx = cx - ax;
      const dy = cy - ay;
      const dist = Math.sqrt(dx * dx + dy * dy);
      if (dist > MESH_FADE_OUTER) continue;
      const t = dist <= MESH_FADE_INNER
        ? 1
        : 1 - (dist - MESH_FADE_INNER) / (MESH_FADE_OUTER - MESH_FADE_INNER);
      const op = MESH_MAX_OP * Math.max(0, Math.min(1, t));
      cells.push({ cx, cy, op });
    }
  }
  return cells;
}

function hexPath(cx, cy, s) {
  // Pointy-top hex (matches mesh cells).
  const pts = [];
  for (let i = 0; i < 6; i++) {
    const a = Math.PI / 6 + (i * Math.PI) / 3;
    pts.push(`${cx + s * Math.cos(a)},${cy + s * Math.sin(a)}`);
  }
  return pts.join(' ');
}

function pointyTopHexPath(cx, cy, s) {
  // Pointy-top hex — matches the AutoArchitect logo (top apex visible).
  // First vertex at top, then 60° increments clockwise.
  const pts = [];
  for (let i = 0; i < 6; i++) {
    const a = -Math.PI / 2 + (i * Math.PI) / 3;
    pts.push(`${cx + s * Math.cos(a)},${cy + s * Math.sin(a)}`);
  }
  return pts.join(' ');
}

/* Static arc of tool chips placed around the Architect hex. The chips
   never rotate — instead one is highlighted at a time on a slow timer
   to suggest "Architect is using this tool right now". */
const TOOL_RING_R = 200;

// Deterministic small jitter so the ring of tools doesn't read as a
// perfect mechanical wheel — feels more like a constellation. Same
// seed every render so chips don't twitch on each repaint.
function jitter(seed, mag) {
  // Simple LCG-derived value in [-1, 1].
  const x = Math.sin(seed * 12.9898) * 43758.5453;
  return (x - Math.floor(x) - 0.5) * 2 * mag;
}

function toolChipPositions() {
  const n = ORBIT_TOOLS.length;
  return ORBIT_TOOLS.map((tool, i) => {
    const a = (i / n) * 2 * Math.PI - Math.PI / 2;
    // Drift each chip ±18° angularly + ±18px radially. Keeps the rough
    // ring shape but breaks the "wheel of pegs" feel.
    const angleJitter = jitter(i + 1, 0.32);
    const radialJitter = jitter(i + 7, 18);
    const a2 = a + angleJitter * 0.16;
    const r2 = TOOL_RING_R + radialJitter;
    return {
      ...tool,
      x: ARCHITECT.cx + r2 * Math.cos(a2),
      y: ARCHITECT.cy + r2 * Math.sin(a2),
    };
  });
}

const ArchitectOrbit = ({ width = ORBIT_W, height = ORBIT_H }) => {
  const meshCells = React.useMemo(
    () => buildMesh(width, height, ARCHITECT.cx, ARCHITECT.cy),
    [width, height],
  );
  const meshActive = React.useMemo(() => {
    const idx = [];
    for (let i = 0; i < meshCells.length; i++) {
      if (Math.random() < MESH_ACTIVE_FRACTION) idx.push(i);
    }
    return new Set(idx);
  }, [meshCells]);

  const toolChips = React.useMemo(toolChipPositions, []);

  // Cycle the "in use" highlight through tool chips one at a time.
  const [activeTool, setActiveTool] = React.useState(0);
  React.useEffect(() => {
    const id = setInterval(() => {
      setActiveTool((i) => (i + 1) % ORBIT_TOOLS.length);
    }, TOOL_HIGHLIGHT_INTERVAL_MS);
    return () => clearInterval(id);
  }, []);

  // Ephemeral subagents — small hexagons emerging below Architect.
  const slotAngles = React.useMemo(
    () => Array.from({ length: 4 }, (_, i) => Math.PI * 0.25 + (i / 3) * Math.PI * 0.5),
    [],
  );
  const [subagents, setSubagents] = React.useState([]);
  const subId = React.useRef(0);

  React.useEffect(() => {
    let mounted = true;
    let timer = null;
    const fire = () => {
      if (!mounted) return;
      setSubagents((prev) => {
        const taken = new Set(prev.map((s) => s.slot));
        const free = slotAngles.map((_, i) => i).filter((i) => !taken.has(i));
        if (free.length === 0) return prev;
        const slot = free[Math.floor(Math.random() * free.length)];
        const id = ++subId.current;
        return [...prev, { id, slot, born: Date.now() }];
      });
      timer = setTimeout(fire, SUBAGENT_INTERVAL_MS);
    };
    timer = setTimeout(fire, 1200);
    const sweep = setInterval(() => {
      if (!mounted) return;
      const now = Date.now();
      setSubagents((prev) =>
        prev.filter((s) => now - s.born < SUBAGENT_LIFETIME_MS).slice(-SUBAGENT_MAX_VISIBLE),
      );
    }, 800);
    return () => { mounted = false; if (timer) clearTimeout(timer); clearInterval(sweep); };
  }, [slotAngles]);

  // Activity ticker
  const [tickIdx, setTickIdx] = React.useState(0);
  React.useEffect(() => {
    const id = setInterval(() => setTickIdx((i) => (i + 1) % 3), 2200);
    return () => clearInterval(id);
  }, []);
  const ticks = ['todo', 'in_progress', 'done'];

  const SUBAGENT_RING_R = 130;
  const subagentPositions = subagents.map((s) => {
    const a = slotAngles[s.slot];
    return {
      ...s,
      x: ARCHITECT.cx + SUBAGENT_RING_R * Math.cos(a),
      y: ARCHITECT.cy + SUBAGENT_RING_R * Math.sin(a),
    };
  });

  return (
    <svg
      viewBox={`0 0 ${ORBIT_W} ${ORBIT_H}`}
      width="100%"
      preserveAspectRatio="xMidYMid meet"
      style={{ display: 'block', maxWidth: '100%', height: 'auto' }}
    >
      <defs>
        <radialGradient id="architectGlow" cx="50%" cy="50%" r="55%">
          <stop offset="0%"  stopColor="var(--accent-2)" stopOpacity="0.55" />
          <stop offset="55%" stopColor="var(--accent)"   stopOpacity="0.20" />
          <stop offset="100%" stopColor="var(--accent)"  stopOpacity="0" />
        </radialGradient>
        <linearGradient id="hexFill" x1="0" y1="0" x2="1" y2="1">
          <stop offset="0" stopColor="var(--accent)"   stopOpacity="0.14" />
          <stop offset="1" stopColor="var(--accent-2)" stopOpacity="0.06" />
        </linearGradient>
        <linearGradient id="hexStroke" x1="0" y1="0" x2="1" y2="1">
          <stop offset="0" stopColor="var(--accent)" />
          <stop offset="1" stopColor="var(--accent-2)" />
        </linearGradient>
        <radialGradient id="llmGlow" cx="50%" cy="50%" r="50%">
          <stop offset="0%"  stopColor="var(--info)"  stopOpacity="0.55" />
          <stop offset="100%" stopColor="var(--info)" stopOpacity="0" />
        </radialGradient>
      </defs>

      {/* 1. Substrate hex mesh */}
      <g style={{ pointerEvents: 'none' }}>
        {meshCells.map((c, i) => (
          <polygon
            key={i}
            points={hexPath(c.cx, c.cy, MESH_HEX_SIZE * 0.95)}
            fill="none"
            stroke="var(--accent-2)"
            strokeOpacity={c.op}
            strokeWidth={1}
            style={meshActive.has(i) ? {
              animation: `mesh-breathe ${5 + (i % 4)}s ease-in-out ${(i % 7) * 0.5}s infinite`,
            } : undefined}
          />
        ))}
      </g>

      {/* 2. LLM tether — a short dashed line from the hex corner to the
          LLM badge with a thought-pulse dot travelling along it. Reads as
          "Architect is consulting the LLM right now" without making the
          LLM look like a peer node. */}
      {(() => {
        // Hex upper-right vertex — pointy-top orientation has the
        // upper-right vertex at -30° from center.
        const hexX = ARCHITECT.cx + ARCHITECT.r * Math.cos(-Math.PI / 6);
        const hexY = ARCHITECT.cy + ARCHITECT.r * Math.sin(-Math.PI / 6);
        return (
          <g style={{ pointerEvents: 'none' }}>
            <line
              x1={hexX} y1={hexY}
              x2={LLM.cx} y2={LLM.cy + LLM.r * 0.7}
              stroke="var(--info)"
              strokeOpacity={0.45}
              strokeWidth={1.2}
              strokeDasharray="3 4"
            />
            {/* Travelling "thought" dot — slides along the tether toward
                the LLM and fades. Implies Architect → LLM query traffic. */}
            <circle r={2.5} fill="var(--accent-2)" opacity={0.9}>
              <animateMotion
                dur="2.6s"
                repeatCount="indefinite"
                path={`M ${hexX} ${hexY} L ${LLM.cx} ${LLM.cy + LLM.r * 0.7}`}
              />
              <animate attributeName="opacity" values="0;0.95;0" dur="2.6s" repeatCount="indefinite" />
            </circle>
            {/* Reverse — response coming back. Slight phase offset so the
                eye sees a steady back-and-forth, not a regular metronome. */}
            <circle r={2.5} fill="var(--info)" opacity={0.9}>
              <animateMotion
                dur="2.6s"
                begin="1.3s"
                repeatCount="indefinite"
                path={`M ${LLM.cx} ${LLM.cy + LLM.r * 0.7} L ${hexX} ${hexY}`}
              />
              <animate attributeName="opacity" values="0;0.95;0" dur="2.6s" begin="1.3s" repeatCount="indefinite" />
            </circle>
          </g>
        );
      })()}

      {/* 3. Tool chips — STATIC; one highlighted at a time. Pill shape
            (rounded rect) with the full short label inside so names don't
            spill outside the chip the way circle-with-truncated-text did. */}
      <g>
        {toolChips.map((t, i) => {
          const isActive = i === activeTool;
          // Width-fits-text — 6.5px per char + 16px padding, min 42, max 110.
          // Bumped max so "web search" / "useConnection" fit cleanly.
          const w = Math.max(42, Math.min(110, t.label.length * 6.5 + 16));
          const h = 22;
          const x = t.x - w / 2;
          const y = t.y - h / 2;
          return (
            <g key={t.name}>
              {/* Active halo */}
              {isActive && (
                <ellipse cx={t.x} cy={t.y} rx={w / 2 + 10} ry={h / 2 + 8} fill="var(--accent)" opacity="0.16">
                  <animate attributeName="opacity" values="0.04;0.28;0.04" dur="1.8s" repeatCount="indefinite" />
                </ellipse>
              )}
              <rect
                x={x} y={y} width={w} height={h} rx={h / 2}
                fill="var(--bg-2)"
                stroke={isActive ? 'var(--accent)' : 'var(--line-3)'}
                strokeWidth={isActive ? 1.5 : 1}
                style={{ transition: 'stroke 300ms ease' }}
              >
                <title>{t.name}</title>
              </rect>
              <text
                x={t.x} y={t.y + 3.5}
                textAnchor="middle"
                fontFamily="var(--font-mono)"
                fontSize={9}
                fontWeight={600}
                fill={isActive ? 'var(--accent-2)' : 'var(--accent-3)'}
                style={{ transition: 'fill 300ms ease', pointerEvents: 'none' }}
              >
                {t.label}
              </text>
            </g>
          );
        })}
      </g>

      {/* 4. Subagent hexagons — ephemeral */}
      <g>
        {subagentPositions.map((s) => (
          <g key={s.id} style={{ animation: `subagent-life ${SUBAGENT_LIFETIME_MS}ms ease-in-out forwards` }}>
            <circle cx={s.x} cy={s.y} r={20} fill="url(#llmGlow)" />
            <line
              x1={ARCHITECT.cx} y1={ARCHITECT.cy}
              x2={s.x} y2={s.y}
              stroke="#34d399"
              strokeOpacity={0.4}
              strokeWidth={1}
              strokeDasharray="3 4"
            />
            <polygon
              points={pointyTopHexPath(s.x, s.y, 10)}
              fill="var(--bg-2)"
              stroke="#34d399"
              strokeWidth={1.5}
            />
            <text x={s.x} y={s.y + 3} textAnchor="middle" fontFamily="var(--font-mono)" fontSize={8} fontWeight={700} fill="#34d399">
              SA
            </text>
          </g>
        ))}
      </g>

      {/* 5. LLM badge — small adjacent node Architect consults. Sized
          subordinately to Architect; the pulsing glow ticks softly so it
          reads as "active" without competing with the hex. */}
      <g>
        <circle cx={LLM.cx} cy={LLM.cy} r={LLM.r + 8} fill="url(#llmGlow)" style={{ animation: 'llm-pulse 3.8s ease-in-out infinite' }} />
        <circle
          cx={LLM.cx} cy={LLM.cy} r={LLM.r}
          fill="var(--bg-1)"
          stroke="var(--info)"
          strokeWidth={1.5}
        />
        <text
          x={LLM.cx} y={LLM.cy + 3}
          textAnchor="middle"
          fontFamily="var(--font-mono)"
          fontSize={10}
          fontWeight={700}
          fill="var(--info)"
          letterSpacing={1}
        >
          LLM
        </text>
        <text
          x={LLM.cx} y={LLM.cy + LLM.r + 12}
          textAnchor="middle"
          fontFamily="var(--font-mono)"
          fontSize={8}
          fill="var(--fg-4)"
          letterSpacing={1.2}
        >
          BYO model
        </text>
      </g>

      {/* 6. Architect — pointy-top hexagon (matches the logo). The motion
          here is a heartbeat: the hex itself + its halo throb in a
          two-thump cadence (~1.4s cycle) to read as "alive and working"
          without the dizzying full-ring rotation we used to have. */}
      <g
        style={{
          animation: 'architect-breathe 4.2s ease-in-out infinite',
          transformOrigin: `${ARCHITECT.cx}px ${ARCHITECT.cy}px`,
        }}
      >
        {/* Outer halo — also throbs with the heartbeat */}
        <circle
          cx={ARCHITECT.cx} cy={ARCHITECT.cy} r={ARCHITECT.r + 32}
          fill="url(#architectGlow)"
        />
        {/* Hexagon body — fill + stroke */}
        <polygon
          points={pointyTopHexPath(ARCHITECT.cx, ARCHITECT.cy, ARCHITECT.r)}
          fill="url(#hexFill)"
          stroke="url(#hexStroke)"
          strokeWidth={2.4}
          strokeLinejoin="round"
        />
      </g>
        {/* Logo cue: the inner "A" triangle + crossbar (scaled to hex). */}
        <g style={{ pointerEvents: 'none' }}>
          <path
            d={`M ${ARCHITECT.cx} ${ARCHITECT.cy - ARCHITECT.r * 0.42}
                L ${ARCHITECT.cx + ARCHITECT.r * 0.36} ${ARCHITECT.cy + ARCHITECT.r * 0.38}
                L ${ARCHITECT.cx - ARCHITECT.r * 0.36} ${ARCHITECT.cy + ARCHITECT.r * 0.38} Z`}
            fill="none"
            stroke="var(--accent-2)"
            strokeOpacity={0.7}
            strokeWidth={1.5}
            strokeLinejoin="round"
          />
          <line
            x1={ARCHITECT.cx - ARCHITECT.r * 0.18}
            y1={ARCHITECT.cy + ARCHITECT.r * 0.10}
            x2={ARCHITECT.cx + ARCHITECT.r * 0.18}
            y2={ARCHITECT.cy + ARCHITECT.r * 0.10}
            stroke="var(--accent-2)"
            strokeOpacity={0.7}
            strokeWidth={1.5}
            strokeLinecap="round"
          />
          <circle cx={ARCHITECT.cx} cy={ARCHITECT.cy} r={2} fill="var(--accent-3)" />
        </g>
        {/* Label outside the hex (north-west) so we don't fight the logo */}
        <text
          x={ARCHITECT.cx} y={ARCHITECT.cy + ARCHITECT.r + 22}
          textAnchor="middle"
          fontFamily="var(--font-display)"
          fontSize={18}
          fontWeight={600}
          fill="var(--fg-1)"
          letterSpacing={-0.3}
        >
          Architect
        </text>
        <text
          x={ARCHITECT.cx} y={ARCHITECT.cy + ARCHITECT.r + 36}
          textAnchor="middle"
          fontFamily="var(--font-mono)"
          fontSize={9.5}
          fill="var(--fg-3)"
          letterSpacing={1.4}
        >
          aac
        </text>

        {/* Activity ticker above the hexagon — todo → in_progress → done */}
        <g transform={`translate(${ARCHITECT.cx}, ${ARCHITECT.cy - ARCHITECT.r - 24})`}>
          <rect x={-58} y={-12} width={116} height={24} rx={12}
            fill="var(--bg-2)"
            stroke="var(--line-3)"
            strokeWidth={1}
          />
          <text
            x={0} y={4}
            textAnchor="middle"
            fontFamily="var(--font-mono)"
            fontSize={10}
            fontWeight={600}
            fill={tickIdx === 0 ? 'var(--fg-3)' : tickIdx === 1 ? 'var(--accent-2)' : 'var(--ok)'}
            style={{ transition: 'fill 300ms ease' }}
          >
            {ticks[tickIdx]}
          </text>
        </g>

      {/* 6.5 Moat capabilities — Connections / Plugins / Cron / Subagents.
          The four orbital systems that elevate the platform from "app
          generator" to "self-healing autonomous runtime." Rendered as
          larger hexagons than tool chips, tethered by dashed lines back
          to Architect, with a subtle pulse to suggest live state. */}
      <g aria-label="moat-orbit">
        {moatNodePositions(ARCHITECT.cx, ARCHITECT.cy).map((n) => (
          <g key={n.id} className="moat-pulse" style={{ animationDelay: `${MOAT_NODES.findIndex((m) => m.id === n.id) * 0.6}s` }}>
            {/* Tether to Architect */}
            <line
              x1={ARCHITECT.cx} y1={ARCHITECT.cy}
              x2={n.x} y2={n.y}
              stroke={n.accent}
              strokeOpacity={0.22}
              strokeWidth={1.2}
              strokeDasharray="4 6"
            />
            {/* Halo glow */}
            <circle
              cx={n.x} cy={n.y} r={MOAT_HEX_R + 10}
              fill={n.accent}
              fillOpacity={0.08}
            />
            {/* Hex body */}
            <polygon
              points={pointyTopHexPath(n.x, n.y, MOAT_HEX_R)}
              fill="var(--bg-2)"
              stroke={n.accent}
              strokeWidth={1.6}
              strokeLinejoin="round"
            />
            {/* Initial INSIDE the hex — single letter sized to fit the
                hex body cleanly; the full name goes outside below. This
                is what made the previous version look like the labels
                were "spilling out": "Connections" at full font size was
                wider than the hex itself. */}
            <text
              x={n.x} y={n.y + 6}
              textAnchor="middle"
              fontFamily="var(--font-display)"
              fontSize={22}
              fontWeight={700}
              fill={n.accent}
              letterSpacing={-0.5}
            >
              {n.label[0]}
            </text>
            {/* Full label below the hex */}
            <text
              x={n.x} y={n.y + MOAT_HEX_R + 14}
              textAnchor="middle"
              fontFamily="var(--font-display)"
              fontSize={12}
              fontWeight={600}
              fill="var(--fg-1)"
              letterSpacing={-0.2}
            >
              {n.label}
            </text>
            {/* Sub-caption below the label */}
            <text
              x={n.x} y={n.y + MOAT_HEX_R + 28}
              textAnchor="middle"
              fontFamily="var(--font-mono)"
              fontSize={8.5}
              letterSpacing={1.2}
              fill="var(--fg-3)"
            >
              {n.sub}
            </text>
          </g>
        ))}
      </g>

      {/* 7. Substrate node (autoarchitect-code) — bottom anchor */}
      <g>
        <line
          x1={ARCHITECT.cx} y1={ARCHITECT.cy + ARCHITECT.r + 40}
          x2={SUBSTRATE.cx} y2={SUBSTRATE.cy - SUBSTRATE.r}
          stroke="var(--accent-2)"
          strokeOpacity={0.35}
          strokeWidth={1.2}
          strokeDasharray="3 5"
        />
        <circle cx={SUBSTRATE.cx} cy={SUBSTRATE.cy} r={SUBSTRATE.r} fill="var(--bg-2)" stroke="var(--accent-2)" strokeWidth={1.5} />
        <circle cx={SUBSTRATE.cx} cy={SUBSTRATE.cy} r={SUBSTRATE.r - 6} fill="none" stroke="var(--accent-2)" strokeOpacity={0.4} strokeDasharray="2 3" />
        <text x={SUBSTRATE.cx} y={SUBSTRATE.cy + 4} textAnchor="middle" fontFamily="var(--font-mono)" fontSize={11} fontWeight={700} fill="var(--accent-2)">
          {SUBSTRATE.short}
        </text>
        <text x={SUBSTRATE.cx} y={SUBSTRATE.cy + 50} textAnchor="middle" fontFamily="var(--font-mono)" fontSize={10} letterSpacing={1.4} fill="var(--fg-3)">
          {SUBSTRATE.label.toUpperCase()}
        </text>
      </g>

      {/* CSS keyframes — kept inline so the component is portable. */}
      <style>{`
        @keyframes mesh-breathe {
          0%, 100% { stroke-opacity: 0.05; }
          50%      { stroke-opacity: 0.20; }
        }
        /* Breathing — single smooth in/out, sinusoidal-feeling curve.
           Scale + opacity move together; the slow ~4s cycle reads as
           "alive" without the jerky two-thump cadence the heartbeat
           shape produced. */
        @keyframes architect-breathe {
          0%, 100% { transform: scale(1);     opacity: 0.9;  }
          50%      { transform: scale(1.035); opacity: 1;    }
        }
        @keyframes llm-pulse {
          0%, 100% { opacity: 0.55; }
          50%      { opacity: 0.95; }
        }
        @keyframes subagent-life {
          0%   { opacity: 0; transform: scale(0.55); transform-origin: center; }
          18%  { opacity: 1; transform: scale(1.05); }
          25%  { opacity: 1; transform: scale(1); }
          80%  { opacity: 1; transform: scale(1); }
          100% { opacity: 0; transform: scale(0.6); }
        }
        /* Token-flow on the curved LLM links — dashes drift in opposite
           directions to imply request → response. Slow enough to read
           as data, not as the diagram itself moving. */
        .llm-flow-down {
          stroke-dashoffset: 0;
          animation: llm-flow-down 5s linear infinite;
        }
        .llm-flow-up {
          stroke-dashoffset: 0;
          animation: llm-flow-up 5s linear infinite;
        }
        @keyframes llm-flow-down { to { stroke-dashoffset: -60; } }
        @keyframes llm-flow-up   { to { stroke-dashoffset:  60; } }
        /* Moat orbital nodes — slow ambient pulse, staggered by node so
           the platform reads as four independent systems doing work. */
        .moat-pulse {
          animation: moat-pulse 5.6s ease-in-out infinite;
          transform-origin: center;
        }
        @keyframes moat-pulse {
          0%, 100% { opacity: 0.78; }
          50%      { opacity: 1;    }
        }
      `}</style>
    </svg>
  );
};

window.ArchitectOrbit = ArchitectOrbit;
// Backwards-compat alias — hero.jsx historically rendered <OrchestrationDiagram />.
window.OrchestrationDiagram = ArchitectOrbit;
