/* ============================================================================
   DIVONCI WORLD — AGENT DEVELOPMENT · TEMPLATE CLONE + ESCALATIONS + PROTECTED
   EXECUTION  (screens_agent_dev_ops.jsx)
   ----------------------------------------------------------------------------
   Three governance surfaces that mount inside the Agent Development board:

     1. TrackTemplatePanel + CloneTemplateDialog
        "Clone Viiera Supabase Expert track to other agents."
        Worker action: agent_development.clone_template (no browser DB write).
        Clones start RESTRICTED/TRAINING — never trusted. Existing equivalent
        active tracks are disabled (or require explicit override confirmation).

     2. UnknownMissionEscalations
        Surfaces viiera_codex_escalations (e.g. unknown screenshot code
        cd78afbc). Link-to-mission + authority-gated dismiss. Never fabricates.

     3. ProtectedExecutionPanel
        Protected Execution Owner health: availability, runner health, last
        heartbeat, queue depth, last failure. Blocked jobs carry a corrective
        action, not a chat fallback. Viiera delegates; she never executes.

   Shared globals: Panel, PHead, Icon, Led, DW, DEVD, DWSnapshot (all on window).
   Writes follow the existing GatedWrite contract: draft the structured payload,
   show the exact Worker route + JSON, never touch Supabase from the browser.
   ========================================================================== */
const { useState: useStateAdo, useMemo: useMemoAdo, useEffect: useEffectAdo } = React;

/* ── local helpers (unique names; do not collide with dev screen) ─────────── */
const ADO_OWNER_LABEL = { diego: "Diego · Sovereign" };
function adoAgent(snap, id) {
  return (snap.agents || []).find((a) => a.id === id) ||
    { id, name: id, accent: "var(--ink-3)", domain: "", role: "" };
}
function AdoAvatar({ agent, size = 30 }) {
  const ac = agent.accent || agent.ac || "var(--violet-bright)";
  const head = agent.head && typeof window.assetUrl === "function" ? window.assetUrl(agent.head) : agent.head;
  if (head) return <img src={head} alt={agent.name} style={{ width: size, height: size, borderRadius: 8, objectFit: "cover", border: `1px solid color-mix(in srgb, ${ac} 55%, transparent)`, flex: "none" }} />;
  return (
    <span style={{ width: size, height: size, borderRadius: 8, flex: "none", display: "grid", placeItems: "center",
      background: `color-mix(in srgb, ${ac} 20%, var(--panel-2))`, border: `1px solid color-mix(in srgb, ${ac} 45%, transparent)`,
      color: ac, fontFamily: "var(--font-display)", fontWeight: 700, fontSize: size * 0.42 }}>
      {(agent.name || "?").slice(0, 1).toUpperCase()}
    </span>
  );
}
function AdoOwnerChip({ id, snap }) {
  if (ADO_OWNER_LABEL[id]) return <span className="evchip" style={{ borderColor: "color-mix(in srgb, var(--gold) 45%, transparent)", color: "var(--gold-bright)" }}><Icon name="crown" style={{ width: 11, height: 11 }} />{ADO_OWNER_LABEL[id]}</span>;
  const a = adoAgent(snap, id);
  return <span className="evchip" style={{ borderColor: `color-mix(in srgb, ${a.accent || a.ac} 40%, transparent)` }}><span style={{ width: 6, height: 6, borderRadius: 99, background: a.accent || a.ac, flex: "none" }} />{a.name}</span>;
}
function adoAgo(iso) {
  try {
    const d = new Date(iso), s = (Date.now() - d.getTime()) / 1000;
    if (s < 60) return Math.max(1, Math.round(s)) + "s ago";
    if (s < 3600) return Math.round(s / 60) + "m ago";
    if (s < 86400) return Math.round(s / 3600) + "h ago";
    return Math.round(s / 86400) + "d ago";
  } catch (e) { return iso; }
}
function AdoStat({ k, v, c }) {
  return <div className="ado-stat"><div className="k">{k}</div><div className="v" style={c ? { color: c } : null}>{v}</div></div>;
}

/* the current viewer's authority — drives whether destructive/resolve actions
   are enabled. In production this comes from the authenticated session/role. */
function adoViewerAuthority() {
  try { return localStorage.getItem("dw_viewer_role") || "operator"; } catch (e) { return "operator"; }
}
const ADO_AUTHORITY_OK = ["diego", "sovereign", "admin"];

/* ════════════════════════════════════════════════════════════════════════════
   1 · TRACK TEMPLATE PANEL — reusable template + clone action
   ════════════════════════════════════════════════════════════════════════ */
function TrackTemplatePanel({ snap }) {
  const tmpl = (snap.trackTemplates || [])[0];
  const [open, setOpen] = useStateAdo(false);
  if (!tmpl) return null;
  const src = adoAgent(snap, tmpl.source_agent_id);
  const startLvlCur = DEVD.levelFromInt(tmpl.start_current_level);
  const startLvlTgt = DEVD.levelFromInt(tmpl.start_target_level);
  return (
    <Panel className="tight" style={{ gridColumn: "1 / -1", "--ac": src.accent || "var(--violet-bright)" }}>
      <PHead icon="layers" eyebrow="Track template · reusable"
        right={<span className="sbadge" style={{ color: "var(--state-focus)", borderColor: "color-mix(in srgb, var(--state-focus) 40%, transparent)" }}><span className="led" style={{ background: "var(--state-focus)" }} />v{tmpl.version} · reusable</span>} />
      <div className="ado-tmpl">
        <div className="ado-tmpl-id">
          <span className="ado-tmpl-icon"><Icon name="database" style={{ width: 19, height: 19, color: "var(--violet-ink)" }} /></span>
          <div style={{ minWidth: 0 }}>
            <div style={{ fontFamily: "var(--font-display)", fontWeight: 600, fontSize: 17 }}>{tmpl.name}</div>
            <div style={{ display: "flex", alignItems: "center", gap: 8, marginTop: 5, flexWrap: "wrap" }}>
              <span className="ado-slug">{tmpl.slug}</span>
              <span style={{ fontFamily: "var(--font-mono)", fontSize: 10.5, color: "var(--ink-4)" }}>from {src.name} · {tmpl.domain}</span>
            </div>
          </div>
        </div>
        <p className="ado-tmpl-desc">{tmpl.description}</p>
        <div className="ado-tmpl-grid">
          <AdoStat k="Start level" v={<span>{startLvlCur.id}<span style={{ color: "var(--ink-4)" }}> → </span>{startLvlTgt.id}</span>} c="var(--state-active)" />
          <AdoStat k="Start trust" v="Training" c="var(--state-focus)" />
          <AdoStat k="Gates" v={tmpl.gates.length} />
          <AdoStat k="Restrictions" v={tmpl.restrictions.length} c="var(--state-crisis)" />
          <AdoStat k="Required evidence" v={tmpl.required_evidence.length} c="var(--state-caution)" />
          <AdoStat k="Priority" v={tmpl.priority} c="var(--state-crisis)" />
        </div>
        <div className="ado-clone-bar">
          <div style={{ display: "flex", flexDirection: "column", gap: 5, minWidth: 0 }}>
            <button className="btn primary" onClick={() => setOpen(true)}><Icon name="branch" className="glyph" style={{ width: 14, height: 14 }} /> Clone to other agents</button>
            <span className="todochip"><Icon name="lock" style={{ width: 10, height: 10 }} /> Worker action · agent_development.clone_template · no direct DB write</span>
          </div>
          <div className="ado-clone-note">
            <Icon name="shield" style={{ width: 13, height: 13, color: "var(--state-caution)", flex: "none" }} />
            <span>Cloning never promotes. Every new track starts <b>restricted / training</b> with its own fresh evidence gates.</span>
          </div>
        </div>
      </div>
      <CloneTemplateDialog open={open} onClose={() => setOpen(false)} tmpl={tmpl} snap={snap} />
    </Panel>
  );
}

/* ── clone composer ───────────────────────────────────────────────────────── */
function CloneTemplateDialog({ open, onClose, tmpl, snap }) {
  const agents = snap.agents || [];
  const tracks = snap.agentDevelopmentTracks || [];
  // an agent already has an EQUIVALENT track if a track in the same domain is active/training
  const equivByAgent = useMemoAdo(() => {
    const m = {};
    tracks.forEach((t) => {
      if (t.domain === tmpl.domain && (t.status === "active" || t.trust_state === "training")) m[t.agent_id] = t;
    });
    return m;
  }, [tracks, tmpl]);

  const candidates = useMemoAdo(() => agents.filter((a) => a.id !== tmpl.source_agent_id), [agents, tmpl]);
  const [picked, setPicked] = useStateAdo({});      // agent_id -> true
  const [override, setOverride] = useStateAdo({});   // agent_id -> true (confirm clone over equivalent)
  const [result, setResult] = useStateAdo(null);

  useEffectAdo(() => { if (open) { setPicked({}); setOverride({}); setResult(null); } }, [open]);
  if (!open) return null;

  const toggle = (id, conflict) => {
    if (conflict && !override[id]) return; // disabled until override confirmed
    setPicked((p) => ({ ...p, [id]: !p[id] }));
  };
  const chosen = candidates.filter((a) => picked[a.id]);
  const startLvlCur = DEVD.levelFromInt(tmpl.start_current_level);
  const startLvlTgt = DEVD.levelFromInt(tmpl.start_target_level);

  // the exact structured payload the Worker action receives
  const payload = {
    action: "agent_development.clone_template",
    template_slug: tmpl.slug,
    target_agent_ids: chosen.map((a) => a.id),
    overwrite_confirmations: Object.keys(override).filter((k) => override[k] && picked[k]),
    start_state: { status: tmpl.start_status, trust_state: tmpl.start_trust_state, current_level: tmpl.start_current_level, target_level: tmpl.start_target_level },
    requested_by: adoViewerAuthority(),
    idempotency_key: "clone_" + tmpl.slug + "_" + chosen.map((a) => a.id).sort().join("-"),
  };
  const ROUTE = "POST /api/divonci-world/actions/agent_development.clone_template";

  // a PREVIEW of the structured result the Worker returns (dry-run mirror, not a
  // live write). Eligible agents → created; equivalent-track agents not overridden → skipped.
  const preview = {
    created: chosen.filter((a) => !(equivByAgent[a.id] && !override[a.id])).map((a) => ({
      agent_id: a.id, track_id: "trk_" + a.id + "_supabase", status: tmpl.start_status, trust_state: tmpl.start_trust_state,
    })),
    skipped: candidates.filter((a) => equivByAgent[a.id] && picked[a.id] && !override[a.id]).map((a) => ({ agent_id: a.id, reason: "active equivalent track exists" })),
    errors: [],
    events_written: chosen.length,
  };
  const valid = chosen.length > 0;

  return (
    <div className="dev-modal-scrim" onClick={onClose}>
      <div className="dev-modal ado-modal" style={{ "--ac": "var(--violet-bright)" }} onClick={(e) => e.stopPropagation()}>
        <div className="dev-modal-head">
          <div>
            <div style={{ fontFamily: "var(--font-display)", fontWeight: 600, fontSize: 17 }}>Clone Supabase Expert track</div>
            <div style={{ fontSize: 11.5, color: "var(--ink-3)", marginTop: 3 }}>Cloning template <span className="ado-slug" style={{ fontSize: 10 }}>{tmpl.slug}</span> to one or more agents. Routed to the Worker action — the browser never writes to Supabase.</div>
          </div>
          <button className="dev-modal-x focusable" onClick={onClose} aria-label="Close"><Icon name="close" style={{ width: 15, height: 15 }} /></button>
        </div>

        {result ? (
          <div className="dev-modal-body">
            <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 14 }}>
              <span style={{ width: 30, height: 30, borderRadius: 9, display: "grid", placeItems: "center", background: "color-mix(in srgb, var(--state-caution) 16%, transparent)", border: "1px solid color-mix(in srgb, var(--state-caution) 40%, transparent)", flex: "none" }}><Icon name="lock" style={{ width: 15, height: 15, color: "var(--state-caution)" }} /></span>
              <div><div style={{ fontSize: 14, fontWeight: 600 }}>Queued — pending Worker action</div><div style={{ fontFamily: "var(--font-mono)", fontSize: 10.5, color: "var(--ink-3)" }}>{ROUTE}</div></div>
            </div>
            <div className="ado-result-cols">
              <div className="ado-result-card ok">
                <div className="ado-result-h"><Icon name="check" style={{ width: 13, height: 13 }} /> Created · {preview.created.length}</div>
                {preview.created.length ? preview.created.map((r) => <div key={r.agent_id} className="ado-result-row"><span>{adoAgent(snap, r.agent_id).name}</span><span className="sbadge" style={{ color: "var(--state-focus)", fontSize: 8.5 }}>training</span></div>) : <div className="ado-result-empty">none</div>}
              </div>
              <div className="ado-result-card warn">
                <div className="ado-result-h"><Icon name="shield" style={{ width: 13, height: 13 }} /> Skipped · {preview.skipped.length}</div>
                {preview.skipped.length ? preview.skipped.map((r) => <div key={r.agent_id} className="ado-result-row"><span>{adoAgent(snap, r.agent_id).name}</span><span style={{ fontSize: 10, color: "var(--ink-4)" }}>{r.reason}</span></div>) : <div className="ado-result-empty">none</div>}
              </div>
            </div>
            <div style={{ fontSize: 11.5, color: "var(--ink-3)", margin: "4px 0 8px", lineHeight: 1.5 }}>Exact payload the action receives. New tracks start <b>restricted/training</b>; {preview.events_written} development events will be written.</div>
            <pre className="dev-payload">{JSON.stringify(payload, null, 2)}</pre>
            <div className="dev-modal-foot"><button className="btn ghost" onClick={() => setResult(null)}>← Edit selection</button><button className="btn primary" onClick={onClose}>Done</button></div>
          </div>
        ) : (
          <div className="dev-modal-body">
            <div className="ado-clone-banner">
              <div><div className="eyebrow">Template</div><div style={{ fontSize: 13.5, fontWeight: 600, marginTop: 3 }}>{tmpl.name}</div></div>
              <div className="ado-startchips">
                <span className="ado-startchip">{startLvlCur.id} → {startLvlTgt.id}</span>
                <span className="ado-startchip" style={{ color: "var(--state-focus)", borderColor: "color-mix(in srgb, var(--state-focus) 40%, transparent)" }}>starts training</span>
                <span className="ado-startchip" style={{ color: "var(--state-crisis)", borderColor: "color-mix(in srgb, var(--state-crisis) 38%, transparent)" }}>{tmpl.restrictions.length} restrictions</span>
              </div>
            </div>

            <div className="dev-field"><span>Pick target agents</span></div>
            <div className="ado-pick-list">
              {candidates.map((a) => {
                const conflict = !!equivByAgent[a.id];
                const on = !!picked[a.id];
                const ovr = !!override[a.id];
                const disabled = conflict && !ovr;
                return (
                  <div key={a.id} className={"ado-pick" + (on ? " on" : "") + (disabled ? " disabled" : "")} style={{ "--ac": a.accent || a.ac }}>
                    <button className="ado-pick-main focusable" onClick={() => toggle(a.id, conflict)} disabled={disabled} aria-pressed={on}>
                      <span className={"ado-check" + (on ? " on" : "")}>{on && <Icon name="check" style={{ width: 11, height: 11 }} />}</span>
                      <AdoAvatar agent={a} size={30} />
                      <span className="ado-pick-id">
                        <span className="nm">{a.name}</span>
                        <span className="rl">{a.role}</span>
                      </span>
                    </button>
                    {conflict ? (
                      <div className="ado-pick-conflict">
                        <span className="ado-conflict-tag"><Icon name="shield" style={{ width: 10, height: 10 }} /> active equivalent track</span>
                        <label className="ado-override"><input type="checkbox" checked={ovr} onChange={() => { setOverride((o) => ({ ...o, [a.id]: !o[a.id] })); if (ovr) setPicked((p) => ({ ...p, [a.id]: false })); }} /> override</label>
                      </div>
                    ) : (
                      <span className="ado-eligible">eligible</span>
                    )}
                  </div>
                );
              })}
            </div>

            <div style={{ display: "flex", alignItems: "center", gap: 11, padding: "11px 14px", borderRadius: 9, background: "color-mix(in srgb, var(--state-caution) 9%, var(--panel-2))", border: "1px solid color-mix(in srgb, var(--state-caution) 32%, transparent)" }}>
              <Icon name="shield" style={{ width: 15, height: 15, color: "var(--state-caution)", flex: "none" }} />
              <span style={{ fontSize: 12, color: "var(--ink-2)", lineHeight: 1.45 }}>No agent is promoted by this action. Each clone is created <b>restricted/training</b> and must pass its own evidence gates before any trust is granted.</span>
            </div>

            <div className="dev-modal-foot">
              <span style={{ marginRight: "auto", fontSize: 11.5, color: "var(--ink-3)" }}>{chosen.length} selected</span>
              <button className="btn ghost" onClick={onClose}>Cancel</button>
              <button className="btn primary" disabled={!valid} style={!valid ? { opacity: 0.5, pointerEvents: "none" } : null} onClick={() => setResult(true)}><Icon name="branch" className="glyph" style={{ width: 13, height: 13 }} /> Queue clone · {chosen.length}</button>
            </div>
          </div>
        )}
      </div>
    </div>
  );
}

/* ════════════════════════════════════════════════════════════════════════════
   2 · UNKNOWN MISSION CODE ESCALATIONS
   ════════════════════════════════════════════════════════════════════════ */
function UnknownMissionEscalations({ snap }) {
  const rows = snap.codexEscalations || [];
  const authority = ADO_AUTHORITY_OK.includes(adoViewerAuthority());
  return (
    <Panel className="tight" style={{ gridColumn: "1 / -1" }}>
      <PHead icon="flag" eyebrow="Unknown mission code escalations"
        right={<span style={{ fontFamily: "var(--font-mono)", fontSize: 10.5, color: rows.some((r) => r.status === "open") ? "var(--state-caution)" : "var(--ink-4)" }}>{rows.filter((r) => r.status === "open").length} open</span>} />
      {rows.length === 0
        ? <div style={{ padding: "20px 16px", textAlign: "center", color: "var(--ink-4)", fontSize: 12.5 }}>No unresolved mission-code escalations.</div>
        : <div className="pbody" style={{ display: "flex", flexDirection: "column", gap: 12 }}>
            {rows.map((r) => <EscalationRow key={r.id} esc={r} authority={authority} snap={snap} />)}
          </div>}
    </Panel>
  );
}

function EscalationRow({ esc, authority, snap }) {
  const [linking, setLinking] = useStateAdo(false);
  const [val, setVal] = useStateAdo("");
  const [queued, setQueued] = useStateAdo(null);
  const sev = esc.severity === "high" ? "var(--state-crisis)" : esc.severity === "medium" ? "var(--state-caution)" : "var(--ink-3)";

  const queueLink = () => setQueued({
    action: "agent_development.link_mission_code", code: esc.code,
    escalation_id: esc.id, mission_or_job_id: val.trim(),
    requested_by: adoViewerAuthority(),
  });
  const queueResolve = () => setQueued({
    action: "agent_development.resolve_escalation", escalation_id: esc.id,
    resolution: "dismissed", requested_by: adoViewerAuthority(),
  });

  return (
    <div className="ado-esc">
      <div className="ado-esc-head">
        <span className="ado-esc-code">{esc.code}</span>
        <div style={{ minWidth: 0, flex: 1 }}>
          <div style={{ fontSize: 13.5, fontWeight: 600, overflowWrap: "anywhere" }}>{esc.title}</div>
          <div style={{ fontFamily: "var(--font-mono)", fontSize: 10, color: "var(--ink-4)", marginTop: 3 }}>{esc.code_kind} code · {esc.source} · {adoAgo(esc.created_at)}</div>
        </div>
        <span className="sbadge" style={{ color: sev, borderColor: `color-mix(in srgb, ${sev} 40%, transparent)`, flex: "none" }}><span className="led" style={{ background: sev }} />{esc.status}</span>
      </div>

      <div className="ado-esc-grid">
        <div><div className="eyebrow">Searched · not found</div>
          <div style={{ display: "flex", flexWrap: "wrap", gap: 6, marginTop: 6 }}>
            {esc.searched.map((s) => <span key={s} className="ado-srch"><Icon name="close" style={{ width: 9, height: 9, color: "var(--state-crisis)" }} />{s}</span>)}
          </div>
        </div>
        <div><div className="eyebrow">Suggested next step</div>
          <div style={{ fontSize: 12, color: "var(--ink-2)", lineHeight: 1.5, marginTop: 6 }}>{esc.suggested_next_step}</div>
        </div>
      </div>

      {queued ? (
        <div style={{ marginTop: 4 }}>
          <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 8 }}>
            <Icon name="lock" style={{ width: 13, height: 13, color: "var(--state-caution)" }} />
            <span style={{ fontSize: 11.5, color: "var(--ink-2)" }}>Queued — pending Worker action</span>
            <button className="minilink" style={{ marginLeft: "auto" }} onClick={() => { setQueued(null); setLinking(false); setVal(""); }}>Reset</button>
          </div>
          <pre className="dev-payload" style={{ maxHeight: 150 }}>{JSON.stringify(queued, null, 2)}</pre>
        </div>
      ) : (
        <div className="ado-esc-actions">
          {linking ? (
            <div className="ado-link-row">
              <input className="twk-field" placeholder="mission or job id (e.g. agent_jobs.id)" value={val} onChange={(e) => setVal(e.target.value)} />
              <button className="btn primary" disabled={!val.trim()} style={!val.trim() ? { opacity: 0.5, pointerEvents: "none" } : null} onClick={queueLink}>Queue link</button>
              <button className="btn ghost" onClick={() => { setLinking(false); setVal(""); }}>Cancel</button>
            </div>
          ) : (
            <React.Fragment>
              <button className="btn ghost" onClick={() => setLinking(true)}><Icon name="branch" className="glyph" style={{ width: 13, height: 13 }} /> Link to mission / job</button>
              <button className="btn ghost" disabled={!authority} style={!authority ? { opacity: 0.45, pointerEvents: "none" } : null} onClick={queueResolve} title={authority ? "" : "Requires Diego / sovereign authority"}>
                <Icon name={authority ? "check" : "lock"} className="glyph" style={{ width: 13, height: 13 }} /> Dismiss / resolve
              </button>
              {!authority && <span className="todochip"><Icon name="lock" style={{ width: 10, height: 10 }} /> resolve needs authority</span>}
              <span className="ado-keep">Keep as escalation — never fabricate a mission.</span>
            </React.Fragment>
          )}
        </div>
      )}
    </div>
  );
}

/* ════════════════════════════════════════════════════════════════════════════
   3 · PROTECTED EXECUTION — owners, runner health, blocked jobs
   ════════════════════════════════════════════════════════════════════════ */
const ADO_HEALTH = {
  healthy:  { c: "var(--state-clear)",   label: "healthy" },
  degraded: { c: "var(--state-caution)", label: "degraded" },
  down:     { c: "var(--state-crisis)",  label: "down" },
};
function ProtectedExecutionPanel({ snap }) {
  const owners = snap.protectedOwners || [];
  const jobs = snap.protectedJobs || [];
  const incidents = snap.devIncidents || [];
  // an owner can take protected work only if online + healthy-enough + has state access
  const availableOwners = owners.filter((o) => o.approved && o.availability === "online" && o.runner_health !== "down" && o.state_access);

  return (
    <Panel className="tight" style={{ gridColumn: "1 / -1" }}>
      <PHead icon="power" eyebrow="Protected execution"
        right={<span style={{ fontFamily: "var(--font-mono)", fontSize: 10.5, color: availableOwners.length ? "var(--state-clear)" : "var(--state-crisis)" }}>{availableOwners.length} runner{availableOwners.length === 1 ? "" : "s"} ready</span>} />
      <div className="pbody" style={{ display: "flex", flexDirection: "column", gap: 13 }}>
        <div style={{ display: "flex", alignItems: "center", gap: 11, padding: "11px 14px", borderRadius: 9, background: "color-mix(in srgb, var(--state-crisis) 8%, var(--panel-2))", border: "1px solid color-mix(in srgb, var(--state-crisis) 30%, transparent)" }}>
          <Icon name="lock" style={{ width: 15, height: 15, color: "var(--state-crisis)", flex: "none" }} />
          <span style={{ fontSize: 12, color: "var(--ink-2)", lineHeight: 1.45 }}>Viiera must not execute protected edits herself. She may only <b>delegate</b> to an approved owner and track the evidence. Protected-runtime jobs route only to a healthy owner with live state access.</span>
        </div>

        <div className="eyebrow">Protected execution owners</div>
        <div className="ado-owner-grid">
          {owners.map((o) => <OwnerCard key={o.id} owner={o} snap={snap} />)}
        </div>

        <div className="eyebrow" style={{ marginTop: 2 }}>Protected jobs</div>
        {jobs.map((j) => <ProtectedJobRow key={j.id} job={j} snap={snap} availableOwners={availableOwners} incidents={incidents} />)}
      </div>
    </Panel>
  );
}

function OwnerCard({ owner, snap }) {
  const agent = adoAgent(snap, owner.owner_agent_id);
  const online = owner.availability === "online";
  const health = ADO_HEALTH[owner.runner_health] || ADO_HEALTH.down;
  return (
    <div className="ado-owner" style={{ "--ac": agent.accent || "var(--violet-bright)" }}>
      <div className="ado-owner-top">
        <AdoAvatar agent={agent} size={32} />
        <div style={{ minWidth: 0, flex: 1 }}>
          <div style={{ fontSize: 13.5, fontWeight: 600, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{owner.label}</div>
          <div style={{ fontFamily: "var(--font-mono)", fontSize: 9.5, color: "var(--ink-4)", marginTop: 2 }}>{agent.name} · {owner.kind}</div>
        </div>
        {owner.approved && <span className="ado-approved"><Icon name="check" style={{ width: 9, height: 9 }} /> approved</span>}
      </div>
      <div className="ado-owner-row">
        <span className="ado-owner-k">Availability</span>
        <span style={{ display: "inline-flex", alignItems: "center", gap: 6 }}><Led state={online ? "clear" : "standby"} size={7} /><span style={{ fontSize: 11.5, color: online ? "var(--state-clear)" : "var(--ink-3)" }}>{owner.availability}</span></span>
      </div>
      <div className="ado-owner-row">
        <span className="ado-owner-k">Runner health</span>
        <span style={{ fontSize: 11.5, color: health.c, fontWeight: 600 }}>{health.label}</span>
      </div>
      <div className="ado-owner-row">
        <span className="ado-owner-k">Last heartbeat</span>
        <span style={{ fontFamily: "var(--font-mono)", fontSize: 11, color: online ? "var(--ink-2)" : "var(--state-crisis)" }}>{adoAgo(owner.last_heartbeat)}</span>
      </div>
      <div className="ado-owner-row">
        <span className="ado-owner-k">Queue depth</span>
        <span style={{ fontFamily: "var(--font-mono)", fontSize: 11, color: "var(--ink-2)" }}>{owner.queue_depth}</span>
      </div>
      <div className="ado-owner-row">
        <span className="ado-owner-k">State access</span>
        <span style={{ fontSize: 11.5, color: owner.state_access ? "var(--state-clear)" : "var(--state-crisis)", fontWeight: 600 }}>{owner.state_access ? "yes" : "no"}</span>
      </div>
      {owner.last_failure && (
        <div className="ado-owner-fail">
          <Icon name="risk" style={{ width: 11, height: 11, color: "var(--state-crisis)", flex: "none", marginTop: 1 }} />
          <span>Last failure · <span style={{ fontFamily: "var(--font-mono)" }}>{owner.last_failure.job_id}</span> — {owner.last_failure.reason}</span>
        </div>
      )}
    </div>
  );
}

function ProtectedJobRow({ job, snap, availableOwners, incidents }) {
  const [queued, setQueued] = useStateAdo(null);
  const agent = adoAgent(snap, job.agent_id);
  const canDelegate = availableOwners.length > 0;
  const target = availableOwners[0];
  const inc = incidents.find((i) => i.id === job.incident_id);

  const queueDelegate = () => setQueued({
    action: "agent_development.delegate_protected_job",
    job_id: job.id, delegate_to: target.id, owner_agent_id: target.owner_agent_id,
    requires: job.requires, requested_by: adoViewerAuthority(),
    note: "Viiera delegates; she does not execute protected edits. Evidence will be tracked on completion.",
  });

  return (
    <div className="ado-job">
      <div className="ado-job-head">
        <span className="ado-job-id">{job.id}</span>
        <div style={{ minWidth: 0, flex: 1 }}>
          <div style={{ fontSize: 13, fontWeight: 600, overflowWrap: "anywhere" }}>{job.title}</div>
          <div style={{ fontFamily: "var(--font-mono)", fontSize: 9.5, color: "var(--ink-4)", marginTop: 2 }}>requires {job.requires} · requested by {agent.name}</div>
        </div>
        <span className="sbadge" style={{ color: "var(--state-crisis)", borderColor: "color-mix(in srgb, var(--state-crisis) 42%, transparent)", flex: "none" }}><span className="led" style={{ background: "var(--state-crisis)" }} />{job.status}</span>
      </div>
      <div className="ado-job-reason"><Icon name="risk" style={{ width: 13, height: 13, color: "var(--state-crisis)", flex: "none", marginTop: 1 }} /><span>{job.reason}</span></div>
      <div className="ado-job-fix">
        <div className="eyebrow" style={{ color: "var(--state-caution)" }}>Corrective action</div>
        <div style={{ fontSize: 12, color: "var(--ink-2)", lineHeight: 1.5, marginTop: 5 }}>{job.corrective_action}</div>
        {inc && <div style={{ fontFamily: "var(--font-mono)", fontSize: 10, color: "var(--ink-4)", marginTop: 6 }}>incident · {inc.id} · {inc.kind}</div>}
      </div>

      {queued ? (
        <div style={{ marginTop: 4 }}>
          <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 8 }}>
            <Icon name="lock" style={{ width: 13, height: 13, color: "var(--state-caution)" }} />
            <span style={{ fontSize: 11.5, color: "var(--ink-2)" }}>Queued — pending Worker action</span>
            <button className="minilink" style={{ marginLeft: "auto" }} onClick={() => setQueued(null)}>Reset</button>
          </div>
          <pre className="dev-payload" style={{ maxHeight: 170 }}>{JSON.stringify(queued, null, 2)}</pre>
        </div>
      ) : (
        <div className="ado-job-actions">
          {canDelegate ? (
            <React.Fragment>
              <button className="btn primary" onClick={queueDelegate}><Icon name="handshake" className="glyph" style={{ width: 14, height: 14 }} /> Delegate to {adoAgent(snap, target.owner_agent_id).name}</button>
              <span className="todochip"><Icon name="lock" style={{ width: 10, height: 10 }} /> delegation only · Viiera never executes</span>
            </React.Fragment>
          ) : (
            <div className="ado-blocked">
              <Icon name="lock" style={{ width: 13, height: 13, color: "var(--state-crisis)", flex: "none" }} />
              <span>No approved owner with a healthy runner and state access. Job stays <b>blocked</b> — bring a Protected Execution Owner online or stand up a trusted local runner. No chat fallback.</span>
            </div>
          )}
        </div>
      )}
    </div>
  );
}

/* ── scoped styles ────────────────────────────────────────────────────────── */
(function injectAdoStyles() {
  if (document.getElementById("ado-surface-styles")) return;
  const css = `
  .ado-slug { font-family: var(--font-mono); font-size: 11px; color: var(--violet-ink); background: color-mix(in srgb, var(--violet-bright) 13%, var(--panel-2)); border: 1px solid color-mix(in srgb, var(--violet-bright) 32%, transparent); padding: 2px 8px; border-radius: 6px; }
  .ado-tmpl { padding: 4px 16px 16px; }
  .ado-tmpl-id { display: flex; gap: 13px; align-items: flex-start; }
  .ado-tmpl-icon { width: 40px; height: 40px; flex: none; border-radius: 11px; display: grid; place-items: center; background: color-mix(in srgb, var(--violet-bright) 14%, var(--panel-2)); border: 1px solid color-mix(in srgb, var(--violet-bright) 32%, transparent); }
  .ado-tmpl-desc { font-size: 12.5px; color: var(--ink-3); line-height: 1.55; margin: 13px 0 14px; max-width: 720px; }
  .ado-tmpl-grid { display: grid; grid-template-columns: repeat(6, 1fr); gap: 1px; background: var(--hairline); border: 1px solid var(--hairline-2); border-radius: 10px; overflow: hidden; }
  .ado-stat { background: var(--panel); padding: 10px 12px; }
  .ado-stat .k { font-family: var(--font-mono); font-size: 8.5px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-4); }
  .ado-stat .v { font-family: var(--font-mono); font-weight: 600; font-size: 15px; margin-top: 5px; color: var(--ink); text-transform: capitalize; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .ado-clone-bar { display: flex; align-items: center; gap: 16px; margin-top: 16px; padding-top: 15px; border-top: 1px solid var(--hairline); flex-wrap: wrap; }
  .ado-clone-note { display: flex; align-items: flex-start; gap: 9px; font-size: 11.5px; color: var(--ink-3); line-height: 1.5; flex: 1; min-width: 240px; }
  /* clone modal */
  .ado-modal { width: min(640px, 100%); }
  .ado-clone-banner { display: flex; align-items: center; justify-content: space-between; gap: 14px; padding: 12px 14px; border-radius: 10px; background: var(--panel-2); border: 1px solid var(--hairline-2); flex-wrap: wrap; }
  .ado-startchips { display: flex; gap: 7px; flex-wrap: wrap; }
  .ado-startchip { font-family: var(--font-mono); font-size: 10px; color: var(--ink-2); border: 1px solid var(--hairline-2); background: var(--panel); padding: 3px 9px; border-radius: 99px; }
  .ado-pick-list { display: flex; flex-direction: column; gap: 7px; max-height: 320px; overflow-y: auto; padding: 1px; }
  .ado-pick { display: flex; align-items: center; gap: 10px; padding: 8px 11px; border-radius: 10px; background: var(--panel-2); border: 1px solid var(--hairline-2); border-left: 3px solid color-mix(in srgb, var(--ac) 60%, transparent); transition: background .15s, box-shadow .15s; }
  .ado-pick.on { background: color-mix(in srgb, var(--ac) 10%, var(--panel-2)); box-shadow: 0 0 0 1px color-mix(in srgb, var(--ac) 45%, transparent); }
  .ado-pick.disabled { opacity: 0.62; }
  .ado-pick-main { flex: 1; min-width: 0; display: flex; align-items: center; gap: 10px; background: none; border: none; color: inherit; font: inherit; cursor: pointer; text-align: left; padding: 0; }
  .ado-pick-main:disabled { cursor: not-allowed; }
  .ado-check { width: 18px; height: 18px; flex: none; border-radius: 5px; border: 1.5px solid var(--hairline-3); display: grid; place-items: center; color: #fff; }
  .ado-check.on { background: var(--ac); border-color: var(--ac); }
  .ado-pick-id { min-width: 0; display: flex; flex-direction: column; }
  .ado-pick-id .nm { font-size: 13px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .ado-pick-id .rl { font-family: var(--font-mono); font-size: 9px; color: var(--ink-4); text-transform: uppercase; letter-spacing: 0.04em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; margin-top: 2px; }
  .ado-eligible { font-family: var(--font-mono); font-size: 9px; color: var(--state-clear); flex: none; }
  .ado-pick-conflict { display: flex; flex-direction: column; align-items: flex-end; gap: 4px; flex: none; }
  .ado-conflict-tag { display: inline-flex; align-items: center; gap: 4px; font-family: var(--font-mono); font-size: 8.5px; color: var(--state-caution); white-space: nowrap; }
  .ado-override { display: inline-flex; align-items: center; gap: 5px; font-family: var(--font-mono); font-size: 10px; color: var(--ink-3); cursor: pointer; }
  .ado-override input { accent-color: var(--state-caution); }
  .ado-result-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-bottom: 12px; }
  .ado-result-card { border-radius: 10px; padding: 11px 12px; border: 1px solid var(--hairline-2); background: var(--panel-2); }
  .ado-result-card.ok { border-color: color-mix(in srgb, var(--state-clear) 32%, transparent); }
  .ado-result-card.warn { border-color: color-mix(in srgb, var(--state-caution) 32%, transparent); }
  .ado-result-h { display: flex; align-items: center; gap: 7px; font-family: var(--font-mono); font-size: 10.5px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-2); margin-bottom: 8px; }
  .ado-result-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 4px 0; font-size: 12px; }
  .ado-result-empty { font-size: 11px; color: var(--ink-4); }
  /* escalations */
  .ado-esc { padding: 13px 14px; border-radius: 11px; background: var(--panel-2); border: 1px solid var(--hairline-2); border-left: 3px solid color-mix(in srgb, var(--state-caution) 55%, transparent); }
  .ado-esc-head { display: flex; align-items: center; gap: 11px; }
  .ado-esc-code { font-family: var(--font-mono); font-size: 12.5px; font-weight: 600; color: var(--state-caution); background: color-mix(in srgb, var(--state-caution) 12%, var(--panel)); border: 1px solid color-mix(in srgb, var(--state-caution) 34%, transparent); padding: 4px 10px; border-radius: 7px; flex: none; }
  .ado-esc-grid { display: grid; grid-template-columns: 1fr 1.4fr; gap: 16px; margin: 13px 0; padding: 12px 0; border-top: 1px solid var(--hairline); border-bottom: 1px solid var(--hairline); }
  .ado-srch { display: inline-flex; align-items: center; gap: 5px; font-family: var(--font-mono); font-size: 10px; color: var(--ink-3); background: var(--panel); border: 1px solid var(--hairline-2); padding: 3px 8px; border-radius: 6px; }
  .ado-esc-actions { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; }
  .ado-keep { font-size: 11px; color: var(--ink-4); margin-left: auto; }
  .ado-link-row { display: flex; gap: 8px; width: 100%; flex-wrap: wrap; }
  .ado-link-row .twk-field { flex: 1; min-width: 200px; box-sizing: border-box; padding: 9px 11px; border-radius: 8px; border: 1px solid var(--hairline-2); background: var(--panel); color: var(--ink); font-family: var(--font-mono); font-size: 12px; }
  /* protected execution */
  .ado-owner-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 11px; }
  .ado-owner { padding: 12px 13px; border-radius: 11px; background: var(--panel-2); border: 1px solid var(--hairline-2); border-left: 3px solid color-mix(in srgb, var(--ac) 60%, transparent); }
  .ado-owner-top { display: flex; align-items: center; gap: 10px; margin-bottom: 11px; }
  .ado-approved { display: inline-flex; align-items: center; gap: 4px; font-family: var(--font-mono); font-size: 8.5px; color: var(--state-clear); border: 1px solid color-mix(in srgb, var(--state-clear) 34%, transparent); padding: 2px 7px; border-radius: 99px; flex: none; }
  .ado-owner-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 5px 0; border-top: 1px solid var(--hairline); }
  .ado-owner-row:first-of-type { border-top: none; }
  .ado-owner-k { font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.04em; text-transform: uppercase; color: var(--ink-4); }
  .ado-owner-fail { display: flex; gap: 7px; margin-top: 9px; padding-top: 9px; border-top: 1px solid var(--hairline); font-size: 10.5px; color: var(--ink-3); line-height: 1.45; }
  .ado-job { padding: 13px 14px; border-radius: 11px; background: color-mix(in srgb, var(--state-crisis) 5%, var(--panel-2)); border: 1px solid color-mix(in srgb, var(--state-crisis) 28%, transparent); display: flex; flex-direction: column; gap: 11px; }
  .ado-job-head { display: flex; align-items: center; gap: 11px; }
  .ado-job-id { font-family: var(--font-mono); font-size: 12px; font-weight: 600; color: var(--state-crisis); background: color-mix(in srgb, var(--state-crisis) 11%, var(--panel)); border: 1px solid color-mix(in srgb, var(--state-crisis) 32%, transparent); padding: 4px 10px; border-radius: 7px; flex: none; }
  .ado-job-reason { display: flex; gap: 8px; font-size: 12px; color: var(--ink-2); line-height: 1.5; }
  .ado-job-fix { padding: 10px 12px; border-radius: 9px; background: color-mix(in srgb, var(--state-caution) 8%, var(--panel)); border: 1px solid color-mix(in srgb, var(--state-caution) 28%, transparent); }
  .ado-job-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
  .ado-blocked { display: flex; align-items: flex-start; gap: 9px; font-size: 11.5px; color: var(--ink-2); line-height: 1.5; padding: 10px 12px; border-radius: 9px; background: color-mix(in srgb, var(--state-crisis) 9%, var(--panel)); border: 1px solid color-mix(in srgb, var(--state-crisis) 32%, transparent); }
  @media (max-width: 720px) {
    .ado-tmpl-grid { grid-template-columns: repeat(3, 1fr); }
    .ado-esc-grid { grid-template-columns: 1fr; gap: 12px; }
    .ado-result-cols { grid-template-columns: 1fr; }
  }
  @media (max-width: 460px) { .ado-tmpl-grid { grid-template-columns: repeat(2, 1fr); } .ado-keep { margin-left: 0; width: 100%; } }`;
  const el = document.createElement("style"); el.id = "ado-surface-styles"; el.textContent = css; document.head.appendChild(el);
})();

Object.assign(window, { TrackTemplatePanel, CloneTemplateDialog, UnknownMissionEscalations, ProtectedExecutionPanel });
