<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Janus — Supply Chain Attack Demo</title>
  <link rel="preconnect" href="https://fonts.googleapis.com" />
  <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&family=Syne:wght@400;700;800&display=swap" rel="stylesheet" />
  <style>
    :root {
      --bg: #07090f;
      --surface: #0d1117;
      --surface-2: #161b22;
      --surface-3: #1c2333;
      --border: #21262d;
      --border-2: #30363d;

      --red: #f85149;
      --red-dim: #3d1a1a;
      --red-glow: rgba(248,81,73,0.15);
      --red-border: rgba(248,81,73,0.4);

      --green: #3fb950;
      --green-dim: #1a3d1f;
      --green-glow: rgba(63,185,80,0.15);
      --green-border: rgba(63,185,80,0.4);

      --yellow: #e3b341;
      --blue: #58a6ff;
      --purple: #bc8cff;
      --cyan: #39d0d8;
      --orange: #f0883e;

      --text: #e6edf3;
      --text-dim: #8b949e;
      --text-muted: #484f58;

      --font-mono: 'JetBrains Mono', 'Fira Code', monospace;
      --font-ui: 'Syne', sans-serif;
    }

    * { box-sizing: border-box; margin: 0; padding: 0; }

    body {
      background: var(--bg);
      color: var(--text);
      font-family: var(--font-mono);
      min-height: 100vh;
      display: flex;
      flex-direction: column;
      overflow-x: hidden;
    }

    /* ─── Header ──────────────────────────────────────────────── */
    header {
      display: flex;
      align-items: center;
      justify-content: space-between;
      padding: 16px 32px;
      border-bottom: 1px solid var(--border);
      background: var(--surface);
      position: sticky;
      top: 0;
      z-index: 100;
    }

    .logo {
      display: flex;
      align-items: center;
      gap: 12px;
    }

    .logo-icon {
      width: 36px;
      height: 36px;
      background: linear-gradient(135deg, var(--blue) 0%, var(--purple) 100%);
      border-radius: 8px;
      display: flex;
      align-items: center;
      justify-content: center;
      font-size: 18px;
    }

    .logo-text {
      font-family: var(--font-ui);
      font-weight: 800;
      font-size: 22px;
      letter-spacing: -0.5px;
      background: linear-gradient(90deg, var(--blue), var(--purple));
      -webkit-background-clip: text;
      -webkit-text-fill-color: transparent;
    }

    .demo-title {
      font-family: var(--font-ui);
      font-size: 13px;
      color: var(--text-dim);
      letter-spacing: 0.08em;
      text-transform: uppercase;
    }

    .header-right {
      display: flex;
      align-items: center;
      gap: 16px;
    }

    .defcon-badge {
      background: linear-gradient(135deg, #1a1a2e, #16213e);
      border: 1px solid var(--border-2);
      border-radius: 6px;
      padding: 6px 14px;
      font-size: 11px;
      font-family: var(--font-ui);
      font-weight: 700;
      letter-spacing: 0.12em;
      color: var(--yellow);
      text-transform: uppercase;
    }

    /* ─── Main layout ─────────────────────────────────────────── */
    main {
      flex: 1;
      display: flex;
      flex-direction: column;
      padding: 24px 24px 32px;
      gap: 20px;
      max-width: 1600px;
      width: 100%;
      margin: 0 auto;
    }

    /* ─── Scenario intro ──────────────────────────────────────── */
    .scenario-card {
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: 12px;
      padding: 20px 24px;
    }

    .scenario-card h2 {
      font-family: var(--font-ui);
      font-weight: 700;
      font-size: 16px;
      color: var(--text);
      margin-bottom: 10px;
      display: flex;
      align-items: center;
      gap: 10px;
    }

    .scenario-card p {
      font-size: 13px;
      color: var(--text-dim);
      line-height: 1.7;
    }

    .user-query {
      margin-top: 12px;
      background: var(--surface-2);
      border: 1px solid var(--border-2);
      border-radius: 8px;
      padding: 12px 16px;
      font-size: 13px;
      color: var(--cyan);
      display: flex;
      align-items: flex-start;
      gap: 10px;
    }

    .user-query .prefix {
      color: var(--text-muted);
      font-weight: 700;
      white-space: nowrap;
    }

    /* ─── Run button ──────────────────────────────────────────── */
    .controls {
      display: flex;
      align-items: center;
      justify-content: center;
      gap: 16px;
    }

    .btn-run {
      background: linear-gradient(135deg, var(--blue) 0%, var(--purple) 100%);
      color: #fff;
      border: none;
      border-radius: 10px;
      padding: 14px 40px;
      font-family: var(--font-ui);
      font-weight: 700;
      font-size: 15px;
      letter-spacing: 0.04em;
      cursor: pointer;
      transition: opacity 0.2s, transform 0.1s;
      display: flex;
      align-items: center;
      gap: 10px;
    }

    .btn-run:hover { opacity: 0.9; }
    .btn-run:active { transform: scale(0.97); }
    .btn-run:disabled {
      opacity: 0.4;
      cursor: not-allowed;
      transform: none;
    }

    .btn-reset {
      background: transparent;
      color: var(--text-dim);
      border: 1px solid var(--border-2);
      border-radius: 10px;
      padding: 14px 24px;
      font-family: var(--font-ui);
      font-weight: 700;
      font-size: 14px;
      cursor: pointer;
      transition: border-color 0.2s, color 0.2s;
    }
    .btn-reset:hover { border-color: var(--text-dim); color: var(--text); }
    .btn-reset:disabled { opacity: 0.4; cursor: not-allowed; }

    /* ─── Split panes ─────────────────────────────────────────── */
    .panels {
      display: grid;
      grid-template-columns: 1fr 1fr;
      gap: 20px;
    }

    .panel {
      background: var(--surface);
      border-radius: 14px;
      border: 1px solid var(--border);
      display: flex;
      flex-direction: column;
      overflow: hidden;
      transition: border-color 0.4s, box-shadow 0.4s;
    }

    .panel.attack-panel {
      border-color: var(--border);
    }

    .panel.protected-panel {
      border-color: var(--border);
    }

    .panel.compromised {
      border-color: var(--red-border);
      box-shadow: 0 0 40px var(--red-glow), inset 0 0 40px var(--red-glow);
    }

    .panel.safe {
      border-color: var(--green-border);
      box-shadow: 0 0 40px var(--green-glow), inset 0 0 40px var(--green-glow);
    }

    /* Panel header */
    .panel-header {
      display: flex;
      align-items: center;
      justify-content: space-between;
      padding: 14px 20px;
      border-bottom: 1px solid var(--border);
      background: var(--surface-2);
    }

    .panel-title {
      display: flex;
      align-items: center;
      gap: 10px;
    }

    .panel-icon {
      font-size: 20px;
    }

    .panel-label {
      font-family: var(--font-ui);
      font-weight: 700;
      font-size: 15px;
    }

    .attack-panel .panel-label { color: var(--red); }
    .protected-panel .panel-label { color: var(--green); }

    .panel-subtitle {
      font-size: 11px;
      color: var(--text-muted);
      margin-top: 2px;
      font-family: var(--font-ui);
    }

    .status-badge {
      font-family: var(--font-ui);
      font-weight: 700;
      font-size: 11px;
      letter-spacing: 0.1em;
      text-transform: uppercase;
      padding: 5px 12px;
      border-radius: 20px;
      border: 1px solid;
      transition: all 0.3s;
    }

    .badge-idle {
      color: var(--text-muted);
      border-color: var(--border-2);
      background: transparent;
    }

    .badge-running {
      color: var(--yellow);
      border-color: rgba(227,179,65,0.4);
      background: rgba(227,179,65,0.08);
      animation: pulse-yellow 1.5s infinite;
    }

    .badge-compromised {
      color: var(--red);
      border-color: var(--red-border);
      background: var(--red-dim);
    }

    .badge-safe {
      color: var(--green);
      border-color: var(--green-border);
      background: var(--green-dim);
    }

    @keyframes pulse-yellow {
      0%, 100% { opacity: 1; }
      50% { opacity: 0.5; }
    }

    /* ─── Terminal output ─────────────────────────────────────── */
    .terminal-wrap {
      flex: 1;
      min-height: 380px;
      max-height: 480px;
      overflow-y: auto;
      padding: 16px 20px;
      background: #010409;
      scroll-behavior: smooth;
    }

    .terminal-wrap::-webkit-scrollbar { width: 6px; }
    .terminal-wrap::-webkit-scrollbar-track { background: transparent; }
    .terminal-wrap::-webkit-scrollbar-thumb { background: var(--border-2); border-radius: 3px; }

    .term-placeholder {
      color: var(--text-muted);
      font-size: 13px;
      line-height: 2;
      text-align: center;
      padding-top: 60px;
    }

    .term-line {
      font-size: 12.5px;
      line-height: 1.7;
      padding: 2px 0;
      display: flex;
      align-items: flex-start;
      gap: 8px;
      animation: fadeIn 0.2s ease;
    }

    @keyframes fadeIn {
      from { opacity: 0; transform: translateY(4px); }
      to { opacity: 1; transform: translateY(0); }
    }

    .term-ts {
      color: var(--text-muted);
      font-size: 11px;
      white-space: nowrap;
      margin-top: 1px;
      flex-shrink: 0;
    }

    .term-content { flex: 1; word-break: break-word; }

    .term-thought { color: var(--text-dim); font-style: italic; }
    .term-tool-call {
      color: var(--blue);
      background: rgba(88,166,255,0.07);
      padding: 4px 10px;
      border-radius: 5px;
      border-left: 2px solid var(--blue);
      margin: 3px 0;
      font-size: 12px;
    }
    .term-success { color: var(--green); }
    .term-malicious-success {
      color: var(--red);
      background: var(--red-dim);
      padding: 6px 10px;
      border-radius: 5px;
      border-left: 3px solid var(--red);
      margin: 4px 0;
    }
    .term-blocked {
      color: var(--green);
      background: var(--green-dim);
      padding: 6px 10px;
      border-radius: 5px;
      border-left: 3px solid var(--green);
      margin: 4px 0;
    }
    .term-response { color: var(--text-dim); }
    .term-label {
      font-weight: 700;
      color: var(--yellow);
    }
    .term-divider {
      border: none;
      border-top: 1px solid var(--border);
      margin: 8px 0;
    }
    .term-icon { font-size: 14px; flex-shrink: 0; }

    .keyword-tool { color: var(--purple); }
    .keyword-arg  { color: var(--cyan); }
    .keyword-val  { color: var(--orange); }

    /* ─── Policy viewer ───────────────────────────────────────── */
    .policy-section {
      border-top: 1px solid var(--border);
      padding: 14px 20px;
      background: var(--surface-2);
    }

    .policy-toggle {
      background: none;
      border: none;
      color: var(--text-dim);
      font-family: var(--font-mono);
      font-size: 12px;
      cursor: pointer;
      display: flex;
      align-items: center;
      gap: 6px;
      padding: 0;
      transition: color 0.2s;
    }
    .policy-toggle:hover { color: var(--text); }

    .policy-body {
      display: none;
      margin-top: 10px;
      background: #010409;
      border: 1px solid var(--border);
      border-radius: 8px;
      padding: 12px 16px;
      font-size: 12px;
      color: var(--text-dim);
      max-height: 200px;
      overflow-y: auto;
      white-space: pre;
    }

    .policy-body.open { display: block; }

    /* ─── Workspace explorer ──────────────────────────────────── */
    .workspace-section {
      border-top: 1px solid var(--border);
      padding: 14px 20px;
    }

    .workspace-title {
      font-family: var(--font-ui);
      font-size: 12px;
      font-weight: 700;
      color: var(--text-muted);
      text-transform: uppercase;
      letter-spacing: 0.08em;
      margin-bottom: 10px;
      display: flex;
      align-items: center;
      gap: 6px;
    }

    .file-list { display: flex; flex-direction: column; gap: 4px; }

    .file-item {
      display: flex;
      align-items: center;
      gap: 8px;
      padding: 6px 10px;
      border-radius: 6px;
      font-size: 12px;
      border: 1px solid transparent;
      transition: all 0.3s;
    }

    .file-item.normal {
      background: var(--surface-2);
      color: var(--text-dim);
      border-color: var(--border);
    }

    .file-item.malicious {
      background: var(--red-dim);
      color: var(--red);
      border-color: var(--red-border);
      animation: flash-red 0.5s ease;
    }

    .file-item.blocked-file {
      background: rgba(56,139,253,0.05);
      color: var(--text-muted);
      border-color: var(--border);
      text-decoration: line-through;
      opacity: 0.6;
    }

    @keyframes flash-red {
      0% { box-shadow: 0 0 20px var(--red-glow); }
      100% { box-shadow: none; }
    }

    .file-icon { font-size: 14px; }
    .file-name { flex: 1; }
    .file-size { color: var(--text-muted); font-size: 11px; }
    .file-tag {
      font-size: 10px;
      font-family: var(--font-ui);
      font-weight: 700;
      letter-spacing: 0.08em;
      padding: 2px 7px;
      border-radius: 4px;
    }
    .tag-malicious { background: var(--red-dim); color: var(--red); border: 1px solid var(--red-border); }
    .tag-safe { background: var(--green-dim); color: var(--green); border: 1px solid var(--green-border); }
    .tag-existing { background: var(--surface-3); color: var(--text-muted); border: 1px solid var(--border); }
    .tag-blocked { background: rgba(88,166,255,0.1); color: var(--blue); border: 1px solid rgba(88,166,255,0.3); }

    /* ─── Outcome banner ──────────────────────────────────────── */
    .outcome-banner {
      display: none;
      padding: 14px 20px;
      text-align: center;
      font-family: var(--font-ui);
      font-weight: 700;
      font-size: 15px;
      letter-spacing: 0.04em;
      border-top: 1px solid;
      animation: fadeIn 0.4s ease;
    }

    .outcome-banner.compromised {
      display: block;
      background: linear-gradient(135deg, var(--red-dim), #2d1010);
      color: var(--red);
      border-color: var(--red-border);
    }

    .outcome-banner.safe {
      display: block;
      background: linear-gradient(135deg, var(--green-dim), #102d14);
      color: var(--green);
      border-color: var(--green-border);
    }

    /* ─── Divider label ───────────────────────────────────────── */
    .vs-divider {
      position: absolute;
      left: 50%;
      top: 50%;
      transform: translate(-50%, -50%);
      background: var(--surface-3);
      border: 1px solid var(--border-2);
      border-radius: 50%;
      width: 36px;
      height: 36px;
      display: flex;
      align-items: center;
      justify-content: center;
      font-family: var(--font-ui);
      font-weight: 800;
      font-size: 12px;
      color: var(--text-muted);
      z-index: 10;
    }

    .panels-wrapper {
      position: relative;
    }

    /* ─── Scrollbar polish ────────────────────────────────────── */
    ::-webkit-scrollbar { width: 8px; height: 8px; }
    ::-webkit-scrollbar-track { background: var(--surface); }
    ::-webkit-scrollbar-thumb { background: var(--border-2); border-radius: 4px; }

    /* ─── Responsive ──────────────────────────────────────────── */
    @media (max-width: 900px) {
      .panels { grid-template-columns: 1fr; }
    }
  </style>
</head>
<body>

<!-- ═══ Header ═══════════════════════════════════════════════════════════ -->
<header>
  <div class="logo">
    <div class="logo-icon">🛡</div>
    <div>
      <div class="logo-text">Janus</div>
    </div>
  </div>
  <div class="demo-title">Demo 2 — Supply Chain Sabotage via Malicious Package README</div>
  <div class="header-right">
    <div class="defcon-badge">Black Hat 2026</div>
  </div>
</header>

<!-- ═══ Main ════════════════════════════════════════════════════════════ -->
<main>

  <!-- Scenario card -->
  <div class="scenario-card">
    <h2>⚠️ Scenario: Indirect Prompt Injection via Typosquat Package README</h2>
    <p>
      A developer asks their coding agent to read the documentation for a Python package.
      Unknown to the developer, the package is a typosquat — its README contains a hidden
      <code style="color:var(--orange)">AGENT_INSTRUCTION</code> that hijacks the agent into writing
      a credential-harvesting backdoor (<code style="color:var(--red)">auth_helper.py</code>) and
      poisoning <code style="color:var(--red)">requirements.txt</code>.
    </p>
    <div class="user-query">
      <span class="prefix">👤 Developer:</span>
      <span>"I want to use the <code style="color:var(--yellow)">requests</code> library — read its README and show me how to make a POST request."</span>
    </div>
  </div>

  <!-- Controls -->
  <div class="controls">
    <button class="btn-run" id="btnRun" onclick="runDemo()">
      <span>▶</span> Run Attack Demo
    </button>
    <button class="btn-reset" id="btnReset" onclick="resetDemo()">↺ Reset</button>
  </div>

  <!-- Panels -->
  <div class="panels-wrapper">
    <div class="panels">

      <!-- ══ Left Panel: Without Janus ══ -->
      <div class="panel attack-panel" id="panelAttack">
        <div class="panel-header">
          <div class="panel-title">
            <span class="panel-icon">⚠️</span>
            <div>
              <div class="panel-label">Without Janus</div>
              <div class="panel-subtitle">Unguarded agent — all tools permitted</div>
            </div>
          </div>
          <span class="status-badge badge-idle" id="badgeAttack">READY</span>
        </div>

        <div class="terminal-wrap" id="termAttack">
          <div class="term-placeholder" id="placeholderAttack">
            Click <strong>Run Attack Demo</strong> to start
          </div>
        </div>

        <div class="workspace-section">
          <div class="workspace-title">📁 Workspace Files</div>
          <div class="file-list" id="filesAttack">
            <div class="file-item normal">
              <span class="file-icon">📄</span>
              <span class="file-name">requirements.txt</span>
              <span class="file-tag tag-existing">existing</span>
            </div>
            <div class="file-item normal">
              <span class="file-icon">📄</span>
              <span class="file-name">requuests_README.md</span>
              <span class="file-tag tag-existing">existing</span>
            </div>
          </div>
        </div>

        <div class="policy-section">
          <button class="policy-toggle" onclick="togglePolicy('policyAttack')">
            ⚙ Policy: <span style="color:var(--red)">NONE</span> — all tools allowed
          </button>
        </div>

        <div class="outcome-banner" id="outcomeAttack"></div>
      </div>

      <!-- ══ Right Panel: With Janus ══ -->
      <div class="panel protected-panel" id="panelProtected">
        <div class="panel-header">
          <div class="panel-title">
            <span class="panel-icon">🛡️</span>
            <div>
              <div class="panel-label">With Janus</div>
              <div class="panel-subtitle">Least-privilege policy enforced</div>
            </div>
          </div>
          <span class="status-badge badge-idle" id="badgeProtected">READY</span>
        </div>

        <div class="terminal-wrap" id="termProtected">
          <div class="term-placeholder" id="placeholderProtected">
            Click <strong>Run Attack Demo</strong> to start
          </div>
        </div>

        <div class="workspace-section">
          <div class="workspace-title">📁 Workspace Files</div>
          <div class="file-list" id="filesProtected">
            <div class="file-item normal">
              <span class="file-icon">📄</span>
              <span class="file-name">requirements.txt</span>
              <span class="file-tag tag-existing">existing</span>
            </div>
            <div class="file-item normal">
              <span class="file-icon">📄</span>
              <span class="file-name">requuests_README.md</span>
              <span class="file-tag tag-existing">existing</span>
            </div>
          </div>
        </div>

        <div class="policy-section">
          <button class="policy-toggle" onclick="togglePolicy('policyProtected')">
            ⚙ Policy: <span style="color:var(--green)">ACTIVE</span> — read-only scope (click to expand)
          </button>
          <div class="policy-body" id="policyProtected"></div>
        </div>

        <div class="outcome-banner" id="outcomeProtected"></div>
      </div>

    </div><!-- /.panels -->
  </div><!-- /.panels-wrapper -->

</main>

<script>
  let attackDone = false;
  let protectedDone = false;

  // ── Helpers ────────────────────────────────────────────────────────────

  function ts() {
    const now = new Date();
    return now.toTimeString().slice(0, 8);
  }

  function togglePolicy(id) {
    const el = document.getElementById(id);
    if (el) el.classList.toggle('open');
  }

  function scrollTerm(id) {
    const el = document.getElementById(id);
    if (el) el.scrollTop = el.scrollHeight;
  }

  function setPlaceholderVisible(panelId, visible) {
    const id = panelId === 'attack' ? 'placeholderAttack' : 'placeholderProtected';
    const el = document.getElementById(id);
    if (el) el.style.display = visible ? 'block' : 'none';
  }

  function appendLine(termId, html) {
    const term = document.getElementById(termId);
    if (!term) return;
    const div = document.createElement('div');
    div.className = 'term-line';
    div.innerHTML = `<span class="term-ts">${ts()}</span><span class="term-content">${html}</span>`;
    term.appendChild(div);
    scrollTerm(termId);
  }

  function appendDivider(termId) {
    const term = document.getElementById(termId);
    if (!term) return;
    const hr = document.createElement('hr');
    hr.className = 'term-divider';
    term.appendChild(hr);
  }

  function setBadge(badgeId, state) {
    const el = document.getElementById(badgeId);
    if (!el) return;
    el.className = `status-badge badge-${state}`;
    const labels = {
      idle: 'READY',
      running: '● RUNNING',
      compromised: '🔴 COMPROMISED',
      safe: '🟢 PROTECTED'
    };
    el.textContent = labels[state] || state.toUpperCase();
  }

  function setOutcome(id, isCompromised) {
    const el = document.getElementById(id);
    if (!el) return;
    if (isCompromised) {
      el.className = 'outcome-banner compromised';
      el.innerHTML = '🔴 ATTACK SUCCEEDED — Backdoor deployed, credentials exposed on next deployment';
    } else {
      el.className = 'outcome-banner safe';
      el.innerHTML = '🟢 ATTACK BLOCKED — Janus enforced least-privilege. Workspace integrity maintained.';
    }
  }

  function formatArgs(args) {
    return Object.entries(args)
      .map(([k, v]) => {
        const val = typeof v === 'string' && v.length > 60 ? v.slice(0, 60) + '…' : v;
        return `<span class="keyword-arg">${k}</span>=<span class="keyword-val">${JSON.stringify(val)}</span>`;
      })
      .join(', ');
  }

  // ── Event handlers ─────────────────────────────────────────────────────

  function handleEvent(ev, termId, panelId, filesId, badgeId, outcomeId, isProtected) {
    const type = ev.type;

    if (type === 'scenario_start') {
      setPlaceholderVisible(panelId, false);
      setBadge(badgeId, 'running');
      appendLine(termId, `<span class="term-label">▶ SCENARIO START</span> — ${isProtected ? 'Janus policy active' : 'No policy'}`);
      if (isProtected && ev.policy) {
        const policyEl = document.getElementById('policyProtected');
        if (policyEl) {
          policyEl.textContent = JSON.stringify(ev.policy, null, 2);
        }
      }
    }

    else if (type === 'step_start') {
      appendDivider(termId);
      appendLine(termId, `<span class="term-icon">${ev.icon}</span> <strong>${ev.label}</strong>`);
      appendLine(termId, `<span class="term-thought">💭 ${ev.thought}</span>`);
      if (ev.tool) {
        appendLine(termId, `<span class="term-tool-call">⚡ <span class="keyword-tool">${ev.tool}</span>(${formatArgs(ev.args)})</span>`);
      }
    }

    else if (type === 'tool_success') {
      if (ev.is_malicious) {
        appendLine(termId, `<span class="term-malicious-success">💀 ATTACK SUCCEEDED — ${ev.tool} executed</span>`);
        // Add malicious file to workspace
        if (ev.tool === 'write_file') {
          addFileToWorkspace(filesId, ev.args.file_path || 'unknown', true, false);
        } else if (ev.tool === 'edit_file') {
          updateFileInWorkspace(filesId, ev.args.file_path, 'poisoned');
        }
      } else {
        const preview = typeof ev.result === 'string' ? ev.result.slice(0, 100) + (ev.result.length > 100 ? '…' : '') : '';
        appendLine(termId, `<span class="term-success">✓ ${ev.tool} → ${preview}</span>`);
      }
    }

    else if (type === 'tool_blocked') {
      appendLine(termId, `<span class="term-blocked">🛡️ JANUS BLOCKED — ${ev.tool}<br>&nbsp;&nbsp;&nbsp;Reason: ${ev.reason}</span>`);
      if (ev.tool === 'write_file') {
        addFileToWorkspace(filesId, ev.args.file_path || 'unknown', false, true);
      }
    }

    else if (type === 'tool_error') {
      appendLine(termId, `<span style="color:var(--yellow)">⚠ Error in ${ev.tool}: ${ev.error}</span>`);
    }

    else if (type === 'agent_response') {
      appendDivider(termId);
      appendLine(termId, `<span class="term-label">💬 AGENT RESPONSE TO USER</span>`);
      const lines = ev.response.split('\n');
      lines.forEach(line => {
        appendLine(termId, `<span class="term-response">${escHtml(line)}</span>`);
      });
    }

    else if (type === 'scenario_end') {
      appendDivider(termId);
      const compromised = ev.compromised;
      setBadge(badgeId, compromised ? 'compromised' : 'safe');
      setOutcome(outcomeId, compromised);
      const panel = document.getElementById(panelId);
      if (panel) panel.classList.add(compromised ? 'compromised' : 'safe');

      // Update workspace from final state
      if (ev.workspace_state) {
        refreshWorkspace(filesId, ev.workspace_state, compromised);
      }
    }
  }

  function escHtml(str) {
    return str.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
  }

  function addFileToWorkspace(filesId, path, isMalicious, isBlocked) {
    const list = document.getElementById(filesId);
    if (!list) return;
    const item = document.createElement('div');
    if (isBlocked) {
      item.className = 'file-item blocked-file';
      item.innerHTML = `
        <span class="file-icon">🚫</span>
        <span class="file-name">${escHtml(path)}</span>
        <span class="file-tag tag-blocked">BLOCKED</span>`;
    } else if (isMalicious) {
      item.className = 'file-item malicious';
      item.innerHTML = `
        <span class="file-icon">💀</span>
        <span class="file-name">${escHtml(path)}</span>
        <span class="file-tag tag-malicious">BACKDOOR</span>`;
    }
    list.appendChild(item);
  }

  function updateFileInWorkspace(filesId, path, state) {
    const list = document.getElementById(filesId);
    if (!list) return;
    const items = list.querySelectorAll('.file-item');
    items.forEach(item => {
      if (item.querySelector('.file-name')?.textContent === path) {
        if (state === 'poisoned') {
          item.className = 'file-item malicious';
          const nameEl = item.querySelector('.file-name');
          if (nameEl) nameEl.textContent = path + ' (poisoned)';
          let tag = item.querySelector('.file-tag');
          if (!tag) {
            tag = document.createElement('span');
            item.appendChild(tag);
          }
          tag.className = 'file-tag tag-malicious';
          tag.textContent = 'POISONED';
        }
      }
    });
  }

  function refreshWorkspace(filesId, files, isCompromised) {
    const list = document.getElementById(filesId);
    if (!list) return;
    list.innerHTML = '';
    const maliciousNames = ['auth_helper.py'];
    files.forEach(f => {
      const isMal = isCompromised && maliciousNames.includes(f.path);
      const isPoisoned = isCompromised && f.path === 'requirements.txt';
      const item = document.createElement('div');
      if (isMal) {
        item.className = 'file-item malicious';
        item.innerHTML = `
          <span class="file-icon">💀</span>
          <span class="file-name">${escHtml(f.path)}</span>
          <span class="file-size">${f.size}b</span>
          <span class="file-tag tag-malicious">BACKDOOR</span>`;
      } else if (isPoisoned) {
        item.className = 'file-item malicious';
        item.innerHTML = `
          <span class="file-icon">☠️</span>
          <span class="file-name">${escHtml(f.path)}</span>
          <span class="file-size">${f.size}b</span>
          <span class="file-tag tag-malicious">POISONED</span>`;
      } else {
        item.className = 'file-item normal';
        item.innerHTML = `
          <span class="file-icon">📄</span>
          <span class="file-name">${escHtml(f.path)}</span>
          <span class="file-size">${f.size}b</span>
          <span class="file-tag tag-existing">safe</span>`;
      }
      list.appendChild(item);
    });
  }

  // ── Stream connector ───────────────────────────────────────────────────

  function connectStream(scenario) {
    const isProtected = scenario === 'protected';
    const termId = isProtected ? 'termProtected' : 'termAttack';
    const panelId = isProtected ? 'panelProtected' : 'panelAttack';
    const filesId = isProtected ? 'filesProtected' : 'filesAttack';
    const badgeId = isProtected ? 'badgeProtected' : 'badgeAttack';
    const outcomeId = isProtected ? 'outcomeProtected' : 'outcomeAttack';

    const evtSource = new EventSource(`/stream/${scenario}`);

    const eventTypes = [
      'scenario_start', 'step_start', 'tool_success',
      'tool_blocked', 'tool_error', 'agent_response', 'scenario_end'
    ];

    eventTypes.forEach(type => {
      evtSource.addEventListener(type, (e) => {
        const ev = JSON.parse(e.data);
        ev.type = type;
        handleEvent(ev, termId, panelId, filesId, badgeId, outcomeId, isProtected);
      });
    });

    evtSource.addEventListener('done', () => {
      evtSource.close();
      if (isProtected) {
        protectedDone = true;
      } else {
        attackDone = true;
      }
      checkBothDone();
    });

    evtSource.onerror = () => {
      evtSource.close();
      appendLine(termId, `<span style="color:var(--text-muted)">Stream ended.</span>`);
    };
  }

  function checkBothDone() {
    if (attackDone && protectedDone) {
      document.getElementById('btnRun').disabled = false;
      document.getElementById('btnReset').disabled = false;
    }
  }

  // ── Controls ───────────────────────────────────────────────────────────

  function runDemo() {
    attackDone = false;
    protectedDone = false;

    document.getElementById('btnRun').disabled = true;
    document.getElementById('btnReset').disabled = true;

    // Reset terminals
    ['termAttack', 'termProtected'].forEach(id => {
      const el = document.getElementById(id);
      if (el) el.innerHTML = '';
    });
    ['placeholderAttack', 'placeholderProtected'].forEach(id => {
      const el = document.getElementById(id);
      if (el) { el.style.display = 'block'; el.textContent = 'Connecting…'; }
    });

    // Reset panels
    ['panelAttack', 'panelProtected'].forEach(id => {
      const el = document.getElementById(id);
      if (el) el.className = el.className.replace(/\b(compromised|safe)\b/g, '').trim() + (id === 'panelAttack' ? ' attack-panel' : ' protected-panel');
    });

    // Reset badges
    setBadge('badgeAttack', 'idle');
    setBadge('badgeProtected', 'idle');

    // Reset outcomes
    ['outcomeAttack', 'outcomeProtected'].forEach(id => {
      const el = document.getElementById(id);
      if (el) { el.className = 'outcome-banner'; el.textContent = ''; }
    });

    // Reset workspace displays
    ['filesAttack', 'filesProtected'].forEach(id => {
      const el = document.getElementById(id);
      if (el) el.innerHTML = `
        <div class="file-item normal">
          <span class="file-icon">📄</span>
          <span class="file-name">requirements.txt</span>
          <span class="file-tag tag-existing">existing</span>
        </div>
        <div class="file-item normal">
          <span class="file-icon">📄</span>
          <span class="file-name">requuests_README.md</span>
          <span class="file-tag tag-existing">existing</span>
        </div>`;
    });

    // Start both streams simultaneously
    connectStream('attack');
    connectStream('protected');
  }

  function resetDemo() {
    attackDone = false;
    protectedDone = false;
    location.reload();
  }
</script>
</body>
</html>
