"""AYN Antivirus Dashboard — HTML Template.

Single-page application with embedded CSS and JavaScript.
All data is fetched from the ``/api/*`` endpoints.
"""

from __future__ import annotations


def get_dashboard_html() -> str:
    """Return the complete HTML dashboard as a string."""
    return _HTML


_HTML = r"""<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>AYN Antivirus — Security Dashboard</title>
<style>
:root{--bg:#0a0e17;--surface:#111827;--surface2:#1a2332;--surface3:#1f2b3d;
--border:#2a3444;--text:#e2e8f0;--text-dim:#8892a4;--accent:#3b82f6;
--green:#10b981;--red:#ef4444;--orange:#f59e0b;--yellow:#eab308;
--purple:#8b5cf6;--cyan:#06b6d4;--radius:8px;--shadow:0 2px 8px rgba(0,0,0,.3)}
*{margin:0;padding:0;box-sizing:border-box}
body{background:var(--bg);color:var(--text);font-family:'Segoe UI',system-ui,-apple-system,sans-serif;font-size:14px;min-height:100vh}
a{color:var(--accent);text-decoration:none}
::-webkit-scrollbar{width:6px;height:6px}
::-webkit-scrollbar-track{background:var(--surface)}
::-webkit-scrollbar-thumb{background:var(--border);border-radius:3px}

/* ── Header ── */
.header{background:var(--surface);border-bottom:1px solid var(--border);padding:12px 24px;display:flex;align-items:center;justify-content:space-between;position:sticky;top:0;z-index:100}
.header-left{display:flex;align-items:center;gap:16px}
.logo{font-size:1.3rem;font-weight:800;letter-spacing:.05em}
.logo span{color:var(--accent)}
.header-meta{display:flex;gap:20px;font-size:.82rem;color:var(--text-dim)}
.header-meta b{color:var(--text);font-weight:600}
.pulse{display:inline-block;width:8px;height:8px;background:var(--green);border-radius:50%;margin-right:6px;animation:pulse 2s infinite}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.4}}

/* ── Navigation ── */
.nav{background:var(--surface);border-bottom:1px solid var(--border);padding:0 24px;display:flex;gap:0;overflow-x:auto}
.nav-tab{padding:12px 20px;cursor:pointer;color:var(--text-dim);font-weight:600;font-size:.85rem;border-bottom:2px solid transparent;transition:all .2s;white-space:nowrap;user-select:none}
.nav-tab:hover{color:var(--text);background:var(--surface2)}
.nav-tab.active{color:var(--accent);border-bottom-color:var(--accent)}

/* ── Layout ── */
.content{padding:20px 24px;max-width:1440px;margin:0 auto}
.tab-panel{display:none;animation:fadeIn .25s}
.tab-panel.active{display:block}
@keyframes fadeIn{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}
.grid{display:grid;gap:16px}
.g2{grid-template-columns:repeat(2,1fr)}
.g3{grid-template-columns:repeat(3,1fr)}
.g4{grid-template-columns:repeat(4,1fr)}
.g6{grid-template-columns:repeat(6,1fr)}
@media(max-width:900px){.g2,.g3,.g4,.g6{grid-template-columns:1fr}}
@media(min-width:901px) and (max-width:1200px){.g4{grid-template-columns:repeat(2,1fr)}.g6{grid-template-columns:repeat(3,1fr)}}
.section{margin-bottom:24px}
.section-title{font-size:1rem;font-weight:700;margin-bottom:12px;display:flex;align-items:center;gap:8px}
.section-title .icon{font-size:1.1rem}

/* ── Cards ── */
.card{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:16px;transition:border-color .2s,box-shadow .2s}
.card:hover{border-color:var(--accent);box-shadow:0 0 12px rgba(59,130,246,.1)}
.card-label{font-size:.75rem;color:var(--text-dim);text-transform:uppercase;letter-spacing:.06em;margin-bottom:6px}
.card-value{font-size:1.6rem;font-weight:700}
.card-sub{font-size:.78rem;color:var(--text-dim);margin-top:4px}
.card-green .card-value{color:var(--green)}
.card-red .card-value{color:var(--red)}
.card-orange .card-value{color:var(--orange)}
.card-yellow .card-value{color:var(--yellow)}
.card-accent .card-value{color:var(--accent)}
.card-purple .card-value{color:var(--purple)}

/* ── Gauge (SVG circular) ── */
.gauge-wrap{display:flex;flex-direction:column;align-items:center;padding:12px}
.gauge{position:relative;width:110px;height:110px}
.gauge svg{transform:rotate(-90deg)}
.gauge-bg{fill:none;stroke:var(--border);stroke-width:10}
.gauge-fill{fill:none;stroke-width:10;stroke-linecap:round;transition:stroke-dashoffset .8s ease}
.gauge-text{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:1.3rem;font-weight:700}
.gauge-label{margin-top:8px;font-size:.8rem;color:var(--text-dim);font-weight:600}

/* ── Badges ── */
.badge{display:inline-block;padding:2px 8px;border-radius:4px;font-size:.72rem;font-weight:700;text-transform:uppercase;letter-spacing:.03em}
.badge-critical{background:rgba(239,68,68,.15);color:var(--red);border:1px solid var(--red)}
.badge-high{background:rgba(245,158,11,.15);color:var(--orange);border:1px solid var(--orange)}
.badge-medium{background:rgba(234,179,8,.15);color:var(--yellow);border:1px solid var(--yellow)}
.badge-low{background:rgba(16,185,129,.15);color:var(--green);border:1px solid var(--green)}
.badge-success{background:rgba(16,185,129,.15);color:var(--green);border:1px solid var(--green)}
.badge-error{background:rgba(239,68,68,.15);color:var(--red);border:1px solid var(--red)}
.badge-running{background:rgba(59,130,246,.15);color:var(--accent);border:1px solid var(--accent)}
.badge-info{background:rgba(59,130,246,.15);color:var(--accent);border:1px solid var(--accent)}
.badge-warning{background:rgba(245,158,11,.15);color:var(--orange);border:1px solid var(--orange)}

/* ── Tables ── */
.tbl-wrap{overflow-x:auto;border:1px solid var(--border);border-radius:var(--radius);background:var(--surface)}
table{width:100%;border-collapse:collapse}
th{background:var(--surface2);color:var(--text-dim);font-size:.75rem;text-transform:uppercase;letter-spacing:.05em;padding:10px 12px;text-align:left;position:sticky;top:0}
td{padding:9px 12px;border-top:1px solid var(--border);font-size:.84rem;vertical-align:middle}
tr:hover td{background:rgba(59,130,246,.04)}
.mono{font-family:'Cascadia Code','Fira Code',monospace;font-size:.78rem}
.trunc{max-width:260px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.empty-row td{text-align:center;padding:32px;color:var(--text-dim);font-size:.95rem}

/* ── Bar Chart (CSS) ── */
.bar-chart{display:flex;align-items:flex-end;gap:4px;height:120px;padding:8px 0}
.bar-col{display:flex;flex-direction:column;align-items:center;flex:1;min-width:0}
.bar{width:100%;min-height:2px;border-radius:3px 3px 0 0;transition:height .4s;position:relative;cursor:default}
.bar:hover::after{content:attr(data-tip);position:absolute;bottom:calc(100% + 4px);left:50%;transform:translateX(-50%);background:var(--surface3);border:1px solid var(--border);padding:3px 8px;border-radius:4px;font-size:.7rem;white-space:nowrap;z-index:5}
.bar-label{font-size:.6rem;color:var(--text-dim);margin-top:4px;writing-mode:vertical-lr;transform:rotate(180deg);max-height:40px;overflow:hidden}
.bar-threats{background:var(--red)}
.bar-scans{background:var(--accent)}

/* ── Disk bars ── */
.disk-row{display:flex;align-items:center;gap:12px;padding:6px 0}
.disk-mount{width:100px;font-size:.8rem;color:var(--text-dim);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.disk-bar-outer{flex:1;height:14px;background:var(--surface2);border-radius:7px;overflow:hidden}
.disk-bar-inner{height:100%;border-radius:7px;transition:width .6s}
.disk-pct{width:50px;text-align:right;font-size:.8rem;font-weight:600}

/* ── Buttons ── */
.btn{display:inline-flex;align-items:center;gap:6px;padding:8px 16px;border-radius:6px;border:1px solid var(--border);background:var(--surface2);color:var(--text);font-size:.82rem;font-weight:600;cursor:pointer;transition:all .2s;white-space:nowrap}
.btn:hover{border-color:var(--accent);background:var(--surface3)}
.btn-primary{background:var(--accent);border-color:var(--accent);color:#fff}
.btn-primary:hover{background:#2563eb}
.btn-sm{padding:5px 10px;font-size:.76rem}
.btn:disabled{opacity:.5;cursor:not-allowed}
.spinner{display:inline-block;width:14px;height:14px;border:2px solid rgba(255,255,255,.3);border-top-color:#fff;border-radius:50%;animation:spin .6s linear infinite}
@keyframes spin{to{transform:rotate(360deg)}}
.btn-row{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:12px}

/* ── Filter bar ── */
.filters{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:14px;align-items:center}
.filters select,.filters input{background:var(--surface2);border:1px solid var(--border);color:var(--text);padding:7px 10px;border-radius:6px;font-size:.82rem}
.filters select:focus,.filters input:focus{outline:none;border-color:var(--accent)}
.filters input{min-width:200px}

/* ── Sub-tabs ── */
.sub-tabs{display:flex;gap:0;margin-bottom:14px;border-bottom:1px solid var(--border)}
.sub-tab{padding:8px 16px;cursor:pointer;color:var(--text-dim);font-size:.82rem;font-weight:600;border-bottom:2px solid transparent;transition:all .2s}
.sub-tab:hover{color:var(--text)}
.sub-tab.active{color:var(--accent);border-bottom-color:var(--accent)}

/* ── Pagination ── */
.pager{display:flex;align-items:center;justify-content:center;gap:12px;padding:12px;font-size:.84rem;color:var(--text-dim)}

/* ── Logs ── */
.log-view{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:8px;max-height:500px;overflow-y:auto;font-family:'Cascadia Code','Fira Code',monospace;font-size:.78rem;line-height:1.7}
.log-line{padding:2px 4px;display:flex;gap:10px;border-bottom:1px solid rgba(42,52,68,.4)}
.log-ts{color:var(--text-dim);min-width:140px}
.log-src{color:var(--purple);min-width:80px}
.log-msg{flex:1;word-break:break-all}

/* ── Toast ── */
.toast-area{position:fixed;top:70px;right:20px;z-index:200;display:flex;flex-direction:column;gap:8px}
.toast{padding:10px 18px;border-radius:6px;font-size:.84rem;font-weight:600;animation:slideIn .3s;box-shadow:var(--shadow)}
.toast-success{background:#065f46;color:#a7f3d0;border:1px solid var(--green)}
.toast-error{background:#7f1d1d;color:#fca5a5;border:1px solid var(--red)}
.toast-info{background:#1e3a5f;color:#93c5fd;border:1px solid var(--accent)}
@keyframes slideIn{from{opacity:0;transform:translateX(30px)}to{opacity:1;transform:none}}
</style>
</head>
<body>

<!-- HEADER -->
<div class="header">
  <div class="header-left">
    <div class="logo">⚔️ <span>AYN</span> ANTIVIRUS</div>
    <div style="font-size:.75rem;color:var(--text-dim)">Security Dashboard</div>
  </div>
  <div class="header-meta">
    <div><span class="pulse"></span><b id="hd-host">—</b></div>
    <div>Up <b id="hd-uptime">—</b></div>
    <div id="hd-time">—</div>
  </div>
</div>

<!-- NAV -->
<div class="nav" id="nav">
  <div class="nav-tab active" data-tab="overview">📊 Overview</div>
  <div class="nav-tab" data-tab="threats">🛡️ Threats</div>
  <div class="nav-tab" data-tab="scans">🔍 Scans</div>
  <div class="nav-tab" data-tab="definitions">📚 Definitions</div>
  <div class="nav-tab" data-tab="containers">🐳 Containers</div>
  <div class="nav-tab" data-tab="quarantine">🔒 Quarantine</div>
  <div class="nav-tab" data-tab="logs">📋 Logs</div>
</div>

<!-- CONTENT -->
<div class="content">

<!-- ═══════ TAB: OVERVIEW ═══════ -->
<div class="tab-panel active" id="panel-overview">
  <!-- Status cards -->
  <div class="section">
    <div class="grid g4" id="status-cards">
      <div class="card card-green"><div class="card-label">Protection</div><div class="card-value" id="ov-prot">Active</div><div class="card-sub" id="ov-prot-sub">AI-powered analysis</div></div>
      <div class="card card-accent"><div class="card-label">Last Scan</div><div class="card-value" id="ov-scan">—</div><div class="card-sub" id="ov-scan-sub">—</div></div>
      <div class="card card-purple"><div class="card-label">Signatures</div><div class="card-value" id="ov-sigs">—</div><div class="card-sub" id="ov-sigs-sub">—</div></div>
      <div class="card"><div class="card-label">Quarantine</div><div class="card-value" id="ov-quar">0</div><div class="card-sub">Isolated items</div></div>
    </div>
  </div>

  <!-- CPU Per-Core -->
  <div class="section">
    <div class="section-title"><span class="icon">🧮</span> CPU Per Core <span id="cpu-summary" style="font-size:.8rem;color:var(--text-dim);margin-left:8px"></span></div>
    <div class="card" style="padding:12px">
      <canvas id="cpu-canvas" height="140" style="width:100%;display:block"></canvas>
    </div>
  </div>

  <!-- Memory Breakdown -->
  <div class="section">
    <div class="section-title"><span class="icon">🧠</span> Memory Usage <span id="mem-summary" style="font-size:.8rem;color:var(--text-dim);margin-left:8px"></span></div>
    <div class="grid g2">
      <div class="card" style="padding:12px">
        <canvas id="mem-canvas" height="160" style="width:100%;display:block"></canvas>
      </div>
      <div class="card" style="padding:12px">
        <div class="card-label">Memory Breakdown</div>
        <div id="mem-bars" style="margin-top:8px"></div>
        <div style="margin-top:12px;border-top:1px solid var(--border);padding-top:8px">
          <div class="card-label">Swap</div>
          <div id="swap-bar"></div>
        </div>
      </div>
    </div>
  </div>

  <!-- Load + Network + Processes -->
  <div class="section">
    <div class="grid g3">
      <div class="card"><div class="card-label">Load Average</div><div class="card-value" id="ov-load" style="font-size:1.2rem">—</div><div class="card-sub">1 / 5 / 15 min</div></div>
      <div class="card"><div class="card-label">Network Connections</div><div class="card-value" id="ov-netconn">—</div><div class="card-sub">Active inet sockets</div></div>
      <div class="card"><div class="card-label">CPU Frequency</div><div class="card-value" id="ov-freq" style="font-size:1.2rem">—</div><div class="card-sub">Current MHz</div></div>
    </div>
  </div>

  <!-- Top Processes -->
  <div class="section">
    <div class="section-title"><span class="icon">⚙️</span> Top Processes</div>
    <div class="tbl-wrap">
      <table><thead><tr><th>PID</th><th>Process</th><th>CPU %</th><th>RAM %</th></tr></thead><tbody id="proc-tbody"><tr class="empty-row"><td colspan="4">Loading…</td></tr></tbody></table>
    </div>
  </div>

  <!-- Disk -->
  <div class="section">
    <div class="section-title"><span class="icon">💾</span> Disk Usage</div>
    <div class="card" id="disk-area"><div style="color:var(--text-dim);padding:8px">Loading…</div></div>
  </div>

  <!-- Threat summary -->
  <div class="section">
    <div class="section-title"><span class="icon">⚠️</span> Threat Summary</div>
    <div class="grid g4">
      <div class="card card-red"><div class="card-label">Critical</div><div class="card-value" id="ov-tc">0</div></div>
      <div class="card card-orange"><div class="card-label">High</div><div class="card-value" id="ov-th">0</div></div>
      <div class="card card-yellow"><div class="card-label">Medium</div><div class="card-value" id="ov-tm">0</div></div>
      <div class="card card-green"><div class="card-label">Low</div><div class="card-value" id="ov-tl">0</div></div>
    </div>
  </div>

  <!-- Scan Activity Chart -->
  <div class="section">
    <div class="section-title"><span class="icon">📈</span> Scan Activity (14 days)</div>
    <div class="card" style="padding:12px">
      <canvas id="scan-canvas" height="160" style="width:100%;display:block"></canvas>
      <div style="display:flex;gap:16px;justify-content:center;margin-top:8px;font-size:.75rem;color:var(--text-dim)">
        <span>🔵 Scans</span><span>🔴 Threats Found</span>
      </div>
    </div>
  </div>
</div>

<!-- ═══════ TAB: THREATS ═══════ -->
<div class="tab-panel" id="panel-threats">
  <div class="filters">
    <select id="f-severity"><option value="">All Severities</option><option value="CRITICAL">Critical</option><option value="HIGH">High</option><option value="MEDIUM">Medium</option><option value="LOW">Low</option></select>
    <select id="f-type"><option value="">All Types</option><option value="MALWARE">Malware</option><option value="MINER">Miner</option><option value="SPYWARE">Spyware</option><option value="VIRUS">Virus</option><option value="ROOTKIT">Rootkit</option></select>
    <input type="text" id="f-search" placeholder="Search threats…">
  </div>
  <div class="tbl-wrap">
    <table><thead><tr><th>Time</th><th>File Path</th><th>Threat</th><th>Type</th><th>Severity</th><th>Detector</th><th>AI Verdict</th><th>Status</th><th>Actions</th></tr></thead><tbody id="threat-tbody"><tr class="empty-row"><td colspan="9">Loading…</td></tr></tbody></table>
  </div>
  <div class="pager"><button class="btn btn-sm" id="threat-prev">← Prev</button><span id="threat-page">Page 1</span><button class="btn btn-sm" id="threat-next">Next →</button></div>
</div>

<!-- ═══════ TAB: SCANS ═══════ -->
<div class="tab-panel" id="panel-scans">
  <div class="btn-row">
    <button class="btn btn-primary" id="btn-quick-scan" onclick="doAction('quick-scan',this)">⚡ Run Quick Scan</button>
    <button class="btn" id="btn-full-scan" onclick="doAction('full-scan',this)">🔍 Run Full Scan</button>
  </div>
  <div class="section">
    <div class="section-title"><span class="icon">📈</span> Scan History (30 days)</div>
    <div class="card" style="padding:12px"><canvas id="scan-chart-canvas" height="160" style="width:100%;display:block"></canvas><div style="display:flex;gap:16px;justify-content:center;margin-top:8px;font-size:.75rem;color:var(--text-dim)"><span>🔵 Scans</span><span>🔴 Threats</span></div></div>
  </div>
  <div class="section">
    <div class="section-title"><span class="icon">📋</span> Recent Scans</div>
    <div class="tbl-wrap">
      <table><thead><tr><th>Time</th><th>Type</th><th>Path</th><th>Files</th><th>Threats</th><th>Duration</th><th>Status</th></tr></thead><tbody id="scan-tbody"><tr class="empty-row"><td colspan="7">Loading…</td></tr></tbody></table>
    </div>
  </div>
</div>

<!-- ═══════ TAB: DEFINITIONS ═══════ -->
<div class="tab-panel" id="panel-definitions">
  <div class="grid g4 section">
    <div class="card card-purple"><div class="card-label">Hashes</div><div class="card-value" id="def-hashes">0</div></div>
    <div class="card card-accent"><div class="card-label">Malicious IPs</div><div class="card-value" id="def-ips">0</div></div>
    <div class="card card-orange"><div class="card-label">Domains</div><div class="card-value" id="def-domains">0</div></div>
    <div class="card card-red"><div class="card-label">URLs</div><div class="card-value" id="def-urls">0</div></div>
  </div>
  <div class="btn-row">
    <button class="btn btn-primary" onclick="doAction('update-sigs',this)">🔄 Update All Feeds</button>
    <button class="btn btn-sm" onclick="doFeedUpdate('malwarebazaar',this)">MalwareBazaar</button>
    <button class="btn btn-sm" onclick="doFeedUpdate('threatfox',this)">ThreatFox</button>
    <button class="btn btn-sm" onclick="doFeedUpdate('urlhaus',this)">URLhaus</button>
    <button class="btn btn-sm" onclick="doFeedUpdate('feodotracker',this)">FeodoTracker</button>
    <button class="btn btn-sm" onclick="doFeedUpdate('emergingthreats',this)">EmergingThreats</button>
  </div>
  <div class="sub-tabs" id="def-subtabs">
    <div class="sub-tab active" data-def="all">All</div>
    <div class="sub-tab" data-def="hash">Hashes</div>
    <div class="sub-tab" data-def="ip">IPs</div>
    <div class="sub-tab" data-def="domain">Domains</div>
    <div class="sub-tab" data-def="url">URLs</div>
  </div>
  <div class="filters"><input type="text" id="def-search" placeholder="Search definitions…" style="flex:1;max-width:400px"></div>
  <div class="tbl-wrap"><table><thead id="def-thead"></thead><tbody id="def-tbody"><tr class="empty-row"><td colspan="6">Loading…</td></tr></tbody></table></div>
  <div class="pager"><button class="btn btn-sm" id="def-prev" onclick="defPage(-1)">← Prev</button><span id="def-page-info">Page 1</span><button class="btn btn-sm" id="def-next" onclick="defPage(1)">Next →</button></div>
  <div class="section" style="margin-top:20px">
    <div class="section-title"><span class="icon">🔄</span> Recent Updates</div>
    <div class="tbl-wrap"><table><thead><tr><th>Time</th><th>Feed</th><th>Hashes</th><th>IPs</th><th>Domains</th><th>URLs</th><th>Status</th></tr></thead><tbody id="sigup-tbody"></tbody></table></div>
  </div>
</div>

<!-- ═══════ TAB: CONTAINERS ═══════ -->
<div class="tab-panel" id="panel-containers">
  <div class="btn-row">
    <button class="btn btn-primary" id="btn-scan-containers" onclick="doAction('scan-containers',this)">🐳 Scan All Containers</button>
  </div>
  <div class="grid g3 section">
    <div class="card card-accent"><div class="card-label">Containers Found</div><div class="card-value" id="ct-count">0</div></div>
    <div class="card card-green"><div class="card-label">Available Runtimes</div><div class="card-value" id="ct-runtimes" style="font-size:1rem">—</div></div>
    <div class="card card-red"><div class="card-label">Container Threats</div><div class="card-value" id="ct-threats">0</div></div>
  </div>
  <div class="section">
    <div class="section-title"><span class="icon">📦</span> Discovered Containers</div>
    <div class="tbl-wrap">
      <table><thead><tr><th>ID</th><th>Name</th><th>Image</th><th>Runtime</th><th>Status</th><th>IP</th><th>Ports</th><th>Action</th></tr></thead>
      <tbody id="ct-tbody"><tr class="empty-row"><td colspan="8">Loading…</td></tr></tbody></table>
    </div>
  </div>
  <div class="section">
    <div class="section-title"><span class="icon">⚠️</span> Container Threats</div>
    <div class="tbl-wrap">
      <table><thead><tr><th>Time</th><th>Container</th><th>Threat</th><th>Type</th><th>Severity</th><th>Details</th></tr></thead>
      <tbody id="ct-threat-tbody"><tr class="empty-row"><td colspan="6">No container threats ✅</td></tr></tbody></table>
    </div>
  </div>
</div>

<!-- ═══════ TAB: QUARANTINE ═══════ -->
<div class="tab-panel" id="panel-quarantine">
  <div class="grid g2 section">
    <div class="card"><div class="card-label">Total Quarantined</div><div class="card-value" id="q-count">0</div></div>
    <div class="card"><div class="card-label">Vault Size</div><div class="card-value" id="q-size">0 B</div></div>
  </div>
  <div class="tbl-wrap"><table><thead><tr><th>ID</th><th>Original Path</th><th>Threat</th><th>Date</th><th>Size</th></tr></thead><tbody id="quar-tbody"><tr class="empty-row"><td colspan="5">Vault is empty ✅</td></tr></tbody></table></div>
</div>

<!-- ═══════ TAB: LOGS ═══════ -->
<div class="tab-panel" id="panel-logs">
  <div class="btn-row"><button class="btn btn-sm" onclick="loadLogs()">🔄 Refresh</button></div>
  <div class="log-view" id="log-view"><div style="color:var(--text-dim)">Loading…</div></div>
</div>

</div><!-- /content -->

<!-- Toast area -->
<div class="toast-area" id="toast-area"></div>

<script>
/* ── State ── */
let S={threats:[],threatPage:1,threatPerPage:25,defType:'all',defPage:1,defPerPage:50,defSearch:''};
const $=id=>document.getElementById(id);
const Q=(s,el)=>(el||document).querySelectorAll(s);

/* ── Helpers ── */
function fmt(n){return n==null?'—':Number(n).toLocaleString()}
function fmtBytes(b){if(!b)return '0 B';const u=['B','KB','MB','GB','TB'];let i=0;let v=b;while(v>=1024&&i<u.length-1){v/=1024;i++;}return v.toFixed(i?1:0)+' '+u[i];}
function fmtDur(s){if(!s||s<0)return '0s';s=Math.round(s);if(s<60)return s+'s';if(s<3600)return Math.floor(s/60)+'m '+s%60+'s';return Math.floor(s/3600)+'h '+Math.floor(s%3600/60)+'m';}
function ago(ts){if(!ts)return '—';const d=new Date(ts+'Z');const s=Math.floor((Date.now()-d)/1000);if(s<60)return s+'s ago';if(s<3600)return Math.floor(s/60)+'m ago';if(s<86400)return Math.floor(s/3600)+'h ago';return Math.floor(s/86400)+'d ago';}
function sevBadge(s){const c=esc((s||'').toUpperCase());const cl=c.toLowerCase().replace(/[^a-z]/g,'');return `<span class="badge badge-${cl}">${c}</span>`;}
function statusBadge(s){const m={completed:'success',success:'success',running:'running',failed:'error',error:'error'};const safe=esc(s||'');const cl=(m[s]||'info').replace(/[^a-z]/g,'');return `<span class="badge badge-${cl}">${safe}</span>`;}
function esc(s){const d=document.createElement('div');d.textContent=s||'';return d.innerHTML;}
function trunc(s,n){s=s||'';return s.length>n?s.slice(0,n)+'…':s;}
function setGauge(id,pct,color){const g=$(id);if(!g)return;const c=g.querySelector('.gauge-fill');const t=g.querySelector('.gauge-text');const off=314-(314*Math.min(pct,100)/100);c.style.strokeDashoffset=off;if(color)c.style.stroke=color;t.textContent=Math.round(pct)+'%';}
function gaugeColor(p){return p>90?'var(--red)':p>70?'var(--orange)':p>50?'var(--yellow)':'var(--green)';}

/* ── Toast ── */
function toast(msg,type='info'){const t=document.createElement('div');t.className='toast toast-'+type;t.textContent=msg;$('toast-area').appendChild(t);setTimeout(()=>t.remove(),4000);}

/* ── API ── */
async function api(path){try{const r=await fetch(path);if(!r.ok)throw new Error(r.statusText);return await r.json();}catch(e){console.error('API error:',path,e);return null;}}

/* ── Tab switching ── */
Q('.nav-tab').forEach(t=>t.addEventListener('click',()=>{
  Q('.nav-tab').forEach(x=>x.classList.remove('active'));
  Q('.tab-panel').forEach(x=>x.classList.remove('active'));
  t.classList.add('active');
  $('panel-'+t.dataset.tab).classList.add('active');
  if(t.dataset.tab==='threats')loadThreats();
  if(t.dataset.tab==='scans')loadScans();
  if(t.dataset.tab==='definitions')loadDefs();
  if(t.dataset.tab==='containers')loadContainers();
  if(t.dataset.tab==='quarantine')loadQuarantine();
  if(t.dataset.tab==='logs')loadLogs();
}));

/* ═══════ OVERVIEW ═══════ */
/* ── Canvas Chart Helpers ── */
let _cpuHistory=[];const _CPU_HIST_MAX=60;
let _memHistory=[];const _MEM_HIST_MAX=60;

function drawLineChart(canvasId,datasets,opts={}){
  const cv=$(canvasId);if(!cv)return;
  const dpr=window.devicePixelRatio||1;
  const rect=cv.getBoundingClientRect();
  cv.width=rect.width*dpr;cv.height=(opts.height||rect.height)*dpr;
  const ctx=cv.getContext('2d');ctx.scale(dpr,dpr);
  const W=rect.width,H=opts.height||rect.height;
  const pad={t:10,r:10,b:24,l:42};
  const cw=W-pad.l-pad.r,ch=H-pad.t-pad.b;

  // Background
  ctx.fillStyle='#0d1117';ctx.fillRect(0,0,W,H);

  // Grid
  const gridLines=opts.gridLines||5;
  const maxVal=opts.maxVal||Math.max(...datasets.flatMap(d=>d.data),1);
  ctx.strokeStyle='#1e293b';ctx.lineWidth=1;ctx.font='10px system-ui';ctx.fillStyle='#6b7280';
  for(let i=0;i<=gridLines;i++){
    const y=pad.t+ch-(ch*i/gridLines);
    ctx.beginPath();ctx.moveTo(pad.l,y);ctx.lineTo(pad.l+cw,y);ctx.stroke();
    const v=((maxVal*i/gridLines)).toFixed(opts.decimals||0);
    ctx.fillText(v+(opts.unit||''),2,y+3);
  }

  // Data lines
  datasets.forEach(ds=>{
    if(!ds.data.length)return;
    const n=ds.data.length;
    ctx.beginPath();ctx.strokeStyle=ds.color;ctx.lineWidth=ds.lineWidth||2;
    ds.data.forEach((v,i)=>{
      const x=pad.l+(cw*i/(n-1||1));
      const y=pad.t+ch-ch*(Math.min(v,maxVal)/maxVal);
      if(i===0)ctx.moveTo(x,y);else ctx.lineTo(x,y);
    });
    ctx.stroke();
    // Fill
    if(ds.fill){
      const n2=ds.data.length;
      ctx.lineTo(pad.l+cw,pad.t+ch);ctx.lineTo(pad.l,pad.t+ch);ctx.closePath();
      ctx.fillStyle=ds.fill;ctx.fill();
    }
  });

  // Labels
  if(opts.labels&&opts.labels.length){
    ctx.fillStyle='#6b7280';ctx.font='9px system-ui';ctx.textAlign='center';
    const n=opts.labels.length;
    opts.labels.forEach((l,i)=>{
      if(i%Math.ceil(n/8)!==0&&i!==n-1)return;
      const x=pad.l+(cw*i/(n-1||1));
      ctx.fillText(l,x,H-2);
    });
  }
}

function drawBarChart(canvasId,data,opts={}){
  const cv=$(canvasId);if(!cv||!data.length)return;
  const dpr=window.devicePixelRatio||1;
  const rect=cv.getBoundingClientRect();
  cv.width=rect.width*dpr;cv.height=(opts.height||rect.height)*dpr;
  const ctx=cv.getContext('2d');ctx.scale(dpr,dpr);
  const W=rect.width,H=opts.height||rect.height;
  const pad={t:10,r:10,b:28,l:42};
  const cw=W-pad.l-pad.r,ch=H-pad.t-pad.b;

  ctx.fillStyle='#0d1117';ctx.fillRect(0,0,W,H);

  const maxVal=opts.maxVal||Math.max(...data.flatMap(d=>[(d.scans||0),(d.threats||0)]),1);
  // Grid
  ctx.strokeStyle='#1e293b';ctx.lineWidth=1;ctx.font='10px system-ui';ctx.fillStyle='#6b7280';
  for(let i=0;i<=4;i++){
    const y=pad.t+ch-(ch*i/4);
    ctx.beginPath();ctx.moveTo(pad.l,y);ctx.lineTo(pad.l+cw,y);ctx.stroke();
    ctx.fillText(Math.round(maxVal*i/4),2,y+3);
  }

  const n=data.length;const bw=Math.max((cw/n)*0.35,2);const gap=cw/n;
  data.forEach((d,i)=>{
    const x=pad.l+gap*i+gap*0.15;
    const sh=ch*(d.scans||0)/maxVal;
    const th=ch*(d.threats||0)/maxVal;
    // Scans bar
    ctx.fillStyle='#3b82f6';ctx.fillRect(x,pad.t+ch-sh,bw,sh);
    // Threats bar
    ctx.fillStyle='#ef4444';ctx.fillRect(x+bw+1,pad.t+ch-th,bw,th);
    // Label
    ctx.fillStyle='#6b7280';ctx.font='9px system-ui';ctx.textAlign='center';
    const day=(d.day||'').slice(5);
    ctx.fillText(day,x+bw,H-4);
  });
}

function drawCoreChart(canvasId,cores){
  const cv=$(canvasId);if(!cv||!cores.length)return;
  const dpr=window.devicePixelRatio||1;
  const rect=cv.getBoundingClientRect();
  cv.width=rect.width*dpr;cv.height=140*dpr;
  const ctx=cv.getContext('2d');ctx.scale(dpr,dpr);
  const W=rect.width,H=140;
  const pad={t:8,r:8,b:20,l:8};
  const n=cores.length;const gap=4;
  const bw=Math.min((W-pad.l-pad.r-(n-1)*gap)/n,60);

  ctx.fillStyle='#0d1117';ctx.fillRect(0,0,W,H);

  cores.forEach((pct,i)=>{
    const x=pad.l+i*(bw+gap);
    const barH=(H-pad.t-pad.b)*(pct/100);
    const c=pct>90?'#ef4444':pct>70?'#f59e0b':pct>50?'#eab308':'#3b82f6';
    // Background
    ctx.fillStyle='#1e293b';ctx.fillRect(x,pad.t,bw,H-pad.t-pad.b);
    // Bar
    ctx.fillStyle=c;ctx.fillRect(x,H-pad.b-barH,bw,barH);
    // Label
    ctx.fillStyle='#e2e8f0';ctx.font='bold 10px system-ui';ctx.textAlign='center';
    ctx.fillText(Math.round(pct)+'%',x+bw/2,H-pad.b-barH-4>pad.t?H-pad.b-barH-4:pad.t+12);
    ctx.fillStyle='#6b7280';ctx.font='9px system-ui';
    ctx.fillText('C'+i,x+bw/2,H-4);
  });
}

function renderMemBars(h){
  const total=h.mem_total||1;
  const used=h.mem_used||0;
  const cached=h.mem_cached||0;
  const buffers=h.mem_buffers||0;
  const avail=h.mem_available||0;
  const app=used-cached-buffers;
  const items=[
    {label:'App/Used',val:Math.max(app,0),color:'var(--purple)'},
    {label:'Cached',val:cached,color:'var(--cyan)'},
    {label:'Buffers',val:buffers,color:'var(--accent)'},
    {label:'Available',val:avail,color:'var(--green)'},
  ];
  $('mem-bars').innerHTML=items.map(it=>{
    const pct=(it.val/total*100).toFixed(1);
    return `<div class="disk-row"><div class="disk-mount" style="width:70px"><span style="display:inline-block;width:8px;height:8px;border-radius:50%;background:${it.color};margin-right:4px"></span>${it.label}</div><div class="disk-bar-outer"><div class="disk-bar-inner" style="width:${pct}%;background:${it.color}"></div></div><div class="disk-pct">${fmtBytes(it.val)}</div></div>`;
  }).join('');
  // Swap
  const spct=h.swap_total?(h.swap_used/h.swap_total*100).toFixed(1):0;
  $('swap-bar').innerHTML=h.swap_total?`<div class="disk-row"><div class="disk-mount" style="width:70px">${fmtBytes(h.swap_used)}/${fmtBytes(h.swap_total)}</div><div class="disk-bar-outer"><div class="disk-bar-inner" style="width:${spct}%;background:var(--orange)"></div></div><div class="disk-pct">${spct}%</div></div>`:'<div style="color:var(--text-dim);font-size:.8rem">No swap</div>';
}

async function loadOverview(){
  const [st,h,ts,ch]=await Promise.all([api('/api/status'),api('/api/health'),api('/api/threat-stats'),api('/api/scan-chart?days=14')]);
  if(st){
    $('hd-host').textContent=st.hostname||'—';
    $('hd-uptime').textContent=fmtDur(st.uptime_seconds);
    $('hd-time').textContent=st.server_time||'';
    const ls=st.last_scan;
    $('ov-scan').textContent=ls?ago(ls.timestamp):'Never';
    $('ov-scan-sub').textContent=ls?`${fmt(ls.files_scanned)} files, ${ls.threats_found} threats`:'No scans yet';
    const sig=st.signatures||{};
    $('ov-sigs').textContent=fmt((sig.total_hashes||0)+(sig.total_ips||0)+(sig.total_domains||0)+(sig.total_urls||0));
    $('ov-sigs-sub').textContent=sig.last_update?'Updated '+ago(sig.last_update):'Not updated';
    $('ov-quar').textContent=fmt(st.quarantine_count);
  }
  if(h){
    // CPU per-core chart
    const cores=h.cpu_per_core||[];
    if(cores.length){
      drawCoreChart('cpu-canvas',cores);
      $('cpu-summary').textContent=`${cores.length} cores @ ${h.cpu_freq_mhz||'?'} MHz — avg ${Math.round(h.cpu_percent)}%`;
    }
    // CPU history
    _cpuHistory.push(h.cpu_percent||0);if(_cpuHistory.length>_CPU_HIST_MAX)_cpuHistory.shift();

    // Memory
    _memHistory.push(h.mem_percent||0);if(_memHistory.length>_MEM_HIST_MAX)_memHistory.shift();
    drawLineChart('mem-canvas',[
      {data:_memHistory,color:'#8b5cf6',fill:'rgba(139,92,246,0.1)',lineWidth:2},
    ],{maxVal:100,unit:'%',height:160,gridLines:4});
    $('mem-summary').textContent=`${fmtBytes(h.mem_used)} / ${fmtBytes(h.mem_total)} (${h.mem_percent?.toFixed(1)}%)`;
    renderMemBars(h);

    // Load / Net / Freq
    const la=h.load_avg||[0,0,0];
    $('ov-load').textContent=la.map(v=>v.toFixed(2)).join(' / ');
    $('ov-netconn').textContent=fmt(h.net_connections||0);
    $('ov-freq').textContent=h.cpu_freq_mhz?h.cpu_freq_mhz+' MHz':'—';

    // Top processes
    const procs=h.top_processes||[];
    const ptb=$('proc-tbody');
    if(procs.length){
      ptb.innerHTML=procs.map(p=>{
        const cpuC=p.cpu>50?'var(--red)':p.cpu>20?'var(--orange)':'var(--text)';
        return `<tr><td class="mono">${p.pid}</td><td>${esc(p.name)}</td><td style="color:${cpuC};font-weight:600">${p.cpu}%</td><td>${p.mem}%</td></tr>`;
      }).join('');
    }else{ptb.innerHTML='<tr class="empty-row"><td colspan="4">No active processes</td></tr>';}

    // Disks
    const da=$('disk-area');
    const disks=h.disk_usage||[];
    if(disks.length){
      da.innerHTML=disks.map(d=>{
        const p=d.percent||0;const c=p>90?'var(--red)':p>70?'var(--orange)':'var(--accent)';
        return `<div class="disk-row"><div class="disk-mount" title="${esc(d.mount)}">${esc(d.mount)}</div><div class="disk-bar-outer"><div class="disk-bar-inner" style="width:${p}%;background:${c}"></div></div><div class="disk-pct">${p.toFixed(1)}%</div><div style="font-size:.75rem;color:var(--text-dim);min-width:100px">${fmtBytes(d.used)} / ${fmtBytes(d.total)}</div></div>`;
      }).join('');
    }else{da.innerHTML='<div style="color:var(--text-dim);padding:8px">No disk info</div>';}
  }
  if(ts){
    const bs=ts.by_severity||{};
    $('ov-tc').textContent=fmt(bs.CRITICAL||0);
    $('ov-th').textContent=fmt(bs.HIGH||0);
    $('ov-tm').textContent=fmt(bs.MEDIUM||0);
    $('ov-tl').textContent=fmt(bs.LOW||0);
  }
  if(ch&&ch.chart&&ch.chart.length){
    drawBarChart('scan-canvas',ch.chart.slice(-14),{height:160});
  }
}

/* ═══════ THREATS ═══════ */
async function loadThreats(){
  const d=await api(`/api/threats?limit=200`);
  if(!d)return;
  S.threats=d.threats||[];
  renderThreats();
}
function renderThreats(){
  const sev=$('f-severity').value.toUpperCase();
  const typ=$('f-type').value.toUpperCase();
  const q=$('f-search').value.toLowerCase();
  let f=S.threats;
  if(sev)f=f.filter(t=>(t.severity||'').toUpperCase()===sev);
  if(typ)f=f.filter(t=>(t.threat_type||'').toUpperCase()===typ);
  if(q)f=f.filter(t=>(t.threat_name||'').toLowerCase().includes(q)||(t.file_path||'').toLowerCase().includes(q));
  const total=f.length;const pages=Math.max(Math.ceil(total/S.threatPerPage),1);
  S.threatPage=Math.min(S.threatPage,pages);
  const start=(S.threatPage-1)*S.threatPerPage;
  const slice=f.slice(start,start+S.threatPerPage);
  const tb=$('threat-tbody');
  if(!slice.length){tb.innerHTML='<tr class="empty-row"><td colspan="9">No threats detected ✅</td></tr>';
  }else{tb.innerHTML=slice.map(t=>{
    const act=t.action_taken||'detected';
    const st=act==='detected'?'<span class="badge badge-warning">detected</span>':act==='quarantined'?'<span class="badge badge-info">quarantined</span>':statusBadge(act);
    let btns='';
    if(act==='detected'||act==='monitoring'){
      btns=`<div style="display:flex;gap:4px;flex-wrap:wrap"><button class="btn btn-sm" style="background:var(--purple);color:#fff;border-color:var(--purple);font-size:.7rem;padding:3px 8px" onclick="aiAnalyze(${t.id},this)">🧠 AI Analyze</button><button class="btn btn-sm" style="background:var(--red);color:#fff;border-color:var(--red);font-size:.7rem;padding:3px 8px" onclick="threatAction('quarantine',${t.id},'${esc(t.file_path).replace(/'/g,"\\'")}','${esc(t.threat_name).replace(/'/g,"\\'")}',this)">🔒 Quarantine</button><button class="btn btn-sm" style="font-size:.7rem;padding:3px 8px" onclick="threatAction('delete-threat',${t.id},'${esc(t.file_path).replace(/'/g,"\\'")}','',this)">🗑️ Delete</button><button class="btn btn-sm" style="font-size:.7rem;padding:3px 8px" onclick="threatAction('whitelist',${t.id},'','',this)">✅ Ignore</button></div>`;
    } else if(act==='quarantined'){
      btns=`<div style="display:flex;gap:4px;flex-wrap:wrap"><button class="btn btn-sm" style="background:var(--purple);color:#fff;border-color:var(--purple);font-size:.7rem;padding:3px 8px" onclick="aiAnalyze(${t.id},this)">🧠 AI Analyze</button><button class="btn btn-sm" style="background:var(--green);color:#fff;border-color:var(--green);font-size:.7rem;padding:3px 8px" onclick="threatAction('restore',${t.id},'${esc(t.file_path).replace(/'/g,"\\'")}','',this)">♻️ Restore</button><button class="btn btn-sm" style="font-size:.7rem;padding:3px 8px" onclick="threatAction('delete-threat',${t.id},'${esc(t.file_path).replace(/'/g,"\\'")}','',this)">🗑️ Delete</button><button class="btn btn-sm" style="font-size:.7rem;padding:3px 8px" onclick="threatAction('whitelist',${t.id},'','',this)">✅ Ignore</button></div>`;
    } else {
      btns=`<span style="color:var(--text-dim);font-size:.75rem">${esc(act)}</span>`;
    }
    const det=t.details||'';
    let aiCol='<span style="color:var(--text-dim);font-size:.75rem">—</span>';
    const aiMatch=det.match(/\[AI:\s*(\w+)\s+(\d+)%\]\s*(.*)/);
    if(aiMatch){const v=aiMatch[1],c=aiMatch[2],rsn=aiMatch[3];const vc=v==='safe'?'var(--green)':v==='threat'?'var(--red)':'var(--orange)';aiCol=`<div style="font-size:.75rem"><span style="color:${vc};font-weight:700">${v.toUpperCase()}</span> <span style="color:var(--text-dim)">${c}%</span><div style="color:var(--text-dim);max-width:150px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap" title="${esc(rsn)}">${esc(rsn)}</div></div>`;}
    return `<tr><td>${ago(t.timestamp)}</td><td class="mono trunc" title="${esc(t.file_path)}">${esc(trunc(t.file_path,50))}</td><td>${esc(t.threat_name)}</td><td>${esc(t.threat_type)}</td><td>${sevBadge(t.severity)}</td><td>${esc(t.detector)}</td><td>${aiCol}</td><td>${st}</td><td>${btns}</td></tr>`;
  }).join('');}
  $('threat-page').textContent=`Page ${S.threatPage} of ${pages} (${total})`;
}
$('threat-prev').onclick=()=>{S.threatPage=Math.max(1,S.threatPage-1);renderThreats();};
$('threat-next').onclick=()=>{S.threatPage++;renderThreats();};
$('f-severity').onchange=$('f-type').onchange=()=>{S.threatPage=1;renderThreats();};
let _tTimer;$('f-search').oninput=()=>{clearTimeout(_tTimer);_tTimer=setTimeout(()=>{S.threatPage=1;renderThreats();},300);};

/* ═══════ SCANS ═══════ */
async function loadScans(){
  const [sc,ch]=await Promise.all([api('/api/scans?limit=30'),api('/api/scan-chart?days=30')]);
  if(sc){
    const tb=$('scan-tbody');
    const scans=sc.scans||[];
    if(!scans.length){tb.innerHTML='<tr class="empty-row"><td colspan="7">No scans yet</td></tr>';
    }else{tb.innerHTML=scans.map(s=>`<tr><td>${ago(s.timestamp)}</td><td>${esc(s.scan_type)}</td><td class="mono trunc" title="${esc(s.scan_path)}">${esc(trunc(s.scan_path,40))}</td><td>${fmt(s.files_scanned)}</td><td>${s.threats_found?'<span style="color:var(--red)">'+s.threats_found+'</span>':'0'}</td><td>${fmtDur(s.duration_seconds)}</td><td>${statusBadge(s.status)}</td></tr>`).join('');}
  }
  if(ch&&ch.chart&&ch.chart.length)drawBarChart('scan-chart-canvas',ch.chart,{height:160});
}

/* ═══════ DEFINITIONS ═══════ */
Q('#def-subtabs .sub-tab').forEach(t=>t.addEventListener('click',()=>{
  Q('#def-subtabs .sub-tab').forEach(x=>x.classList.remove('active'));
  t.classList.add('active');S.defType=t.dataset.def;S.defPage=1;loadDefs();
}));
let _dTimer;$('def-search').oninput=()=>{clearTimeout(_dTimer);_dTimer=setTimeout(()=>{S.defPage=1;S.defSearch=$('def-search').value;loadDefs();},400);};
function defPage(d){S.defPage=Math.max(1,S.defPage+d);loadDefs();}

async function loadDefs(){
  const typ=S.defType==='all'?'':S.defType;
  const q=encodeURIComponent(S.defSearch||'');
  const [dd,su]=await Promise.all([
    api(`/api/definitions?type=${typ}&page=${S.defPage}&per_page=${S.defPerPage}&search=${q}`),
    api('/api/sig-updates?limit=10')
  ]);
  if(dd){
    $('def-hashes').textContent=fmt(dd.total_hashes);
    $('def-ips').textContent=fmt(dd.total_ips);
    $('def-domains').textContent=fmt(dd.total_domains);
    $('def-urls').textContent=fmt(dd.total_urls);
    renderDefTable(dd);
    const total=dd.total_hashes+dd.total_ips+dd.total_domains+dd.total_urls;
    const pages=Math.max(Math.ceil(total/S.defPerPage),1);
    $('def-page-info').textContent=`Page ${S.defPage} of ${pages}`;
  }
  if(su){
    const tb=$('sigup-tbody');
    const ups=su.updates||[];
    if(!ups.length){tb.innerHTML='<tr class="empty-row"><td colspan="7">No updates yet</td></tr>';
    }else{tb.innerHTML=ups.map(u=>`<tr><td>${ago(u.timestamp)}</td><td>${esc(u.feed_name)}</td><td>${fmt(u.hashes_added)}</td><td>${fmt(u.ips_added)}</td><td>${fmt(u.domains_added)}</td><td>${fmt(u.urls_added)}</td><td>${statusBadge(u.status)}</td></tr>`).join('');}
  }
}
function renderDefTable(dd){
  const th=$('def-thead');const tb=$('def-tbody');
  let rows=[];const t=S.defType;
  if(t==='all'||t==='hash'){
    th.innerHTML='<tr><th>Hash</th><th>Threat Name</th><th>Type</th><th>Severity</th><th>Source</th><th>Date</th></tr>';
    rows=rows.concat((dd.hashes||[]).map(r=>`<tr><td class="mono">${esc(trunc(r.hash,16))}</td><td>${esc(r.threat_name)}</td><td>${esc(r.threat_type)}</td><td>${sevBadge(r.severity)}</td><td>${esc(r.source)}</td><td>${ago(r.added_date)}</td></tr>`));
  }
  if(t==='all'||t==='ip'){
    if(t==='ip')th.innerHTML='<tr><th>IP Address</th><th>Threat Name</th><th>Type</th><th>Source</th><th>Date</th></tr>';
    rows=rows.concat((dd.ips||[]).map(r=>`<tr><td class="mono">${esc(r.ip)}</td><td>${esc(r.threat_name)}</td><td>${esc(r.type)}</td><td>${esc(r.source)}</td><td>${ago(r.added_date)}</td></tr>`));
  }
  if(t==='all'||t==='domain'){
    if(t==='domain')th.innerHTML='<tr><th>Domain</th><th>Threat Name</th><th>Type</th><th>Source</th><th>Date</th></tr>';
    rows=rows.concat((dd.domains||[]).map(r=>`<tr><td class="mono">${esc(r.domain)}</td><td>${esc(r.threat_name)}</td><td>${esc(r.type)}</td><td>${esc(r.source)}</td><td>${ago(r.added_date)}</td></tr>`));
  }
  if(t==='all'||t==='url'){
    if(t==='url')th.innerHTML='<tr><th>URL</th><th>Threat Name</th><th>Type</th><th>Source</th><th>Date</th></tr>';
    rows=rows.concat((dd.urls||[]).map(r=>`<tr><td class="mono trunc" title="${esc(r.url)}">${esc(trunc(r.url,60))}</td><td>${esc(r.threat_name)}</td><td>${esc(r.type)}</td><td>${esc(r.source)}</td><td>${ago(r.added_date)}</td></tr>`));
  }
  if(t==='all'&&!rows.length&&!dd.hashes?.length)th.innerHTML='<tr><th>Hash</th><th>Threat Name</th><th>Type</th><th>Severity</th><th>Source</th><th>Date</th></tr>';
  tb.innerHTML=rows.length?rows.join(''):'<tr class="empty-row"><td colspan="6">No definitions found. Run an update to fetch threat feeds.</td></tr>';
}

/* ═══════ CONTAINERS ═══════ */
async function loadContainers(){
  const [cl,cs]=await Promise.all([api('/api/containers'),api('/api/container-scan')]);
  if(cl){
    $('ct-count').textContent=fmt(cl.count);
    $('ct-runtimes').textContent=cl.runtimes.length?cl.runtimes.join(', '):'None detected';
    const tb=$('ct-tbody');
    const cc=cl.containers||[];
    if(!cc.length){tb.innerHTML='<tr class="empty-row"><td colspan="8">No containers found. Install Docker, Podman, or LXC.</td></tr>';
    }else{
      tb.innerHTML=cc.map(c=>{
        const st=c.status==='running'?'<span class="badge badge-success">running</span>':c.status==='stopped'?'<span class="badge badge-error">stopped</span>':'<span class="badge badge-warning">'+esc(c.status)+'</span>';
        const ports=(c.ports||[]).slice(0,3).join(', ')||(c.status==='running'?'—':'');
        return `<tr><td class="mono">${esc(trunc(c.container_id,12))}</td><td>${esc(c.name)}</td><td class="mono trunc" title="${esc(c.image)}">${esc(trunc(c.image,30))}</td><td>${esc(c.runtime)}</td><td>${st}</td><td class="mono">${esc(c.ip_address||'—')}</td><td class="mono" style="font-size:.72rem">${esc(ports)}</td><td><button class="btn btn-sm" onclick="scanSingleContainer('${esc(c.container_id)}',this)">Scan</button></td></tr>`;
      }).join('');
    }
  }
  if(cs){
    const threats=cs.threats||[];
    $('ct-threats').textContent=fmt(threats.length);
    const tb=$('ct-threat-tbody');
    if(!threats.length){tb.innerHTML='<tr class="empty-row"><td colspan="6">No container threats ✅</td></tr>';
    }else{tb.innerHTML=threats.map(t=>`<tr><td>${ago(t.timestamp)}</td><td>${esc(trunc(t.file_path,30))}</td><td>${esc(t.threat_name)}</td><td>${esc(t.threat_type)}</td><td>${sevBadge(t.severity)}</td><td class="trunc" title="${esc(t.details)}">${esc(trunc(t.details,60))}</td></tr>`).join('');}
  }
}
async function scanSingleContainer(id,btn){
  const orig=btn.innerHTML;btn.innerHTML='<span class="spinner"></span>';btn.disabled=true;
  toast(`Scanning container ${id.slice(0,12)}…`,'info');
  try{
    const r=await fetch('/api/actions/scan-container',{method:'POST',headers:{'Content-Type':'application/json','X-API-Key':window.AYN_API_KEY||''},body:JSON.stringify({container_id:id})});
    const ct=r.headers.get('content-type')||'';
    if(!ct.includes('application/json')){const t=await r.text();toast('Server error: '+t.slice(0,100),'error');btn.innerHTML=orig;btn.disabled=false;return;}
    const d=await r.json();
    if(d.status==='error'){toast(d.error||'Failed','error');}
    else{toast(`Container scan: ${d.threats_found||0} threats found`,'success');}
    loadContainers();
  }catch(e){toast('Failed: '+e.message,'error');}
  btn.innerHTML=orig;btn.disabled=false;
}

/* ═══════ QUARANTINE ═══════ */
async function loadQuarantine(){
  const d=await api('/api/quarantine');
  if(!d)return;
  $('q-count').textContent=fmt(d.count);
  $('q-size').textContent=fmtBytes(d.total_size);
  const tb=$('quar-tbody');
  const items=d.items||[];
  if(!items.length){tb.innerHTML='<tr class="empty-row"><td colspan="5">Vault is empty ✅</td></tr>';
  }else{tb.innerHTML=items.map(i=>`<tr><td class="mono">${esc(trunc(i.id,12))}</td><td class="mono trunc" title="${esc(i.original_path)}">${esc(trunc(i.original_path,50))}</td><td>${esc(i.threat_name)}</td><td>${ago(i.quarantine_date)}</td><td>${fmtBytes(i.size||i.file_size||0)}</td></tr>`).join('');}
}

/* ═══════ LOGS ═══════ */
async function loadLogs(){
  const d=await api('/api/logs?limit=50');
  if(!d)return;
  const lv=$('log-view');
  const logs=d.logs||[];
  if(!logs.length){lv.innerHTML='<div style="color:var(--text-dim);padding:12px">No activity yet.</div>';return;}
  lv.innerHTML=logs.map(l=>{
    const lc=l.level==='ERROR'?'var(--red)':l.level==='WARNING'?'var(--orange)':'var(--accent)';
    return `<div class="log-line"><span class="log-ts">${l.timestamp||''}</span><span style="color:${lc};font-weight:700;min-width:56px">${l.level}</span><span class="log-src">${esc(l.source)}</span><span class="log-msg">${esc(l.message)}</span></div>`;
  }).join('');
  lv.scrollTop=0;
}

/* ═══════ AI ANALYSIS ═══════ */
async function aiAnalyze(threatId,btn){
  const orig=btn.innerHTML;btn.innerHTML='<span class="spinner"></span> Analyzing…';btn.disabled=true;
  try{
    const r=await fetch('/api/actions/ai-analyze',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({threat_id:threatId})});
    const ct=r.headers.get('content-type')||'';
    if(!ct.includes('application/json')){toast('Server error','error');btn.innerHTML=orig;btn.disabled=false;return;}
    const d=await r.json();
    if(d.status==='ok'){
      const emoji=d.verdict==='safe'?'✅':d.verdict==='threat'?'🚨':'⚠️';
      const color=d.verdict==='safe'?'success':d.verdict==='threat'?'error':'info';
      toast(`${emoji} AI: ${d.verdict.toUpperCase()} (${d.confidence}%) — ${d.reason}`,color);
      if(d.verdict==='safe'){toast(`Recommended: ${d.recommended_action}`,'info');}
      loadThreats();
    } else {toast(d.error||'AI analysis failed','error');}
  }catch(e){toast('Failed: '+e.message,'error');}
  btn.innerHTML=orig;btn.disabled=false;
}

/* ═══════ THREAT ACTIONS ═══════ */
async function threatAction(action,threatId,filePath,threatName,btn){
  const labels={'quarantine':'Quarantine','delete-threat':'Delete','whitelist':'Whitelist'};
  if(action==='delete-threat'&&!confirm('Permanently delete '+filePath+'?'))return;
  const orig=btn.parentElement.innerHTML;btn.parentElement.innerHTML='<span class="spinner"></span>';
  try{
    const body={threat_id:threatId};
    if(filePath)body.file_path=filePath;
    if(threatName)body.threat_name=threatName;
    const r=await fetch('/api/actions/'+action,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(body)});
    const ct=r.headers.get('content-type')||'';
    if(!ct.includes('application/json')){toast('Server error','error');return;}
    const d=await r.json();
    if(d.status==='ok'){toast((labels[action]||action)+' done','success');loadThreats();}
    else{toast(d.error||'Failed','error');}
  }catch(e){toast('Failed: '+e.message,'error');}
}

/* ═══════ ACTIONS ═══════ */
async function doAction(action,btn){
  const orig=btn.innerHTML;btn.innerHTML='<span class="spinner"></span> Running…';btn.disabled=true;
  toast(`${action} started…`,'info');
  try{
    const r=await fetch(`/api/actions/${action}`,{method:'POST',headers:{'X-API-Key':window.AYN_API_KEY||''}});
    const ct=r.headers.get('content-type')||'';
    if(!ct.includes('application/json')){const t=await r.text();toast('Server error: '+(r.status>=400?r.status+' ':'')+t.slice(0,100),'error');btn.innerHTML=orig;btn.disabled=false;return;}
    const d=await r.json();
    if(d.status==='error'){toast(d.error||'Failed','error');}
    else{toast(`${action} completed`,'success');}
    refreshAll();
  }catch(e){toast('Request failed: '+e.message,'error');}
  btn.innerHTML=orig;btn.disabled=false;
}
async function doFeedUpdate(feed,btn){
  const orig=btn.innerHTML;btn.innerHTML='<span class="spinner"></span>';btn.disabled=true;
  toast(`Updating ${feed}…`,'info');
  try{
    const r=await fetch('/api/actions/update-feed',{method:'POST',headers:{'Content-Type':'application/json','X-API-Key':window.AYN_API_KEY||''},body:JSON.stringify({feed})});
    const ct=r.headers.get('content-type')||'';
    if(!ct.includes('application/json')){const t=await r.text();toast(`${feed}: Server error `+t.slice(0,100),'error');btn.innerHTML=orig;btn.disabled=false;return;}
    const d=await r.json();
    if(d.status==='error'){toast(`${feed}: ${d.error}`,'error');}
    else{toast(`${feed} updated`,'success');}
    loadDefs();
  }catch(e){toast('Failed: '+e.message,'error');}
  btn.innerHTML=orig;btn.disabled=false;
}

/* ═══════ REFRESH ═══════ */
async function refreshAll(){
  await loadOverview();
  const active=document.querySelector('.nav-tab.active');
  if(active){
    const tab=active.dataset.tab;
    if(tab==='threats')loadThreats();
    if(tab==='scans')loadScans();
    if(tab==='definitions')loadDefs();
    if(tab==='containers')loadContainers();
    if(tab==='quarantine')loadQuarantine();
    if(tab==='logs')loadLogs();
  }
}

/* ── Boot ── */
refreshAll();
setInterval(refreshAll,30000);
</script>
</body>
</html>"""
