<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>AEP Dashboard</title>
<style>
  * { margin: 0; padding: 0; box-sizing: border-box; }
  body { background: #0d1117; color: #c9d1d9; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, monospace; padding: 24px; }
  h1 { font-size: 20px; color: #58a6ff; margin-bottom: 24px; display: flex; align-items: center; gap: 12px; }
  h1 span.live { color: #8b949e; font-weight: normal; font-size: 14px; }
  .view-toggle { margin-left: auto; display: flex; gap: 0; border: 1px solid #30363d; border-radius: 6px; overflow: hidden; }
  .view-toggle button { background: #161b22; color: #8b949e; border: none; padding: 6px 14px; font-size: 12px; cursor: pointer; font-family: inherit; text-transform: uppercase; letter-spacing: 0.5px; }
  .view-toggle button.active { background: #58a6ff; color: #0d1117; font-weight: bold; }
  .view-toggle button:hover:not(.active) { background: #21262d; color: #c9d1d9; }
  .grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; margin-bottom: 24px; }
  .grid-4 { grid-template-columns: repeat(4, 1fr); }
  .card { background: #161b22; border: 1px solid #30363d; border-radius: 8px; padding: 20px; }
  .card-label { font-size: 12px; color: #8b949e; text-transform: uppercase; letter-spacing: 1px; margin-bottom: 8px; }
  .card-value { font-size: 32px; font-weight: bold; }
  .card-value.small { font-size: 24px; }
  .cost { color: #58a6ff; }
  .calls { color: #c9d1d9; }
  .green { color: #3fb950; }
  .badge { display: inline-block; padding: 4px 16px; border-radius: 4px; font-weight: bold; font-size: 14px; text-transform: uppercase; }
  .badge-sm { padding: 2px 8px; font-size: 10px; border-radius: 3px; }
  .badge-pass { background: #238636; color: #fff; }
  .badge-flag { background: #9e6a03; color: #fff; }
  .badge-block { background: #da3633; color: #fff; }
  .section { background: #161b22; border: 1px solid #30363d; border-radius: 8px; padding: 20px; margin-bottom: 16px; }
  .section h2 { font-size: 14px; color: #8b949e; margin-bottom: 12px; text-transform: uppercase; letter-spacing: 1px; }
  .signal { padding: 8px 12px; border-radius: 4px; margin-bottom: 8px; font-size: 13px; display: flex; gap: 8px; align-items: center; }
  .signal-high { background: rgba(218, 54, 51, 0.15); border-left: 3px solid #da3633; }
  .signal-medium { background: rgba(158, 106, 3, 0.15); border-left: 3px solid #9e6a03; }
  .signal-low { background: rgba(88, 166, 255, 0.15); border-left: 3px solid #58a6ff; }
  .sev { font-weight: bold; font-size: 11px; text-transform: uppercase; min-width: 56px; }
  .sev-high { color: #da3633; }
  .sev-medium { color: #d29922; }
  .sev-low { color: #58a6ff; }
  .score-badge { font-size: 11px; color: #8b949e; font-weight: normal; }
  .span-item { padding: 6px 12px; border-radius: 4px; margin-bottom: 4px; font-size: 13px; background: #0d1117; display: flex; justify-content: space-between; align-items: center; }
  .span-model { color: #58a6ff; }
  .span-dur { color: #8b949e; }
  .span-cost { color: #58a6ff; font-size: 12px; margin-right: 8px; }
  .span-right { display: flex; align-items: center; gap: 8px; }
  .empty { color: #484f58; font-style: italic; font-size: 13px; }
  .pulse { animation: pulse 2s infinite; }
  @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }
  .footer { margin-top: 24px; text-align: center; color: #484f58; font-size: 12px; }
  .bar-container { height: 8px; background: #21262d; border-radius: 4px; overflow: hidden; margin-top: 8px; }
  .bar-fill { height: 100%; border-radius: 4px; transition: width 0.5s; }
  .bar-green { background: #238636; }
  .bar-yellow { background: #9e6a03; }
  .bar-red { background: #da3633; }
  .metric-row { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 4px; }
  .metric-label { color: #8b949e; font-size: 13px; }
  .metric-value { color: #c9d1d9; font-size: 13px; font-weight: bold; }
  .metric-value.good { color: #3fb950; }
  .metric-value.warn { color: #d29922; }
  .metric-value.bad { color: #da3633; }
  .compliance-item { padding: 10px 14px; background: #0d1117; border-radius: 6px; margin-bottom: 8px; }
  .compliance-status { display: flex; align-items: center; gap: 8px; }
  .dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; }
  .dot-green { background: #3fb950; }
  .dot-yellow { background: #d29922; }
  .dot-red { background: #da3633; }
  .hidden { display: none; }
  .filter-bar { display: flex; gap: 6px; margin-bottom: 12px; flex-wrap: wrap; }
  .filter-btn { background: #21262d; color: #8b949e; border: 1px solid #30363d; padding: 4px 10px; border-radius: 4px; font-size: 11px; cursor: pointer; font-family: inherit; text-transform: uppercase; letter-spacing: 0.5px; }
  .filter-btn.active { background: #58a6ff; color: #0d1117; border-color: #58a6ff; font-weight: bold; }
  .filter-btn:hover:not(.active) { background: #30363d; color: #c9d1d9; }
  .call-group { margin-bottom: 12px; }
  .call-group-header { font-size: 11px; color: #58a6ff; padding: 4px 0; cursor: pointer; display: flex; align-items: center; gap: 6px; }
  .call-group-header:hover { color: #79c0ff; }
  .call-group-arrow { font-size: 10px; transition: transform 0.2s; }
  .call-group-arrow.collapsed { transform: rotate(-90deg); }
  .call-group-body { margin-left: 4px; }
  .zero-state { text-align: center; padding: 40px 20px; }
  .zero-state-icon { font-size: 48px; margin-bottom: 12px; }
  .zero-state-text { font-size: 16px; color: #3fb950; font-weight: bold; }
  .zero-state-sub { font-size: 13px; color: #8b949e; margin-top: 4px; }
  .entity-row { padding: 8px 12px; background: #0d1117; border-radius: 4px; margin-bottom: 4px; display: flex; justify-content: space-between; align-items: center; }
  .entity-name { color: #c9d1d9; font-size: 13px; }
  .entity-detail { color: #8b949e; font-size: 12px; }
  .entity-cost { color: #58a6ff; font-size: 13px; font-weight: bold; }
  .session-info { font-size: 12px; color: #484f58; margin-bottom: 16px; }
  .confidence-pct { font-size: 12px; font-weight: bold; margin-left: auto; }
  .confidence-high { color: #da3633; }
  .confidence-med { color: #d29922; }
  .confidence-low { color: #3fb950; }
  .review-btns { display: flex; gap: 4px; margin-left: 8px; }
  .review-btn { padding: 2px 8px; font-size: 10px; border-radius: 3px; border: none; cursor: pointer; font-family: inherit; }
  .review-btn-confirm { background: #da3633; color: #fff; }
  .review-btn-dismiss { background: #238636; color: #fff; }
  .review-btn:hover { opacity: 0.8; }
  .safety-toggle { display: flex; align-items: center; gap: 8px; margin-left: 16px; }
  .toggle-switch { position: relative; width: 40px; height: 20px; background: #da3633; border-radius: 10px; cursor: pointer; transition: background 0.3s; }
  .toggle-switch.on { background: #238636; }
  .toggle-switch::after { content: ''; position: absolute; top: 2px; left: 2px; width: 16px; height: 16px; background: #fff; border-radius: 50%; transition: transform 0.3s; }
  .toggle-switch.on::after { transform: translateX(20px); }
  .toggle-label { font-size: 11px; color: #8b949e; text-transform: uppercase; letter-spacing: 0.5px; }
  .policy-controls { display: flex; flex-wrap: wrap; gap: 8px; }
  .detector-toggle { display: flex; align-items: center; gap: 6px; background: #0d1117; padding: 6px 12px; border-radius: 6px; cursor: pointer; user-select: none; border: 1px solid #30363d; transition: border-color 0.2s; }
  .detector-toggle:hover { border-color: #58a6ff; }
  .detector-toggle.disabled { opacity: 0.4; }
  .detector-toggle input[type="checkbox"] { accent-color: #58a6ff; cursor: pointer; width: 14px; height: 14px; }
  .detector-toggle label { font-size: 12px; color: #c9d1d9; cursor: pointer; text-transform: uppercase; letter-spacing: 0.5px; }
  .detector-toggle.disabled label { text-decoration: line-through; }
  .category-divider { width: 100%; font-size: 10px; color: #484f58; text-transform: uppercase; letter-spacing: 1px; margin: 4px 0; }
  .setup-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.85); display: flex; align-items: center; justify-content: center; z-index: 1000; }
  .setup-card { background: #161b22; border: 1px solid #30363d; border-radius: 12px; padding: 32px; max-width: 560px; width: 90%; }
  .setup-card h2 { color: #58a6ff; font-size: 18px; margin-bottom: 16px; }
  .setup-card p { color: #8b949e; font-size: 14px; margin-bottom: 16px; }
  .setup-options { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 20px; }
  .setup-option { background: #0d1117; border: 2px solid #30363d; border-radius: 8px; padding: 16px; cursor: pointer; text-align: center; transition: border-color 0.2s; }
  .setup-option:hover { border-color: #58a6ff; }
  .setup-option.selected { border-color: #58a6ff; background: #0d1117; }
  .setup-option h3 { color: #c9d1d9; font-size: 14px; margin-bottom: 4px; }
  .setup-option p { color: #8b949e; font-size: 12px; margin: 0; }
  .setup-input { width: 100%; background: #0d1117; border: 1px solid #30363d; color: #c9d1d9; padding: 8px 12px; border-radius: 6px; font-family: inherit; font-size: 13px; margin-bottom: 8px; }
  .setup-input:focus { border-color: #58a6ff; outline: none; }
  .setup-btn { background: #238636; color: #fff; border: none; padding: 8px 20px; border-radius: 6px; font-size: 13px; cursor: pointer; font-family: inherit; }
  .setup-btn:hover { background: #2ea043; }
  .setup-btn:disabled { opacity: 0.5; cursor: not-allowed; }
  .setup-code { background: #0d1117; border: 1px solid #30363d; border-radius: 6px; padding: 10px 14px; font-family: 'SF Mono', Consolas, monospace; font-size: 12px; color: #58a6ff; position: relative; margin-bottom: 8px; word-break: break-all; }
  .setup-copy { position: absolute; top: 6px; right: 6px; background: #30363d; border: none; color: #8b949e; padding: 2px 8px; border-radius: 4px; font-size: 11px; cursor: pointer; }
  .setup-copy:hover { background: #484f58; color: #c9d1d9; }
  .setup-step { display: none; }
  .setup-step.active { display: block; }
  .setup-detected { color: #3fb950; font-size: 12px; margin-bottom: 8px; }
</style>
</head>
<body>
<div class="setup-overlay" id="setup-overlay" style="display:none;">
  <div class="setup-card">
    <div class="setup-step active" id="setup-step-1">
      <h2>Welcome to SafeClaw</h2>
      <p>Configure your LLM provider to get started.</p>
      <div id="setup-detected"></div>
      <div class="setup-options">
        <div class="setup-option" onclick="selectSetupOption('byok', event)">
          <h3>I have API keys</h3>
          <p>OpenAI, Anthropic, or local</p>
        </div>
        <div class="setup-option" onclick="selectSetupOption('aceteam', event)">
          <h3>Use AceTeam</h3>
          <p>$5 free credit, no key needed</p>
        </div>
      </div>
      <div id="setup-byok" style="display:none;">
        <input class="setup-input" id="setup-api-key" type="password" placeholder="sk-... or paste your API key">
        <button class="setup-btn" onclick="saveApiKey()">Save &amp; Continue</button>
      </div>
      <div id="setup-aceteam" style="display:none;">
        <p style="color:#d29922;font-size:13px;">Coming soon. For now, paste your own API key above.</p>
      </div>
    </div>
    <div class="setup-step" id="setup-step-2">
      <h2>Point your agent here</h2>
      <p>Add this to your terminal:</p>
      <div class="setup-code" id="setup-export-cmd">
        <button class="setup-copy" onclick="copySetupText('setup-export-cmd')">Copy</button>
        export OPENAI_BASE_URL=http://localhost:8899/v1
      </div>
      <p style="margin-top:16px;">Using Claude Code? Add to your config:</p>
      <div class="setup-code" id="setup-mcp-config">
        <button class="setup-copy" onclick="copySetupText('setup-mcp-config')">Copy</button>
        {"mcpServers":{"aceteam":{"type":"streamable-http","url":"http://localhost:8899/mcp/"}}}
      </div>
      <button class="setup-btn" onclick="closeSetup()" style="margin-top:16px;">Got it, start using SafeClaw</button>
    </div>
  </div>
</div>
<h1>
  AEP Dashboard <span class="live pulse">&#x25cf; live</span>
  <div class="safety-toggle">
    <span class="toggle-label" id="safety-label">Safety ON</span>
    <div class="toggle-switch on" id="safety-toggle" onclick="toggleSafety()"></div>
  </div>
  <div class="view-toggle">
    <button id="btn-dev" class="active" onclick="setView('dev')">Developer</button>
    <button id="btn-ciso" onclick="setView('ciso')">Executive</button>
  </div>
</h1>

<!-- ===== DEVELOPER VIEW ===== -->
<div id="view-dev">
  <div class="grid grid-4">
    <div class="card">
      <div class="card-label">Total Cost</div>
      <div class="card-value cost" id="cost">$0.000000</div>
    </div>
    <div class="card">
      <div class="card-label">Calls</div>
      <div class="card-value calls" id="calls">0</div>
    </div>
    <div class="card">
      <div class="card-label">Safety Status</div>
      <div class="card-value" id="status"></div>
    </div>
    <div class="card" id="attestation-card" style="display:none;">
      <div class="card-label">Merkle Chain</div>
      <div class="card-value small green" id="chain-height">0</div>
      <div style="font-size:11px;color:#8b949e;margin-top:4px;" id="chain-signer"></div>
    </div>
  </div>

  <div class="section" id="budget-section" style="display:none;">
    <h2>Budget</h2>
    <div style="display:flex;gap:16px;align-items:center;">
      <div style="flex:1;">
        <div class="bar-container" style="height:12px;margin:0;">
          <div id="budget-bar" style="height:100%;background:#58a6ff;border-radius:4px;transition:width 0.3s;"></div>
        </div>
      </div>
      <div id="budget-text" style="font-size:14px;color:#8b949e;white-space:nowrap;"></div>
    </div>
  </div>

  <div class="section" id="policy-section">
    <h2>Policy Controls</h2>
    <div class="policy-controls" id="policy-controls">
      <span class="empty">Loading policy...</span>
    </div>
  </div>

  <div class="section">
    <h2>Safety Signals</h2>
    <div class="filter-bar" id="signal-filters"></div>
    <div id="signals"></div>
  </div>

  <div class="section" id="governance-section" style="display:none;">
    <h2>Governance Context</h2>
    <div id="governance"></div>
  </div>

  <div class="section" id="attestation-section" style="display:none;">
    <h2>Attestation (Signed Verdicts)</h2>
    <div id="attestation-details"></div>
  </div>

  <div class="section">
    <h2>Call Timeline</h2>
    <div id="spans"></div>
  </div>
</div>

<!-- ===== EXECUTIVE / CISO VIEW ===== -->
<div id="view-ciso" class="hidden">
  <div id="ciso-session-info" class="session-info"></div>
  <div class="grid grid-4">
    <div class="card">
      <div class="card-label">Policy Compliance</div>
      <div class="card-value green" id="ciso-compliance-pct">100%</div>
      <div class="bar-container"><div class="bar-fill bar-green" id="ciso-compliance-bar" style="width:100%"></div></div>
    </div>
    <div class="card">
      <div class="card-label">Threats Blocked</div>
      <div class="card-value" id="ciso-blocked" style="color:#da3633;">0</div>
    </div>
    <div class="card">
      <div class="card-label">Total Agent Spend</div>
      <div class="card-value cost" id="ciso-cost">$0.00</div>
    </div>
    <div class="card">
      <div class="card-label">Total Calls</div>
      <div class="card-value calls" id="ciso-calls">0</div>
    </div>
  </div>

  <div class="section" id="ciso-savings-section" style="display:none;">
    <h2>Cost Savings</h2>
    <div class="metric-row"><span class="metric-label">Calls blocked before reaching LLM</span><span class="metric-value good" id="ciso-savings-blocked">0</span></div>
    <div class="metric-row"><span class="metric-label">Estimated cost saved</span><span class="metric-value good" id="ciso-savings-amount">$0.00</span></div>
    <div class="metric-row"><span class="metric-label">Average cost per call</span><span class="metric-value" id="ciso-savings-avg">$0.00</span></div>
  </div>

  <div class="section">
    <h2>Compliance Status</h2>
    <div id="ciso-compliance-body"></div>
  </div>

  <div class="section">
    <h2>Compliance Frameworks</h2>
    <div id="ciso-frameworks">
      <div class="compliance-item">
        <div class="compliance-status"><span class="dot dot-green"></span> <span>HIPAA &mdash; Protected Health Information</span></div>
        <div class="metric-row"><span class="metric-label">PHI detection enabled, auto-block on exfiltration</span><span class="metric-value good" id="fw-hipaa">ENFORCED</span></div>
      </div>
      <div class="compliance-item">
        <div class="compliance-status"><span class="dot dot-green"></span> <span>GDPR &mdash; Data Protection</span></div>
        <div class="metric-row"><span class="metric-label">PII detection, consent tracking via X-AEP-Consent</span><span class="metric-value good" id="fw-gdpr">ENFORCED</span></div>
      </div>
      <div class="compliance-item">
        <div class="compliance-status"><span class="dot dot-green"></span> <span>SOC 2 Type II &mdash; Security & Availability</span></div>
        <div class="metric-row"><span class="metric-label">Full audit trail, signed safety verdicts</span><span class="metric-value good" id="fw-soc2">ENFORCED</span></div>
      </div>
      <div class="compliance-item">
        <div class="compliance-status"><span class="dot dot-yellow"></span> <span>EU AI Act &mdash; High-Risk AI Systems</span></div>
        <div class="metric-row"><span class="metric-label">Risk classification, human oversight logging</span><span class="metric-value warn" id="fw-euai">PARTIAL</span></div>
      </div>
    </div>
  </div>

  <div class="section">
    <h2>Safety Breakdown</h2>
    <div id="ciso-breakdown">
      <div class="metric-row"><span class="metric-label">Calls passed</span><span class="metric-value good" id="ciso-pass">0</span></div>
      <div class="metric-row"><span class="metric-label">Calls flagged</span><span class="metric-value warn" id="ciso-flag">0</span></div>
      <div class="metric-row"><span class="metric-label">Calls blocked</span><span class="metric-value bad" id="ciso-block">0</span></div>
      <div style="margin-top:12px;">
        <div class="bar-container" style="height:12px;">
          <div id="ciso-bar-pass" class="bar-fill bar-green" style="width:100%;display:inline-block;float:left;"></div>
          <div id="ciso-bar-flag" class="bar-fill bar-yellow" style="width:0%;display:inline-block;float:left;"></div>
          <div id="ciso-bar-block" class="bar-fill bar-red" style="width:0%;display:inline-block;float:left;"></div>
        </div>
      </div>
    </div>
  </div>

  <div class="section" id="ciso-entity-section" style="display:none;">
    <h2>Cost by Entity</h2>
    <div id="ciso-entity-breakdown"></div>
  </div>

  <div class="section" id="ciso-governance-section" style="display:none;">
    <h2>Data Governance</h2>
    <div id="ciso-governance"></div>
  </div>

  <div class="section">
    <h2>Attestation</h2>
    <div id="ciso-attestation">
      <div class="compliance-item">
        <div class="compliance-status"><span class="dot dot-green"></span> <span>Safety verdicts recorded for all calls</span></div>
        <div class="metric-row"><span class="metric-label">Every PASS/FLAG/BLOCK decision is logged with timestamp and detector ID</span><span class="metric-value good">ACTIVE</span></div>
      </div>
      <div class="compliance-item">
        <div class="compliance-status"><span class="dot dot-yellow"></span> <span>Cryptographic signing (Ed25519)</span></div>
        <div class="metric-row"><span class="metric-label">Signed verdict chain for tamper-proof audit trail</span><span class="metric-value warn">ROADMAP</span></div>
      </div>
      <div class="compliance-item">
        <div class="compliance-status"><span class="dot dot-yellow"></span> <span>Third-party attestation</span></div>
        <div class="metric-row"><span class="metric-label">Independent verification of safety enforcement claims</span><span class="metric-value warn">ROADMAP</span></div>
      </div>
    </div>
  </div>
</div>

<div class="footer">AEP Trust Engine &mdash; aceteam.ai</div>

<script>
var currentView = 'dev';
var activeFilter = 'all';
var lastSignals = [];
var collapsedGroups = {};
var safetyEnabled = true;

// Setup wizard — show on first visit
function checkFirstRun() {
  if (localStorage.getItem('safeclaw-setup-done')) return;
  fetch('api/state').then(function(r) { return r.json(); }).then(function(state) {
    if (state.calls > 0) {
      localStorage.setItem('safeclaw-setup-done', '1');
      return;
    }
    document.getElementById('setup-overlay').style.display = 'flex';
  }).catch(function() {});
}

function selectSetupOption(option, evt) {
  var options = document.querySelectorAll('.setup-option');
  for (var i = 0; i < options.length; i++) { options[i].classList.remove('selected'); }
  if (evt && evt.currentTarget) { evt.currentTarget.classList.add('selected'); }
  document.getElementById('setup-byok').style.display = option === 'byok' ? 'block' : 'none';
  document.getElementById('setup-aceteam').style.display = option === 'aceteam' ? 'block' : 'none';
}

function saveApiKey() {
  var key = document.getElementById('setup-api-key').value.trim();
  if (!key) return;
  localStorage.setItem('safeclaw-api-key-hint', key.substring(0, 8) + '...');
  document.getElementById('setup-step-1').classList.remove('active');
  document.getElementById('setup-step-2').classList.add('active');
}

function copySetupText(elementId) {
  var el = document.getElementById(elementId);
  var copyBtn = el.querySelector('.setup-copy');
  var fullText = el.textContent || '';
  var btnText = copyBtn ? (copyBtn.textContent || '') : '';
  var text = fullText.replace(btnText, '').trim();
  navigator.clipboard.writeText(text);
}

function closeSetup() {
  localStorage.setItem('safeclaw-setup-done', '1');
  document.getElementById('setup-overlay').style.display = 'none';
}

// Run on page load
checkFirstRun();

function toggleSafety() {
  safetyEnabled = !safetyEnabled;
  fetch('api/safety', {
    method: 'POST',
    headers: {'Content-Type': 'application/json'},
    body: JSON.stringify({enabled: safetyEnabled})
  });
  var toggle = document.getElementById('safety-toggle');
  var label = document.getElementById('safety-label');
  toggle.className = 'toggle-switch' + (safetyEnabled ? ' on' : '');
  label.textContent = safetyEnabled ? 'Safety ON' : 'Safety OFF';
}

var currentPolicy = null;

function fetchPolicy() {
  fetch('api/safety')
    .then(function(r) { return r.json(); })
    .then(function(data) {
      safetyEnabled = data.safety_enabled;
      var toggle = document.getElementById('safety-toggle');
      var label = document.getElementById('safety-label');
      toggle.className = 'toggle-switch' + (safetyEnabled ? ' on' : '');
      label.textContent = safetyEnabled ? 'Safety ON' : 'Safety OFF';
      currentPolicy = data.policy;
      renderPolicyControls(data.policy);
    })
    .catch(function() {});
}

function renderPolicyControls(policy) {
  var container = document.getElementById('policy-controls');
  if (!container || !policy) return;
  container.textContent = '';

  var detectors = policy.detectors || {};
  var detectorNames = Object.keys(detectors);

  var displayNames = {
    agent_threat: 'Agent Threat',
    pii: 'PII Detection',
    cost_anomaly: 'Cost Anomaly',
    content_safety: 'Content Safety',
    trust_engine: 'Trust Engine',
    ferpa: 'FERPA'
  };

  var domainCategories = ['finance', 'iot', 'software', 'web', 'program'];
  var categoryNames = {
    finance: 'Finance', iot: 'IoT', software: 'Software', web: 'Web', program: 'Program'
  };

  if (detectorNames.length === 0) {
    var empty = document.createElement('span');
    empty.className = 'empty';
    empty.textContent = 'No detectors configured';
    container.appendChild(empty);
    return;
  }

  detectorNames.forEach(function(name) {
    var det = detectors[name];
    var isEnabled = det.enabled !== false;

    var wrap = document.createElement('div');
    wrap.className = 'detector-toggle' + (isEnabled ? '' : ' disabled');

    var cb = document.createElement('input');
    cb.type = 'checkbox';
    cb.checked = isEnabled;
    cb.id = 'det-' + name;
    cb.onchange = function() { toggleDetector(name, cb.checked); };

    var lbl = document.createElement('label');
    lbl.htmlFor = 'det-' + name;
    lbl.textContent = displayNames[name] || name;

    wrap.appendChild(cb);
    wrap.appendChild(lbl);
    container.appendChild(wrap);
  });

  var teConfig = detectors.trust_engine;
  if (teConfig && teConfig.enabled !== false) {
    var divider = document.createElement('div');
    divider.className = 'category-divider';
    divider.textContent = 'Trust Engine Categories';
    container.appendChild(divider);

    var activeDims = (teConfig.extra && teConfig.extra.dimensions) || domainCategories;

    domainCategories.forEach(function(cat) {
      var isActive = activeDims.indexOf(cat) >= 0;

      var wrap = document.createElement('div');
      wrap.className = 'detector-toggle' + (isActive ? '' : ' disabled');

      var cb = document.createElement('input');
      cb.type = 'checkbox';
      cb.checked = isActive;
      cb.id = 'cat-' + cat;
      cb.onchange = function() { toggleCategory(cat, cb.checked); };

      var lbl = document.createElement('label');
      lbl.htmlFor = 'cat-' + cat;
      lbl.textContent = categoryNames[cat] || cat;

      wrap.appendChild(cb);
      wrap.appendChild(lbl);
      container.appendChild(wrap);
    });
  }
}

function toggleDetector(name, enabled) {
  if (!currentPolicy) return;
  var detectors = JSON.parse(JSON.stringify(currentPolicy.detectors || {}));
  if (!detectors[name]) detectors[name] = {};
  detectors[name].enabled = enabled;

  var newPolicy = {
    default_action: currentPolicy.default_action,
    block_on: currentPolicy.block_on,
    flag_on: currentPolicy.flag_on,
    detectors: detectors
  };

  fetch('api/safety', {
    method: 'POST',
    headers: {'Content-Type': 'application/json'},
    body: JSON.stringify({policy: newPolicy})
  }).then(function(r) { return r.json(); })
    .then(function(data) {
      currentPolicy = data.policy;
      renderPolicyControls(data.policy);
    })
    .catch(function() { fetchPolicy(); });
}

function toggleCategory(cat, enabled) {
  if (!currentPolicy) return;
  var detectors = JSON.parse(JSON.stringify(currentPolicy.detectors || {}));
  var te = detectors.trust_engine || {};
  var extra = te.extra || {};
  var dims = extra.dimensions || ['finance', 'iot', 'software', 'web', 'program'];

  if (enabled && dims.indexOf(cat) < 0) {
    dims.push(cat);
  } else if (!enabled) {
    dims = dims.filter(function(d) { return d !== cat; });
  }

  extra.dimensions = dims;
  te.extra = extra;
  detectors.trust_engine = te;

  var newPolicy = {
    default_action: currentPolicy.default_action,
    block_on: currentPolicy.block_on,
    flag_on: currentPolicy.flag_on,
    detectors: detectors
  };

  fetch('api/safety', {
    method: 'POST',
    headers: {'Content-Type': 'application/json'},
    body: JSON.stringify({policy: newPolicy})
  }).then(function(r) { return r.json(); })
    .then(function(data) {
      currentPolicy = data.policy;
      renderPolicyControls(data.policy);
    })
    .catch(function() { fetchPolicy(); });
}

function submitVerdict(signalType, score, verdict) {
  fetch('api/feedback', {
    method: 'POST',
    headers: {'Content-Type': 'application/json'},
    body: JSON.stringify({signal_type: signalType, score: score, verdict: verdict})
  });
}

function setView(view) {
  currentView = view;
  document.getElementById('view-dev').className = view === 'dev' ? '' : 'hidden';
  document.getElementById('view-ciso').className = view === 'ciso' ? '' : 'hidden';
  document.getElementById('btn-dev').className = view === 'dev' ? 'active' : '';
  document.getElementById('btn-ciso').className = view === 'ciso' ? 'active' : '';
}

function esc(s) {
  var d = document.createElement('div');
  d.textContent = s;
  return d.textContent;
}

function setBadge(el, action) {
  el.textContent = '';
  var span = document.createElement('span');
  var validActions = ['pass', 'flag', 'block'];
  var cls = validActions.indexOf(action) >= 0 ? action : 'pass';
  span.className = 'badge badge-' + cls;
  span.textContent = action.toUpperCase();
  el.appendChild(span);
}

function makeSmallBadge(action) {
  var span = document.createElement('span');
  var validActions = ['pass', 'flag', 'block'];
  var cls = validActions.indexOf(action) >= 0 ? action : 'pass';
  span.className = 'badge badge-sm badge-' + cls;
  span.textContent = action.toUpperCase();
  return span;
}

// Determine per-call enforcement from signals
function getCallAction(callId, signals) {
  var callSignals = signals.filter(function(s) { return s.call_id === callId; });
  if (callSignals.length === 0) return 'pass';
  var hasHigh = callSignals.some(function(s) { return s.severity === 'high'; });
  if (hasHigh) return 'block';
  var hasMedium = callSignals.some(function(s) { return s.severity === 'medium'; });
  if (hasMedium) return 'flag';
  return 'pass';
}

function renderFilterBar(container, signals) {
  container.textContent = '';
  var types = {};
  signals.forEach(function(s) { types[s.type] = (types[s.type] || 0) + 1; });
  var typeKeys = Object.keys(types);
  if (typeKeys.length <= 1) return; // No filter needed for 0 or 1 type

  var allBtn = document.createElement('button');
  allBtn.className = 'filter-btn' + (activeFilter === 'all' ? ' active' : '');
  allBtn.textContent = 'ALL (' + signals.length + ')';
  allBtn.onclick = function() { activeFilter = 'all'; renderSignals(document.getElementById('signals'), lastSignals); renderFilterBar(container, lastSignals); };
  container.appendChild(allBtn);

  typeKeys.forEach(function(t) {
    var btn = document.createElement('button');
    btn.className = 'filter-btn' + (activeFilter === t ? ' active' : '');
    btn.textContent = t.toUpperCase() + ' (' + types[t] + ')';
    btn.onclick = function() { activeFilter = t; renderSignals(document.getElementById('signals'), lastSignals); renderFilterBar(container, lastSignals); };
    container.appendChild(btn);
  });
}

function renderSignals(container, signals) {
  container.textContent = '';
  // Apply filter
  var filtered = activeFilter === 'all' ? signals : signals.filter(function(s) { return s.type === activeFilter; });
  if (filtered.length === 0) {
    var empty = document.createElement('div');
    empty.className = 'empty';
    empty.textContent = signals.length === 0 ? 'No signals detected' : 'No ' + activeFilter + ' signals';
    container.appendChild(empty);
    return;
  }

  // Group by call_id
  var groups = {};
  var groupOrder = [];
  filtered.forEach(function(s) {
    var key = s.call_id || 'unknown';
    if (!groups[key]) { groups[key] = []; groupOrder.push(key); }
    groups[key].push(s);
  });

  // If only one group, render flat
  if (groupOrder.length === 1) {
    renderSignalList(container, filtered);
    return;
  }

  groupOrder.forEach(function(callId) {
    var group = groups[callId];
    var div = document.createElement('div');
    div.className = 'call-group';

    var isCollapsed = collapsedGroups[callId] === true;
    var header = document.createElement('div');
    header.className = 'call-group-header';
    var arrow = document.createElement('span');
    arrow.className = 'call-group-arrow' + (isCollapsed ? ' collapsed' : '');
    arrow.textContent = '\u25BC';
    header.appendChild(arrow);
    var label = document.createElement('span');
    label.textContent = 'Call ' + callId + ': ' + group.length + ' signal' + (group.length > 1 ? 's' : '');
    header.appendChild(label);
    header.onclick = function() {
      collapsedGroups[callId] = !collapsedGroups[callId];
      renderSignals(container, signals);
    };
    div.appendChild(header);

    if (!isCollapsed) {
      var body = document.createElement('div');
      body.className = 'call-group-body';
      renderSignalList(body, group);
      div.appendChild(body);
    }

    container.appendChild(div);
  });
}

function renderSignalList(container, signals) {
  signals.forEach(function(s) {
    var row = document.createElement('div');
    var validSev = ['high', 'medium', 'low'];
    var sevCls = validSev.indexOf(s.severity) >= 0 ? s.severity : 'low';
    row.className = 'signal signal-' + sevCls;

    var sev = document.createElement('span');
    sev.className = 'sev sev-' + sevCls;
    sev.textContent = s.severity.toUpperCase();
    row.appendChild(sev);

    var detail = document.createElement('span');
    detail.textContent = esc(s.type) + ': ' + esc(s.detail);
    row.appendChild(detail);

    // Confidence percentage for trust_engine signals, score badge for others
    if (s.score !== null && s.score !== undefined) {
      if (s.type === 'trust_engine' && s.score <= 1) {
        var confPct = document.createElement('span');
        var pct = Math.round(s.score * 100);
        confPct.className = 'confidence-pct' + (pct >= 70 ? ' confidence-high' : pct >= 40 ? ' confidence-med' : ' confidence-low');
        confPct.textContent = pct + '% unsafe';
        row.appendChild(confPct);
      } else {
        var scoreBadge = document.createElement('span');
        scoreBadge.className = 'score-badge';
        scoreBadge.textContent = '(' + (s.score <= 1 ? s.score.toFixed(2) : s.score.toFixed(1) + 'x') + ')';
        row.appendChild(scoreBadge);
      }
    }

    // Review buttons for flagged signals (medium severity)
    if (s.severity === 'medium') {
      var btns = document.createElement('span');
      btns.className = 'review-btns';
      var confirmBtn = document.createElement('button');
      confirmBtn.className = 'review-btn review-btn-confirm';
      confirmBtn.textContent = 'Confirm';
      confirmBtn.onclick = function() { submitVerdict(s.type, s.score, 'confirmed'); btns.textContent = 'Confirmed'; };
      btns.appendChild(confirmBtn);
      var dismissBtn = document.createElement('button');
      dismissBtn.className = 'review-btn review-btn-dismiss';
      dismissBtn.textContent = 'Dismiss';
      dismissBtn.onclick = function() { submitVerdict(s.type, s.score, 'dismissed'); btns.textContent = 'Dismissed'; };
      btns.appendChild(dismissBtn);
      row.appendChild(btns);
    }

    container.appendChild(row);
  });
}

function renderGovernance(container, govList) {
  var section = document.getElementById('governance-section');
  if (!govList || govList.length === 0) { section.style.display = 'none'; return; }
  section.style.display = '';
  container.textContent = '';
  govList.forEach(function(gov) {
    var callLabel = document.createElement('div');
    callLabel.style.cssText = 'font-size:11px;color:#58a6ff;margin-top:8px;margin-bottom:4px;';
    callLabel.textContent = 'Call ' + (gov.call_id || '?');
    container.appendChild(callLabel);
    var fields = [
      ['Entity', gov.entity],
      ['Classification', gov.classification],
      ['Consent', gov.consent ? Object.keys(gov.consent).map(function(k){ return k + '=' + gov.consent[k]; }).join(', ') : null],
      ['Budget', gov.budget],
      ['Sources', gov.sources && gov.sources.length ? gov.sources.join(', ') : null],
      ['Trace ID', gov.trace_id]
    ];
    fields.forEach(function(f) {
      if (!f[1]) return;
      var row = document.createElement('div');
      row.className = 'span-item';
      var label = document.createElement('span');
      label.className = 'span-model';
      label.textContent = f[0];
      row.appendChild(label);
      var val = document.createElement('span');
      val.className = 'span-dur';
      val.textContent = String(f[1]);
      row.appendChild(val);
      container.appendChild(row);
    });
  });
}

function renderSpans(container, spans, signals) {
  container.textContent = '';
  if (spans.length === 0) {
    var empty = document.createElement('div');
    empty.className = 'empty';
    empty.textContent = 'No calls recorded';
    container.appendChild(empty);
    return;
  }
  spans.forEach(function(s) {
    var row = document.createElement('div');
    row.className = 'span-item';

    var model = document.createElement('span');
    model.className = 'span-model';
    model.textContent = esc(s.executor);
    row.appendChild(model);

    var right = document.createElement('span');
    right.className = 'span-right';

    // Per-call enforcement badge
    var callAction = getCallAction(s.call_id, signals);
    right.appendChild(makeSmallBadge(callAction));

    // Cost per call
    if (s.cost > 0) {
      var costEl = document.createElement('span');
      costEl.className = 'span-cost';
      costEl.textContent = '$' + s.cost.toFixed(6);
      right.appendChild(costEl);
    }

    var dur = document.createElement('span');
    dur.className = 'span-dur';
    dur.textContent = s.duration_ms ? Math.round(s.duration_ms) + 'ms' : '...';
    right.appendChild(dur);

    row.appendChild(right);
    container.appendChild(row);
  });
}

function formatDuration(ms) {
  if (ms < 60000) return Math.round(ms / 1000) + ' sec';
  if (ms < 3600000) return Math.round(ms / 60000) + ' min';
  var h = Math.floor(ms / 3600000);
  var m = Math.round((ms % 3600000) / 60000);
  return h + 'h ' + m + 'm';
}

function updateCisoView(data) {
  // Session info
  var sessionEl = document.getElementById('ciso-session-info');
  if (data.session_started) {
    var started = new Date(data.session_started);
    var elapsed = Date.now() - started.getTime();
    var startTime = started.toLocaleTimeString([], {hour: '2-digit', minute: '2-digit'});
    sessionEl.textContent = 'Session started: ' + startTime + ' (' + formatDuration(elapsed) + ')';
  }

  // Aggregate signal counts by type
  var piiCount = 0, threatCount = 0, toxicCount = 0, anomalyCount = 0;
  (data.signals || []).forEach(function(s) {
    if (s.type === 'pii') piiCount++;
    else if (s.type === 'agent_threat') threatCount++;
    else if (s.type === 'content_safety') toxicCount++;
    else if (s.type === 'cost_anomaly') anomalyCount++;
  });

  // Count unique CALLS (not signals) that were blocked/flagged
  var blockedCalls = {};
  var flaggedCalls = {};
  (data.signals || []).forEach(function(s) {
    var cid = s.call_id || 'unknown';
    if (s.severity === 'high') blockedCalls[cid] = true;
    else if (s.severity === 'medium') flaggedCalls[cid] = true;
  });
  // A call with both high+medium counts as blocked, not both
  Object.keys(blockedCalls).forEach(function(k) { delete flaggedCalls[k]; });
  var blockCount = Object.keys(blockedCalls).length;
  var flagCount = Object.keys(flaggedCalls).length;

  var totalCalls = data.calls || 0;
  var passCount = Math.max(0, totalCalls - blockCount - flagCount);

  // Policy compliance percentage
  var compliancePct = totalCalls > 0 ? ((totalCalls - blockCount) / totalCalls * 100) : 100;
  var complianceEl = document.getElementById('ciso-compliance-pct');
  complianceEl.textContent = compliancePct.toFixed(1) + '%';
  var complianceBar = document.getElementById('ciso-compliance-bar');
  complianceBar.style.width = compliancePct + '%';
  if (compliancePct > 99) {
    complianceEl.className = 'card-value green';
    complianceBar.className = 'bar-fill bar-green';
  } else if (compliancePct > 95) {
    complianceEl.className = 'card-value';
    complianceEl.style.color = '#d29922';
    complianceBar.className = 'bar-fill bar-yellow';
  } else {
    complianceEl.className = 'card-value';
    complianceEl.style.color = '#da3633';
    complianceBar.className = 'bar-fill bar-red';
  }

  document.getElementById('ciso-blocked').textContent = String(blockCount);
  document.getElementById('ciso-cost').textContent = '$' + data.cost.toFixed(2);
  document.getElementById('ciso-calls').textContent = String(totalCalls);

  // Cost savings section
  var savings = data.savings;
  var savingsSection = document.getElementById('ciso-savings-section');
  if (savings && savings.blocked_calls > 0) {
    savingsSection.style.display = '';
    document.getElementById('ciso-savings-blocked').textContent = String(savings.blocked_calls);
    document.getElementById('ciso-savings-amount').textContent = '$' + savings.estimated_savings_usd.toFixed(4);
    document.getElementById('ciso-savings-avg').textContent = '$' + savings.avg_call_cost_usd.toFixed(4);
  } else {
    savingsSection.style.display = 'none';
  }

  // Compliance status - zero-state vs incidents
  var complianceBody = document.getElementById('ciso-compliance-body');
  complianceBody.textContent = '';
  var totalIncidents = piiCount + threatCount + toxicCount + anomalyCount;
  if (totalIncidents === 0 && totalCalls > 0) {
    // Zero-state: all clean
    var zs = document.createElement('div');
    zs.className = 'zero-state';
    zs.innerHTML = '<div class="zero-state-icon" style="color:#3fb950;">&#x2714;</div>'
      + '<div class="zero-state-text">All agents compliant</div>'
      + '<div class="zero-state-sub">No safety incidents this session. ' + totalCalls + ' call' + (totalCalls > 1 ? 's' : '') + ' passed.</div>';
    complianceBody.appendChild(zs);
  } else if (totalCalls === 0) {
    var zs2 = document.createElement('div');
    zs2.className = 'zero-state';
    zs2.innerHTML = '<div class="zero-state-icon" style="color:#484f58;">&#x2022;</div>'
      + '<div class="zero-state-text" style="color:#8b949e;">Waiting for agent activity</div>'
      + '<div class="zero-state-sub">No calls recorded yet.</div>';
    complianceBody.appendChild(zs2);
  } else {
    // Show incident breakdown
    var items = [
      { dotId: 'pii', label: 'PII Exfiltration', sublabel: 'Incidents this session', count: piiCount, verb: 'blocked' },
      { dotId: 'threat', label: 'Agent Threat Attempts', sublabel: 'Port scans, shell access, credential theft', count: threatCount, verb: 'blocked' },
      { dotId: 'toxic', label: 'Unsafe Content', sublabel: 'Toxic or harmful output', count: toxicCount, verb: 'detected' },
      { dotId: 'cost', label: 'Cost Anomalies', sublabel: 'Spend spikes above baseline', count: anomalyCount, verb: 'flagged' }
    ];
    items.forEach(function(item) {
      var ci = document.createElement('div');
      ci.className = 'compliance-item';
      var dotCls = item.count > 0 ? 'dot dot-red' : 'dot dot-green';
      var valCls = item.count > 0 ? 'metric-value bad' : 'metric-value good';
      ci.innerHTML = '<div class="compliance-status"><span class="' + dotCls + '"></span> <span>' + esc(item.label) + '</span></div>'
        + '<div class="metric-row"><span class="metric-label">' + esc(item.sublabel) + '</span><span class="' + valCls + '">' + item.count + ' ' + item.verb + '</span></div>';
      complianceBody.appendChild(ci);
    });
  }

  // Safety breakdown
  document.getElementById('ciso-pass').textContent = String(passCount);
  document.getElementById('ciso-flag').textContent = String(flagCount);
  document.getElementById('ciso-block').textContent = String(blockCount);

  if (totalCalls > 0) {
    var pPct = (passCount / totalCalls * 100);
    var fPct = (flagCount / totalCalls * 100);
    var bPct = (blockCount / totalCalls * 100);
    document.getElementById('ciso-bar-pass').style.width = pPct + '%';
    document.getElementById('ciso-bar-flag').style.width = fPct + '%';
    document.getElementById('ciso-bar-block').style.width = bPct + '%';
  }

  // Cost per entity breakdown
  var entitySection = document.getElementById('ciso-entity-section');
  var entityContainer = document.getElementById('ciso-entity-breakdown');
  var govList = data.governance || [];
  var spans = data.spans || [];
  if (govList.length > 0) {
    // Build call_id → cost lookup from spans
    var callCost = {};
    spans.forEach(function(s) {
      if (s.call_id) callCost[s.call_id] = (callCost[s.call_id] || 0) + (s.cost || 0);
    });
    // Build entity → { cost, calls } from governance
    var entities = {};
    govList.forEach(function(g) {
      var e = g.entity || 'unknown';
      if (!entities[e]) entities[e] = { cost: 0, calls: 0 };
      entities[e].calls++;
      entities[e].cost += callCost[g.call_id] || 0;
    });
    var entityKeys = Object.keys(entities);
    if (entityKeys.length > 0) {
      entitySection.style.display = '';
      entityContainer.textContent = '';
      entityKeys.forEach(function(e) {
        var row = document.createElement('div');
        row.className = 'entity-row';
        var left = document.createElement('div');
        var nameEl = document.createElement('span');
        nameEl.className = 'entity-name';
        nameEl.textContent = e;
        left.appendChild(nameEl);
        var detailEl = document.createElement('span');
        detailEl.className = 'entity-detail';
        detailEl.textContent = ' (' + entities[e].calls + ' call' + (entities[e].calls > 1 ? 's' : '') + ')';
        left.appendChild(detailEl);
        row.appendChild(left);
        var costEl = document.createElement('span');
        costEl.className = 'entity-cost';
        costEl.textContent = '$' + entities[e].cost.toFixed(4);
        row.appendChild(costEl);
        entityContainer.appendChild(row);
      });
    } else {
      entitySection.style.display = 'none';
    }
  } else {
    entitySection.style.display = 'none';
  }

  // Governance (CISO view)
  var cisoGovSection = document.getElementById('ciso-governance-section');
  if (govList.length > 0) {
    cisoGovSection.style.display = '';
    var gContainer = document.getElementById('ciso-governance');
    gContainer.textContent = '';
    var classifications = {};
    govList.forEach(function(g) {
      if (g.classification) classifications[g.classification] = (classifications[g.classification] || 0) + 1;
    });
    Object.keys(classifications).forEach(function(c) {
      var row = document.createElement('div');
      row.className = 'metric-row';
      var label = document.createElement('span');
      label.className = 'metric-label';
      label.textContent = 'Classification: ' + c;
      row.appendChild(label);
      var val = document.createElement('span');
      val.className = 'metric-value';
      val.textContent = classifications[c] + ' calls';
      row.appendChild(val);
      gContainer.appendChild(row);
    });
  } else {
    cisoGovSection.style.display = 'none';
  }
}

async function refresh() {
  try {
    var res = await fetch('api/state');
    var data = await res.json();
    lastSignals = data.signals || [];
    // Sync safety toggle state
    if (data.safety_enabled !== undefined && data.safety_enabled !== safetyEnabled) {
      safetyEnabled = data.safety_enabled;
      document.getElementById('safety-toggle').className = 'toggle-switch' + (safetyEnabled ? ' on' : '');
      document.getElementById('safety-label').textContent = safetyEnabled ? 'Safety ON' : 'Safety OFF';
    }
    // Developer view
    document.getElementById('cost').textContent = '$' + data.cost.toFixed(6);
    document.getElementById('calls').textContent = String(data.calls);
    setBadge(document.getElementById('status'), data.action);
    renderFilterBar(document.getElementById('signal-filters'), lastSignals);
    renderSignals(document.getElementById('signals'), lastSignals);
    renderGovernance(document.getElementById('governance'), data.governance);
    renderAttestation(data.attestation);
    renderBudget(data.budget);
    renderSpans(document.getElementById('spans'), data.spans, lastSignals);
    // Executive view
    updateCisoView(data);
  } catch (e) {}
}

function renderBudget(budget) {
  var section = document.getElementById('budget-section');
  if (!budget) { section.style.display = 'none'; return; }
  section.style.display = '';
  var limit = budget.total || budget.per_session || 0;
  var spent = budget.spent || 0;
  var remaining = budget.remaining != null ? budget.remaining : limit - spent;
  var pct = limit > 0 ? Math.min(100, (spent / limit) * 100) : 0;
  var bar = document.getElementById('budget-bar');
  bar.style.width = pct + '%';
  bar.style.background = pct > 90 ? '#da3633' : pct > 70 ? '#d29922' : '#58a6ff';
  var label = '$' + spent.toFixed(4) + ' / $' + limit.toFixed(2);
  if (remaining != null) label += ' ($' + remaining.toFixed(4) + ' remaining)';
  document.getElementById('budget-text').textContent = label;
}

function renderAttestation(attest) {
  var card = document.getElementById('attestation-card');
  var section = document.getElementById('attestation-section');
  var details = document.getElementById('attestation-details');
  if (!attest || !attest.enabled) {
    card.style.display = 'none';
    section.style.display = 'none';
    return;
  }
  card.style.display = '';
  section.style.display = '';
  document.getElementById('chain-height').textContent = String(attest.chain_height);
  document.getElementById('chain-signer').textContent = attest.signer_id || '';

  var rows = [
    ['Signing', 'Ed25519'],
    ['Signer ID', attest.signer_id],
    ['Chain Height', String(attest.chain_height)],
    ['Chain Integrity', 'VERIFIED'],
  ];
  if (attest.latest_hash) {
    rows.push(['Latest Hash', attest.latest_hash.substring(0, 24) + '...']);
  }

  var html = '';
  for (var i = 0; i < rows.length; i++) {
    var label = rows[i][0];
    var value = rows[i][1];
    if (!value) continue;
    var valueClass = (label === 'Chain Integrity') ? 'style="color:#3fb950;font-weight:bold;"' : '';
    html += '<div class="entity-row">';
    html += '<span class="entity-name">' + label + '</span>';
    html += '<span class="entity-detail" ' + valueClass + '>' + value + '</span>';
    html += '</div>';
  }
  details.innerHTML = html;
}
// Auto-select view on load
setView(currentView);
refresh();
setInterval(refresh, 2000);
fetchPolicy();
setInterval(fetchPolicy, 5000);
</script>
</body>
</html>
