/**
 * Dashboard Page
 * Enhanced overview with stats, charts, and recent activity
 */

/** Auto-start multi-provider proxy and show env vars modal for OpenClaw users when block mode is enabled. */
async function showOpenClawProxyModal() {
    // Start the multi-provider proxy automatically
    try {
        await fetch('/api/proxy/start', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ provider: 'openai', multi: true, integration: 'openclaw' })
        });
    } catch { /* proxy start failed — modal still useful */ }

    const providers = [
        ['OPENAI_BASE_URL', 'http://127.0.0.1:8742/openai/v1'],
        ['ANTHROPIC_BASE_URL', 'http://127.0.0.1:8742/anthropic'],
        ['GEMINI_BASE_URL', 'http://127.0.0.1:8742/gemini'],
        ['GROQ_BASE_URL', 'http://127.0.0.1:8742/groq'],
        ['MISTRAL_BASE_URL', 'http://127.0.0.1:8742/mistral'],
        ['XAI_BASE_URL', 'http://127.0.0.1:8742/xai'],
    ];

    const modalContent = document.createElement('div');

    const desc = document.createElement('p');
    desc.style.cssText = 'font-size: 13px; color: var(--text-secondary); margin-bottom: 16px;';
    desc.textContent = 'Set your AI provider base URL to the proxy address, then restart your agent.';
    modalContent.appendChild(desc);

    const grid = document.createElement('div');
    grid.style.cssText = 'display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 12px;';

    const boxOC = document.createElement('div');
    boxOC.style.cssText = 'background: var(--bg-secondary); border: 1px solid var(--border-default); border-radius: 8px; padding: 14px;';
    const boxOCTitle = document.createElement('div');
    boxOCTitle.style.cssText = 'font-weight: 600; font-size: 13px; margin-bottom: 6px;';
    boxOCTitle.textContent = 'OpenClaw / ClawdBot';
    boxOC.appendChild(boxOCTitle);
    const boxOCDesc = document.createElement('div');
    boxOCDesc.style.cssText = 'font-size: 12px; color: var(--text-secondary); margin-bottom: 10px;';
    boxOCDesc.textContent = 'Set baseUrl in openclaw.json provider config:';
    boxOC.appendChild(boxOCDesc);
    const ocCode = document.createElement('div');
    ocCode.style.cssText = 'background: var(--bg-tertiary); border-radius: 6px; padding: 10px; font-family: monospace; font-size: 12px; line-height: 1.6; word-break: break-all;';
    ocCode.textContent = '"baseUrl": "http://127.0.0.1:8742/openai/v1"';
    boxOC.appendChild(ocCode);
    grid.appendChild(boxOC);

    const boxEnv = document.createElement('div');
    boxEnv.style.cssText = 'background: var(--bg-secondary); border: 1px solid var(--border-default); border-radius: 8px; padding: 14px;';
    const boxEnvTitle = document.createElement('div');
    boxEnvTitle.style.cssText = 'font-weight: 600; font-size: 13px; margin-bottom: 6px;';
    boxEnvTitle.textContent = 'Other Agents / Frameworks';
    boxEnv.appendChild(boxEnvTitle);
    const boxEnvDesc = document.createElement('div');
    boxEnvDesc.style.cssText = 'font-size: 12px; color: var(--text-secondary); margin-bottom: 10px;';
    boxEnvDesc.textContent = 'Set the base URL environment variable:';
    boxEnv.appendChild(boxEnvDesc);
    const envCode = document.createElement('div');
    envCode.style.cssText = 'background: var(--bg-tertiary); border-radius: 6px; padding: 10px; font-family: monospace; font-size: 12px; line-height: 1.6; word-break: break-all;';
    envCode.textContent = 'export OPENAI_BASE_URL=http://127.0.0.1:8742/openai/v1';
    boxEnv.appendChild(envCode);
    grid.appendChild(boxEnv);

    modalContent.appendChild(grid);

    const note = document.createElement('p');
    note.style.cssText = 'font-size: 12px; color: var(--text-secondary);';
    note.textContent = 'Supports OpenAI, Anthropic, Gemini, and Ollama. See docs for provider-specific URLs.';
    modalContent.appendChild(note);

    Modal.show({
        title: 'Route Traffic Through Proxy',
        content: modalContent,
        size: 'medium',
        actions: [
            {
                label: 'Go to Proxy Settings',
                primary: false,
                onClick: () => { if (window.Sidebar) Sidebar.navigate('proxy-openclaw'); }
            },
            { label: 'Got it', primary: true }
        ]
    });
}

/** Stop proxy and show instructions to unset env vars and restart OpenClaw. */
async function showOpenClawProxyStopModal() {
    // Stop the proxy (best-effort — may be in-process)
    try {
        await fetch('/api/proxy/stop', { method: 'POST' });
    } catch { /* proxy may not be running or in-process */ }

    const modalContent = document.createElement('div');

    const banner = document.createElement('div');
    banner.style.cssText = 'background: color-mix(in srgb, #f59e0b 15%, var(--bg-card)); border: 1px solid color-mix(in srgb, #f59e0b 40%, var(--border-default)); border-radius: 8px; padding: 12px; margin-bottom: 12px;';
    const bannerStrong = document.createElement('strong');
    bannerStrong.style.color = '#d97706';
    bannerStrong.textContent = 'Action required: ';
    banner.appendChild(bannerStrong);
    const bannerText = document.createElement('span');
    bannerText.style.cssText = 'color: var(--text-primary); font-size: 13px;';
    bannerText.textContent = 'Unset the proxy variables below, then restart OpenClaw. The plugin will continue monitoring without the proxy.';
    banner.appendChild(bannerText);
    modalContent.appendChild(banner);

    const intro = document.createElement('p');
    intro.style.marginBottom = '12px';
    intro.textContent = 'Unset these variables to avoid connection errors:';
    modalContent.appendChild(intro);

    const codeBlock = document.createElement('div');
    codeBlock.style.cssText = 'background: var(--bg-tertiary); border-radius: 6px; padding: 12px; font-family: monospace; font-size: 13px; margin-bottom: 12px; line-height: 1.8;';

    const addLine = (text, color) => {
        const div = document.createElement('div');
        if (color) div.style.color = color;
        div.textContent = text;
        codeBlock.appendChild(div);
    };

    const vars = ['OPENAI_BASE_URL', 'ANTHROPIC_BASE_URL', 'GEMINI_BASE_URL', 'GROQ_BASE_URL', 'MISTRAL_BASE_URL', 'XAI_BASE_URL'];

    addLine('# Linux / macOS', 'var(--text-secondary)');
    vars.forEach(v => addLine(`unset ${v}`));
    addLine('');
    addLine('# Windows (PowerShell)', 'var(--text-secondary)');
    vars.forEach(v => addLine(`Remove-Item Env:\\${v} -ErrorAction SilentlyContinue`));

    modalContent.appendChild(codeBlock);

    Modal.show({
        title: 'Block Mode Disabled — Restart OpenClaw',
        content: modalContent,
        size: 'medium',
        actions: [{ label: 'Got it', primary: true }]
    });
}

const DashboardPage = {
    data: null,
    threats: null,
    autoRefreshInterval: null,
    autoRefreshEnabled: false,
    currentContainer: null,

    async render(container) {
        this.currentContainer = container;
        container.textContent = '';

        // Loading state
        const loading = document.createElement('div');
        loading.className = 'loading-container';
        const spinner = document.createElement('div');
        spinner.className = 'spinner';
        loading.appendChild(spinner);
        container.appendChild(loading);

        try {
            // Fetch analytics and recent threats
            const [analytics, threats] = await Promise.all([
                API.getThreatAnalytics(),
                API.getThreats({ page_size: 50 }),
            ]);
            this.data = analytics;
            this.threats = threats.items || [];
            this.renderContent(container);
        } catch (error) {
            this.renderError(container, error);
        }
    },

    async renderContent(container) {
        container.textContent = '';

        // NOTE: OpenClaw plugin nudge + "What's new" card now live as app-level
        // global banners (components/global-banners.js) so they appear on every
        // page and persist across navigation. Don't add them here.
        if (false) {  // eslint-disable-line no-constant-condition
        try {
            const hooksStatus = await fetch('/api/hooks/status').then(r => r.ok ? r.json() : null).catch(() => null);
            const dismissed = localStorage.getItem('sv-openclaw-banner-dismissed') === '1';
            if (hooksStatus && hooksStatus.openclaw_detected && !hooksStatus.installed && !dismissed) {
                const banner = document.createElement('div');
                banner.className = 'sv-dash-banner';
                banner.style.cssText = 'position: relative; display: flex; align-items: center; gap: 16px; padding: 14px 44px 14px 16px; background: var(--bg-card); border: 1px solid var(--border-default); border-left: 3px solid var(--accent-primary); border-radius: 8px; margin-bottom: 16px;';

                // Icon — compact, accent-tinted
                const icon = document.createElement('div');
                icon.style.cssText = 'flex-shrink: 0; width: 36px; height: 36px; background: rgba(94,173,184,0.14); border-radius: 8px; display: flex; align-items: center; justify-content: center; font-size: 18px; line-height: 1;';
                icon.textContent = '\u26A1';
                icon.setAttribute('aria-hidden', 'true');
                banner.appendChild(icon);

                // Text column
                const textCol = document.createElement('div');
                textCol.style.cssText = 'flex: 1; min-width: 0;';

                // Title row — headline + small "RECOMMENDED" pill
                const titleRow = document.createElement('div');
                titleRow.style.cssText = 'display: flex; align-items: center; gap: 8px; margin-bottom: 3px; flex-wrap: wrap;';

                const title = document.createElement('div');
                title.style.cssText = 'font-size: 13px; font-weight: 700; color: var(--text-primary); line-height: 1.3;';
                title.textContent = 'Run SecureVector natively inside OpenClaw';
                titleRow.appendChild(title);

                const pill = document.createElement('span');
                pill.style.cssText = 'font-size: 9.5px; font-weight: 700; letter-spacing: 0.5px; color: var(--accent-primary); background: rgba(94,173,184,0.12); border: 1px solid rgba(94,173,184,0.3); padding: 2px 6px; border-radius: 4px; text-transform: uppercase;';
                pill.textContent = 'Recommended';
                titleRow.appendChild(pill);

                textCol.appendChild(titleRow);

                const desc = document.createElement('div');
                desc.style.cssText = 'font-size: 12px; color: var(--text-secondary); line-height: 1.45;';
                desc.textContent = 'Zero latency. Full audit trail. No proxy or env vars required.';
                textCol.appendChild(desc);

                banner.appendChild(textCol);

                // Primary CTA — an actual button, not a text link
                const cta = document.createElement('button');
                cta.style.cssText = 'flex-shrink: 0; font-size: 12px; font-weight: 600; color: #fff; background: var(--accent-primary); border: none; padding: 8px 14px; border-radius: 6px; cursor: pointer; white-space: nowrap; transition: opacity 0.15s, transform 0.05s;';
                cta.textContent = 'Install plugin';
                cta.addEventListener('mouseenter', () => { cta.style.opacity = '0.9'; });
                cta.addEventListener('mouseleave', () => { cta.style.opacity = '1'; });
                cta.addEventListener('mousedown', () => { cta.style.transform = 'scale(0.98)'; });
                cta.addEventListener('mouseup', () => { cta.style.transform = 'scale(1)'; });
                cta.addEventListener('click', () => {
                    if (window.Sidebar) { Sidebar.expandSection('integrations'); Sidebar.navigate('proxy-openclaw'); }
                });
                banner.appendChild(cta);

                // Dismiss — absolute-positioned in corner, visually out of the way
                const dismissBtn = document.createElement('button');
                dismissBtn.style.cssText = 'position: absolute; top: 8px; right: 10px; background: transparent; border: none; color: var(--text-muted); font-size: 16px; cursor: pointer; padding: 2px 6px; line-height: 1; border-radius: 4px; transition: color 0.15s, background 0.15s;';
                dismissBtn.title = 'Dismiss';
                dismissBtn.setAttribute('aria-label', 'Dismiss');
                dismissBtn.textContent = '\u00D7';
                dismissBtn.addEventListener('mouseenter', () => { dismissBtn.style.color = 'var(--text-primary)'; dismissBtn.style.background = 'var(--bg-secondary)'; });
                dismissBtn.addEventListener('mouseleave', () => { dismissBtn.style.color = 'var(--text-muted)'; dismissBtn.style.background = 'transparent'; });
                dismissBtn.addEventListener('click', (e) => {
                    e.stopPropagation();
                    localStorage.setItem('sv-openclaw-banner-dismissed', '1');
                    banner.remove();
                });
                banner.appendChild(dismissBtn);

                container.appendChild(banner);
            }
        } catch (e) { /* banner is non-critical */ }

        // What's New — one-time per-version announcement card
        try {
            const WHATS_NEW_VERSION = '3.4.0';
            const ackKey = 'sv-whats-new-acked';
            const ackedVersion = localStorage.getItem(ackKey);
            if (ackedVersion !== WHATS_NEW_VERSION) {
                const card = document.createElement('div');
                card.className = 'sv-dash-banner';
                card.style.cssText = 'position: relative; background: var(--bg-card); border: 1px solid var(--border-default); border-radius: 8px; padding: 16px 44px 16px 18px; margin-bottom: 16px;';

                // Header row — version tag + title
                const header = document.createElement('div');
                header.style.cssText = 'display: flex; align-items: center; gap: 10px; margin-bottom: 10px; flex-wrap: wrap;';

                const tag = document.createElement('span');
                tag.style.cssText = 'font-size: 10px; font-weight: 700; letter-spacing: 0.6px; color: var(--accent-primary); background: rgba(94,173,184,0.12); border: 1px solid rgba(94,173,184,0.3); padding: 3px 8px; border-radius: 4px; text-transform: uppercase;';
                tag.textContent = `v${WHATS_NEW_VERSION}`;
                header.appendChild(tag);

                const headerTitle = document.createElement('div');
                headerTitle.style.cssText = 'font-size: 14px; font-weight: 700; color: var(--text-primary);';
                headerTitle.textContent = 'What\u2019s new';
                header.appendChild(headerTitle);

                card.appendChild(header);

                // Feature list
                const list = document.createElement('div');
                list.style.cssText = 'display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 10px 16px;';
                const items = [
                    { icon: '\u26A1', title: 'Native OpenClaw plugin',  body: 'Zero-latency input, output, tool, and cost monitoring \u2014 no proxy required.' },
                    { icon: '\uD83D\uDEE0', title: 'Tool audit trail',   body: 'Every tool call your agent makes \u2014 allow, block, or log\u2011only \u2014 recorded automatically.' },
                    { icon: '\uD83D\uDCB0', title: 'Cost tracking updates', body: 'Refreshed cost tracking for 76 models, incl. Opus 4.7, GPT\u20115.4, Gemini 3.x, MiniMax M2.7.' },
                    { icon: '\uD83D\uDD0D', title: 'Skill Scanner + policy',   body: 'Static analysis for agent skills with trusted publishers and per-category rules.' },
                ];
                items.forEach(({ icon, title, body }) => {
                    const row = document.createElement('div');
                    row.style.cssText = 'display: flex; align-items: flex-start; gap: 10px;';
                    const ico = document.createElement('div');
                    ico.style.cssText = 'flex-shrink: 0; width: 28px; height: 28px; background: var(--bg-secondary); border-radius: 6px; display: flex; align-items: center; justify-content: center; font-size: 14px; line-height: 1;';
                    ico.textContent = icon;
                    row.appendChild(ico);
                    const col = document.createElement('div');
                    col.style.cssText = 'min-width: 0;';
                    const t = document.createElement('div');
                    t.style.cssText = 'font-size: 12.5px; font-weight: 700; color: var(--text-primary); margin-bottom: 2px; line-height: 1.3;';
                    t.textContent = title;
                    col.appendChild(t);
                    const b = document.createElement('div');
                    b.style.cssText = 'font-size: 12px; color: var(--text-secondary); line-height: 1.45;';
                    b.textContent = body;
                    col.appendChild(b);
                    row.appendChild(col);
                    list.appendChild(row);
                });
                card.appendChild(list);

                // Footer row — "Open Guide" link + dismiss
                const footer = document.createElement('div');
                footer.style.cssText = 'display: flex; align-items: center; justify-content: flex-start; gap: 14px; margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--border-default);';

                const guideLink = document.createElement('button');
                guideLink.style.cssText = 'background: transparent; border: none; color: var(--accent-primary); font-size: 12px; font-weight: 600; cursor: pointer; padding: 0;';
                guideLink.textContent = 'Open the Guide \u2192';
                guideLink.addEventListener('click', () => { if (window.Sidebar) Sidebar.navigate('guide'); });
                footer.appendChild(guideLink);

                const gotIt = document.createElement('button');
                gotIt.style.cssText = 'margin-left: auto; background: var(--accent-primary); color: #fff; border: none; font-size: 12px; font-weight: 600; padding: 7px 14px; border-radius: 6px; cursor: pointer; transition: opacity 0.15s;';
                gotIt.textContent = 'Got it';
                gotIt.addEventListener('mouseenter', () => { gotIt.style.opacity = '0.9'; });
                gotIt.addEventListener('mouseleave', () => { gotIt.style.opacity = '1'; });
                gotIt.addEventListener('click', () => {
                    localStorage.setItem(ackKey, WHATS_NEW_VERSION);
                    card.remove();
                });
                footer.appendChild(gotIt);

                card.appendChild(footer);

                // Corner dismiss (equivalent to "Got it")
                const closeBtn = document.createElement('button');
                closeBtn.style.cssText = 'position: absolute; top: 8px; right: 10px; background: transparent; border: none; color: var(--text-muted); font-size: 16px; cursor: pointer; padding: 2px 6px; line-height: 1; border-radius: 4px; transition: color 0.15s, background 0.15s;';
                closeBtn.title = 'Dismiss';
                closeBtn.setAttribute('aria-label', 'Dismiss');
                closeBtn.textContent = '\u00D7';
                closeBtn.addEventListener('mouseenter', () => { closeBtn.style.color = 'var(--text-primary)'; closeBtn.style.background = 'var(--bg-secondary)'; });
                closeBtn.addEventListener('mouseleave', () => { closeBtn.style.color = 'var(--text-muted)'; closeBtn.style.background = 'transparent'; });
                closeBtn.addEventListener('click', () => {
                    localStorage.setItem(ackKey, WHATS_NEW_VERSION);
                    card.remove();
                });
                card.appendChild(closeBtn);

                container.appendChild(card);
            }
        } catch (e) { /* whats-new is non-critical */ }
        }  // end if (false) — legacy banner code superseded by GlobalBanners

        // Budget guardian alerts — rendered first so they're impossible to miss
        try {
            const gd = await API.getBudgetGuardian();
            if (gd) {
                const hasGlobalAlert = gd.global_budget_usd != null && (gd.global_over_budget || gd.global_warning);
                const hasAgentAlerts = gd.agent_alerts && gd.agent_alerts.some(a => a.over_budget || a.warning);
                if (hasGlobalAlert || hasAgentAlerts) {
                    const alertsBox = document.createElement('div');
                    alertsBox.style.cssText = 'margin-bottom: 16px; display: flex; flex-direction: column; gap: 8px;';

                    const buildBudgetBar = (label, today, budget, pct, over, action) => {
                        const overColor = 'rgba(220,38,38,0.75)';
                        const warnColor = 'rgba(180,130,0,0.75)';
                        const color = over ? overColor : warnColor;
                        const bar = document.createElement('div');
                        bar.style.cssText = `padding: 10px 14px; border-radius: 8px; border: 1px solid ${color}; background: ${over ? 'rgba(220,38,38,0.06)' : 'rgba(180,130,0,0.06)'}; display: flex; align-items: center; gap: 12px;`;

                        const info = document.createElement('div');
                        info.style.cssText = 'flex: 1; min-width: 0;';

                        const infoTop = document.createElement('div');
                        infoTop.style.cssText = 'font-size: 13px; font-weight: 600; color: var(--text-primary); margin-bottom: 4px;';
                        infoTop.textContent = `${label}: $${today.toFixed(4)} of $${budget.toFixed(2)} today (${Math.round(pct * 100)}%)`;
                        info.appendChild(infoTop);

                        const track = document.createElement('div');
                        track.style.cssText = 'height: 5px; border-radius: 3px; background: var(--bg-tertiary); overflow: hidden;';
                        const fill = document.createElement('div');
                        fill.style.cssText = `height: 100%; border-radius: 3px; background: ${color}; width: ${Math.min(pct * 100, 100)}%;`;
                        track.appendChild(fill);
                        info.appendChild(track);
                        bar.appendChild(info);

                        const badge = document.createElement('span');
                        badge.className = over && action === 'block' ? 'badge badge-error' : 'badge badge-warning';
                        badge.textContent = over && action === 'block' ? 'Blocked' : over ? 'Over limit' : '80%+ used';
                        bar.appendChild(badge);

                        const goBtn = document.createElement('button');
                        goBtn.className = 'btn btn-secondary btn-sm';
                        goBtn.textContent = 'View →';
                        goBtn.addEventListener('click', () => { if (window.Sidebar) Sidebar.navigate('cost-settings'); });
                        bar.appendChild(goBtn);

                        return bar;
                    };

                    if (hasGlobalAlert) {
                        alertsBox.appendChild(buildBudgetBar(
                            'Global budget', gd.global_today_spend_usd,
                            gd.global_budget_usd, gd.global_pct_used,
                            gd.global_over_budget, gd.global_budget_action
                        ));
                    }
                    if (hasAgentAlerts) {
                        gd.agent_alerts.filter(a => a.over_budget || a.warning).forEach(a => {
                            alertsBox.appendChild(buildBudgetBar(
                                a.agent_id.length > 28 ? a.agent_id.slice(0, 28) + '…' : a.agent_id,
                                a.today_spend_usd, a.budget_usd, a.pct_used,
                                a.over_budget, a.budget_action
                            ));
                        });
                    }
                    container.appendChild(alertsBox);
                }
            }
        } catch (e) { /* budget alerts are non-critical */ }

        // ── Compact status bar + metrics grid ──────────────────────────────
        try {
            const valueSection = document.createElement('div');
            valueSection.style.cssText = 'margin-bottom: 18px;';

            // Fetch additional data in parallel
            const [toolsData, settings, scanHistory, costData] = await Promise.all([
                API.getEssentialTools().catch(() => null),
                API.getSettings().catch(() => null),
                fetch('/api/skill-scans/history?limit=10&offset=0').then(r => r.ok ? r.json() : null).catch(() => null),
                API.getDashboardCostSummary().catch(() => null),
            ]);

            const blockedTools = toolsData ? toolsData.tools.filter(t => t.effective_action === 'block').length : 0;
            const totalTools = toolsData ? toolsData.tools.length : 0;
            const toolEnforcement = settings && settings.tool_permissions_enabled;
            const blockMode = settings && settings.block_threats;
            const outputScan = settings && settings.scan_llm_responses;
            const skillScans = scanHistory ? (scanHistory.total || (scanHistory.records || []).length) : 0;
            const todayCost = costData ? '$' + (costData.today_cost_usd || 0).toFixed(4) : '$0.0000';

            const avgLatencyMs = this.data.avg_latency_ms;
            let latencyStr = '\u2014';
            if (avgLatencyMs != null) {
                latencyStr = avgLatencyMs >= 1000
                    ? (avgLatencyMs / 1000).toFixed(1) + 's'
                    : Math.round(avgLatencyMs) + 'ms';
            }

            // Compact status bar
            const statusBar = document.createElement('div');
            statusBar.style.cssText = 'display: flex; align-items: center; gap: 12px; padding: 10px 16px; background: var(--bg-card); border: 1px solid var(--border-default); border-radius: 8px; margin-bottom: 14px; font-size: 13px;';
            const statusDot = document.createElement('span');
            statusDot.style.cssText = 'width: 8px; height: 8px; border-radius: 50%; background: #10b981; flex-shrink: 0;';
            statusBar.appendChild(statusDot);
            const statusLabel = document.createElement('span');
            statusLabel.style.cssText = 'font-weight: 600; color: var(--text-primary);';
            statusLabel.textContent = 'Active';
            statusBar.appendChild(statusLabel);
            const statusSep = document.createElement('span');
            statusSep.style.cssText = 'color: var(--text-muted);';
            statusSep.textContent = '\u2014';
            statusBar.appendChild(statusSep);
            const statusSummary = document.createElement('span');
            statusSummary.style.cssText = 'color: var(--text-secondary);';
            const blockedCount = this.data.blocked_count || 0;
            statusSummary.textContent = `${this.data.total_threats.toLocaleString()} requests scanned, ${blockedCount} threat${blockedCount !== 1 ? 's' : ''} blocked, ${skillScans} skill${skillScans !== 1 ? 's' : ''} scanned`;
            statusBar.appendChild(statusSummary);
            valueSection.appendChild(statusBar);

            // Value metrics grid
            const metricsGrid = document.createElement('div');
            metricsGrid.style.cssText = 'display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: 10px; margin-bottom: 14px;';

            const makeMetric = (value, label, color, navPage) => {
                const card = document.createElement('div');
                card.style.cssText = 'background: var(--bg-card); border: 1px solid var(--border-default); border-radius: 8px; padding: 12px 14px; cursor: pointer; transition: border-color 0.15s, transform 0.1s;';
                card.addEventListener('mouseenter', () => { card.style.borderColor = color + '66'; card.style.transform = 'translateY(-1px)'; });
                card.addEventListener('mouseleave', () => { card.style.borderColor = 'var(--border-default)'; card.style.transform = ''; });
                if (navPage) card.addEventListener('click', () => { if (window.Sidebar) Sidebar.navigate(navPage); });

                const valEl = document.createElement('div');
                valEl.style.cssText = 'font-size: 20px; font-weight: 800; color: ' + color + '; line-height: 1.1; margin-bottom: 4px;';
                valEl.textContent = value;
                card.appendChild(valEl);

                const lblEl = document.createElement('div');
                lblEl.style.cssText = 'font-size: 11px; color: var(--text-secondary); font-weight: 500; line-height: 1.3;';
                lblEl.textContent = label;
                card.appendChild(lblEl);

                return card;
            };

            metricsGrid.appendChild(makeMetric(
                this.data.total_threats.toLocaleString(),
                'Requests scanned',
                '#5eadb8', 'threats'
            ));
            metricsGrid.appendChild(makeMetric(
                this.data.critical_count || 0,
                'Critical threats',
                this.data.critical_count > 0 ? '#ef4444' : '#10b981', 'threats'
            ));
            metricsGrid.appendChild(makeMetric(
                this.data.blocked_count || 0,
                'Threats blocked',
                this.data.blocked_count > 0 ? '#ef4444' : '#10b981', 'threats'
            ));
            metricsGrid.appendChild(makeMetric(
                blockedTools + '/' + totalTools,
                'Risky tools blocked',
                blockedTools > 0 ? '#f59e0b' : '#94a3b8', 'tool-permissions'
            ));
            metricsGrid.appendChild(makeMetric(
                skillScans,
                'Skills scanned',
                '#5eadb8', 'skill-scanner'
            ));
            metricsGrid.appendChild(makeMetric(
                latencyStr,
                'Avg analysis time',
                '#8b5cf6', null
            ));
            metricsGrid.appendChild(makeMetric(
                todayCost,
                "Today's cost",
                '#f59e0b', 'costs'
            ));

            valueSection.appendChild(metricsGrid);

            container.appendChild(valueSection);
        } catch (e) { /* value section is non-critical */ }

        // "What's New" feature discovery strip — shown until dismissed
        if (!localStorage.getItem('sv-newfeatures-dismissed')) {
            const strip = document.createElement('div');
            strip.style.cssText = 'margin-bottom: 16px;';

            const stripHeader = document.createElement('div');
            stripHeader.style.cssText = 'display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px;';

            const stripTitle = document.createElement('div');
            stripTitle.style.cssText = 'display: flex; align-items: center; gap: 6px; font-size: 11px; font-weight: 700; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.5px;';
            const pulseDot = document.createElement('span');
            pulseDot.style.cssText = 'display: inline-block; width: 6px; height: 6px; border-radius: 50%; background: #f97316; flex-shrink: 0;';
            stripTitle.appendChild(pulseDot);
            stripTitle.appendChild(document.createTextNode("What's New"));
            stripHeader.appendChild(stripTitle);

            const dismissBtn = document.createElement('button');
            dismissBtn.style.cssText = 'display: flex; align-items: center; gap: 4px; padding: 2px 10px; border-radius: var(--radius-full); font-size: 11px; font-weight: 600; border: 1px solid var(--border-default); background: var(--bg-secondary); color: var(--text-secondary); cursor: pointer; transition: all 0.15s;';
            dismissBtn.textContent = '✕ Dismiss';
            dismissBtn.title = 'Dismiss';
            dismissBtn.addEventListener('mouseenter', () => { dismissBtn.style.borderColor = '#ef4444'; dismissBtn.style.color = '#ef4444'; });
            dismissBtn.addEventListener('mouseleave', () => { dismissBtn.style.borderColor = 'var(--border-default)'; dismissBtn.style.color = 'var(--text-secondary)'; });
            dismissBtn.addEventListener('click', () => {
                localStorage.setItem('sv-newfeatures-dismissed', '1');
                strip.remove();
            });
            stripHeader.appendChild(dismissBtn);
            strip.appendChild(stripHeader);

            const featureCards = document.createElement('div');
            featureCards.style.cssText = 'display: grid; grid-template-columns: 1fr 1fr; gap: 10px;';

            const makeFeatureCard = (title, desc, page) => {
                const card = document.createElement('div');
                card.style.cssText = 'background: var(--bg-card); border: 1px solid rgba(94,173,184,0.22); border-radius: 8px; padding: 14px; cursor: pointer; transition: border-color 0.15s;';
                card.addEventListener('mouseenter', () => card.style.borderColor = 'rgba(94,173,184,0.5)');
                card.addEventListener('mouseleave', () => card.style.borderColor = 'rgba(94,173,184,0.22)');

                const badge = document.createElement('div');
                badge.style.cssText = 'display: inline-block; font-size: 9px; font-weight: 700; background: rgba(249,115,22,0.12); color: #f97316; border-radius: 3px; padding: 1px 6px; margin-bottom: 8px; letter-spacing: 0.4px; text-transform: uppercase;';
                badge.textContent = 'New';
                card.appendChild(badge);

                const cardTitle = document.createElement('div');
                cardTitle.style.cssText = 'font-size: 13px; font-weight: 600; color: var(--text-primary); margin-bottom: 5px;';
                cardTitle.textContent = title;
                card.appendChild(cardTitle);

                const cardDesc = document.createElement('div');
                cardDesc.style.cssText = 'font-size: 12px; color: var(--text-secondary); line-height: 1.5; margin-bottom: 10px;';
                cardDesc.textContent = desc;
                card.appendChild(cardDesc);

                const link = document.createElement('span');
                link.style.cssText = 'font-size: 11px; font-weight: 600; color: var(--accent-primary);';
                link.textContent = 'Set up →';
                card.appendChild(link);

                card.addEventListener('click', () => { if (window.Sidebar) Sidebar.navigate(page); });
                return card;
            };

            featureCards.appendChild(makeFeatureCard(
                'Tool Permissions',
                'Control exactly which tools your agent is allowed to call. Block risky file, shell, or network operations before they run.',
                'tool-permissions'
            ));
            featureCards.appendChild(makeFeatureCard(
                'Cost Tracking',
                'Track every dollar your agents spend per model and session. Set daily budgets to hard-stop runaway LLM costs.',
                'cost-settings'
            ));

            strip.appendChild(featureCards);
            container.appendChild(strip);
        }

        // 1. Security Controls — most actionable, show first
        const securityControls = await this.renderSecurityControls();
        container.appendChild(securityControls);

        // Charts row — threat trend + cost trend side by side
        const chartsRow = document.createElement('div');
        chartsRow.style.cssText = 'display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-bottom: 16px;';

        const trendCard = Card.create({ title: 'LLM Requests — Last 7 Days', gradient: true });
        this.renderTrendChart(trendCard.querySelector('.card-body'));
        chartsRow.appendChild(trendCard);

        const costTrendCard = Card.create({ title: 'Provider Cost — Last 7 Days', gradient: true });
        await this.renderCostTrendChart(costTrendCard.querySelector('.card-body'));
        chartsRow.appendChild(costTrendCard);

        container.appendChild(chartsRow);

        // 4. Recent activity
        const activityCard = Card.create({ title: 'Recent Threat Activity', gradient: true });
        this.renderRecentActivity(activityCard.querySelector('.card-body'));
        container.appendChild(activityCard);
    },

    createStatCard(stat) {
        const card = document.createElement('div');
        card.className = 'stat-card stat-' + (stat.color || 'primary');
        if (stat.tooltip) {
            card.style.cursor = 'help';
            card.title = stat.tooltip;
        }

        const iconWrap = document.createElement('div');
        iconWrap.className = 'stat-icon';
        iconWrap.appendChild(this.createIcon(stat.icon));
        card.appendChild(iconWrap);

        const content = document.createElement('div');
        content.className = 'stat-content';

        const value = document.createElement('div');
        value.className = 'stat-value';
        value.textContent = stat.raw ? stat.value : stat.value + (stat.suffix || '');
        content.appendChild(value);

        const labelRow = document.createElement('div');
        labelRow.style.cssText = 'display: flex; align-items: center; gap: 4px;';

        const label = document.createElement('div');
        label.className = 'stat-label';
        label.textContent = stat.label;
        labelRow.appendChild(label);

        if (stat.tooltip) {
            const hint = document.createElement('span');
            hint.style.cssText = 'font-size: 10px; color: var(--text-muted); line-height: 1; flex-shrink: 0;';
            hint.textContent = 'ⓘ';
            labelRow.appendChild(hint);
        }

        content.appendChild(labelRow);
        card.appendChild(content);
        return card;
    },

    createIcon(name) {
        const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
        svg.setAttribute('viewBox', '0 0 24 24');
        svg.setAttribute('fill', 'none');
        svg.setAttribute('stroke', 'currentColor');
        svg.setAttribute('stroke-width', '2');

        const paths = {
            shield: 'M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z',
            alert: 'M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0zM12 9v4M12 17h.01',
            activity: 'M22 12h-4l-3 9L9 3l-3 9H2',
            gauge: 'M12 2a10 10 0 1 0 10 10H12V2zM12 12l6-6',
            check: 'M22 11.08V12a10 10 0 1 1-5.93-9.14M22 4L12 14.01l-3-3',
            clock: 'M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zM12 6v6l4 2',
        };

        const pathData = paths[name] || paths.shield;
        const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
        path.setAttribute('d', pathData);
        svg.appendChild(path);

        return svg;
    },

    getAverageRiskScore() {
        if (!this.threats || this.threats.length === 0) return 0;
        const total = this.threats.reduce((sum, t) => sum + (t.risk_score || 0), 0);
        return Math.round(total / this.threats.length);
    },

    getAverageLatency() {
        if (!this.threats || this.threats.length === 0) return 0;
        const total = this.threats.reduce((sum, t) => sum + (t.processing_time_ms || 0), 0);
        return Math.round(total / this.threats.length);
    },

    getRiskColor(score) {
        if (score >= 80) return 'danger';
        if (score >= 60) return 'warning';
        if (score >= 40) return 'info';
        return 'success';
    },

    renderRiskDistribution(container) {
        // Group threats by risk level
        const levels = { critical: 0, high: 0, medium: 0, low: 0 };

        this.threats.forEach(t => {
            const score = t.risk_score || 0;
            if (score >= 80) levels.critical++;
            else if (score >= 60) levels.high++;
            else if (score >= 40) levels.medium++;
            else levels.low++;
        });

        const total = Object.values(levels).reduce((a, b) => a + b, 0);

        if (total === 0) {
            const empty = document.createElement('p');
            empty.className = 'empty-state-inline';
            empty.textContent = 'No threat data yet';
            container.appendChild(empty);
            return;
        }

        const chart = document.createElement('div');
        chart.className = 'risk-donut-chart';

        // Donut chart visualization
        const donut = document.createElement('div');
        donut.className = 'donut-container';

        const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
        svg.setAttribute('viewBox', '0 0 100 100');
        svg.setAttribute('class', 'donut-svg');

        let currentAngle = -90;
        const colors = { critical: '#ef4444', high: '#f97316', medium: '#f59e0b', low: '#60a5fa' };
        const radius = 40;
        const cx = 50, cy = 50;

        Object.entries(levels).forEach(([level, count]) => {
            if (count === 0) return;

            const angle = (count / total) * 360;
            const startAngle = currentAngle;
            const endAngle = currentAngle + angle;

            const x1 = cx + radius * Math.cos((startAngle * Math.PI) / 180);
            const y1 = cy + radius * Math.sin((startAngle * Math.PI) / 180);
            const x2 = cx + radius * Math.cos((endAngle * Math.PI) / 180);
            const y2 = cy + radius * Math.sin((endAngle * Math.PI) / 180);

            const largeArc = angle > 180 ? 1 : 0;

            const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
            path.setAttribute('d', 'M ' + cx + ' ' + cy + ' L ' + x1 + ' ' + y1 + ' A ' + radius + ' ' + radius + ' 0 ' + largeArc + ' 1 ' + x2 + ' ' + y2 + ' Z');
            path.setAttribute('fill', colors[level]);
            path.setAttribute('class', 'donut-segment');
            svg.appendChild(path);

            currentAngle = endAngle;
        });

        // Center hole
        const hole = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
        hole.setAttribute('cx', '50');
        hole.setAttribute('cy', '50');
        hole.setAttribute('r', '25');
        hole.setAttribute('fill', 'var(--bg-secondary)');
        svg.appendChild(hole);

        // Center text
        const text = document.createElementNS('http://www.w3.org/2000/svg', 'text');
        text.setAttribute('x', '50');
        text.setAttribute('y', '53');
        text.setAttribute('text-anchor', 'middle');
        text.setAttribute('fill', 'var(--text-primary)');
        text.setAttribute('font-size', '14');
        text.setAttribute('font-weight', '600');
        text.textContent = total;
        svg.appendChild(text);

        donut.appendChild(svg);
        chart.appendChild(donut);

        // Legend
        const legend = document.createElement('div');
        legend.className = 'chart-legend';

        Object.entries(levels).forEach(([level, count]) => {
            const item = document.createElement('div');
            item.className = 'legend-item';

            const dot = document.createElement('span');
            dot.className = 'legend-dot';
            dot.style.background = colors[level];
            item.appendChild(dot);

            const label = document.createElement('span');
            label.className = 'legend-label';
            label.textContent = level.charAt(0).toUpperCase() + level.slice(1);
            item.appendChild(label);

            const value = document.createElement('span');
            value.className = 'legend-value';
            value.textContent = count;
            item.appendChild(value);

            legend.appendChild(item);
        });

        chart.appendChild(legend);
        container.appendChild(chart);
    },

    renderThreatTypes(container) {
        const types = this.data.threat_types || {};

        if (Object.keys(types).length === 0) {
            const empty = document.createElement('p');
            empty.className = 'empty-state-inline';
            empty.textContent = 'No threat categories yet';
            container.appendChild(empty);
            return;
        }

        const entries = Object.entries(types).sort((a, b) => b[1] - a[1]);
        const maxCount = Math.max(...entries.map(e => e[1]));

        const chart = document.createElement('div');
        chart.className = 'horizontal-bar-chart';

        entries.slice(0, 5).forEach(([type, count], index) => {
            const row = document.createElement('div');
            row.className = 'bar-row';

            const label = document.createElement('div');
            label.className = 'bar-label';
            label.textContent = this.formatType(type);
            row.appendChild(label);

            const barWrap = document.createElement('div');
            barWrap.className = 'bar-wrap';

            const bar = document.createElement('div');
            bar.className = 'bar bar-' + (index % 4);
            const percentage = maxCount > 0 ? (count / maxCount) * 100 : 0;
            bar.style.width = '0%';
            // Animate bar
            setTimeout(() => {
                bar.style.width = percentage + '%';
            }, 100 + index * 50);
            barWrap.appendChild(bar);

            const countEl = document.createElement('span');
            countEl.className = 'bar-count';
            countEl.textContent = count;
            barWrap.appendChild(countEl);

            row.appendChild(barWrap);
            chart.appendChild(row);
        });

        container.appendChild(chart);
    },

    renderTrendChart(container) {
        // Build last-7-days buckets from loaded threats
        const days = 7;
        const buckets = [];
        const now = new Date();
        // Use local dates so chart labels match timestamps shown in the UI
        const toLocalDateStr = ts => {
            const d = new Date(ts.includes('T') ? ts : ts.replace(' ', 'T') + 'Z');
            return d.getFullYear() + '-' + String(d.getMonth()+1).padStart(2,'0') + '-' + String(d.getDate()).padStart(2,'0');
        };
        for (let i = days - 1; i >= 0; i--) {
            const d = new Date();
            d.setDate(d.getDate() - i);
            const dateStr = d.getFullYear() + '-' + String(d.getMonth()+1).padStart(2,'0') + '-' + String(d.getDate()).padStart(2,'0');
            buckets.push({
                label: (d.getMonth()+1).toString().padStart(2,'0') + '/' + d.getDate().toString().padStart(2,'0'),
                dateStr,
                total: 0,
                threats: 0,
            });
        }

        (this.threats || []).forEach(t => {
            const dateStr = toLocalDateStr(t.created_at || new Date().toISOString());
            const bucket = buckets.find(b => b.dateStr === dateStr);
            if (bucket) {
                bucket.total++;
                if ((t.risk_score || 0) >= 60) bucket.threats++;
            }
        });

        const maxVal = Math.max(...buckets.map(b => b.total), 1);

        // Fixed-height chart: value(12px) + bar(flex) + day label(16px)
        const wrap = document.createElement('div');
        wrap.style.cssText = 'display: flex; align-items: stretch; gap: 5px; height: 110px;';

        buckets.forEach(bucket => {
            const col = document.createElement('div');
            col.style.cssText = 'flex: 1; display: flex; flex-direction: column; align-items: center; min-width: 0;';

            // Value label (top, fixed height)
            const valLbl = document.createElement('div');
            valLbl.style.cssText = 'height: 14px; font-size: 9px; color: var(--text-secondary); text-align: center; line-height: 14px; white-space: nowrap;';
            valLbl.textContent = bucket.total > 0 ? bucket.total : '';
            col.appendChild(valLbl);

            // Bar area (grows to fill)
            const barArea = document.createElement('div');
            barArea.style.cssText = 'flex: 1; width: 100%; position: relative;';
            barArea.title = `${bucket.dateStr}: ${bucket.total} requests, ${bucket.threats} threats`;

            const pct = (bucket.total / maxVal) * 100;
            const barTotal = document.createElement('div');
            barTotal.style.cssText = `position: absolute; bottom: 0; left: 0; right: 0; height: ${pct}%; background: var(--accent-primary); opacity: 0.3; border-radius: 3px 3px 0 0; min-height: ${bucket.total > 0 ? 3 : 0}px;`;
            barArea.appendChild(barTotal);

            if (bucket.threats > 0) {
                const threatPct = (bucket.threats / maxVal) * 100;
                const barThreat = document.createElement('div');
                barThreat.style.cssText = `position: absolute; bottom: 0; left: 0; right: 0; height: ${threatPct}%; background: #ef4444; opacity: 0.7; border-radius: 3px 3px 0 0; min-height: 3px;`;
                barArea.appendChild(barThreat);
            }
            col.appendChild(barArea);

            // Day label (bottom, fixed height)
            const lbl = document.createElement('div');
            lbl.style.cssText = 'height: 16px; font-size: 10px; color: var(--text-secondary); text-align: center; line-height: 16px; white-space: nowrap;';
            lbl.textContent = bucket.label;
            col.appendChild(lbl);

            wrap.appendChild(col);
        });

        container.appendChild(wrap);

        // Legend
        const legend = document.createElement('div');
        legend.style.cssText = 'display: flex; gap: 12px; margin-top: 4px; font-size: 11px; color: var(--text-secondary);';
        [['var(--accent-primary)', 'Requests'], ['#ef4444', 'Threats (risk ≥60%)']]
            .forEach(([color, label]) => {
                const item = document.createElement('span');
                item.style.cssText = 'display: flex; align-items: center; gap: 4px;';
                const dot = document.createElement('span');
                dot.style.cssText = `width: 8px; height: 8px; border-radius: 2px; background: ${color}; opacity: 0.75; flex-shrink: 0;`;
                item.appendChild(dot);
                item.appendChild(document.createTextNode(label));
                legend.appendChild(item);
            });
        container.appendChild(legend);
    },

    async renderCostTrendChart(container) {
        // Fetch last 7 days of cost records
        const days = 7;
        const buckets = [];
        const now = new Date();
        const toLocalDateStr2 = ts => {
            const d = new Date(ts.includes('T') ? ts : ts.replace(' ', 'T') + 'Z');
            return d.getFullYear() + '-' + String(d.getMonth()+1).padStart(2,'0') + '-' + String(d.getDate()).padStart(2,'0');
        };
        for (let i = days - 1; i >= 0; i--) {
            const d = new Date();
            d.setDate(d.getDate() - i);
            const dateStr = d.getFullYear() + '-' + String(d.getMonth()+1).padStart(2,'0') + '-' + String(d.getDate()).padStart(2,'0');
            buckets.push({ label: (d.getMonth()+1).toString().padStart(2,'0') + '/' + d.getDate().toString().padStart(2,'0'), dateStr, cost: 0 });
        }

        try {
            const start = new Date(now);
            start.setDate(start.getDate() - 7);
            const records = await API.getCostRecords({ start: start.toISOString(), page_size: 200 });
            (records.items || []).forEach(r => {
                const dateStr = toLocalDateStr2(r.recorded_at || new Date().toISOString());
                const bucket = buckets.find(b => b.dateStr === dateStr);
                if (bucket) bucket.cost += r.total_cost_usd || 0;
            });
        } catch (e) {}

        const maxVal = Math.max(...buckets.map(b => b.cost), 0.000001);

        const wrap = document.createElement('div');
        wrap.style.cssText = 'display: flex; align-items: stretch; gap: 5px; height: 110px;';

        buckets.forEach(bucket => {
            const col = document.createElement('div');
            col.style.cssText = 'flex: 1; display: flex; flex-direction: column; align-items: center; min-width: 0;';

            // Value label (top, fixed height)
            const valLbl = document.createElement('div');
            valLbl.style.cssText = 'height: 14px; font-size: 9px; color: var(--text-secondary); text-align: center; line-height: 14px; white-space: nowrap;';
            valLbl.textContent = bucket.cost > 0 ? '$' + bucket.cost.toFixed(2) : '';
            col.appendChild(valLbl);

            // Bar area (grows)
            const barArea = document.createElement('div');
            barArea.style.cssText = 'flex: 1; width: 100%; position: relative;';
            barArea.title = `${bucket.dateStr}: $${bucket.cost.toFixed(4)}`;

            const pct = (bucket.cost / maxVal) * 100;
            const bar = document.createElement('div');
            bar.style.cssText = `position: absolute; bottom: 0; left: 0; right: 0; height: ${pct}%; background: #10b981; opacity: 0.6; border-radius: 3px 3px 0 0; min-height: ${bucket.cost > 0 ? 3 : 0}px;`;
            barArea.appendChild(bar);
            col.appendChild(barArea);

            // Day label (bottom, fixed height)
            const lbl = document.createElement('div');
            lbl.style.cssText = 'height: 16px; font-size: 10px; color: var(--text-secondary); text-align: center; line-height: 16px; white-space: nowrap;';
            lbl.textContent = bucket.label;
            col.appendChild(lbl);

            wrap.appendChild(col);
        });

        container.appendChild(wrap);

        const legend = document.createElement('div');
        legend.style.cssText = 'display: flex; gap: 12px; margin-top: 4px; font-size: 11px; color: var(--text-secondary);';
        const item = document.createElement('span');
        item.style.cssText = 'display: flex; align-items: center; gap: 4px;';
        const dot = document.createElement('span');
        dot.style.cssText = 'width: 8px; height: 8px; border-radius: 2px; background: #10b981; opacity: 0.75; flex-shrink: 0;';
        item.appendChild(dot);
        item.appendChild(document.createTextNode('Daily spend (USD)'));
        legend.appendChild(item);
        container.appendChild(legend);
    },

    renderRecentActivity(container) {
        const threats = this.threats.slice(0, 8);

        if (threats.length === 0) {
            const empty = document.createElement('div');
            empty.className = 'empty-state-inline';

            const emptyText = document.createElement('p');
            emptyText.textContent = 'No recent activity';
            empty.appendChild(emptyText);

            const emptySubtext = document.createElement('p');
            emptySubtext.className = 'empty-subtext';
            emptySubtext.textContent = 'Threats will appear here when detected';
            empty.appendChild(emptySubtext);

            container.appendChild(empty);
            return;
        }

        const table = document.createElement('div');
        table.className = 'activity-table';

        // Header
        const header = document.createElement('div');
        header.className = 'activity-header';

        const cols = ['Content', 'Type', 'Risk', 'Time'];
        cols.forEach(col => {
            const cell = document.createElement('div');
            cell.className = 'activity-cell';
            cell.textContent = col;
            header.appendChild(cell);
        });
        table.appendChild(header);

        // Rows
        threats.forEach(threat => {
            const row = document.createElement('div');
            row.className = 'activity-row';

            // Content preview
            const contentCell = document.createElement('div');
            contentCell.className = 'activity-cell content-cell';
            const content = threat.text_preview || threat.text_content || threat.indicator || threat.name || 'Analyzed content';
            contentCell.textContent = content.length > 50 ? content.substring(0, 50) + '...' : content;
            contentCell.title = content;
            row.appendChild(contentCell);

            // Type
            const typeCell = document.createElement('div');
            typeCell.className = 'activity-cell';
            const typeBadge = document.createElement('span');
            typeBadge.className = 'type-badge-small';
            typeBadge.textContent = this.formatType(threat.threat_type || 'detected');
            typeCell.appendChild(typeBadge);
            row.appendChild(typeCell);

            // Risk
            const riskCell = document.createElement('div');
            riskCell.className = 'activity-cell';
            const riskBadge = document.createElement('span');
            riskBadge.className = 'risk-badge risk-' + this.getRiskLevel(threat.risk_score);
            riskBadge.textContent = (threat.risk_score || 0) + '%';
            riskCell.appendChild(riskBadge);
            row.appendChild(riskCell);

            // Time
            const timeCell = document.createElement('div');
            timeCell.className = 'activity-cell time-cell';
            timeCell.textContent = this.formatTime(threat.created_at || threat.first_seen);
            row.appendChild(timeCell);

            row.addEventListener('click', () => {
                if (window.Sidebar) Sidebar.navigate('threats');
            });

            table.appendChild(row);
        });

        container.appendChild(table);
    },

    formatType(type) {
        if (!type || type === 'unknown') return 'Detected';
        return type
            .split('_')
            .map(word => word.charAt(0).toUpperCase() + word.slice(1))
            .join(' ');
    },

    formatTime(dateStr) {
        if (!dateStr) return '-';
        try {
            const date = new Date(dateStr);
            const now = new Date();
            const diffMs = now - date;
            const diffMins = Math.floor(diffMs / 60000);

            if (diffMins < 1) return 'Just now';
            if (diffMins < 60) return diffMins + 'm ago';
            if (diffMins < 1440) return Math.floor(diffMins / 60) + 'h ago';
            return Math.floor(diffMins / 1440) + 'd ago';
        } catch (e) {
            return '-';
        }
    },

    getRiskLevel(score) {
        if (score >= 80) return 'critical';
        if (score >= 60) return 'high';
        if (score >= 40) return 'medium';
        return 'low';
    },

    toggleAutoRefresh() {
        this.autoRefreshEnabled = !this.autoRefreshEnabled;
        if (this.autoRefreshEnabled) {
            this.autoRefreshInterval = setInterval(() => {
                if (this.currentContainer) {
                    this.render(this.currentContainer);
                }
            }, getPollInterval());
            const _sec = Math.round(getPollInterval() / 1000);
            if (window.Toast) Toast.info(`Auto refresh enabled (${_sec}s)`);
        } else {
            if (this.autoRefreshInterval) {
                clearInterval(this.autoRefreshInterval);
                this.autoRefreshInterval = null;
            }
            if (window.Toast) Toast.info('Auto refresh disabled');
        }
    },

    async renderCostWidget() {
        const widget = document.createElement('div');
        widget.style.cssText = 'background: var(--bg-card); border: 1px solid var(--border-color); border-radius: 8px; padding: 16px; margin-bottom: 20px; cursor: pointer;';
        widget.title = 'Click to open Cost Tracking';
        widget.addEventListener('click', () => { if (window.Sidebar) Sidebar.navigate('costs'); });

        const header = document.createElement('div');
        header.style.cssText = 'display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px;';

        const titleEl = document.createElement('div');
        titleEl.style.cssText = 'font-size: 14px; font-weight: 600; color: var(--text-primary);';
        titleEl.textContent = '💰 Cost Tracking';
        header.appendChild(titleEl);

        const viewLink = document.createElement('span');
        viewLink.style.cssText = 'font-size: 12px; color: var(--accent-primary); cursor: pointer;';
        viewLink.textContent = 'View all →';
        header.appendChild(viewLink);

        widget.appendChild(header);

        try {
            const summary = await API.getDashboardCostSummary();

            const grid = document.createElement('div');
            grid.style.cssText = 'display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 12px;';

            const items = [
                { label: "Today's Cost", value: `$${(summary.today_cost_usd || 0).toFixed(4)}` },
                { label: "Today's Requests", value: (summary.today_requests || 0).toLocaleString() },
                { label: 'Top Agent', value: summary.top_agent || '—' },
                { label: 'Top Model', value: summary.top_model || '—' },
            ];

            items.forEach(({ label, value }) => {
                const cell = document.createElement('div');
                cell.style.cssText = 'text-align: center;';
                const v = document.createElement('div');
                v.style.cssText = 'font-size: 18px; font-weight: 700; color: var(--text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;';
                v.textContent = value;
                v.title = value;
                const l = document.createElement('div');
                l.style.cssText = 'font-size: 11px; color: var(--text-secondary); margin-top: 2px;';
                l.textContent = label;
                cell.appendChild(v);
                cell.appendChild(l);
                grid.appendChild(cell);
            });

            widget.appendChild(grid);

            if (summary.has_unknown_pricing) {
                const warn = document.createElement('div');
                warn.style.cssText = 'margin-top: 10px; font-size: 11px; color: var(--color-warning, #f59e0b);';
                warn.textContent = '⚠ Some models have unknown pricing — costs may be understated.';
                widget.appendChild(warn);
            }
        } catch (e) {
            const err = document.createElement('div');
            err.style.cssText = 'font-size: 13px; color: var(--text-secondary);';
            err.textContent = 'Cost data unavailable.';
            widget.appendChild(err);
        }

        return widget;
    },

    async renderSecurityControls() {
        const section = document.createElement('div');
        section.className = 'security-controls-section';
        section.style.cssText = 'display: flex; gap: 16px; margin-bottom: 24px;';

        // Fetch current settings
        let settings = { block_threats: false, scan_llm_responses: true };
        try {
            settings = await API.getSettings();
        } catch (e) {}

        // Block Mode Card
        const blockCard = document.createElement('div');
        blockCard.className = 'security-control-card';
        blockCard.style.cssText = 'flex: 1; background: var(--bg-card); border: 1px solid var(--border-default); border-radius: 12px; padding: 20px; display: flex; justify-content: space-between; align-items: center;';
        if (!settings.block_threats) blockCard.classList.add('flashing-border');

        const blockInfo = document.createElement('div');
        const blockTitle = document.createElement('div');
        blockTitle.style.cssText = 'font-weight: 600; font-size: 15px; margin-bottom: 4px;';
        blockTitle.textContent = 'Block Mode';
        blockInfo.appendChild(blockTitle);
        const blockDesc = document.createElement('div');
        blockDesc.style.cssText = 'color: var(--text-secondary); font-size: 13px;';
        blockDesc.textContent = 'Block threats on input and output';
        blockInfo.appendChild(blockDesc);
        blockCard.appendChild(blockInfo);

        const blockToggle = document.createElement('label');
        blockToggle.className = 'toggle';
        const blockCheckbox = document.createElement('input');
        blockCheckbox.type = 'checkbox';
        blockCheckbox.checked = settings.block_threats;
        blockCheckbox.addEventListener('change', async (e) => {
            const newState = e.target.checked;
            if (!confirm(newState ? 'Enable Block Mode?\n\nInput threats will be BLOCKED before reaching the LLM.\nOutput threats will be BLOCKED before reaching the client.' : 'Disable Block Mode?\n\nAll threats will be logged only.')) {
                e.target.checked = !newState;
                return;
            }
            // Show modal immediately
            if (newState) {
                showOpenClawProxyModal();
            } else {
                showOpenClawProxyStopModal();
            }

            // Save settings in background
            API.updateSettings({ block_threats: newState }).then(() => {
                Toast.success(newState ? 'Block mode enabled' : 'Block mode disabled');
            }).catch(() => {
                Toast.error('Failed to update');
                e.target.checked = !newState;
            });
        });
        blockToggle.appendChild(blockCheckbox);
        const blockSlider = document.createElement('span');
        blockSlider.className = 'toggle-slider';
        blockToggle.appendChild(blockSlider);
        blockCard.appendChild(blockToggle);
        section.appendChild(blockCard);

        // Output Scan Card
        const outputCard = document.createElement('div');
        outputCard.className = 'security-control-card';
        outputCard.style.cssText = 'flex: 1; background: var(--bg-card); border: 1px solid var(--border-default); border-radius: 12px; padding: 20px; display: flex; justify-content: space-between; align-items: center;';
        if (!settings.scan_llm_responses) outputCard.classList.add('flashing-border');

        const outputInfo = document.createElement('div');
        const outputTitle = document.createElement('div');
        outputTitle.style.cssText = 'font-weight: 600; font-size: 15px; margin-bottom: 4px;';
        outputTitle.textContent = 'Output Scan (Redact Sensitive Info)';
        outputInfo.appendChild(outputTitle);
        const outputDesc = document.createElement('div');
        outputDesc.style.cssText = 'color: var(--text-secondary); font-size: 13px;';
        outputDesc.textContent = 'Scan LLM responses, redact secrets when stored';
        outputInfo.appendChild(outputDesc);
        outputCard.appendChild(outputInfo);

        const outputToggle = document.createElement('label');
        outputToggle.className = 'toggle';
        const outputCheckbox = document.createElement('input');
        outputCheckbox.type = 'checkbox';
        outputCheckbox.checked = settings.scan_llm_responses;
        outputCheckbox.addEventListener('change', async (e) => {
            const newState = e.target.checked;
            if (!confirm(newState ? 'Enable Output Scan?\n\nLLM responses will be scanned.' : 'Disable Output Scan?\n\nResponses will not be monitored.')) {
                e.target.checked = !newState;
                return;
            }
            try {
                await API.updateSettings({ scan_llm_responses: newState });
                Toast.success(newState ? 'Output scan enabled' : 'Output scan disabled');
            } catch (err) {
                Toast.error('Failed to update');
                e.target.checked = !newState;
            }
        });
        outputToggle.appendChild(outputCheckbox);
        const outputSlider = document.createElement('span');
        outputSlider.className = 'toggle-slider';
        outputToggle.appendChild(outputSlider);
        outputCard.appendChild(outputToggle);
        section.appendChild(outputCard);

        return section;
    },

    renderError(container, error) {
        container.textContent = '';

        const errorDiv = document.createElement('div');
        errorDiv.className = 'error-state';

        const icon = document.createElement('div');
        icon.className = 'error-icon';
        icon.textContent = '!';
        errorDiv.appendChild(icon);

        const message = document.createElement('p');
        message.textContent = 'Failed to load dashboard data';
        errorDiv.appendChild(message);

        const retry = document.createElement('button');
        retry.className = 'btn btn-primary';
        retry.textContent = 'Retry';
        retry.addEventListener('click', () => this.render(container));
        errorDiv.appendChild(retry);

        container.appendChild(errorDiv);
    },
};

window.DashboardPage = DashboardPage;
