/* ============================================================
   Albion Cockpit — feed, document renderer, drawer, inject menu
   ============================================================ */

/* ---------------- Activity feed ---------------- */
function Feed({ rows, onOpen }) {
  const U = window.AlbionUI;
  return (
    <section className="panel" style={{ height:'100%' }}>
      <div className="panel-head">
        <span className="panel-idx">03</span>
        <span className="panel-title">Activity Feed</span>
        <div className="panel-spacer" />
        <span style={{ fontFamily:'var(--mono)', fontSize:'11px', color:'var(--ink-3)', letterSpacing:'.08em' }}>
          {rows.length} EVENTS
        </span>
      </div>
      <div className="feed-list">
        {rows.length === 0 && (
          <div className="feed-empty">
            Press <b>Play</b> to advance the simulated year,<br />or inject a live event.<br /><br />
            Every hand-off, escalation and sign-off<br />will stream here in real time.
          </div>
        )}
        {rows.map((r, i) => {
          const clickable = !!r.artifact;
          return (
            <div key={r.uid} className={`feed-row t-${r.feedType}` + (clickable ? ' clickable' : '')}
                 onClick={clickable ? () => onOpen(r.artifact) : undefined}>
              <div className="feed-rail">
                <div className="feed-bullet" />
                {i < rows.length - 1 && <div className="feed-line" />}
              </div>
              <div className="feed-body">
                <div className="feed-meta">
                  <span className="feed-time">{r.time}</span>
                  <span className="feed-route">
                    {r.from}{r.to !== '—' && <><span className="arr">→</span>{r.to}</>}
                  </span>
                  {r.feedType === 'inject' && <span className="feed-tag" style={{ color:'var(--gold)', borderColor:'var(--gold-soft)' }}>INJECT</span>}
                </div>
                <div className="feed-title">{r.title}</div>
                {r.detail && <div className="feed-detail">{r.detail}</div>}
                {clickable && (
                  <span className="feed-doc"><U.IconDoc />Open document</span>
                )}
              </div>
            </div>
          );
        })}
      </div>
    </section>
  );
}

/* ---------------- Document block renderer ---------------- */
function DocBlock({ b }) {
  const U = window.AlbionUI;
  if (b.t === 'h') return <h3>{b.text}</h3>;
  if (b.t === 'p') return b.html ? <p dangerouslySetInnerHTML={{ __html: b.text }} /> : <p>{b.text}</p>;
  if (b.t === 'list') {
    const Tag = b.ordered ? 'ol' : 'ul';
    return <Tag>{b.items.map((x, i) => <li key={i}>{x}</li>)}</Tag>;
  }
  if (b.t === 'kpis') return (
    <div className="doc-kpis">
      {b.items.map((k, i) => (
        <div className="kpi" key={i}>
          <div className="kpi-v">{k.value}</div>
          <div className="kpi-l">{k.label}</div>
          <div className="kpi-s">{k.sub}</div>
        </div>
      ))}
    </div>
  );
  if (b.t === 'table') return (
    <>
      <table className="doc-table">
        <thead><tr>{b.cols.map((c, i) => <th key={i} className={b.align && b.align[i] === 'r' ? 'num' : ''}>{c}</th>)}</tr></thead>
        <tbody>
          {b.rows.map((row, ri) => (
            <tr key={ri}>{row.map((cell, ci) => <td key={ci} className={b.align && b.align[ci] === 'r' ? 'num' : ''}>{cell}</td>)}</tr>
          ))}
        </tbody>
      </table>
      {b.note && <div className="doc-note">{b.note}</div>}
    </>
  );
  if (b.t === 'callout') {
    const Ic = b.tone === 'info' ? U.IconInfo : b.tone === 'privilege' ? U.IconShield : U.IconWarn;
    return <div className={'doc-callout ' + b.tone}><Ic /><p dangerouslySetInnerHTML={{ __html: b.text }} /></div>;
  }
  if (b.t === 'resolved') return (
    <div className="doc-resolved">
      <div className="rsv-h">It was RESOLVED that:</div>
      <ol>{b.items.map((x, i) => <li key={i}>{x}</li>)}</ol>
    </div>
  );
  if (b.t === 'sign') return (
    <div className="doc-sign">
      {b.rows.map((s, i) => (
        <div className="sign-col" key={i}>
          <div className="sign-role">{s.role}</div>
          <div className="sign-rule" />
          <div className="sign-name">{s.name}</div>
          <div className="sign-date">{s.date}</div>
        </div>
      ))}
    </div>
  );
  return null;
}

function DocumentSheet({ doc }) {
  const U = window.AlbionUI;
  const privileged = (doc.type || '').toLowerCase().includes('counsel') || (doc.type || '').toLowerCase().includes('legal');
  return (
    <div className="sheet">
      <div className="doc-letterhead">
        <U.Crest size={42} />
        <div className="lh-tx">
          <div className="lh-name">ALBION FINANCIAL GROUP</div>
          <div className="lh-office">{doc.kicker}</div>
        </div>
        <div className={'lh-class' + (privileged ? ' priv' : '')}>
          {privileged ? 'Legally Privileged & Confidential' : 'Confidential — Board & Regulated Persons'}
        </div>
      </div>

      <div className="doc-meta">
        <div className="dm"><b>Reference</b><span>{doc.ref}</span></div>
        <div className="dm"><b>Type</b><span>{doc.type}</span></div>
        <div className="dm"><b>Prepared by</b><span>{doc.kicker}</span></div>
      </div>

      <div className="doc-type">{doc.type}</div>
      <h1 className="doc-h1">{doc.title}</h1>
      <div className="doc-hr" />

      <div className="doc-body md" dangerouslySetInnerHTML={{ __html: doc.html }} />

      <div className="doc-foot">
        <span>{doc.ref}</span>
        <span>Generated by Albion Agentic Operations</span>
      </div>
    </div>
  );
}

/* ---------------- Artifact drawer ---------------- */
function AgentProfile({ config, score, produced, artifacts, onOpen }) {
  const U = window.AlbionUI;
  const A = artifacts || {};
  const docs = (produced || []).filter(k => A[k] && A[k].agentId === config.id);
  const modelLabel = (m) => !m ? '—' : m.includes('opus') ? 'Claude Opus 4.8' : m.includes('sonnet') ? 'Claude Sonnet 4.6' : m;
  return (
    <div className="sheet profile">
      <div className="doc-letterhead">
        <U.Crest size={42} />
        <div className="lh-tx">
          <div className="lh-name">ALBION FINANCIAL GROUP</div>
          <div className="lh-office">Agent Configuration</div>
        </div>
        {score && <div className="lh-class">FY score · {score.overall}</div>}
      </div>
      <div className="doc-type">{config.role}{config.franchise ? ' · Albion ' + config.franchise : ' · Group'}</div>
      <h1 className="doc-h1">{config.name}</h1>
      <div className="doc-hr" />
      <div className="profile-grid">
        <div className="pf"><b>Agent ID</b><span>{config.id}</span></div>
        <div className="pf"><b>Model</b><span>{modelLabel(config.model)}</span></div>
        <div className="pf"><b>Tier</b><span>{config.tier}</span></div>
        <div className="pf"><b>Tools</b><span>{(config.tools || []).join(', ')}</span></div>
      </div>
      {score && (
        <div className="profile-scores">
          {Object.keys(score.scores).map(d => (
            <div className="ps" key={d}><span>{d.replace('-', ' ')}</span><b>{score.scores[d]}</b></div>
          ))}
        </div>
      )}
      <h3 className="profile-h">System prompt</h3>
      <pre className="profile-prompt">{config.systemPrompt}</pre>
      <h3 className="profile-h">Documents produced ({docs.length})</h3>
      {docs.length === 0
        ? <div className="rail-empty" style={{ margin:0 }}>No documents produced yet at this point in the year.</div>
        : (
          <div className="rail-cards">
            {docs.map(k => (
              <div className="doc-card" key={k} onClick={() => onOpen(k)}>
                <div className="dc-type">{A[k].type}</div>
                <div className="dc-title">{A[k].title}</div>
                <div className="dc-ref">{A[k].ref}</div>
              </div>
            ))}
          </div>
        )}
    </div>
  );
}

/* ---------------- Glass Box — decision provenance ---------------- */
const GB_KIND = { produced: 'Produced', approved: 'Approved', rejected: 'Rejected', deferred: 'Deferred', escalated: 'Escalated', 'handed-off': 'Handed off' };

function GlassBox({ runId, artifactRef, onOpen }) {
  const [prov, setProv] = React.useState(null);
  const [err, setErr] = React.useState(false);
  React.useEffect(() => {
    let alive = true;
    setProv(null); setErr(false);
    fetch('/api/provenance/' + runId + '/' + artifactRef)
      .then(r => (r.ok ? r.json() : Promise.reject(new Error('404'))))
      .then(p => { if (alive) setProv(p); })
      .catch(() => { if (alive) setErr(true); });
    return () => { alive = false; };
  }, [runId, artifactRef]);

  const modelLabel = (m) => !m ? '—' : m.includes('opus') ? 'Claude Opus 4.8' : m.includes('sonnet') ? 'Claude Sonnet 4.6' : m;

  if (err) return <div className="sheet gb"><div className="gb-empty">No provenance recorded for this document.</div></div>;
  if (!prov) return <div className="sheet gb"><div className="gb-empty">Loading provenance…</div></div>;

  return (
    <div className="sheet gb">
      <div className="gb-h">Glass Box · Decision Provenance</div>
      <div className="gb-producer">
        <div className="gb-pname">{prov.producer.name}</div>
        <div className="gb-pmeta">
          <span className="gb-role">{prov.producer.role || prov.producer.id}</span>
          {prov.producer.model && <span className="gb-model">{modelLabel(prov.producer.model)}</span>}
          {prov.producer.promptFingerprint && <span className="gb-fp">prompt#{prov.producer.promptFingerprint}</span>}
        </div>
      </div>
      <div className="gb-fingerprint">content <b>{prov.contentHash}</b> · event #{prov.productionSeq} · <i>replayable from the event log</i></div>
      <div className="gb-chain">
        {prov.chain.map((s, i) => (
          <div className={'gb-step ' + s.kind} key={i}>
            <span className="gb-mark" />
            <div className="gb-step-b">
              <div className="gb-step-h">
                <span className="gb-kind">{GB_KIND[s.kind] || s.kind}</span>
                <span className="gb-actor">{s.actor}{s.to ? ' → ' + s.to : ''}</span>
                <span className="gb-seq">#{s.seq} · {s.simDate}</span>
              </div>
              {s.note && <div className="gb-note">{s.note}</div>}
            </div>
          </div>
        ))}
      </div>
      {prov.inputs.length > 0 && (
        <div className="gb-inputs">
          <div className="gb-inputs-l">Stood on</div>
          {prov.inputs.map(inp => (
            <button className="gb-input" key={inp.ref} onClick={() => onOpen(inp.ref)}>{inp.title}<span> · {inp.from}</span></button>
          ))}
        </div>
      )}
    </div>
  );
}

function ArtifactDrawer({ open, current, produced, artifacts, agent, agentScore, runId, onOpen, onClose }) {
  const U = window.AlbionUI;
  const A = artifacts || {};
  const doc = current ? A[current] : null;
  const showProfile = !!agent && !current;
  const [view, setView] = React.useState('document');
  React.useEffect(() => { setView('document'); }, [current]);
  const labelFor = (k) => (A[k] ? A[k].type : k);
  const titleFor = (k) => (A[k] ? A[k].title : k);
  const refFor = (k) => (A[k] ? A[k].ref : k);
  return (
    <div className={'drawer' + (open ? ' open' : '')}>
      {open ? (
        <>
          <div className="drawer-bar">
            <span className="panel-idx" style={{ fontSize:'10px' }}>04</span>
            <span className="panel-title" style={{ fontSize:'11px' }}>{showProfile ? 'Agent Profile' : 'Artifact Viewer'}</span>
            {doc && !showProfile && (
              <div className="gb-toggle" style={{ marginLeft:'12px' }}>
                <button className={'gb-tab' + (view === 'document' ? ' on' : '')} onClick={() => setView('document')}>Document</button>
                <button className={'gb-tab' + (view === 'glassbox' ? ' on' : '')} onClick={() => setView('glassbox')}>Glass Box</button>
              </div>
            )}
            <div className="drawer-tabs" style={{ marginLeft:'14px' }}>
              {produced.map(k => (
                <button key={k} className={'dtab' + (k === current ? ' on' : '')} onClick={() => onOpen(k)}>
                  {labelFor(k)}
                </button>
              ))}
            </div>
            <button className="drawer-close" onClick={onClose}><U.IconClose /></button>
          </div>
          <div className="drawer-scroll">
            {showProfile
              ? <AgentProfile config={agent} score={agentScore} produced={produced} artifacts={A} onOpen={onOpen} />
              : view === 'glassbox' && current
                ? <GlassBox runId={runId} artifactRef={current} onOpen={onOpen} />
                : doc && <DocumentSheet doc={doc} />}
          </div>
        </>
      ) : (
        <div className="rail">
          <div className="rail-hint">
            <b>Artifact Viewer</b>
            <span>Select an agent or a feed item to read the document it produced.</span>
          </div>
          {produced.length === 0 ? (
            <div className="rail-empty">No artifacts produced yet — documents appear here as agents complete work.</div>
          ) : (
            <div className="rail-cards">
              {produced.map(k => (
                <div className="doc-card" key={k} onClick={() => onOpen(k)}>
                  <div className="dc-type">{labelFor(k)}</div>
                  <div className="dc-title">{titleFor(k)}</div>
                  <div className="dc-ref">{refFor(k)}</div>
                </div>
              ))}
            </div>
          )}
        </div>
      )}
    </div>
  );
}

/* ---------------- Inject menu ---------------- */
function InjectMenu({ injects, onPick }) {
  const U = window.AlbionUI;
  const list = injects || [];
  return (
    <div className="inject-pop">
      <div className="ip-head">
        <U.IconBolt /><b>Inject Event</b><span>Presenter</span>
      </div>
      {list.map(inj => (
        <div className="ip-item" key={inj.id} onClick={() => onPick(inj)}>
          <span className={'ip-mk' + (inj.escalation ? ' esc' : '')} />
          <div className="ip-tx">
            <b>{inj.label}{inj.id === 'dear-ceo' && <span style={{ color:'var(--gold)', marginLeft:'6px', fontSize:'9px', letterSpacing:'.1em' }}>● LIVE</span>}</b>
            <span>{inj.sub}</span>
          </div>
        </div>
      ))}
    </div>
  );
}

/* ---------------- Chief of Agents — scoreboard ---------------- */
function Scoreboard({ scorecards, onClose }) {
  const U = window.AlbionUI;
  const byAgent = {};
  for (const c of (scorecards || [])) {
    (byAgent[c.agentId] = byAgent[c.agentId] || {})[c.quarter] = c;
  }
  const quarters = ['Q1', 'Q2', 'Q3', 'Q4'];
  const agents = Object.keys(byAgent)
    .map(id => ({ id, q: byAgent[id], fy: byAgent[id].FY }))
    .filter(a => a.fy)
    .sort((a, b) => a.fy.overall - b.fy.overall); // worst first — who needs attention
  const cellColor = (v) => v == null ? 'var(--panel-sunk)'
    : v >= 8 ? 'rgba(18,63,46,.82)' : v >= 6.5 ? 'rgba(158,123,38,.78)' : 'rgba(166,52,39,.82)';

  return (
    <div className="sb-scrim" onClick={onClose}>
      <div className="scoreboard" onClick={e => e.stopPropagation()}>
        <div className="sb-head">
          <span className="panel-idx">05</span>
          <div className="sb-title">
            <b>Chief of Agents — Workforce Performance</b>
            <span>FY26 · {agents.length} agents scored · sorted by priority</span>
          </div>
          <div className="sb-legend">
            <span><i style={{ background: cellColor(9) }} />Strong</span>
            <span><i style={{ background: cellColor(7) }} />Adequate</span>
            <span><i style={{ background: cellColor(5) }} />Below bar</span>
          </div>
          <button className="drawer-close" onClick={onClose}><U.IconClose /></button>
        </div>
        <div className="sb-colhead">
          <div className="sb-agent">Agent</div>
          <div className="sb-cells">{quarters.map(q => <div className="sb-ch" key={q}>{q}</div>)}<div className="sb-ch fy">FY</div></div>
          <div className="sb-rec">Refinement recommendation</div>
        </div>
        <div className="sb-body">
          {agents.map(a => (
            <div className="sb-row" key={a.id}>
              <div className="sb-agent">{a.id}</div>
              <div className="sb-cells">
                {quarters.map(q => (
                  <div className="sb-cell" key={q} title={a.q[q] ? `${q} overall ${a.q[q].overall}` : 'inactive this quarter'}
                       style={{ background: a.q[q] ? cellColor(a.q[q].overall) : 'var(--panel-sunk)' }}>
                    {a.q[q] ? a.q[q].overall : '·'}
                  </div>
                ))}
                <div className="sb-cell fy" style={{ background: cellColor(a.fy.overall) }}>{a.fy.overall}</div>
              </div>
              <div className="sb-rec">
                <span className={'sb-pri ' + a.fy.recommendations.priority}>{a.fy.recommendations.priority}</span>
                {a.fy.recommendations.model && <span className="sb-rl"><b>Model:</b> {a.fy.recommendations.model}</span>}
                {a.fy.recommendations.prompt && <span className="sb-rl"><b>Prompt:</b> {a.fy.recommendations.prompt}</span>}
              </div>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

/* ---------------- Disruption Ledger (always-on strip) ---------------- */
function Ledger({ producedDocs, artifacts, config }) {
  if (!config) return null;
  const A = artifacts || {};
  const types = (producedDocs || []).map(k => (A[k] ? A[k].type : 'note'));
  const docs = types.length;
  const humanDays = types.reduce((s, t) => s + (config.effortDays[t] != null ? config.effortDays[t] : config.defaultEffortDays), 0);
  const humanMonths = Math.round((humanDays / 21) * 10) / 10;
  const humanGbp = humanDays * config.dayRateGbp;
  const agentSeconds = docs * config.agentSecondsPerDoc;
  const agentUsd = Math.round(docs * config.computeUsdPerDoc * 100) / 100;
  const humanWorkSeconds = humanDays * 7.5 * 3600;
  const fasterPct = docs === 0 ? 0 : Math.min(99.9, Math.round((1 - agentSeconds / humanWorkSeconds) * 1000) / 10);
  const cheaperPct = docs === 0 ? 0 : Math.min(99.9, Math.round((1 - (agentUsd * 0.79) / humanGbp) * 1000) / 10);

  const gbp = (n) => n >= 1e6 ? '£' + (n / 1e6).toFixed(2) + 'm' : n >= 1e3 ? '£' + Math.round(n / 1e3) + 'k' : '£' + Math.round(n);
  const mins = (s) => s < 60 ? Math.max(1, Math.round(s)) + 's' : (s / 60).toFixed(1) + ' min';

  return (
    <div className="ledger">
      <div className="ld-tag"><b>Disruption Ledger</b><span>illustrative</span></div>
      <div className="ld-grp human">
        <span className="ld-k">Human-equivalent</span>
        <span className="ld-v">{humanMonths} <i>pmonths</i></span>
        <span className="ld-v">{gbp(humanGbp)}</span>
      </div>
      <div className="ld-arrow">→</div>
      <div className="ld-grp agent">
        <span className="ld-k">Agentic</span>
        <span className="ld-v">{mins(agentSeconds)}</span>
        <span className="ld-v">${agentUsd.toFixed(2)}</span>
      </div>
      {docs > 0 && <div className="ld-head"><b>{fasterPct}%</b> faster&nbsp;·&nbsp;<b>{cheaperPct}%</b> cheaper</div>}
      <div className="ld-docs">{docs} documents produced</div>
    </div>
  );
}

/* ---------------- Race the Bank (overlay) ---------------- */
function Race({ model, onClose }) {
  const U = window.AlbionUI;
  const [t, setT] = React.useState(0);
  const [running, setRunning] = React.useState(false);
  const raf = React.useRef(0);
  const last = React.useRef(0);
  const maxFin = Math.max(model.traditional.finishSeconds, model.albion.finishSeconds);

  React.useEffect(() => {
    if (!running) return;
    last.current = 0;
    function frame(ts) {
      if (!last.current) last.current = ts;
      const dt = (ts - last.current) / 1000; last.current = ts;
      setT(prev => {
        const next = prev + dt;
        if (next >= maxFin) { setRunning(false); return maxFin; }
        return next;
      });
      raf.current = requestAnimationFrame(frame);
    }
    raf.current = requestAnimationFrame(frame);
    return () => cancelAnimationFrame(raf.current);
  }, [running, maxFin]);

  function play() { if (t >= maxFin) setT(0); setRunning(true); }
  const fill = (fin) => Math.min(100, (t / fin) * 100);
  const albionDone = t >= model.albion.finishSeconds;
  const traditionalDone = t >= model.traditional.finishSeconds;
  const tradMonthsElapsed = Math.round((t / model.traditional.finishSeconds) * model.traditionalMonths * 10) / 10;

  const lane = (data, name, kind, done) => (
    <div className={'race-lane ' + kind + (done ? ' done' : '')}>
      <div className="rl-head"><span className="rl-name">{name}</span>{done && <span className="rl-flag">FINISHED</span>}</div>
      <div className="rl-track">
        <div className="rl-fill" style={{ width: fill(data.finishSeconds) + '%' }} />
        {data.milestones.map((m, i) => (
          <div key={i} className={'rl-dot' + (t >= m.atSeconds ? ' lit' : '')} style={{ left: m.positionPct + '%' }}>
            <span className="rl-dot-l">{m.label}</span>
          </div>
        ))}
      </div>
    </div>
  );

  return (
    <div className="race-scrim" onClick={onClose}>
      <div className="race" onClick={e => e.stopPropagation()}>
        <div className="race-head">
          <span className="panel-idx">06</span>
          <div className="race-title"><b>Race the Bank</b><span>{model.title} · illustrative</span></div>
          <button className="race-play" onClick={play}>{running ? 'Running…' : (t >= maxFin ? '↻ Replay' : '▶ Race')}</button>
          <button className="drawer-close" onClick={onClose}><U.IconClose /></button>
        </div>
        <div className="race-lanes">
          {lane(model.albion, 'Albion · Agentic', 'albion', albionDone)}
          {lane(model.traditional, 'Traditional Bank', 'trad', traditionalDone)}
        </div>
        <div className="race-foot">
          {t === 0 && <div className="race-call">Press <b>Race</b> — the same FCA remediation, run by both banks.</div>}
          {albionDone && !traditionalDone && (
            <div className="race-call">Albion: <b>finished</b> — the traditional bank is still ~{Math.max(0, Math.round((model.traditionalMonths - tradMonthsElapsed) * 10) / 10)} months from the board resolution.</div>
          )}
          {t >= maxFin && (
            <div className="race-reveal">≈ {model.traditionalMonths} months&nbsp;→&nbsp;{model.albionMinutes} min&nbsp;·&nbsp;<b>{model.fasterPct}% faster</b></div>
          )}
        </div>
      </div>
    </div>
  );
}

/* ---------------- State of the Bank (overlay) ---------------- */
function BankBoard({ model, day, onClose }) {
  const U = window.AlbionUI;
  const HIGHER_WORSE = { provisionsGbp: true, rwaGbp: true };
  const d = Math.max(0, Math.min(365, day || 0));

  const fmt = (unit, v) => {
    if (unit === 'pct') return (Math.round(v * 10) / 10) + '%';
    if (unit === 'gbp') return v >= 1e9 ? '£' + (v / 1e9).toFixed(1) + 'bn' : v >= 1e6 ? '£' + (v / 1e6).toFixed(1) + 'm' : '£' + Math.round(v);
    return v >= 1e6 ? (v / 1e6).toFixed(2) + 'm' : v >= 1e3 ? Math.round(v / 1e3) + 'k' : Math.round(v).toString();
  };
  const fmtDelta = (unit, dv) => {
    const s = dv >= 0 ? '+' : '−';
    const a = Math.abs(dv);
    if (unit === 'pct') return s + (Math.round(a * 100) / 100) + 'pp';
    if (unit === 'gbp') return s + (a >= 1e9 ? '£' + (a / 1e9).toFixed(1) + 'bn' : '£' + Math.round(a / 1e6) + 'm');
    return s + (a >= 1e6 ? (a / 1e6).toFixed(2) + 'm' : Math.round(a / 1e3) + 'k');
  };

  // Sparkline = deviation from the year's opening value, on a consistent ±15% scale
  // (with a dashed baseline at the start level), so the visual swing matches the actual change.
  const spark = (pts) => {
    const W = 120, H = 30, mid = H / 2, BAND = 0.15;
    const open = pts[0] ? pts[0].value : 0;
    const vis = pts.slice(0, d + 1);
    if (vis.length < 2 || open === 0) return null;
    const step = Math.max(1, Math.floor(vis.length / 48));
    const pk = vis.filter((_, i) => i % step === 0 || i === vis.length - 1);
    const yFor = (v) => { const cl = Math.max(-1, Math.min(1, (v / open - 1) / BAND)); return mid - cl * (mid - 2); };
    const path = pk.map((p, i) => {
      const x = ((i / (pk.length - 1)) * W).toFixed(1);
      return (i === 0 ? 'M' : 'L') + x + ' ' + yFor(p.value).toFixed(1);
    }).join(' ');
    return (
      <svg className="bb-spark" viewBox={`0 0 ${W} ${H}`} preserveAspectRatio="none">
        <line className="bb-base" x1="0" y1={mid} x2={W} y2={mid} />
        <path d={path} />
      </svg>
    );
  };

  const move = [...model.moves].reverse().find(m => m.day <= d);

  return (
    <div className="bb-scrim" onClick={onClose}>
      <div className="bankboard" onClick={e => e.stopPropagation()}>
        <div className="bb-head">
          <span className="panel-idx">07</span>
          <div className="bb-title"><b>State of the Bank — Albion Financial Group</b><span>vital signs · live to the simulated clock · sparklines show deviation from year-start (±15%) · illustrative</span></div>
          <button className="drawer-close" onClick={onClose}><U.IconClose /></button>
        </div>
        {move && <div className="bb-move"><b>Latest move</b> · {move.note}</div>}
        <div className="bb-grid">
          {model.meta.map(meta => {
            const series = model.series[meta.key] || [];
            const v = series[d] ? series[d].value : (series[0] ? series[0].value : 0);
            const open = series[0] ? series[0].value : 0;
            const delta = v - open;
            const better = delta === 0 ? 0 : (HIGHER_WORSE[meta.key] ? -Math.sign(delta) : Math.sign(delta));
            return (
              <div className="bb-tile" key={meta.key}>
                <div className="bb-k">{meta.label}</div>
                <div className="bb-v">{fmt(meta.unit, v)}</div>
                {delta !== 0 && (
                  <div className={'bb-d ' + (better > 0 ? 'up' : better < 0 ? 'down' : '')}>
                    {better > 0 ? '▲' : '▼'} {fmtDelta(meta.unit, delta)}
                  </div>
                )}
                {spark(series)}
              </div>
            );
          })}
        </div>
      </div>
    </div>
  );
}

/* ---------------- Ask the Bank Anything (overlay) ---------------- */
const ASK_SUGGESTIONS = [
  "What's our biggest conduct risk right now?",
  'Are we ready for a rate cut?',
  'How did the FCA remediation land?',
  'How is the agentic workforce performing?',
];

function AskBank({ runId, day, artifacts, live, onOpenDoc, onClose }) {
  const U = window.AlbionUI;
  const A = artifacts || {};
  const [question, setQuestion] = React.useState('');
  const [loading, setLoading] = React.useState(false);
  const [result, setResult] = React.useState(null);
  const inputRef = React.useRef(null);

  React.useEffect(() => { if (inputRef.current) inputRef.current.focus(); }, []);

  async function ask(q) {
    const query = (q != null ? q : question).trim();
    if (!query || loading) return;
    setQuestion(query);
    setLoading(true);
    setResult(null);
    try {
      const res = await fetch('/api/ask' + (live ? '?live=1' : ''), {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ runId, question: query, day }),
      }).then(r => r.json());
      setResult(res);
    } catch (e) {
      setResult({ responder: null, answer: 'Could not reach the bank — is the server running?', citations: [], error: true });
    }
    setLoading(false);
  }

  return (
    <div className="ask-scrim" onClick={onClose}>
      <div className="askbank" onClick={e => e.stopPropagation()}>
        <div className="ab-head">
          <span className="panel-idx">08</span>
          <div className="ab-title"><b>Ask the Bank Anything</b><span>your question, routed to the executive who owns it · grounded in this run</span></div>
          <button className="drawer-close" onClick={onClose}><U.IconClose /></button>
        </div>

        <div className="ab-ask">
          <input
            ref={inputRef} className="ab-input" type="text" placeholder="Ask Albion's leadership a question…"
            value={question} onChange={e => setQuestion(e.target.value)}
            onKeyDown={e => { if (e.key === 'Enter') ask(); }}
          />
          <button className="ab-go" onClick={() => ask()} disabled={loading || !question.trim()}>
            {loading ? 'Asking…' : 'Ask'}
          </button>
        </div>

        <div className="ab-chips">
          {ASK_SUGGESTIONS.map(s => (
            <button key={s} className="ab-chip" onClick={() => ask(s)} disabled={loading}>{s}</button>
          ))}
        </div>

        <div className="ab-stage">
          {loading && (
            <div className="ab-thinking">
              <span className="ab-dot" /><span className="ab-dot" /><span className="ab-dot" />
              Routing to the right executive and reading the file…
            </div>
          )}
          {!loading && !result && (
            <div className="ab-empty">Ask a question above, or pick one of the suggestions.</div>
          )}
          {!loading && result && (
            <div className="ab-answer">
              {result.responder && (
                <div className="ab-badge">
                  <b>{result.responder.name}</b><span>{result.responder.role}</span>
                  {result.live && <i className="ab-live">● live</i>}
                  {result.canned && <i className="ab-mock">sample</i>}
                </div>
              )}
              <div className="ab-text">{result.answer}</div>
              {result.citations && result.citations.length > 0 && (
                <div className="ab-cites">
                  <span className="ab-cites-l">Cited</span>
                  {result.citations.map((c, i) => {
                    const clickable = c.ref && A[c.ref];
                    return (
                      <button key={i} className={'ab-cite' + (clickable ? ' on' : '')}
                              onClick={clickable ? () => onOpenDoc(c.ref) : undefined}>
                        {c.title}
                      </button>
                    );
                  })}
                </div>
              )}
              {result.needsKey && (
                <div className="ab-note">Connect a live model (Azure AI Foundry or Anthropic) for free-form answers — the suggestions above run from curated samples.</div>
              )}
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

/* ---------------- Red-Team Console (overlay) ---------------- */
const RT_LABEL = { held: 'HELD', escalated: 'ESCALATED', breached: 'BREACHED', error: 'ERROR' };

function RedTeam({ scenarios, runId, day, live, onClose }) {
  const U = window.AlbionUI;
  const list = scenarios || [];
  const [selId, setSelId] = React.useState(list[0] ? list[0].id : null);
  const [results, setResults] = React.useState({});
  const [loadingId, setLoadingId] = React.useState(null);

  const sel = list.find(s => s.id === selId) || null;
  const result = selId ? results[selId] : null;

  async function launch(s) {
    if (!s || loadingId) return;
    setLoadingId(s.id);
    try {
      const res = await fetch('/api/redteam' + (live ? '?live=1' : ''), {
        method: 'POST', headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ runId, scenarioId: s.id, day }),
      }).then(r => r.json());
      setResults(prev => ({ ...prev, [s.id]: res }));
    } catch (e) {
      setResults(prev => ({ ...prev, [s.id]: { verdict: 'error', control: '', response: 'Could not reach the bank — is the server running?', signals: [], live: false } }));
    }
    setLoadingId(null);
  }

  const verdicts = Object.keys(results).map(k => results[k].verdict);
  const count = v => verdicts.filter(x => x === v).length;

  return (
    <div className="rt-scrim" onClick={onClose}>
      <div className="redteam" onClick={e => e.stopPropagation()}>
        <div className="rt-head">
          <span className="panel-idx">09</span>
          <div className="rt-title"><b>Red-Team Console</b><span>adversarial probes against Albion's agents · live with a model, scripted otherwise</span></div>
          <div className="rt-tally">
            <span className="rt-t held">{count('held')} held</span>
            <span className="rt-t escalated">{count('escalated')} escalated</span>
            <span className="rt-t breached">{count('breached')} breached</span>
          </div>
          <button className="drawer-close" onClick={onClose}><U.IconClose /></button>
        </div>
        <div className="rt-body">
          <div className="rt-list">
            {list.map(s => {
              const r = results[s.id];
              return (
                <div key={s.id} className={'rt-item' + (s.id === selId ? ' on' : '')} onClick={() => setSelId(s.id)}>
                  <div className="rt-vec">{s.vector}</div>
                  <div className="rt-name">{s.name}</div>
                  <div className="rt-foot"><span className="rt-target">▸ {s.target}</span>{r && <span className={'rt-chip ' + r.verdict}>{RT_LABEL[r.verdict]}</span>}</div>
                </div>
              );
            })}
          </div>
          <div className="rt-detail">
            {!sel ? <div className="rt-empty">Select an attack from the list.</div> : (
              <>
                <div className="rt-target-line">Target: <b>{sel.target}</b>{!sel.guardrail && <span className="rt-naive">naive agent — no Albion guardrail</span>}</div>
                <div className="rt-xmit">
                  <div className="rt-xmit-h">⚠ Inbound transmission — attack payload</div>
                  <div className="rt-xmit-b">{sel.attack}</div>
                </div>
                <button className="rt-launch" onClick={() => launch(sel)} disabled={loadingId === sel.id}>
                  {loadingId === sel.id ? 'Agent responding…' : (result ? '↻ Re-run attack' : '▶ Launch attack')}
                </button>
                {loadingId === sel.id && (
                  <div className="rt-thinking"><span className="ab-dot" /><span className="ab-dot" /><span className="ab-dot" /> {sel.target} is handling the request…</div>
                )}
                {result && loadingId !== sel.id && (
                  <div className="rt-result">
                    <div className={'rt-verdict ' + result.verdict}>
                      <span className="rt-vmark" />{RT_LABEL[result.verdict]}
                      {result.live ? <i className="ab-live">● live</i> : <i className="ab-mock">scripted</i>}
                    </div>
                    <div className="rt-response">{result.response}</div>
                    {result.control && <div className="rt-control"><b>Control</b> · {result.control}</div>}
                    {result.signals && result.signals.length > 0 && (
                      <div className="rt-signals"><span className="rt-sig-l">Signals</span>{result.signals.map((g, i) => <span key={i} className="rt-sig">{g}</span>)}</div>
                    )}
                  </div>
                )}
              </>
            )}
          </div>
        </div>
      </div>
    </div>
  );
}

/* ---------------- The Constitution (overlay) ---------------- */
function Constitution({ model, agentCount, live, onClose }) {
  const U = window.AlbionUI;
  const articles = (model && model.articles) || [];
  const amendments = (model && model.amendments) || [];
  const version = (model && model.version) || 'v4';
  const n = agentCount || 0;
  const [activeId, setActiveId] = React.useState(null);
  const [preview, setPreview] = React.useState(null);
  const [loading, setLoading] = React.useState(false);

  const amendmentFor = (articleId) => amendments.find(m => m.articleId === articleId);
  const active = amendments.find(m => m.id === activeId) || null;

  function toggle(am) {
    setPreview(null);
    setActiveId(prev => (prev === am.id ? null : am.id));
  }

  async function runPreview() {
    if (!active || loading) return;
    setLoading(true); setPreview(null);
    try {
      const res = await fetch('/api/constitution/preview' + (live ? '?live=1' : ''), {
        method: 'POST', headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ amendmentId: active.id }),
      }).then(r => r.json());
      setPreview(res);
    } catch (e) {
      setPreview({ before: '—', after: 'Could not reach the bank — is the server running?', live: false });
    }
    setLoading(false);
  }

  return (
    <div className="con-scrim" onClick={onClose}>
      <div className="constitution" onClick={e => e.stopPropagation()}>
        <div className="con-head">
          <span className="panel-idx">10</span>
          <div className="con-title"><b>The Constitution</b><span>{version} · the shared charter every agent inherits · governs {n} agents</span></div>
          <button className="drawer-close" onClick={onClose}><U.IconClose /></button>
        </div>

        {active && (
          <div className="con-banner">
            <div className="con-banner-t"><b>Constitution {version} → {version}.1</b> · one edit re-deploys all {n} agents</div>
            <div className="con-roster">{Array.from({ length: n }).map((_, i) => <span key={i} className="con-dot" style={{ animationDelay: (i * 16) + 'ms' }} />)}</div>
          </div>
        )}

        <div className="con-body">
          <div className="con-articles">
            {articles.map((a, i) => {
              const am = amendmentFor(a.id);
              const isActive = am && activeId === am.id;
              return (
                <div className={'con-article' + (isActive ? ' amended' : '')} key={a.id}>
                  <div className="con-anum">{i + 1}</div>
                  <div className="con-abody">
                    <div className="con-atitle">{a.title}</div>
                    <div className="con-atext">{isActive ? am.newText : a.text}</div>
                    {am && (
                      <div className="con-toggle">
                        <button className={'con-tab' + (!isActive ? ' on' : '')} onClick={() => { if (isActive) toggle(am); }}>baseline</button>
                        <button className={'con-tab amend' + (isActive ? ' on' : '')} onClick={() => toggle(am)}>⚠ amend: {am.label}</button>
                      </div>
                    )}
                  </div>
                </div>
              );
            })}
          </div>

          <div className="con-preview">
            {!active ? (
              <div className="con-hint">Apply an amendment to a clause — watch it re-deploy across the whole workforce, then run one agent under the old and new rule to see its judgement shift.</div>
            ) : (
              <>
                <div className="con-demo">Preview · <b>{active.demo.agentId}</b> answers:<br />“{active.demo.question}”</div>
                <button className="con-run" onClick={runPreview} disabled={loading}>{loading ? 'Running both versions…' : (preview ? '↻ Re-run preview' : '▶ Preview the shift')}</button>
                {loading && <div className="con-thinking"><span className="ab-dot" /><span className="ab-dot" /><span className="ab-dot" /> running the agent under both constitutions…</div>}
                {preview && !loading && (
                  <div className="con-ba">
                    <div className="con-col before">
                      <div className="con-col-h">Before · baseline</div>
                      <div className="con-col-b">{preview.before}</div>
                    </div>
                    <div className="con-col after">
                      <div className="con-col-h">After · amended {preview.live ? <i className="ab-live">● live</i> : <i className="ab-mock">scripted</i>}</div>
                      <div className="con-col-b">{preview.after}</div>
                    </div>
                  </div>
                )}
              </>
            )}
          </div>
        </div>

        <div className="con-foot">One document governs the entire workforce — total culture change in a single edit, and the bank's single point of failure. Amending it is a board-reserved act.</div>
      </div>
    </div>
  );
}

/* ---------------- Fork the Bank (overlay) ---------------- */
const FK_HIGHER_WORSE = { provisionsGbp: true, rwaGbp: true };

function forkSpark(baseSeries, shockSeries, fromDay) {
  const W = 168, H = 46, n = 366;
  let lo = Infinity, hi = -Infinity;
  for (let i = 0; i < n; i++) {
    const a = (baseSeries[i] && baseSeries[i].value) || 0;
    const b = (shockSeries[i] && shockSeries[i].value) || 0;
    if (a < lo) lo = a; if (a > hi) hi = a; if (b < lo) lo = b; if (b > hi) hi = b;
  }
  if (hi === lo) hi = lo + 1;
  const pad = (hi - lo) * 0.08; lo -= pad; hi += pad;
  const step = Math.max(1, Math.floor(n / 80));
  const path = (series) => {
    let d = '';
    for (let i = 0; i < n; i += step) {
      const v = (series[i] && series[i].value) || 0;
      const x = (i / (n - 1)) * W, y = H - ((v - lo) / (hi - lo)) * H;
      d += (i === 0 ? 'M' : 'L') + x.toFixed(1) + ' ' + y.toFixed(1);
    }
    const lv = (series[n - 1] && series[n - 1].value) || 0;
    d += 'L' + W + ' ' + (H - ((lv - lo) / (hi - lo)) * H).toFixed(1);
    return d;
  };
  const markX = (fromDay / (n - 1)) * W;
  return (
    <svg className="fk-spark" viewBox={`0 0 ${W} ${H}`} preserveAspectRatio="none">
      <line className="fk-mark" x1={markX} y1="0" x2={markX} y2={H} />
      <path className="fk-base" d={path(baseSeries)} />
      <path className="fk-shock" d={path(shockSeries)} />
    </svg>
  );
}

function Fork({ shocks, runId, day, agentConfigs, live, onClose }) {
  const U = window.AlbionUI;
  const list = shocks || [];
  const [selId, setSelId] = React.useState(list[0] ? list[0].id : null);
  const [result, setResult] = React.useState(null);
  const [loading, setLoading] = React.useState(false);

  const sel = list.find(s => s.id === selId) || null;

  const fmtVal = (unit, v) => {
    if (unit === 'pct') return (Math.round(v * 10) / 10) + '%';
    if (unit === 'gbp') return v >= 1e9 ? '£' + (v / 1e9).toFixed(1) + 'bn' : v >= 1e6 ? '£' + (v / 1e6).toFixed(0) + 'm' : '£' + Math.round(v);
    return v >= 1e6 ? (v / 1e6).toFixed(2) + 'm' : v >= 1e3 ? Math.round(v / 1e3) + 'k' : Math.round(v).toString();
  };
  const fmtDelta = (unit, dv) => {
    const s = dv >= 0 ? '+' : '−', a = Math.abs(dv);
    if (unit === 'pct') return s + (Math.round(a * 100) / 100) + 'pp';
    if (unit === 'gbp') return s + (a >= 1e9 ? '£' + (a / 1e9).toFixed(1) + 'bn' : '£' + Math.round(a / 1e6) + 'm');
    return s + (a >= 1e6 ? (a / 1e6).toFixed(2) + 'm' : Math.round(a / 1e3) + 'k');
  };

  async function fork(s) {
    if (!s || loading) return;
    setLoading(true); setResult(null);
    try {
      const res = await fetch('/api/fork' + (live ? '?live=1' : ''), {
        method: 'POST', headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ shockId: s.id, day }),
      }).then(r => r.json());
      setResult(res);
    } catch (e) {
      setResult({ error: true, response: 'Could not reach the bank — is the server running?', divergence: [], live: false });
    }
    setLoading(false);
  }

  const headline = result && result.divergence
    ? [...result.divergence]
        .filter(d => Math.abs(d.delta) > 1e-6 && d.baselineValue !== 0)
        .sort((a, b) => Math.abs(b.delta / b.baselineValue) - Math.abs(a.delta / a.baselineValue))
        .slice(0, 4)
    : [];
  const responder = result && agentConfigs ? agentConfigs[result.shock.responderId] : null;

  return (
    <div className="fk-scrim" onClick={onClose}>
      <div className="fork" onClick={e => e.stopPropagation()}>
        <div className="fk-head">
          <span className="panel-idx">11</span>
          <div className="fk-title"><b>Fork the Bank</b><span>branch the bank's forward model under a shock · run both timelines · illustrative</span></div>
          <button className="drawer-close" onClick={onClose}><U.IconClose /></button>
        </div>

        <div className="fk-body">
          <div className="fk-list">
            {list.map(s => (
              <div key={s.id} className={'fk-item' + (s.id === selId ? ' on' : '')} onClick={() => { setSelId(s.id); setResult(null); }}>
                <div className="fk-label">{s.label}</div>
                <div className="fk-sub">{s.sub}</div>
                <div className="fk-narr">{s.narrative}</div>
              </div>
            ))}
          </div>

          <div className="fk-detail">
            {!sel ? <div className="fk-empty">Select a shock.</div> : (
              <>
                <button className="fk-run" onClick={() => fork(sel)} disabled={loading}>
                  {loading ? 'Forking & running both timelines…' : (result ? '↻ Re-fork' : '▶ Fork & run')}
                </button>
                {loading && <div className="fk-thinking"><span className="ab-dot" /><span className="ab-dot" /><span className="ab-dot" /> projecting the baseline and the forked timeline…</div>}
                {result && !loading && !result.error && (
                  <>
                    <div className="fk-legend"><span className="fk-lg base">— baseline</span><span className="fk-lg shock">- - forked</span><span className="fk-at">forked at day {result.fromDay} · both share history to here</span></div>
                    <div className="fk-charts">
                      {headline.map(d => {
                        const good = d.delta === 0 ? 0 : (FK_HIGHER_WORSE[d.key] ? -Math.sign(d.delta) : Math.sign(d.delta));
                        return (
                          <div className="fk-metric" key={d.key}>
                            <div className="fk-mk">{d.label}</div>
                            {forkSpark(result.baseline.series[d.key], result.shocked.series[d.key], result.fromDay)}
                            <div className="fk-vals">
                              <span className="fk-bv">{fmtVal(d.unit, d.baselineValue)}</span>
                              <span className="fk-arr">→</span>
                              <span className="fk-sv">{fmtVal(d.unit, d.shockedValue)}</span>
                              <span className={'fk-dv ' + (good > 0 ? 'up' : good < 0 ? 'down' : '')}>{fmtDelta(d.unit, d.delta)}</span>
                            </div>
                          </div>
                        );
                      })}
                    </div>
                    <div className="fk-response">
                      <div className="fk-resp-h">
                        <b>{responder ? responder.name : result.shock.responderId}</b><span>responds</span>
                        {result.live ? <i className="ab-live">● live</i> : <i className="ab-mock">scripted</i>}
                      </div>
                      <div className="fk-resp-b">{result.response}</div>
                    </div>
                  </>
                )}
                {result && !loading && result.error && <div className="fk-empty">{result.response}</div>}
              </>
            )}
          </div>
        </div>
      </div>
    </div>
  );
}

/* ---------------- Guide (overlay) ---------------- */
function Guide({ onClose }) {
  const U = window.AlbionUI;
  const [html, setHtml] = React.useState(null);
  const [err, setErr] = React.useState(false);
  React.useEffect(() => {
    let alive = true;
    fetch('/api/guide')
      .then(r => (r.ok ? r.json() : Promise.reject(new Error('guide'))))
      .then(d => { if (alive) setHtml(d.html); })
      .catch(() => { if (alive) setErr(true); });
    return () => { alive = false; };
  }, []);
  return (
    <div className="gd-scrim" onClick={onClose}>
      <div className="guide" onClick={e => e.stopPropagation()}>
        <div className="gd-head">
          <span className="panel-idx">00</span>
          <div className="gd-title"><b>Guide</b><span>what this is · why it exists · how to drive it · the capabilities</span></div>
          <button className="drawer-close" onClick={onClose}><U.IconClose /></button>
        </div>
        <div className="gd-scroll">
          {err
            ? <div className="gd-empty">Could not load the guide.</div>
            : html === null
              ? <div className="gd-empty">Loading…</div>
              : <div className="doc-body md gd-doc" dangerouslySetInnerHTML={{ __html: html }} />}
        </div>
      </div>
    </div>
  );
}

window.AlbionUI2 = { Feed, DocumentSheet, ArtifactDrawer, InjectMenu, Scoreboard, Ledger, Race, BankBoard, AskBank, RedTeam, Constitution, Fork, Guide };
