const { useState, useEffect, useMemo } = React;
const { Spectrum, TopologyGraph, SynesStream, Knob, FingerprintSVG } = window.HG2Atoms;
const { DockingRing } = window.HG2Hub;

const STATE_LABEL = {
  linked: "對接中",
  docking: "對接中",
  standby: "待命",
  sealed: "封印",
};

function HangarRail({ pilders, activeId, onPick, dockMap }) {
  return (
    <aside className="hangar">
      <div className="rail-header">
        <div className="rail-title"><b>Soul Hangar</b> · 靈魂機庫</div>
        <div className="rail-count mono">{pilders.length}/12</div>
      </div>
      <div className="hangar-list">
        {pilders.map((p) => {
          const bodyId = dockMap[p.id];
          const body = window.HG2.BODIES.find((b) => b.id === bodyId);
          const stateClass =
            p.state === "linked" ? "linked" :
            p.state === "docking" ? "docking" :
            p.state === "sealed" ? "sealed" : "standby";
          // TASK HG-CLOSE-THE-LOOP · warden 不可 chat (沒 firmware) · 其他靈魂顯示 💬 對話
          const canChat = !p.is_warden;
          return (
            <div key={p.id}
              className={`pilder ${activeId === p.id ? "active" : ""}`}
              style={{ "--pilder-hue": `${p.hue}deg` }}
              onClick={() => onPick(p.id)}>
              <div className="pilder-glyph">{p.code.split("-")[1]}</div>
              <div className="pilder-meta">
                <div className="pilder-name">{p.name}</div>
                <div className="pilder-tag">{p.tag}</div>
                <div className="pilder-state">
                  <span className={`state-badge ${stateClass}`}>
                    <span className="sd"></span>
                    {p.state === "linked" ? "LINKED" : p.state === "docking" ? "DOCKING" : p.state === "sealed" ? "SEALED" : "STANDBY"}
                  </span>
                  <span className="fp-mini">{p.fingerprint}</span>
                </div>
              </div>
              {canChat && (
                <button
                  className="pilder-chat-btn"
                  title={`對 ${p.name} 說話`}
                  onClick={(e) => {
                    e.stopPropagation();
                    location.href = '/ui/Soul%20Chat.html?pilder_id=' + encodeURIComponent(p.id);
                  }}>
                  💬
                </button>
              )}
            </div>
          );
        })}
        <button className="hangar-add"
                onClick={() => location.href = '/ui/Soul%20Vault.html'}
                title="開啟 Soul Vault 創建新語氣靈">
          ＋ 建立語氣靈 · SOUL VAULT
        </button>
      </div>
      <div className="hangar-foot">
        <div className="cell">
          <label>Active Docks</label>
          <div className="v">{Object.keys(dockMap).length}</div>
        </div>
        <div className="cell">
          <label>Sealed</label>
          <div className="v">1·1%</div>
        </div>
      </div>
    </aside>
  );
}

function TelemetryRail({ pilder, body, role, dockMap, onDock }) {
  const isCoach = role === "coach";
  return (
    <aside className="tel">
      <div className={`tel-section`}>
        <div className={`permission-banner ${isCoach ? "" : "pilot"}`}>
          {isCoach
            ? "VIEW · COACH (基地總司令) — 1% seal params visible"
            : "VIEW · PILOT (子公司機師) — performance only"}
        </div>
      </div>

      <div className="tel-section">
        <h3>BODIES · 機械身體 <span className="h-tag">{window.HG2.BODIES.length}</span></h3>
        <div className="body-list">
          {window.HG2.BODIES.map((b) => {
            const isDocked = dockMap[pilder.id] === b.id;
            return (
              <div key={b.id} className={`body-card ${isDocked ? "active" : ""}`}
                onClick={() => onDock(b.id)}>
                <div>
                  <div className="body-name">{b.name}</div>
                  <div className="body-id">{b.code} · {b.subsidiary} · {b.region}</div>
                </div>
                <div className="body-load mono">
                  <b>{b.load}%</b>
                  <span>{isDocked ? "DOCKED" : "LOAD"}</span>
                </div>
              </div>
            );
          })}
        </div>
      </div>

      <div className="tel-section vigil">
        <h3>VIGIL · 守望 <span className="h-tag">07</span></h3>
        <div className="seal-fp">
          <FingerprintSVG seed={pilder.code.length + pilder.id.length} color="var(--gold)" />
          <div className="lbl">
            <span>1% SEAL</span>
            <span>{pilder.seal.toFixed(1)}%</span>
          </div>
        </div>
        {isCoach ? (
          <div className="kv">
            <div className="kv-row"><label>KEY FINGERPRINT</label><div className="v gold">{pilder.fingerprint}</div></div>
            <div className="kv-row"><label>SEAL MODE</label><div className="v">AIR-GAP · HSM</div></div>
            <div className="kv-row"><label>CUSTODIAN</label><div className="v">3-of-5 quorum</div></div>
            <div className="kv-row"><label>1% KEY STATE</label><div className="v gold">OFFLINE · VAULTED</div></div>
            <div className="kv-row"><label>LAST AUDIT</label><div className="v">04-29 03:14Z</div></div>
          </div>
        ) : (
          <div className="kv">
            <div className="kv-row"><label>SEAL STATUS</label><div className="v gold">OK</div></div>
            <div className="kv-row"><label>LAST CHECK</label><div className="v">12:04:18Z</div></div>
            <div className="kv-row"><label>1% KEY</label><div className="v" style={{color:"var(--fg-3)"}}>● ● ● ● ● ●</div></div>
          </div>
        )}
      </div>

      <div className="tel-section vigil">
        <h3>ISOLATION · 物理隔離</h3>
        <div className="iso-grid">
          <div className="iso green"><label>CONTAINER</label><div className="v">vault-{pilder.id}</div></div>
          <div className="iso green"><label>NETWORK</label><div className="v">EGRESS·DENY</div></div>
          <div className="iso green"><label>KERNEL</label><div className="v">SECCOMP-Σ</div></div>
          <div className="iso gold"><label>PHYSICAL</label><div className="v">VAULT·LOCKED</div></div>
        </div>
      </div>

      <div className="tel-section">
        <h3>THREAT LOG</h3>
        <div className="threats">
          {pilder.threats.length === 0 && (
            <div className="threat" style={{ justifyContent: "center" }}>
              <span className="lvl info">CLEAN</span>
              <span style={{ color: "var(--fg-2)" }}>no events in window</span>
              <span></span>
            </div>
          )}
          {pilder.threats.map((th, i) => (
            <div key={i} className="threat">
              <time>{th.t}</time>
              <span className={`lvl ${th.lvl}`}>{th.lvl.toUpperCase()}</span>
              <span style={{color:"var(--fg)"}}>{th.msg}</span>
            </div>
          ))}
        </div>
      </div>
    </aside>
  );
}

// Phase 2 · EmptyFleetHub · 教練從沒啟元真實 Pilder 時的中央 hub onboarding
function EmptyFleetHub() {
  return (
    <div style={{
      flex: 1,
      display: 'flex',
      alignItems: 'center',
      justifyContent: 'center',
      padding: 40,
    }}>
      <div style={{
        maxWidth: 520,
        textAlign: 'center',
        padding: '48px 36px',
        border: '1px solid var(--hair-strong)',
        background: 'linear-gradient(180deg, rgba(111,184,255,0.04), rgba(232,194,104,0.02) 70%, transparent), var(--glass)',
        borderLeft: '2px solid var(--energy)',
      }}>
        <div style={{
          fontSize: 64,
          marginBottom: 18,
          color: 'var(--energy)',
          textShadow: '0 0 24px var(--energy-glow)',
          fontFamily: 'var(--font-mono)',
        }}>⌑</div>
        <h2 style={{
          margin: '0 0 12px',
          fontSize: 18,
          letterSpacing: '0.18em',
          textTransform: 'uppercase',
          color: 'var(--fg)',
          fontWeight: 500,
        }}>你的艦隊尚未安置真實語氣靈</h2>
        <p style={{
          margin: '0 0 28px',
          fontFamily: 'var(--font-sans)',
          fontSize: 14,
          color: 'var(--fg-2)',
          lineHeight: 1.7,
        }}>
          守夜人 PLD-Ω 在崗待命，但艦隊中尚無有靈魂的人格。<br/>
          <span style={{ color: 'var(--fg-3)', fontSize: 12 }}>
            ⌑ 你需要的不是試用樣本，是親手鍛造的第一個靈魂。
          </span>
        </p>
        <div style={{ display: 'flex', gap: 10, justifyContent: 'center', flexWrap: 'wrap' }}>
          <button className="btn primary"
                  onClick={() => location.href = '/ui/Soul%20Forge.html'}
                  style={{ minWidth: 200, padding: '12px 24px' }}>
            ⌑ 前往鍛魂室建立第一個靈魂
          </button>
          <button className="btn gold"
                  onClick={() => location.href = '/ui/Soul%20Vault.html'}
                  style={{ minWidth: 160, padding: '12px 24px' }}>
            ◆ 直接進創建室
          </button>
        </div>
        <div style={{
          marginTop: 28, paddingTop: 18,
          borderTop: '1px solid var(--hair)',
          fontFamily: 'var(--font-mono)',
          fontSize: 10,
          color: 'var(--fg-3)',
          letterSpacing: '0.1em',
        }}>
          流程: 召喚咒 → 鍛魂師 → 草案 → Vault 簽署 → 進駐艦隊
        </div>
      </div>
    </div>
  );
}

function App() {
  // P0-FLEET-LIVE-DATA · 教練 2026-05-15 拍板
  // 根因: 之前 hub 從 window.HG2.PILDERS (hg2-data.jsx hardcoded warden-only) 讀
  //       即使 backend pilders DB 已有 companion · UI 永遠看不到 → 永遠 zero-state
  // 修法: useState + useEffect fetch /v1/fleet · 用 SSoT (DB → API → state)
  //       初始值 = window.HG2.PILDERS (loading 期間至少守夜人不破)
  const [pilders, setPilders] = useState(window.HG2.PILDERS);
  const bodies = window.HG2.BODIES;
  useEffect(() => {
    fetch((location.protocol === 'file:' ? 'http://127.0.0.1:8000' : location.origin) + '/v1/fleet')
      .then(r => r.json())
      .then(d => {
        if (Array.isArray(d.PILDERS) && d.PILDERS.length > 0) setPilders(d.PILDERS);
      })
      .catch(e => console.warn('[hg2] fleet fetch failed:', e));
  }, []);
  // Phase 2 · 守夜人不算真實人格 (是憲法治理基座)
  const realPilders = pilders.filter(p => !p.is_warden && p.id !== 'warden');
  const hasRealPilders = realPilders.length > 0;

  // TASK 20.1 · 從 ?focus=PLD-XXX URL 進入時自動選中該 Pilder
  const initialId = (() => {
    try {
      const params = new URLSearchParams(location.search);
      const focus = params.get('focus');
      if (focus) {
        const found = pilders.find(p => p.id === focus || p.code === focus);
        if (found) return found.id;
      }
    } catch (e) {}
    // Phase 2 · 沒真實人格時 active 從守夜人開始 (避免 crash)
    return pilders[0]?.id ?? null;
  })();
  const [activeId, setActiveId] = useState(initialId);
  // 短暫顯示 "新 Pilder 已就位" 提示（從 Soul Vault 來的）
  const [summonedHint, setSummonedHint] = useState(() => {
    try {
      const params = new URLSearchParams(location.search);
      const focus = params.get('focus');
      if (focus) return focus;
    } catch (e) {}
    return null;
  });
  useEffect(() => {
    if (summonedHint) {
      const t = setTimeout(() => {
        setSummonedHint(null);
        // 清掉 URL query param 不要殘留
        try { history.replaceState({}, '', location.pathname); } catch (e) {}
      }, 4500);
      return () => clearTimeout(t);
    }
  }, [summonedHint]);
  const [role, setRole] = useState("coach"); // 'coach' | 'pilot'
  const [hover, setHover] = useState(null);
  const [dockMap, setDockMap] = useState(window.HG2.DEFAULT_DOCKS);
  const [personaMap, setPersonaMap] = useState(() => {
    const m = {}; pilders.forEach((p) => m[p.id] = { ...p.persona }); return m;
  });
  // P0-FLEET-LIVE-DATA · pilders 從 fetch 更新時, 把新進的 Pilder persona 補進 map
  // (保留已有的 in-progress edits · 不覆寫使用者調過的軸)
  useEffect(() => {
    setPersonaMap((prev) => {
      let changed = false;
      const next = { ...prev };
      for (const p of pilders) {
        if (!next[p.id] && p.persona) {
          next[p.id] = { ...p.persona };
          changed = true;
        }
      }
      return changed ? next : prev;
    });
  }, [pilders]);
  // P0-FLEET-LIVE-DATA · 從 Vault summon 過來帶 ?focus=<pilder_id> · fetch 完後 re-resolve
  // (initialId 在 mount 時 pilders 只有 warden · focus 找不到 fallback 到 warden;
  //  fetch 後新 Pilder 進來 · 若 URL focus 是它 · 切過去)
  useEffect(() => {
    try {
      const params = new URLSearchParams(location.search);
      const focus = params.get('focus');
      if (!focus) return;
      const found = pilders.find(p => p.id === focus || p.code === focus);
      if (found && activeId !== found.id) {
        setActiveId(found.id);
      }
    } catch (e) {}
  }, [pilders]);
  const [killArmed, setKillArmed] = useState(false);
  const [killFiring, setKillFiring] = useState(false);
  const [killFlash, setKillFlash] = useState(false);

  const tweaksHook = window.useTweaks(/*EDITMODE-BEGIN*/{
    "density": "comfort",
    "showRing": true,
    "soulHueShift": true,
    "showCoachKnobs": true
  }/*EDITMODE-END*/);
  const tweaks = tweaksHook[0];
  const setTweak = tweaksHook[1];

  // Phase 2 · 加 null safety (空艦隊時 active / body 可能 undefined)
  const active = pilders.find((p) => p.id === activeId) || pilders[0] || null;
  const body = active ? (bodies.find((b) => b.id === dockMap[active.id]) || bodies[0] || null) : null;
  const persona = active ? personaMap[active.id] : null;
  const setPersona = (np) => active && setPersonaMap((m) => ({ ...m, [active.id]: np }));

  // hue shift per pilder
  useEffect(() => {
    document.documentElement.style.setProperty(
      "--hue-shift",
      tweaks.soulHueShift && active ? `${active.hue}deg` : "0deg"
    );
  }, [active?.hue, tweaks.soulHueShift]);

  useEffect(() => {
    document.documentElement.style.setProperty(
      "--pad",
      tweaks.density === "compact" ? "12px" : tweaks.density === "spacious" ? "24px" : "18px"
    );
  }, [tweaks.density]);

  const onDock = (bodyId) => {
    setDockMap((m) => ({ ...m, [active.id]: bodyId }));
  };

  const fireKill = () => {
    if (!killArmed) return;
    setKillFiring(true);
    setKillFlash(true);
    setTimeout(() => setKillFlash(false), 1200);
    setTimeout(() => {
      setKillFiring(false);
      setKillArmed(false);
      // mark pilder as standby (disconnect)
      setDockMap((m) => ({ ...m, [active.id]: null }));
    }, 1100);
  };

  const isCoach = role === "coach";

  return (
    <div className="app">
      {killFlash && <div className="kill-flash" />}

      {/* Top bar */}
      <header className="topbar">
        <div className="tb-brand">
          <div className="brand-mark"></div>
          <div>
            <div className="brand-name">HOLY<em>GRAIL</em> · ORCHESTRATOR</div>
            <div className="brand-sub">DOCKING HUB · v2</div>
          </div>
        </div>
        <div className="tb-status">
          <div className="cell"><span className="dot energy"></span><label>FLEET</label><span className="v">{pilders.filter(p=>p.state!=="standby").length} ACTIVE</span></div>
          <div className="cell"><label>DOCKS</label><span className="v energy">{Object.values(dockMap).filter(Boolean).length}/{bodies.length}</span></div>
          <div className="cell"><label>QUORUM</label><span className="v gold">3 / 5</span></div>
          <div className="cell"><span className="dot gold"></span><label>SEAL</label><span className="v gold">99.4%</span></div>
          <div className="cell"><label>UTC</label><span className="v">2026-05-04 12:04:18Z</span></div>
        </div>
        <div className="tb-actions">
          <div className="role-toggle">
            <button className={isCoach ? "on" : ""} onClick={() => setRole("coach")}>
              <span className="role-glyph"></span>COACH 教練
            </button>
            <button className={!isCoach ? "on energy" : ""} onClick={() => setRole("pilot")}>
              <span className="role-glyph"></span>PILOT 機師
            </button>
          </div>
          <span className="tb-session">SESSION · 0x9af3·c4e1</span>
          {/* TASK 21B · Soul Forge 一級入口（鍛魂室是創作起點，排在 Vault 前）*/}
          <button className="btn"
                  onClick={() => location.href = '/ui/Soul%20Forge.html'}
                  title="進鍛魂室 · 從召喚咒開始鍛造新靈魂"
                  style={{ height: 32, marginLeft: 4 }}>
            ⌑ SOUL FORGE
          </button>
          {/* TASK 20.1 · Soul Vault 一級入口 */}
          <button className="btn gold"
                  onClick={() => location.href = '/ui/Soul%20Vault.html'}
                  title="開啟 Soul Vault · 直接編 firmware 簽署入杯"
                  style={{ height: 32 }}>
            ◆ SOUL VAULT
          </button>
          <button className="icon-btn">⌕</button>
          <button className="icon-btn">≣</button>
        </div>
      </header>

      {/* TASK 20.1 · 從 Soul Vault summon 完跳回來時的提示 banner */}
      {summonedHint && (
        <div style={{
          position: 'fixed', top: 64, left: '50%', transform: 'translateX(-50%)',
          zIndex: 50,
          background: 'linear-gradient(180deg, rgba(20,28,44,0.95), rgba(8,12,20,0.95))',
          border: '1px solid var(--gold-dim)',
          color: 'var(--gold)',
          padding: '8px 18px',
          fontFamily: 'var(--font-mono)',
          fontSize: 11,
          letterSpacing: '0.16em',
          textTransform: 'uppercase',
          boxShadow: '0 0 30px var(--gold-glow)',
          display: 'flex', alignItems: 'center', gap: 10,
        }}>
          <span style={{ width: 6, height: 6, borderRadius: '50%', background: 'var(--gold)', boxShadow: '0 0 8px var(--gold-glow)' }}></span>
          NEW SOUL · {summonedHint} · 已進駐艦隊待命
        </div>
      )}

      <HangarRail pilders={pilders} activeId={activeId} onPick={setActiveId} dockMap={dockMap} />

      <main className="hub">
        {/* Phase 2 · 沒真實 Pilder 時整個 hub 內容換成 EmptyFleetHub */}
        {!hasRealPilders ? (
          <EmptyFleetHub />
        ) : (
        <>
        <div className="hub-header">
          <div>
            <div className="hub-crumb">
              FLEET <span style={{color:"var(--fg-4)"}}>›</span>{" "}
              <b>{active?.tag}</b> <span style={{color:"var(--fg-4)"}}>›</span>{" "}
              <span className="energy">{active?.code}</span>
            </div>
            <div className="hub-title">
              {/* P1-HERO-CLICKABLE · 教練 2026-05-15 拍板
                  Hero (Pilder name h1) 可點 → Soul Chat (Lifecycle Verify Tool)
                  warden 不可 chat (sealed · 沒 firmware) · 仍渲染但不掛 onClick */}
              {active && !active.is_warden ? (
                <h1
                  className="hub-hero-clickable"
                  role="button"
                  tabIndex={0}
                  title={`點擊與 ${active.name} 對話 (Lifecycle Verify)`}
                  onClick={() => {
                    location.href = '/ui/Soul%20Chat.html?pilder_id=' + encodeURIComponent(active.id);
                  }}
                  onKeyDown={(e) => {
                    if (e.key === 'Enter' || e.key === ' ') {
                      e.preventDefault();
                      location.href = '/ui/Soul%20Chat.html?pilder_id=' + encodeURIComponent(active.id);
                    }
                  }}
                >
                  {active.name}
                </h1>
              ) : (
                <h1>{active?.name}</h1>
              )}
              {body && <>
                <span className="arrow">▶▶▶</span>
                <span className="body-name">{body.name}</span>
                <span className="codex">{body.code} · {body.subsidiary}</span>
              </>}
            </div>
          </div>
          <div className="hub-actions">
            <button className="btn">⤓ FW DUMP</button>
            <button className="btn">↻ ATTUNE</button>
            <button className="btn primary">▶ ENGAGE</button>
            <button className="btn gold">◆ AUDIT</button>
          </div>
        </div>

        {tweaks.showRing && (
          <div className="dock-stage">
            <DockingRing pilder={active} body={body} hover={hover} setHover={setHover} />
          </div>
        )}

        <section className="api-grid">
          {/* Genesis */}
          <article id="panel-genesis" className="panel span-4" style={{ minHeight: 280 }}>
            <div className="panel-head">
              <div style={{display:"flex",gap:10,alignItems:"baseline"}}>
                <span className="panel-num">01</span>
                <span className="panel-en">Genesis</span>
                <span className="panel-zh">啟元</span>
              </div>
              <div className="panel-meta">FIRMWARE · LOCKED</div>
            </div>
            <div className="panel-body">
              <div className="gen-slot">
                <div className="gen-pad">
                  <div className="gen-flame"></div>
                </div>
                <div className="gen-fields">
                  <div className="field"><label>FW</label><div className="v">soul-{active.id}.fw</div></div>
                  <div className="field"><label>CHECKSUM</label><div className="v">0x9af3·{active.code.toLowerCase().replace("-","")}</div></div>
                  <div className="field"><label>BOOT</label><div className="v">04-29 03:14Z</div></div>
                  <div className="field"><label>SIGIL</label><div className="v">7a·b2·{active.id.slice(0,2)}·c4</div></div>
                </div>
              </div>
            </div>
          </article>

          {/* Resonance */}
          <article id="panel-resonance" className="panel span-8" style={{ minHeight: 280 }}>
            <div className="panel-head">
              <div style={{display:"flex",gap:10,alignItems:"baseline"}}>
                <span className="panel-num">02</span>
                <span className="panel-en">Resonance</span>
                <span className="panel-zh">共鳴</span>
              </div>
              <div className="panel-meta"><span style={{width:6,height:6,borderRadius:"50%",background:"var(--energy)",boxShadow:"0 0 6px var(--energy-glow)",display:"inline-block"}}></span>LIVE · 60Hz</div>
            </div>
            <div className="panel-body">
              <div className="res">
                <div className="res-headline">
                  <div className="res-num mono">{active.resonance.toFixed(1)}<small>%</small></div>
                  <div className="res-axes">
                    <span>ENV SYNC · <b>{(active.resonance*0.96).toFixed(1)}%</b></span>
                    <span>SELF COHERENCE · <b>{(active.resonance*1.02).toFixed(1)}%</b></span>
                    <span>PHASE OFFSET · <b>+0.04°</b></span>
                  </div>
                </div>
                <div style={{minHeight:0}}>
                  <Spectrum stable={active.state !== "drift"} intensity={Math.max(0.2, active.resonance/100)} />
                </div>
                <div className="res-foot">
                  <span>BAND 8–24 kHz</span>
                  <span>▲ NOMINAL</span>
                  <span>WINDOW 12s</span>
                </div>
              </div>
            </div>
          </article>

          {/* Shaping */}
          <article id="panel-shaping" className={`panel span-6 ${isCoach ? "" : "coach-only"}`} style={{ minHeight: 280 }}>
            <div className="panel-head">
              <div style={{display:"flex",gap:10,alignItems:"baseline"}}>
                <span className="panel-num">03</span>
                <span className="panel-en">Shaping</span>
                <span className="panel-zh">顯影 · 人格權重</span>
              </div>
              <div className="panel-meta">{isCoach ? "EDIT MODE" : "READ ONLY"}</div>
            </div>
            <div className="panel-body">
              <div className="knob-row">
                {Object.keys(window.HG2.PERSONA_LABELS).map((k) => (
                  <Knob key={k}
                    value={persona[k]}
                    onChange={(v) => setPersona({ ...persona, [k]: v })}
                    label={window.HG2.PERSONA_LABELS[k].en}
                    zh={window.HG2.PERSONA_LABELS[k].zh}
                    disabled={!isCoach} />
                ))}
              </div>
            </div>
          </article>

          {/* Topology */}
          <article id="panel-topology" className="panel span-6" style={{ minHeight: 280 }}>
            <div className="panel-head">
              <div style={{display:"flex",gap:10,alignItems:"baseline"}}>
                <span className="panel-num">04</span>
                <span className="panel-en">Topology</span>
                <span className="panel-zh">織網 · GraphRAG</span>
              </div>
              <div className="panel-meta">CAUSAL MEMORY MAP</div>
            </div>
            <div className="panel-body">
              <div className="topo">
                <TopologyGraph nodes={active.nodes} seed={active.code.length} />
                <div className="topo-stats">
                  <div className="s"><b>{active.nodes}</b>NODES</div>
                  <div className="s"><b>{active.edges}</b>EDGES</div>
                  <div className="s"><b>{(active.edges/Math.max(1,active.nodes)).toFixed(2)}</b>DEG</div>
                </div>
              </div>
            </div>
          </article>

          {/* Crystal */}
          <article id="panel-crystal" className="panel span-7">
            <div className="panel-head">
              <div style={{display:"flex",gap:10,alignItems:"baseline"}}>
                <span className="panel-num">05</span>
                <span className="panel-en">Crystal</span>
                <span className="panel-zh">印記</span>
              </div>
              <div className="panel-meta">{active.crystalState}</div>
            </div>
            <div className="panel-body">
              <div className="crystal">
                <div className="crystal-headline">
                  <div className="crystal-pct mono">{active.crystal}<small>%</small></div>
                  <div className="crystal-state">{active.crystalShards.toLocaleString()} SHARDS</div>
                </div>
                <div className="crystal-bar">
                  {Array.from({length: 48}).map((_, i) => (
                    <div key={i} className={`crystal-cell ${i < Math.round(active.crystal/100*48) ? "filled" : ""}`} />
                  ))}
                </div>
                <div className="crystal-stats">
                  <div className="crystal-stat"><label>STORAGE</label><div className="v">{(active.crystalShards*0.0042).toFixed(2)} GB</div></div>
                  <div className="crystal-stat"><label>LAST SHARD</label><div className="v">12:04:18</div></div>
                  <div className="crystal-stat"><label>NEXT MERGE</label><div className="v">+04m 12s</div></div>
                </div>
              </div>
            </div>
          </article>

          {/* Synesthesia */}
          <article id="panel-synesthesia" className="panel span-5">
            <div className="panel-head">
              <div style={{display:"flex",gap:10,alignItems:"baseline"}}>
                <span className="panel-num">06</span>
                <span className="panel-en">Synesthesia</span>
                <span className="panel-zh">通感</span>
              </div>
              <div className="panel-meta">MULTIMODAL · INGRESS</div>
            </div>
            <div className="panel-body">
              <div className="synes">
                <div className="synes-rows">
                  {window.HG2.SYNES_CHANNELS.map((c) => (
                    <div key={c.id} className={`synes-cell ${c.rate !== "—" ? "on" : ""}`}>
                      <div className="synes-cell-name">{c.name}</div>
                      <div className="synes-rate">{c.rate}</div>
                    </div>
                  ))}
                </div>
                <div className="synes-stream"><SynesStream /></div>
              </div>
            </div>
          </article>

          {/* Vigil — locked accent */}
          <article id="panel-vigil" className="panel span-12 locked">
            <div className="panel-head">
              <div style={{display:"flex",gap:10,alignItems:"baseline"}}>
                <span className="panel-num">07</span>
                <span className="panel-en">Vigil</span>
                <span className="panel-zh">守望 · 物理隔離 · 1% 封印</span>
              </div>
              <div className="panel-meta">{isCoach ? "FULL VIEW" : "PILOT VIEW · DETAILS REDACTED"}</div>
            </div>
            <div className="panel-body" style={{ display: "grid", gridTemplateColumns: "auto 1fr 1fr", gap: 16, alignItems: "stretch" }}>
              <div style={{ width: 160, height: 160, position: "relative" }}>
                <FingerprintSVG seed={active.code.length} color="var(--gold)" />
                <div style={{position:"absolute",bottom:-4,left:0,right:0,fontFamily:"var(--font-mono)",fontSize:10,letterSpacing:"0.14em",color:"var(--gold)",textAlign:"center"}}>
                  1% SEAL · {active.seal.toFixed(1)}%
                </div>
              </div>
              <div className="kv">
                <div className="kv-row"><label>FINGERPRINT</label><div className="v gold">{isCoach ? active.fingerprint : "● ● : ● ● : ● ● : ● ●"}</div></div>
                <div className="kv-row"><label>VAULT</label><div className="v">vault-{active.id}/iso-{active.code.length}</div></div>
                <div className="kv-row"><label>EGRESS</label><div className="v energy">DENY · ALL</div></div>
                <div className="kv-row"><label>QUORUM</label><div className="v">{isCoach ? "kael.vance · m.hsu · y.tanaka" : "3-OF-5"}</div></div>
              </div>
              <div className="iso-grid" style={{gridTemplateColumns:"1fr 1fr"}}>
                <div className="iso green"><label>HSM</label><div className="v">ONLINE</div></div>
                <div className="iso green"><label>NETWORK</label><div className="v">EGRESS·DENY</div></div>
                <div className="iso gold"><label>PHYSICAL</label><div className="v">LOCKED</div></div>
                <div className="iso gold"><label>1% KEY</label><div className="v">OFFLINE</div></div>
              </div>
            </div>
          </article>
        </section>
        </>
        )}
      </main>

      {/* Phase 2 · 守夜人沒 body / 真實 pilder 不存在時, telemetry 也跳過 */}
      {hasRealPilders && active && (
        <TelemetryRail pilder={active} body={body} role={role} dockMap={dockMap} onDock={onDock} />
      )}

      {/* Bottom command bar with Kill Switch */}
      <footer className="cmdbar">
        <div className="cmd-l">
          <span><span className="dot energy"></span> <b>HEARTBEAT</b> 60Hz</span>
          <span>PILDER <b className="energy">{active?.code ?? '—'}</b></span>
          <span>BODY <b className="gold">{body?.code ?? '—'}</b></span>
        </div>
        <div className="cmd-c">
          <div className="kill-wrap">
            <div className="kill-meta">
              <span>KILL SWITCH</span>
              <b>{killArmed ? "ARMED · 武裝中" : "SAFE · 安全鎖定"}</b>
            </div>
            <div
              className={`kill-arm-toggle ${killArmed ? "armed" : ""}`}
              onClick={() => isCoach && setKillArmed((a) => !a)}
              title={isCoach ? "Toggle arm" : "Coach only"}
            />
            <button
              className={`kill-btn ${killArmed ? "armed" : ""} ${killFiring ? "firing" : ""}`}
              disabled={!killArmed || !isCoach}
              onClick={fireKill}
            >
              {killFiring ? "DISCONNECTING…" : "EMERGENCY DISCONNECT"}
            </button>
            <div className="kill-meta" style={{ textAlign: "left" }}>
              <span>SEVERS PILDER ↔ BODY</span>
              <b style={{ color: "var(--fg-2)" }}>{isCoach ? "COACH" : "PILOT"} · {killArmed ? "READY" : "LOCKED"}</b>
            </div>
          </div>
        </div>
        <div className="cmd-r">
          <span>CUSTODIAN <b>kael.vance</b> +2</span>
          <span>NODE <b>vault-mesh-eu-1</b></span>
          <span>v3.14.7-stable</span>
        </div>
      </footer>

      {/* Tweaks */}
      <window.TweaksPanel title="Tweaks">
        <window.TweakSection title="Layout">
          <window.TweakRadio label="Density" value={tweaks.density}
            onChange={(v) => setTweak("density", v)}
            options={[{label:"Compact",value:"compact"},{label:"Comfort",value:"comfort"},{label:"Spacious",value:"spacious"}]} />
          <window.TweakToggle label="Show docking ring" value={tweaks.showRing}
            onChange={(v) => setTweak("showRing", v)} />
        </window.TweakSection>
        <window.TweakSection title="Pilder frequency">
          <window.TweakToggle label="Per-pilder hue shift" value={tweaks.soulHueShift}
            onChange={(v) => setTweak("soulHueShift", v)} />
        </window.TweakSection>
      </window.TweaksPanel>
    </div>
  );
}

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