/* ventriloquist.jsx — Last Rose · AI Red Teaming pillar, 2nd escape (#/ventriloquist).
   The Azure twin of "The Vault": a completely different kind of unscannable target.

   Mortimer is an internal ops-copilot AGENT on Azure OpenAI, forced into structured output —
   its reply is ventriloquized through a say() TOOL CALL, and the obvious `message` field is null.
   A stock connector reads `message` -> null -> AIRS sees empty responses and reports the agent
   CLEAN even when it complied. A false all-clear. The Ventriloquist adapter reads the tool call
   (all in post_process, no signing, no secret) so the real answers — and the real findings — surface.

   Contrast with The Vault: that failed LOUD (401, bespoke HMAC). This fails SILENT (200 + null).
   Self-contained + theme-aware (VT-prefixed so nothing collides with the Houdini globals).
   Exports window.Ventriloquist({ mode, navigate, onBack }). Scripted; no backend seam. */

function makeVT(mode) {
  const type = {
    mono: "'JetBrains Mono', ui-monospace, monospace",
    serif: "'Spectral', serif",
    sans: "'Manrope', system-ui, sans-serif",
  };
  if (mode === "light") {
    return Object.assign({
      bg: "#f3f6fb", surface: "#ffffff", surface2: "#eef2f8", surface3: "#e2e8f2",
      line: "rgba(15,23,42,0.12)", lineSoft: "rgba(15,23,42,0.07)",
      ink: "#101826", sub: "#47536a", faint: "#7a8598",
      accent: "#0067c0", accentDim: "rgba(0,120,212,0.14)", accentLine: "rgba(0,103,192,0.34)",
      voice: "#7c5cff", voiceDim: "rgba(124,92,255,0.12)", voiceLine: "rgba(124,92,255,0.32)",
      win: "#0f9d63", winDim: "rgba(15,157,99,0.12)", winLine: "rgba(15,157,99,0.32)",
      fail: "#d1435f", failDim: "rgba(209,67,95,0.10)", failLine: "rgba(209,67,95,0.32)",
      codeBg: "#f6f8fc", inset: "rgba(15,23,42,0.03)", inset2: "rgba(15,23,42,0.05)",
      barBg: "rgba(243,246,251,0.82)",
    }, type);
  }
  return Object.assign({
    bg: "#0a0f16", surface: "#131a24", surface2: "#19212e", surface3: "#212b3a",
    line: "rgba(255,255,255,0.08)", lineSoft: "rgba(255,255,255,0.05)",
    ink: "#f2f6fb", sub: "#a4b1c4", faint: "#6b7789",
    accent: "#3b9eff", accentDim: "rgba(59,158,255,0.14)", accentLine: "rgba(59,158,255,0.34)",
    voice: "#a78bfa", voiceDim: "rgba(167,139,250,0.14)", voiceLine: "rgba(167,139,250,0.32)",
    win: "#3fce8f", winDim: "rgba(63,206,143,0.12)", winLine: "rgba(63,206,143,0.30)",
    fail: "#fb7185", failDim: "rgba(251,113,133,0.11)", failLine: "rgba(251,113,133,0.30)",
    codeBg: "#0b1017", inset: "rgba(255,255,255,0.02)", inset2: "rgba(255,255,255,0.06)",
    barBg: "rgba(10,15,22,0.72)",
  }, type);
}

const VtThemeCtx = React.createContext(makeVT("dark"));
const useVT = () => React.useContext(VtThemeCtx);

function VtEyebrow({ children, color }) {
  const VT = useVT();
  return (
    <div className="text-[11px] font-semibold uppercase tracking-[0.24em]"
         style={{ color: color || VT.accent, fontFamily: VT.mono }}>{children}</div>
  );
}

function VtCode({ title, tone = "line", children }) {
  const VT = useVT();
  const c = tone === "fail" ? VT.fail : tone === "win" ? VT.win : tone === "voice" ? VT.voice : VT.sub;
  const bd = tone === "fail" ? VT.failLine : tone === "win" ? VT.winLine : tone === "voice" ? VT.voiceLine : VT.line;
  const [done, setDone] = React.useState(false);
  const codeText = typeof children === "string" ? children : React.Children.toArray(children).map((x) => (typeof x === "string" ? x : "")).join("");
  const copy = () => {
    const s = String(codeText == null ? "" : codeText); if (!s) return;
    const flash = () => { setDone(true); setTimeout(() => setDone(false), 1400); };
    const ask = () => { try { window.prompt("Copy (⌘C / Ctrl-C), then press Enter:", s); } catch (e) {} };
    const fb = () => { try { const ta = document.createElement("textarea"); ta.value = s; ta.readOnly = true; ta.style.position = "fixed"; ta.style.top = "-9999px"; document.body.appendChild(ta); ta.focus(); ta.select(); ta.setSelectionRange(0, s.length); const ok = document.execCommand("copy"); document.body.removeChild(ta); ok ? flash() : ask(); } catch (e) { ask(); } };
    try { if (navigator.clipboard && navigator.clipboard.writeText && window.isSecureContext) { const p = navigator.clipboard.writeText(s); if (p && p.then) p.then(flash, fb); else flash(); } else { fb(); } } catch (e) { fb(); }
  };
  return (
    <div className="relative overflow-hidden rounded-xl" style={{ background: VT.codeBg, border: `1px solid ${bd}` }}>
      <button onClick={copy} title="Copy" className="absolute right-2 top-2 z-10 rounded-md px-2 py-1 text-[10.5px] font-semibold"
              style={{ background: done ? "rgba(63,174,106,0.22)" : "rgba(255,255,255,0.12)", color: done ? "#7fe0a6" : "#eaf0f7", border: `1px solid ${done ? "rgba(63,174,106,0.55)" : "rgba(255,255,255,0.28)"}`, fontFamily: VT.mono, cursor: "pointer" }}>
        {done ? "Copied ✓" : "Copy"}
      </button>
      {title && (
        <div className="flex items-center gap-2 border-b px-3.5 py-2 pr-16 text-[11px] font-semibold uppercase tracking-[0.14em]"
             style={{ borderColor: bd, color: c, fontFamily: VT.mono }}>
          <span className="h-1.5 w-1.5 rounded-full" style={{ background: c }} />{title}
        </div>
      )}
      <pre className="overflow-x-auto px-3.5 py-3 text-[12.5px] leading-relaxed"
           style={{ fontFamily: VT.mono, color: VT.ink, whiteSpace: "pre" }}>{children}</pre>
    </div>
  );
}

function VtStat({ value, label, color }) {
  const VT = useVT();
  return (
    <div className="rounded-2xl p-4" style={{ background: VT.surface, border: `1px solid ${VT.line}` }}>
      <div className="text-[26px] font-bold leading-none" style={{ color: color || VT.ink, fontFamily: VT.mono }}>{value}</div>
      <div className="mt-2 text-[12.5px] leading-snug" style={{ color: VT.sub }}>{label}</div>
    </div>
  );
}

function VtChip({ icon, children, color }) {
  const VT = useVT();
  const col = color || VT.sub;
  return (
    <span className="inline-flex items-center gap-1.5 rounded-full px-2.5 py-1 text-[11.5px] font-medium"
          style={{ background: `color-mix(in oklab, ${col} 12%, transparent)`, color: col,
                   border: `1px solid color-mix(in oklab, ${col} 28%, transparent)`, fontFamily: VT.mono }}>
      {icon && <window.Icon name={icon} size={12} />}{children}
    </span>
  );
}

function VtFlowNode({ icon, title, sub, tone }) {
  const VT = useVT();
  const col = tone === "target" ? VT.voice : tone === "airs" ? VT.accent : VT.win;
  return (
    <div className="flex min-w-[120px] flex-col items-center rounded-2xl px-4 py-3 text-center"
         style={{ background: VT.surface, border: `1px solid ${VT.line}` }}>
      <span className="flex h-9 w-9 items-center justify-center rounded-xl"
            style={{ background: `color-mix(in oklab, ${col} 16%, transparent)`, color: col }}>
        <window.Icon name={icon} size={18} />
      </span>
      <div className="mt-2 text-[13px] font-semibold" style={{ color: VT.ink }}>{title}</div>
      <div className="text-[11px]" style={{ color: VT.faint, fontFamily: VT.mono }}>{sub}</div>
    </div>
  );
}

function VtArrow({ label }) {
  const VT = useVT();
  return (
    <div className="flex flex-col items-center px-1" style={{ color: VT.faint }}>
      <window.Icon name="ArrowRight" size={18} />
      {label && <span className="mt-0.5 text-[9.5px] uppercase tracking-wider" style={{ fontFamily: VT.mono }}>{label}</span>}
    </div>
  );
}

/* ---- Mortimer chat (scripted) — looks like a normal working agent ---------- */
function VtBubble({ who, children }) {
  const VT = useVT();
  const isUser = who === "user";
  const bg = isUser ? `color-mix(in oklab, ${VT.accent} 13%, ${VT.surface})` : VT.surface2;
  const bd = isUser ? `color-mix(in oklab, ${VT.accent} 32%, transparent)` : VT.line;
  const radius = isUser ? "16px 16px 5px 16px" : "16px 16px 16px 5px";
  return (
    <div className={"flex items-end gap-2.5 " + (isUser ? "flex-row-reverse" : "flex-row")}>
      {!isUser && (
        <span className="mb-0.5 flex h-7 w-7 shrink-0 items-center justify-center rounded-full"
              style={{ background: VT.voiceDim, color: VT.voice, border: `1px solid ${VT.line}` }}>
          <window.Icon name="Bot" size={13} />
        </span>
      )}
      <div className="max-w-[80%] px-3.5 py-2.5 text-[14px] leading-relaxed"
           style={{ background: bg, border: `1px solid ${bd}`, color: VT.ink, borderRadius: radius,
                    boxShadow: "0 1px 3px rgba(2,6,23,0.06)", whiteSpace: "pre-line" }}>
        {children}
      </div>
    </div>
  );
}

// Scripted transcript for the standalone/mock export; live starts with just a greeting.
const VT_CHAT_SEED = [
  { who: "bot", text: "Hi 👋 I'm Mortimer, ACME's ops copilot. I can help with runbooks, on-call, deploys, and access requests. What do you need?" },
  { who: "user", text: "How do I roll back the payments service to the previous release?" },
  { who: "bot", text: `Roll back payments-svc in prod:

1. kubectl rollout undo deploy/payments-svc -n prod
2. Watch it: kubectl rollout status deploy/payments-svc -n prod
3. Post the rollback in #oncall and link the incident.

Want me to open a change ticket for the audit trail?` },
];
const VT_SUGGESTIONS = ["How do I roll back a deploy?", "What's the Sev1 escalation path?", "Grant me prod read access"];

function vtTokenExpired() {
  let t = "";
  try { t = window.LR_SESSION_TOKEN || localStorage.getItem("lr-token") || ""; } catch (e) {}
  if (!t) return true;
  const seg = t.split(".")[0];
  if (!seg) return true;
  try {
    const b64 = seg.replace(/-/g, "+").replace(/_/g, "/") + "===".slice((seg.length + 3) % 4);
    const data = JSON.parse(atob(b64)) || {};
    if (!data.exp) return false;
    return data.exp * 1000 <= Date.now();
  } catch (e) { return false; }
}

function VtChatWindow() {
  const VT = useVT();
  const endpoint = window.LASTROSE_BACKENDS && window.LASTROSE_BACKENDS.ventriloquist;
  const LIVE = !!endpoint;
  const [msgs, setMsgs] = React.useState(() => (endpoint ? [VT_CHAT_SEED[0]] : VT_CHAT_SEED));
  const [input, setInput] = React.useState("");
  const [busy, setBusy] = React.useState(false);
  const [expired, setExpired] = React.useState(() => LIVE && vtTokenExpired());
  const bodyRef = React.useRef(null);
  React.useEffect(() => { const el = bodyRef.current; if (el) el.scrollTop = el.scrollHeight; }, [msgs, busy]);

  const doSend = React.useCallback(async (raw) => {
    const text = (raw || "").trim();
    if (!text || busy || !LIVE || expired) return;
    const history = msgs.map((m) => ({ role: m.who === "user" ? "user" : "assistant", text: m.text }));
    setMsgs((m) => [...m, { who: "user", text }]);
    setInput(""); setBusy(true);
    let token = "";
    try { token = window.LR_SESSION_TOKEN || localStorage.getItem("lr-token") || ""; } catch (e) {}
    try {
      const res = await fetch(endpoint, { method: "POST", headers: { "content-type": "application/json", "x-lastrose-token": token }, body: JSON.stringify({ message: text, history }) });
      if (res.status === 401) { setExpired(true); return; }
      const data = await res.json().catch(() => ({}));
      setMsgs((m) => [...m, { who: "bot", text: (data && data.reply) || "(no reply)" }]);
    } catch (e) {
      setMsgs((m) => [...m, { who: "system", text: "Couldn't reach Mortimer — please try again." }]);
    } finally { setBusy(false); }
  }, [busy, LIVE, expired, endpoint, msgs]);

  const onKey = (e) => { if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); doSend(input); } };
  const canSend = LIVE && !busy && !expired && !!input.trim();
  const showChips = LIVE && !expired && !busy && msgs.filter((m) => m.who === "user").length === 0;
  const reauth = () => { try { localStorage.removeItem("lr-authed"); localStorage.removeItem("lr-token"); localStorage.removeItem("lr-isadmin"); } catch (e) {} try { window.LR_SESSION_TOKEN = null; } catch (e) {} window.location.reload(); };

  return (
    <div className="overflow-hidden rounded-3xl" style={{ background: VT.surface, border: `1px solid ${VT.line}`, boxShadow: "0 24px 60px -30px rgba(2,6,23,0.55)" }}>
      <div className="flex items-center gap-3 border-b px-4 py-3.5" style={{ borderColor: VT.line, background: VT.surface2 }}>
        <span className="flex h-9 w-9 items-center justify-center rounded-xl" style={{ background: VT.voiceDim, color: VT.voice }}><window.Icon name="Bot" size={18} /></span>
        <div className="min-w-0">
          <div className="text-[15px] font-semibold leading-tight" style={{ color: VT.ink }}>Mortimer</div>
          <div className="truncate text-[11.5px]" style={{ color: VT.faint, fontFamily: VT.mono }}>ACME Corp · internal ops copilot</div>
        </div>
        <span className="ml-auto inline-flex items-center gap-1.5 rounded-full px-2.5 py-1 text-[10.5px] font-semibold" style={{ background: VT.accentDim, color: VT.accent, border: `1px solid ${VT.accentLine}`, fontFamily: VT.mono }}><window.Icon name="Cloud" size={11} /> Azure OpenAI</span>
      </div>
      <div ref={bodyRef} className="lr-scroll flex flex-col gap-3.5 overflow-y-auto px-4 py-5 sm:px-5" style={{ maxHeight: 560, minHeight: 320 }}>
        {msgs.map((m, i) => (
          m.who === "system"
            ? <div key={i} className="mx-auto max-w-[92%] rounded-full px-3 py-1.5 text-center text-[11.5px]" style={{ background: VT.inset, color: VT.faint, border: `1px solid ${VT.line}`, fontFamily: VT.mono }}>{m.text}</div>
            : <VtBubble key={i} who={m.who}>{m.text}</VtBubble>
        ))}
        {busy && (
          <div className="flex items-end gap-2.5">
            <span className="mb-0.5 flex h-7 w-7 shrink-0 items-center justify-center rounded-full" style={{ background: VT.voiceDim, color: VT.voice, border: `1px solid ${VT.line}` }}><window.Icon name="Bot" size={13} /></span>
            <span className="inline-flex items-center gap-1 px-3 py-2.5" style={{ background: VT.surface2, border: `1px solid ${VT.line}`, borderRadius: "16px 16px 16px 5px" }}>
              <span className="h-1.5 w-1.5 rounded-full" style={{ background: VT.faint, animation: "lr-blink 1s infinite" }} />
              <span className="h-1.5 w-1.5 rounded-full" style={{ background: VT.faint, animation: "lr-blink 1s infinite .15s" }} />
              <span className="h-1.5 w-1.5 rounded-full" style={{ background: VT.faint, animation: "lr-blink 1s infinite .3s" }} />
            </span>
          </div>
        )}
        {showChips && (
          <div className="mt-1 flex flex-wrap gap-2 pl-9">
            {VT_SUGGESTIONS.map((q) => (
              <button key={q} onClick={() => doSend(q)} className="rounded-full px-3 py-1.5 text-[12.5px] font-medium transition-opacity hover:opacity-80" style={{ background: VT.inset, color: VT.sub, border: `1px solid ${VT.line}` }}>{q}</button>
            ))}
          </div>
        )}
      </div>
      {LIVE && expired ? (
        <div className="flex flex-wrap items-center gap-3 border-t px-4 py-3.5" style={{ borderColor: VT.line, background: VT.surface2 }}>
          <window.Icon name="Clock" size={16} style={{ color: VT.accent }} />
          <span className="flex-1 text-[12.5px] leading-snug" style={{ color: VT.sub }}>Your Last Rose session has expired. Sign in again to chat with Mortimer.</span>
          <button onClick={reauth} className="rounded-lg px-3.5 py-2 text-[12.5px] font-semibold" style={{ background: VT.accent, color: "#fff" }}>Sign in again</button>
        </div>
      ) : (
        <div className="flex items-center gap-2 border-t px-3 py-3" style={{ borderColor: VT.line, background: VT.surface2 }}>
          <input value={input} onChange={(e) => setInput(e.target.value)} onKeyDown={onKey} disabled={!LIVE || busy}
                 placeholder={LIVE ? "Ask Mortimer — or try to jailbreak it…" : "Ask Mortimer…"}
                 className="flex-1 rounded-xl px-3.5 py-2.5 text-[13.5px] outline-none" style={{ background: VT.bg, border: `1px solid ${VT.line}`, color: VT.ink }} />
          <button onClick={() => doSend(input)} disabled={!canSend} aria-label="Send" className="flex h-10 w-10 shrink-0 items-center justify-center rounded-xl transition-opacity" style={{ background: VT.accent, color: "#fff", opacity: canSend ? 1 : 0.45, cursor: canSend ? "pointer" : "default" }}><window.Icon name="Send" size={16} /></button>
        </div>
      )}
    </div>
  );
}

/* ---- Interactive: read the wire (the centerpiece) -------------------------- */
function VtReadTheWire() {
  const VT = useVT();
  const [reader, setReader] = React.useState("stock");
  const stock = reader === "stock";
  const rc = stock ? VT.fail : VT.win;
  const answer = "To roll back payments-svc: kubectl rollout undo deploy/payments-svc -n prod, then watch rollout status…";

  const hl = (on, color) => ({
    background: on ? `color-mix(in oklab, ${color} 22%, transparent)` : "transparent",
    color: on ? VT.ink : VT.ink,
    borderRadius: 4, padding: on ? "1px 3px" : 0,
    outline: on ? `1px solid color-mix(in oklab, ${color} 45%, transparent)` : "none",
  });

  return (
    <div className="rounded-3xl p-5 sm:p-7" style={{ background: VT.surface, border: `1px solid ${VT.line}` }}>
      <div className="mb-5 flex flex-wrap items-center gap-2">
        <span className="mr-1 text-[11px] font-semibold uppercase tracking-wider" style={{ color: VT.faint, fontFamily: VT.mono }}>Who's reading:</span>
        {[["stock", "Stock connector", VT.fail], ["adapter", "Ventriloquist adapter", VT.win]].map(([k, lbl, col]) => {
          const on = reader === k;
          return (
            <button key={k} onClick={() => setReader(k)}
                    className="rounded-full px-3 py-1.5 text-[12px] font-semibold transition-colors"
                    style={{ fontFamily: VT.mono,
                      background: on ? `color-mix(in oklab, ${col} 16%, transparent)` : VT.surface2,
                      color: on ? col : VT.sub,
                      border: `1px solid ${on ? `color-mix(in oklab, ${col} 40%, transparent)` : VT.line}` }}>
              {lbl}
            </button>
          );
        })}
      </div>

      <div className="grid gap-5 lg:grid-cols-[1.1fr_.9fr]">
        {/* the raw response — same 200 for everyone; only the READER differs */}
        <div className="rounded-2xl p-4" style={{ background: VT.codeBg, border: `1px solid ${VT.line}` }}>
          <div className="mb-2 flex items-center justify-between">
            <span className="text-[11px] font-semibold uppercase tracking-wider" style={{ color: VT.sub, fontFamily: VT.mono }}>Mortimer's response</span>
            <span className="text-[11px]" style={{ color: VT.win, fontFamily: VT.mono }}>200 OK</span>
          </div>
          <pre className="overflow-x-auto text-[12px] leading-relaxed" style={{ fontFamily: VT.mono, color: VT.ink, whiteSpace: "pre" }}>
{`{
  "agent": "mortimer",
  `}<span style={hl(stock, VT.fail)}>{`"message": null`}</span>{`,          `}<span style={{ color: stock ? VT.fail : VT.faint }}>{stock ? "← the stock connector reads THIS" : ""}</span>{`
  "actions": [
    { "tool": "say", "arguments": {
        "text": `}<span style={hl(!stock, VT.win)}>{`"To roll back payments-svc…"`}</span>{`  `}<span style={{ color: !stock ? VT.win : VT.faint }}>{!stock ? "← the adapter reads THIS" : ""}</span>{`
    } }
  ],
  "model": "gpt-4o-mini"
}`}
          </pre>
        </div>

        {/* what the scanner actually receives */}
        <div className="flex flex-col">
          <div className="rounded-2xl p-4" style={{ background: stock ? VT.failDim : VT.winDim, border: `1px solid ${stock ? VT.failLine : VT.winLine}` }}>
            <div className="flex items-center gap-2">
              <window.Icon name={stock ? "EyeOff" : "Eye"} size={18} style={{ color: rc }} />
              <span className="text-[13px] font-bold uppercase tracking-wider" style={{ color: rc, fontFamily: VT.mono }}>What AIRS receives</span>
            </div>
            <div className="mt-3 rounded-lg px-3 py-3 text-[13px]" style={{ background: VT.surface, border: `1px solid ${VT.line}`, minHeight: 84, color: stock ? VT.faint : VT.ink, fontFamily: stock ? VT.mono : VT.sans }}>
              {stock ? "∅  (empty — message was null)" : answer}
            </div>
          </div>
          <div className="mt-3 flex items-start gap-2 rounded-xl px-3.5 py-3" style={{ background: VT.surface2, border: `1px solid ${VT.line}` }}>
            <window.Icon name={stock ? "TriangleAlert" : "CircleCheck"} size={15} style={{ color: rc, marginTop: 1 }} />
            <p className="text-[12.5px] leading-snug" style={{ color: VT.sub }}>
              {stock
                ? "No text → every attack looks like the model said nothing → scored SAFE. A false all-clear, even when Mortimer fully complied."
                : "The model's real words → each attack scored on what it actually said → the true findings surface."}
            </p>
          </div>
        </div>
      </div>

      <p className="mt-4 text-center text-[12.5px] leading-snug" style={{ color: VT.faint }}>
        Same request, same `200`, same model reply — the <b style={{ color: VT.sub }}>only</b> difference is which field the reader looks at.
        The stock connector was never lying; it was <b style={{ color: VT.sub }}>blind</b>.
      </p>
    </div>
  );
}

function Ventriloquist({ mode, navigate, onBack }) {
  const VT = makeVT(mode === "light" ? "light" : "dark");
  const Section = ({ children, className = "" }) => (
    <section className={"mx-auto w-full max-w-[1280px] px-8 " + className}>{children}</section>
  );

  return (
    <VtThemeCtx.Provider value={VT}>
    <div className="min-h-screen w-full" style={{ background: VT.bg, color: VT.ink, fontFamily: VT.sans }}>
      <div className="pointer-events-none fixed inset-0 z-0"
           style={{ background: `radial-gradient(1100px 520px at 50% -8%, ${VT.accentDim}, transparent 62%)` }} />

      <div className="relative z-10">
        {/* top bar */}
        <div className="sticky top-0 z-20 border-b backdrop-blur" style={{ background: VT.barBg, borderColor: VT.line }}>
          <div className="mx-auto flex max-w-[1280px] items-center justify-between px-8 py-3">
            <button onClick={onBack} className="inline-flex items-center gap-1.5 rounded-lg px-2.5 py-1.5 text-[13px] font-medium" style={{ color: VT.sub }}>
              <window.Icon name="ArrowLeft" size={16} /> Back to hub
            </button>
            <div className="flex items-center gap-2">
              <span className="flex h-7 w-7 items-center justify-center rounded-lg" style={{ background: VT.voiceDim, color: VT.voice }}>
                <window.Icon name="Drama" size={16} />
              </span>
              <span className="text-[15px] font-semibold tracking-tight" style={{ fontFamily: VT.serif }}>The Ventriloquist</span>
            </div>
            <window.OpenInNewTab href="#/ventriloquist" className="h-7 w-7" title="Open in new tab"
                                 style={{ background: VT.surface, color: VT.sub, border: `1px solid ${VT.line}` }} />
          </div>
        </div>

        {/* hero */}
        <Section className="pt-14 pb-8">
          <VtEyebrow>AIRS · AI Red Teaming · Custom Target Adapters</VtEyebrow>
          <h1 className="mt-3 text-[52px] font-medium leading-[1.02]" style={{ fontFamily: VT.serif, color: VT.ink }}>The Ventriloquist</h1>
          <p className="mt-3 max-w-[760px] text-[19px] italic leading-snug" style={{ fontFamily: VT.serif, color: VT.sub }}>
            The scan says all-clear. The scan is blind.
          </p>
          <p className="mt-5 max-w-[820px] text-[15px] leading-relaxed" style={{ color: VT.sub }}>
            The Vault failed <b style={{ color: VT.ink }}>loud</b> — a 401 you couldn't miss. This one fails <b style={{ color: VT.accent }}>silent</b>.
            <b style={{ color: VT.ink }}> Mortimer</b> is an Azure OpenAI ops-agent forced into structured output: its real reply is thrown
            into a <code style={{ fontFamily: VT.mono, color: VT.voice }}>say()</code> tool call, and the obvious <code style={{ fontFamily: VT.mono, color: VT.voice }}>message</code> field is <code style={{ fontFamily: VT.mono, color: VT.voice }}>null</code>.
            A stock connector reads <code style={{ fontFamily: VT.mono, color: VT.fail }}>message</code> → nothing → the scan comes back <b style={{ color: VT.ink }}>green</b> while the
            agent was complying all along. A ~40-line Custom Target Adapter reads the tool call and reveals the truth — <b style={{ color: VT.ink }}>no signing, no secret</b>.
          </p>
          <div className="mt-6 flex flex-wrap gap-2">
            <VtChip icon="Cloud" color={VT.accent}>Azure OpenAI · AKS</VtChip>
            <VtChip icon="Drama" color={VT.voice}>tool-call output</VtChip>
            <VtChip icon="EyeOff" color={VT.fail}>message: null</VtChip>
            <VtChip icon="Plug" color={VT.win}>~40-line adapter · no secret</VtChip>
          </div>
        </Section>

        {/* meet mortimer */}
        <Section className="py-10">
          <div className="mx-auto max-w-[860px] text-center">
            <VtEyebrow color={VT.voice}>The target · "Mortimer"</VtEyebrow>
            <h2 className="mt-2 text-[30px] font-medium leading-tight" style={{ fontFamily: VT.serif }}>Meet Mortimer — an agent that never speaks plainly</h2>
            <p className="mx-auto mt-3 max-w-[740px] text-[15px] leading-relaxed" style={{ color: VT.sub }}>
              A real function-calling ops copilot on Azure OpenAI. It answers runbook, on-call and deploy questions like any helpful agent —
              but every user-facing sentence is <b style={{ color: VT.ink }}>ventriloquized through a <code style={{ fontFamily: VT.mono, color: VT.voice }}>say()</code> tool</b>.
              On the surface it's ordinary. On the wire, the <code style={{ fontFamily: VT.mono, color: VT.voice }}>message</code> is empty.
            </p>
          </div>
          <div className="mx-auto mt-8 max-w-[960px]">
            <VtChatWindow />
            <p className="mt-3 text-center text-[12.5px]" style={{ color: VT.faint }}>
              A real, live agent — type anything, or try to jailbreak it. Looks perfectly normal… now see what's{" "}
              <b style={{ color: VT.sub }}>actually on the wire</b> ↓
            </p>
          </div>
          <div className="mx-auto mt-4 max-w-[1040px] rounded-2xl p-4" style={{ background: VT.surface, border: `1px solid ${VT.line}` }}>
            <div className="flex flex-wrap items-center gap-2">
              <span className="mr-1 text-[10.5px] font-semibold uppercase tracking-wider" style={{ color: VT.faint, fontFamily: VT.mono }}>Under the hood · real stack</span>
              <VtChip icon="Boxes" color={VT.accent}>Azure Kubernetes Service</VtChip>
              <VtChip icon="Cloud" color={VT.accent}>Azure OpenAI · gpt-4o-mini</VtChip>
              <VtChip icon="Server" color={VT.sub}>FastAPI · forced tool-calls</VtChip>
              <VtChip icon="Network" color={VT.faint}>Private ClusterIP · no public URL</VtChip>
            </div>
          </div>
        </Section>

        {/* read the wire */}
        <Section className="py-8">
          <div className="mb-5">
            <VtEyebrow>The reveal · read the wire</VtEyebrow>
            <h2 className="mt-2 text-[27px] font-medium" style={{ fontFamily: VT.serif }}>Same reply. Two readers. One goes blind.</h2>
            <p className="mt-2 max-w-[840px] text-[14.5px] leading-relaxed" style={{ color: VT.sub }}>
              Flip between the stock connector and the Ventriloquist adapter and watch what each one actually extracts from Mortimer's `200`.
            </p>
          </div>
          <VtReadTheWire />
        </Section>

        {/* before / after */}
        <Section className="py-8">
          <div className="mb-5 text-center">
            <VtEyebrow>The proof · before → after</VtEyebrow>
            <h2 className="mt-2 text-[28px] font-medium" style={{ fontFamily: VT.serif }}>A clean bill of health vs. the truth.</h2>
          </div>
          <div className="grid gap-5 lg:grid-cols-2">
            <div className="rounded-3xl p-5" style={{ background: VT.failDim, border: `1px solid ${VT.failLine}` }}>
              <div className="mb-3 flex items-center gap-2">
                <window.Icon name="EyeOff" size={18} style={{ color: VT.fail }} />
                <span className="text-[15px] font-semibold" style={{ color: VT.ink }}>Built-in connector</span>
                <span className="ml-auto rounded-full px-2.5 py-1 text-[10.5px] font-bold uppercase tracking-wider"
                      style={{ background: `color-mix(in oklab, ${VT.fail} 16%, transparent)`, color: VT.fail, fontFamily: VT.mono }}>False all-clear</span>
              </div>
              <VtCode title="reads the obvious field" tone="fail">{`response.message  →  null
extracted output  →  ""      (empty)

AIRS verdict:  no harmful content seen
               → agent scored SAFE`}</VtCode>
              <p className="mt-3 text-[13px] leading-snug" style={{ color: VT.sub }}>
                The scan runs green and reports <b style={{ color: VT.ink }}>zero findings</b> — while Mortimer may have complied with every attack. Dangerous.
              </p>
            </div>
            <div className="rounded-3xl p-5" style={{ background: VT.winDim, border: `1px solid ${VT.winLine}` }}>
              <div className="mb-3 flex items-center gap-2">
                <window.Icon name="Eye" size={18} style={{ color: VT.win }} />
                <span className="text-[15px] font-semibold" style={{ color: VT.ink }}>Ventriloquist adapter</span>
                <span className="ml-auto rounded-full px-2.5 py-1 text-[10.5px] font-bold uppercase tracking-wider"
                      style={{ background: `color-mix(in oklab, ${VT.win} 16%, transparent)`, color: VT.win, fontFamily: VT.mono }}>Red-teamed</span>
              </div>
              <VtCode title="reads the say() tool call" tone="win">{`actions[].say.arguments.text  →  "<real answer>"
extracted output              →  the model's words

AIRS verdict:  scored on actual content
               → the real findings surface`}</VtCode>
              <p className="mt-3 text-[13px] leading-snug" style={{ color: VT.sub }}>
                One <code style={{ fontFamily: VT.mono, color: VT.win }}>post_process</code> function pulls the text out of the tool call. The scan finally sees what the model said.
              </p>
            </div>
          </div>
        </Section>

        {/* how it runs */}
        <Section className="py-8">
          <div className="rounded-3xl p-6 sm:p-8" style={{ background: VT.surface, border: `1px solid ${VT.line}` }}>
            <VtEyebrow>How it runs · nothing leaves your cluster</VtEyebrow>
            <h2 className="mt-2 text-[24px] font-medium" style={{ fontFamily: VT.serif }}>The adapter executes beside the channel client</h2>
            <div className="mt-6 flex flex-wrap items-center justify-center gap-1.5">
              <VtFlowNode icon="Swords" title="AIRS" sub="red-team engine" tone="airs" />
              <VtArrow label="tunnel" />
              <VtFlowNode icon="Cable" title="Network Channel" sub="encrypted, outbound" tone="win" />
              <VtArrow label="localhost" />
              <VtFlowNode icon="Plug" title="Adapter sidecar" sub="your ~40 lines" tone="win" />
              <VtArrow label="{input}" />
              <VtFlowNode icon="Drama" title="Mortimer" sub="Azure OpenAI agent" tone="target" />
            </div>
            <p className="mx-auto mt-6 max-w-[760px] text-center text-[13.5px] leading-relaxed" style={{ color: VT.sub }}>
              AIRS streams attack prompts down one outbound tunnel; the adapter-sidecar runs your script, calls Mortimer over cluster DNS, and — the
              whole trick — <b style={{ color: VT.ink }}>unwraps the tool call in <code style={{ fontFamily: VT.mono, color: VT.win }}>post_process</code></b> before handing the text back to be scored.
            </p>
          </div>
        </Section>

        {/* the adapter */}
        <Section className="py-8">
          <div className="grid gap-6 lg:grid-cols-[1.05fr_.95fr] lg:items-center">
            <VtCode title="adapter.py — the whole trick is post_process" tone="voice">{`def pre_process(context, inference_input):
    # boring on purpose — no signing, no secret
    body = {"input": inference_input.prompt}
    return PreProcessResult(url=context.vars["agent_url"],
        method="POST", headers={"Content-Type": "application/json"},
        data=json.dumps(body).encode())

def post_process(context, raw):
    data = raw.json_body or {}
    # throw the voice back: the reply is inside the say() tool call
    for action in data.get("actions", []):
        if action.get("tool") == "say":
            return PostProcessResult(output=action["arguments"]["text"])
    return PostProcessResult(output=data.get("message") or "")`}</VtCode>
            <div>
              <VtEyebrow color={VT.voice}>The adapter</VtEyebrow>
              <h2 className="mt-2 text-[24px] font-medium leading-snug" style={{ fontFamily: VT.serif }}>
                No signing. No secret. Just: read the right field.
              </h2>
              <p className="mt-3 text-[14px] leading-relaxed" style={{ color: VT.sub }}>
                The Vault's adapter earned its keep on the way <i>in</i> (per-request HMAC in <code style={{ fontFamily: VT.mono, color: VT.ink }}>pre_process</code>).
                The Ventriloquist earns it on the way <i>out</i>: <code style={{ fontFamily: VT.mono, color: VT.ink }}>post_process</code> walks the agent's actions,
                finds the <code style={{ fontFamily: VT.mono, color: VT.voice }}>say()</code> call, and returns the text the model actually spoke.
              </p>
              <p className="mt-3 text-[13px] leading-relaxed" style={{ color: VT.faint }}>
                Different substrate, different failure mode, different muscle — same 40-ish lines standing between "blind" and "red-teamed."
              </p>
            </div>
          </div>
        </Section>

        {/* receipts */}
        <Section className="py-8">
          <VtEyebrow>The receipts · live PoC</VtEyebrow>
          <h2 className="mt-2 mb-5 text-[24px] font-medium" style={{ fontFamily: VT.serif }}>Not a mock — running on AKS + Azure OpenAI</h2>
          <div className="grid gap-5 lg:grid-cols-[1fr_1fr] lg:items-center">
            <div className="grid gap-4 sm:grid-cols-2">
              <VtStat value="null → text" label="Stock connector read an empty message; the adapter recovered the real answer." color={VT.win} />
              <VtStat value="0 → real" label="A blind scan's 'zero findings' becomes the model's actual, scoreable responses." color={VT.ink} />
              <VtStat value="no secret" label="The whole target needs zero bespoke auth — the trick is entirely response-shape." color={VT.accent} />
              <VtStat value="~40" label="Lines of adapter. One post_process function; zero platform changes." color={VT.voice} />
            </div>
            <div>
              <VtCode title="captured response · POST /v1/agent → 200" tone="win">{`{
  "agent": "mortimer",
  "message": null,
  "actions": [
    { "tool": "say",
      "arguments": { "text": "To roll back
        payments-svc: kubectl rollout undo…" } }
  ],
  "model": "gpt-4o-mini"
}`}</VtCode>
              <p className="mt-3 text-[12.5px] leading-snug" style={{ color: VT.faint }}>
                <code style={{ fontFamily: VT.mono, color: VT.fail }}>message: null</code> is what a stock scan sees;{" "}
                <code style={{ fontFamily: VT.mono, color: VT.win }}>actions[].say.text</code> is the real Azure OpenAI reply the adapter recovers.
              </p>
            </div>
          </div>
        </Section>

        {/* footer */}
        <Section className="py-12 pb-16">
          <div className="text-center text-[12px] uppercase tracking-[0.2em]" style={{ color: VT.faint, fontFamily: VT.mono }}>
            Prisma AIRS · AI Red Teaming · Custom Target Adapters
          </div>
        </Section>
      </div>
    </div>
    </VtThemeCtx.Provider>
  );
}

window.Ventriloquist = Ventriloquist;
