Instruction: Create an AUTARCH web template for the 'Mitm Proxy' page. It should extend base.html, have a page header, and use AUTARCH's CSS variables and UI patterns (sections, tab bars, data tables, buttons).

Input:
Template: mitm_proxy.html
Sections: 7
Tabs: 2
Forms/API calls: 0
Has JavaScript: True

Output:
{% extends "base.html" %}
{% block title %}MITM Proxy - AUTARCH{% endblock %}

{% block content %}
<div class="page-header" style="display:flex;align-items:center;gap:1rem;flex-wrap:wrap">
    <div>
        <h1>MITM Proxy</h1>
        <p style="margin:0;font-size:0.85rem;color:var(--text-secondary)">
            HTTP/HTTPS interception proxy with traffic analysis, rule engine, and secret detection.
        </p>
    </div>
    <a href="{{ url_for('offense.index') }}" class="btn btn-sm" style="margin-left:auto">&larr; Offense</a>
</div>

<!-- Tab Bar -->
<div class="tab-bar">
    <button class="tab active" data-tab-group="mitm" data-tab="proxy" onclick="showTab('mitm','proxy')">Proxy</button>
    <button class="tab" data-tab-group="mitm" data-tab="rules" onclick="showTab('mitm','rules')">Rules</button>
    <button class="tab" data-tab-group="mitm" data-tab="traffic" onclick="showTab('mitm','traffic');mitmLoadTraffic()">Traffic Log</button>
</div>

<!-- ==================== PROXY TAB ==================== -->
<div class="tab-content active" data-tab-group="mitm" data-tab="proxy">

<!-- Status Card -->
<div class="section">
    <h2>Proxy Status</h2>
    <table class="data-table" style="max-width:500px">
        <tbody>
            <tr><td>State</td><td><strong id="mitm-state">--</strong></td></tr>
            <tr><td>Listen Address</td><td id="mitm-addr">--</td></tr>
            <tr><td>Engine</td><td id="mitm-engine">--</td></tr>
            <tr><td>Request Count</td><td id="mitm-reqcount">0</td></tr>
            <tr><td>SSL Strip</td><td id="mitm-sslstrip">OFF</td></tr>
            <tr><td>Upstream Proxy</td><td id="mitm-upstream">None</td></tr>
        </tbody>
    </table>
    <div class="tool-actions" style="margin-top:12px">
        <button id="btn-mitm-start" class="btn btn-primary" onclick="mitmStart()">Start Proxy</button>
        <button id="btn-mitm-stop" class="btn btn-danger" onclick="mitmStop()" disabled>Stop Proxy</button>
        <button id="btn-mitm-refresh" class="btn btn-sm" onclick="mitmStatus()" style="margin-left:8px">Refresh</button>
    </div>
</div>

<!-- Configuration -->
<div class="section">
    <h2>Configuration</h2>
    <div style="display:flex;gap:16px;flex-wrap:wrap;align-items:flex-end">
        <div class="form-group" style="min-width:160px">
            <label>Listen Host</label>
            <input type="text" id="mitm-host" class="form-control" value="127.0.0.1" placeholder="127.0.0.1">
        </div>
        <div class="form-group" style="min-width:100px">
            <label>Listen Port</label>
            <input type="number" id="mitm-port" class="form-control" value="8888" min="1" max="65535">
        </div>
        <div class="form-group" style="min-width:220px">
            <label>Upstream Proxy <span style="color:var(--text-muted)">(optional)</span></label>
            <input type="text" id="mitm-upstream-input" class="form-control" placeholder="host:port">
        </div>
    </div>
    <div style="margin-top:12px">
        <label style="display:inline-flex;align-items:center;gap:8px;cursor:pointer">
            <input type="checkbox" id="mitm-sslstrip-toggle" onchange="mitmToggleSSL()">
            <span>Enable SSL Stripping</span>
            <span style="font-size:0.75rem;color:var(--text-muted)">(rewrite HTTPS links to HTTP)</span>
        </label>
    </div>
</div>

<!-- CA Certificate -->
<div class="section">
    <h2>CA Certificate</h2>
    <p style="font-size:0.85rem;color:var(--text-secondary);margin-bottom:12px">
        Generate and install a CA certificate for HTTPS interception.
        The client must trust this certificate to avoid browser warnings.
    </p>
    <div class="tool-actions">
        <button id="btn-cert-gen" class="btn btn-primary" onclick="mitmGenCert()">Generate CA Certificate</button>
        <button id="btn-cert-dl" class="btn btn-sm" onclick="mitmDownloadCert()">Download CA Cert</button>
    </div>
    <div id="cert-info" style="margin-top:12px"></div>
    <details style="margin-top:12px;font-size:0.82rem;color:var(--text-secondary)">
        <summary style="cursor:pointer;color:var(--accent)">Installation Instructions</summary>
        <div style="margin-top:8px;padding:12px;background:var(--bg-card);border-radius:var(--radius);line-height:1.7">
            <strong>Windows:</strong> Double-click the .pem file &rarr; Install Certificate &rarr;
            Local Machine &rarr; Trusted Root Certification Authorities.<br>
            <strong>macOS:</strong> Double-click the .pem &rarr; Add to System keychain &rarr;
            Trust &rarr; Always Trust.<br>
            <strong>Linux:</strong> Copy to <code>/usr/local/share/ca-certificates/</code> and run
            <code>sudo update-ca-certificates</code>.<br>
            <strong>Firefox:</strong> Settings &rarr; Privacy &amp; Security &rarr; Certificates &rarr;
            View Certificates &rarr; Import.<br>
            <strong>Chrome:</strong> Settings &rarr; Privacy &rarr; Security &rarr; Manage certificates &rarr;
            Authorities &rarr; Import.<br>
            <strong>Android:</strong> Settings &rarr; Security &rarr; Encryption &rarr;
            Install from storage &rarr; select .pem file.<br>
            <strong>iOS:</strong> AirDrop or email the .pem &rarr; Install Profile &rarr;
            Settings &rarr; General &rarr; About &rarr; Certificate Trust Settings &rarr; Enable.
        </div>
    </details>
</div>

</div>

<!-- ==================== RULES TAB ==================== -->
<div class="tab-content" data-tab-group="mitm" data-tab="rules">

<!-- Add Rule Form -->
<div class="section">
    <h2>Add Rule</h2>
    <div style="display:flex;gap:16px;flex-wrap:wrap;align-items:flex-end">
        <div class="form-group" style="min-width:220px;flex:1">
            <label>URL Pattern <span style="color:var(--text-muted)">(regex)</span></label>
            <input type="text" id="rule-url" class="form-control" placeholder=".*example\.com.*">
        </div>
        <div class="form-group" style="min-width:120px">
            <label>Method</label>
            <select id="rule-method" class="form-control">
                <option value="ANY">ANY</option>
                <option value="GET">GET</option>
                <option value="POST">POST</option>
                <option value="PUT">PUT</option>
                <option value="DELETE">DELETE</option>
                <option value="PATCH">PATCH</option>
            </select>
        </div>
        <div class="form-group" style="min-width:160px">
            <label>Action</label>
            <select id="rule-action" class="form-control" onchange="mitmRuleActionChanged()">
                <option value="block">Block</option>
                <option value="redirect">Redirect</option>
                <option value="modify_header">Modify Header</option>
                <option value="inject_header">Inject Header</option>
                <option value="modify_body">Modify Body</option>
            </select>
        </div>
    </div>
    <!-- Dynamic params -->
    <div id="rule-params" style="margin-top:12px;display:none">
        <div id="rule-params-redirect" style="display:none">
            <div class="form-group" style="max-width:500px">
                <label>Redirect Target URL</label>
                <input type="text" id="rule-redirect-url" class="form-control" placeholder="https://example.com/new">
            </div>
        </div>
        <div id="rule-params-header" style="display:none">
            <div style="display:flex;gap:16px;flex-wrap:wrap">
                <div class="form-group" style="min-width:180px;flex:1">
                    <label>Header Name</label>
                    <input type="text" id="rule-header-name" class="form-control" placeholder="X-Custom-Header">
                </div>
                <div class="form-group" style="min-width:220px;flex:1">
                    <label>Header Value</label>
                    <input type="text" id="rule-header-value" class="form-control" placeholder="value">
                </div>
            </div>
        </div>
        <div id="rule-params-body" style="display:none">
            <div style="display:flex;gap:16px;flex-wrap:wrap">
                <div class="form-group" style="min-width:220px;flex:1">
                    <label>Search String</label>
                    <input type="text" id="rule-body-search" class="form-control" placeholder="original text">
                </div>
                <div class="form-group" style="min-width:220px;flex:1">
                    <label>Replace With</label>
                    <input type="text" id="rule-body-replace" class="form-control" placeholder="replacement text">
                </div>
            </div>
        </div>
    </div>
    <div class="tool-actions" style="margin-top:12px">
        <button id="btn-add-rule" class="btn btn-primary" onclick="mitmAddRule()">Add Rule</button>
    </div>
</div>

<!-- Active Rules Table -->
<div class="section">
    <h2>Active Rules</h2>
    <div style="overflow-x:auto">
        <table class="data-table" style="font-size:0.82rem">
            <thead>
                <tr>
                    <th>ID</th>
                    <th>URL Pattern</th>
                    <th>Method</th>
                    <th>Action</th>
                    <th>Status</th>
                    <th>Actions</th>
                </tr>
            </thead>
            <tbody id="rules-table">
                <tr><td colspan="6" class="empty-state">No rules configured. Add a rule above.</td></tr>
            </tbody>
        </table>
    </div>
    <div class="tool-actions" style="margin-top:8px">
        <button class="btn btn-sm" onclick="mitmLoadRules()">Refresh Rules</button>
    </div>
</div>

</div>

<!-- ==================== TRAFFIC LOG TAB ==================== -->
<div class="tab-content" data-tab-group="mitm" data-tab="traffic">

<!-- Filters -->
<div class="section">
    <h2>Traffic Log</h2>
    <div style="display:flex;gap:12px;flex-wrap:wrap;align-items:flex-end;margin-bottom:12px">
        <div class="form-group" style="min-width:200px;flex:1">
            <label>URL Filter</label>
            <input type="text" id="traffic-filter-url" class="form-control" placeholder="Search URL...">
        </div>
        <div class="form-group" style="min-width:100px">
            <label>Method</label>
            <select id="traffic-filter-method" class="form-control">
                <option value="">All</option>
                <option value="GET">GET</option>
                <option value="POST">POST</option>
                <option value="PUT">PUT</option>
                <option value="DELETE">DELETE</option>
                <option value="CONNECT">CONNECT</option>
            </select>
        </div>
        <div class="form-group" style="min-width:100px">
            <label>Status</label>
            <input type="text" id="traffic-filter-status" class="form-control" placeholder="200">
        </div>
        <button class="btn btn-primary" onclick="mitmLoadTraffic()">Filter</button>
        <button class="btn btn-sm" onclick="mitmClearTraffic()">Clear All</button>
        <button class="btn btn-sm" onclick="mitmExportTraffic('json')">Export JSON</button>
        <button class="btn btn-sm" onclick="mitmExportTraffic('csv')">Export CSV</button>
    </div>
    <p style="font-size:0.75rem;color:var(--text-muted)" id="traffic-summary">--</p>

    <!-- Traffic Table -->
    <div style="overflow-x:auto;margin-top:8px">
        <table class="data-table" style="font-size:0.8rem">
            <thead>
                <tr>
                    <th style="width:50px">ID</th>
                    <th style="width:140px">Time</th>
                    <th style="width:70px">Method</th>
                    <th>URL</th>
                    <th style="width:60px">Status</th>
                    <th style="width:70px">Size</th>
                    <th style="width:70px">Duration</th>
                    <th style="width:50px">Sec</th>
                </tr>
            </thead>
            <tbody id="traffic-table">
                <tr><td colspan="8" class="empty-state">No traffic captured. Start the proxy and route traffic through it.</td></tr>
            </tbody>
        </table>
    </div>
</div>

<!-- Request Detail (expandable) -->
<div class="section" id="traffic-detail-section" style="display:none">
    <h2>Request Detail <span id="traffic-detail-id" style="font-size:0.8rem;color:var(--text-muted)"></span></h2>
    <button class="btn btn-sm" onclick="document.getElementById('traffic-detail-section').style.display='none'" style="float:right;margin-top:-32px">Close</button>

    <div style="display:flex;gap:16px;flex-wrap:wrap;margin-top:8px">
        <!-- Request -->
        <div style="flex:1;min-width:300px">
            <h3 style="font-size:0.85rem;margin-bottom:8px;color:var(--accent)">Request</h3>
            <div style="font-size:0.78rem;margin-bottom:6px">
                <strong id="detail-method">GET</strong>
                <span id="detail-url" style="color:var(--text-secondary);word-break:break-all"></span>
            </div>
            <div style="font-size:0.75rem;color:var(--text-muted);margin-bottom:4px">Headers:</div>
            <pre class="output-panel" id="detail-req-headers" style="max-height:200px;font-size:0.72rem;overflow:auto"></pre>
            <div style="font-size:0.75rem;color:var(--text-muted);margin-top:8px;margin-bottom:4px">Body:</div>
            <pre class="output-panel" id="detail-req-body" style="max-height:200px;font-size:0.72rem;overflow:auto"></pre>
        </div>
        <!-- Response -->
        <div style="flex:1;min-width:300px">
            <h3 style="font-size:0.85rem;margin-bottom:8px;color:var(--success)">Response <span id="detail-status" style="font-weight:bold"></span></h3>
            <div style="font-size:0.78rem;margin-bottom:6px">
                <span id="detail-size" style="color:var(--text-secondary)"></span>
                <span id="detail-duration" style="color:var(--text-muted);margin-left:12px"></span>
            </div>
            <div style="font-size:0.75rem;color:var(--text-muted);margin-bottom:4px">Headers:</div>
            <pre class="output-panel" id="detail-resp-headers" style="max-height:200px;font-size:0.72rem;overflow:auto"></pre>
            <div style="font-size:0.75rem;color:var(--text-muted);margin-top:8px;margin-bottom:4px">Body:</div>
            <pre class="output-panel" id="detail-resp-body" style="max-height:200px;font-size:0.72rem;overflow:auto"></pre>
        </div>
    </div>

    <!-- Secrets -->
    <div id="detail-secrets" style="display:none;margin-top:12px">
        <h3 style="font-size:0.85rem;margin-bottom:8px;color:var(--warning)">Secrets Found</h3>
        <div id="detail-secrets-list"></div>
    </div>
</div>

</div>

<!-- ==================== JavaScript ==================== -->
<script>
(function() {

    /* ── Helpers ──────────────────────────────────────────────────── */

    var esc = escapeHtml;
    var autoRefreshTimer = null;

    /* ── Status ──────────────────────────────────────────────────── */

    function mitmStatus() {
        fetchJSON('/mitm-proxy/status').then(function(d) {
            var stateEl = document.getElementById('mitm-state');
            if (d.running) {
                stateEl.textContent = 'RUNNING';
                stateEl.style.color = 'var(--success)';
                document.getElementById('btn-mitm-start').disabled = true;
                document.getElementById('btn-mitm-stop').disabled = false;
            } else {
                stateEl.textContent = 'STOPPED';
                stateEl.style.color = 'var(--danger)';
                document.getElementById('btn-mitm-start').disabled = false;
                document.getElementById('btn-mitm-stop').disabled = true;
            }
            document.getElementById('mitm-addr').textContent = d.host + ':' + d.port;
            document.getElementById('mitm-engine').textContent = d.engine || '--';
            document.getElementById('mitm-reqcount').textContent = d.request_count || 0;
            document.getElementById('mitm-sslstrip').textContent = d.ssl_strip ? 'ON' : 'OFF';
            document.getElementById('mitm-sslstrip').style.color = d.ssl_strip ? 'var(--warning)' : 'var(--text-muted)';
            document.getElementById('mitm-upstream').textContent = d.upstream_proxy || 'None';
            document.getElementById('mitm-sslstrip-toggle').checked = d.ssl_strip;
        });
    }

    window.mitmStatus = mitmStatus;
    mitmStatus();

    /* ── Start / Stop ────────────────────────────────────────────── */

    window.mitmStart = function() {
        var btn = document.getElementById('btn-mitm-start');
        setLoading(btn, true);
        var body = {
            host: document.getElementById('mitm-host').value.trim() || '127.0.0.1',
            port: parseInt(document.getElementById('mitm-port').value) || 8888,
            upstream: document.getElementById('mitm-upstream-input').value.trim() || null
        };
        postJSON('/mitm-proxy/start', body).then(function(d) {
            setLoading(btn, false);
            if (d.success) {
                mitmStatus();
                startAutoRefresh();
            } else {
                alert(d.error || 'Failed to start proxy');
            }
        }).catch(function() { setLoading(btn, false); });
    };

    window.mitmStop = function() {
        var btn = document.getElementById('btn-mitm-stop');
        setLoading(btn, true);
        postJSON('/mitm-proxy/stop', {}).then(function(d) {
            setLoading(btn, false);
            mitmStatus();
            stopAutoRefresh();
        }).catch(function() { setLoading(btn, false); });
    };

    function startAutoRefresh() {
        stopAutoRefresh();
        autoRefreshTimer = setInterval(function() { mitmStatus(); }, 5000);
    }

    function stopAutoRefresh() {
        if (autoRefreshTimer) { clearInterval(autoRefreshTimer); autoRefreshTimer = null; }
    }

    /* ── SSL Strip Toggle ────────────────────────────────────────── */

    window.mitmToggleSSL = function() {
        var enabled = document.getElementById('mitm-sslstrip-toggle').checked;
        postJSON('/mitm-proxy/ssl-strip', { enabled: enabled }).then(function(d) {
            mitmStatus();
        });
    };

    /* ── Certificate ─────────────────────────────────────────────── */

    window.mitmGenCert = function() {
        var btn = document.getElementById('btn-cert-gen');
        setLoading(btn, true);
        postJSON('/mitm-proxy/cert/generate', {}).then(function(d) {
            setLoading(btn, false);
            var info = document.getElementById('cert-info');
            if (d.success) {
                info.innerHTML = '<div style="padding:10px;background:var(--bg-card);border-radius:var(--radius);font-size:0.82rem">'
                    + '<span style="color:var(--success)">&#x2713; ' + esc(d.message) + '</span><br>'
                    + '<span style="color:var(--text-muted)">Path: ' + esc(d.cert_path) + '</span></div>';
            } else {
                info.innerHTML = '<span style="color:var(--danger)">' + esc(d.error) + '</span>';
            }
        }).catch(function() { setLoading(btn, false); });
    };

    window.mitmDownloadCert = function() {
        window.location.href = '/mitm-proxy/cert';
    };

    /* ── Rules ───────────────────────────────────────────────────── */

    window.mitmRuleActionChanged = function() {
        var action = document.getElementById('rule-action').value;
        var paramsDiv = document.getElementById('rule-params');
        paramsDiv.style.display = (action === 'block') ? 'none' : 'block';
        document.getElementById('rule-params-redirect').style.display = (action === 'redirect') ? 'block' : 'none';
        document.getElementById('rule-params-header').style.display =
            (action === 'modify_header' || action === 'inject_header') ? 'block' : 'none';
        document.getElementById('rule-params-body').style.display = (action === 'modify_body') ? 'block' : 'none';
    };

    window.mitmAddRule = function() {
        var action = document.getElementById('rule-action').value;
        var params = {};
        if (action === 'redirect') {
            params.target_url = document.getElementById('rule-redirect-url').value.trim();
        } else if (action === 'modify_header' || action === 'inject_header') {
            params.header_name = document.getElementById('rule-header-name').value.trim();
            params.header_value = document.getElementById('rule-header-value').value.trim();
        } else if (action === 'modify_body') {
            params.search = document.getElementById('rule-body-search').value;
            params.replace = document.getElementById('rule-body-replace').value;
        }
        var body = {
            match_url: document.getElementById('rule-url').value.trim() || '.*',
            match_method: document.getElementById('rule-method').value,
            action: action,
            params: params
        };
        var btn = document.getElementById('btn-add-rule');
        setLoading(btn, true);
        postJSON('/mitm-proxy/rules', body).then(function(d) {
            setLoading(btn, false);
            if (d.success) {
                document.getElementById('rule-url').value = '';
                mitmLoadRules();
            } else {
                alert(d.error || 'Failed to add rule');
            }
        }).catch(function() { setLoading(btn, false); });
    };

    window.mitmLoadRules = function() {
        fetchJSON('/mitm-proxy/rules').then(function(d) {
            var rules = d.rules || [];
            var tbody = document.getElementById('rules-table');
            if (!rules.length) {
                tbody.innerHTML = '<tr><td colspan="6" class="empty-state">No rules configured.</td></tr>';
                return;
            }
            var html = '';
            rules.forEach(function(r) {
                var stateColor = r.enabled ? 'var(--success)' : 'var(--text-muted)';
                var stateText = r.enabled ? 'Enabled' : 'Disabled';
                var paramStr = '';
                if (r.action === 'redirect' && r.params && r.params.target_url) {
                    paramStr = ' &rarr; ' + esc(r.params.target_url.substring(0, 40));
                } else if ((r.action === 'modify_header' || r.action === 'inject_header') && r.params) {
                    paramStr = ' [' + esc(r.params.header_name || '') + ']';
                } else if (r.action === 'modify_body' && r.params) {
                    paramStr = ' s/' + esc((r.params.search || '').substring(0, 20)) + '/';
                }
                html += '<tr>'
                    + '<td>' + r.id + '</td>'
                    + '<td style="max-width:250px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap" title="' + esc(r.match_url) + '">' + esc(r.match_url) + '</td>'
                    + '<td>' + esc(r.match_method) + '</td>'
                    + '<td>' + esc(r.action) + paramStr + '</td>'
                    + '<td style="color:' + stateColor + '">' + stateText + '</td>'
                    + '<td style="white-space:nowrap">'
                    + '<button class="btn btn-sm" onclick="mitmToggleRule(' + r.id + ')" title="Toggle">' + (r.enabled ? 'Disable' : 'Enable') + '</button> '
                    + '<button class="btn btn-sm btn-danger" onclick="mitmDeleteRule(' + r.id + ')" title="Delete">Del</button>'
                    + '</td></tr>';
            });
            tbody.innerHTML = html;
        });
    };

    window.mitmToggleRule = function(id) {
        postJSON('/mitm-proxy/rules/' + id + '/toggle', {}).then(function() {
            mitmLoadRules();
        });
    };

    window.mitmDeleteRule = function(id) {
        if (!confirm('Delete rule #' + id + '?')) return;
        fetchJSON('/mitm-proxy/rules/' + id, { method: 'DELETE' }).then(function() {
            mitmLoadRules();
        });
    };

    // Load rules on tab switch
    mitmLoadRules();

    /* ── Traffic ─────────────────────────────────────────────────── */

    window.mitmLoadTraffic = function() {
        var params = new URLSearchParams();
        params.set('limit', '100');
        var url = document.getElementById('traffic-filter-url').value.trim();
        var method = document.getElementById('traffic-filter-method').value;
        var status = document.getElementById('traffic-filter-status').value.trim();
        if (url) params.set('filter_url', url);
        if (method) params.set('filter_method', method);
        if (status) params.set('filter_status', status);

        fetchJSON('/mitm-proxy/traffic?' + params.toString()).then(function(d) {
            var entries = d.entries || [];
            var total = d.total || 0;
            document.getElementById('traffic-summary').textContent =
                'Showing ' + entries.length + ' of ' + total + ' entries';

            var tbody = document.getElementById('traffic-table');
            if (!entries.length) {
                tbody.innerHTML = '<tr><td colspan="8" class="empty-state">No traffic captured.</td></tr>';
                return;
            }
            var html = '';
            entries.forEach(function(e) {
                var statusColor = '';
                if (e.status >= 200 && e.status < 300) statusColor = 'color:var(--success)';
                else if (e.status >= 300 && e.status < 400) statusColor = 'color:var(--accent)';
                else if (e.status >= 400 && e.status < 500) statusColor = 'color:var(--warning)';
                else if (e.status >= 500) statusColor = 'color:var(--danger)';

                var sizeStr = e.size > 1024 ? (e.size / 1024).toFixed(1) + 'K' : e.size + 'B';
                var timeStr = e.timestamp ? e.timestamp.split('T')[1].split('.')[0] : '--';
                var secFlag = e.secrets_found
                    ? '<span style="color:var(--warning);font-weight:bold" title="Secrets detected">!</span>'
                    : '';
                var truncUrl = e.url.length > 80 ? e.url.substring(0, 77) + '...' : e.url;

                html += '<tr style="cursor:pointer" onclick="mitmShowDetail(' + e.id + ')">'
                    + '<td>' + e.id + '</td>'
                    + '<td style="font-size:0.72rem;color:var(--text-muted)">' + esc(timeStr) + '</td>'
                    + '<td><strong>' + esc(e.method) + '</strong></td>'
                    + '<td style="max-width:350px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap" title="' + esc(e.url) + '">' + esc(truncUrl) + '</td>'
                    + '<td style="' + statusColor + '">' + e.status + '</td>'
                    + '<td style="text-align:right">' + sizeStr + '</td>'
                    + '<td style="text-align:right">' + e.duration + 'ms</td>'
                    + '<td style="text-align:center">' + secFlag + '</td>'
                    + '</tr>';
            });
            tbody.innerHTML = html;
        });
    };

    window.mitmShowDetail = function(id) {
        fetchJSON('/mitm-proxy/traffic/' + id).then(function(d) {
            if (!d.success) return;
            var e = d.entry;
            document.getElementById('traffic-detail-section').style.display = 'block';
            document.getElementById('traffic-detail-id').textContent = '#' + e.id;
            document.getElementById('detail-method').textContent = e.method;
            document.getElementById('detail-url').textContent = e.url;
            document.getElementById('detail-status').textContent = e.status;
            document.getElementById('detail-size').textContent = (e.size > 1024 ? (e.size / 1024).toFixed(1) + ' KB' : e.size + ' bytes');
            document.getElementById('detail-duration').textContent = e.duration + ' ms';

            // Headers
            var reqH = '';
            if (e.request_headers) {
                Object.keys(e.request_headers).forEach(function(k) {
                    reqH += k + ': ' + e.request_headers[k] + '\n';
                });
            }
            document.getElementById('detail-req-headers').textContent = reqH || '(none)';
            document.getElementById('detail-req-body').textContent = e.request_body || '(empty)';

            var respH = '';
            if (e.response_headers) {
                Object.keys(e.response_headers).forEach(function(k) {
                    respH += k + ': ' + e.response_headers[k] + '\n';
                });
            }
            document.getElementById('detail-resp-headers').textContent = respH || '(none)';
            document.getElementById('detail-resp-body').textContent = e.response_body || '(empty)';

            // Secrets
            var secrets = e.secrets_found || [];
            var secretsDiv = document.getElementById('detail-secrets');
            var secretsList = document.getElementById('detail-secrets-list');
            if (secrets.length) {
                secretsDiv.style.display = 'block';
                var shtml = '';
                secrets.forEach(function(s) {
                    shtml += '<div style="padding:6px 10px;margin-bottom:4px;background:rgba(245,158,11,0.1);border-left:3px solid var(--warning);border-radius:4px;font-size:0.8rem">'
                        + '<strong style="color:var(--warning)">' + esc(s.type) + '</strong>: '
                        + '<code style="color:var(--text-primary)">' + esc(s.value_masked) + '</code>'
                        + ' <span style="color:var(--text-muted);font-size:0.72rem">(' + esc(s.location) + ')</span>'
                        + '</div>';
                });
                secretsList.innerHTML = shtml;
            } else {
                secretsDiv.style.display = 'none';
            }

            // Scroll to detail
            document.getElementById('traffic-detail-section').scrollIntoView({ behavior: 'smooth' });
        });
    };

    window.mitmClearTraffic = function() {
        if (!confirm('Clear all captured traffic?')) return;
        fetchJSON('/mitm-proxy/traffic', { method: 'DELETE' }).then(function() {
            mitmLoadTraffic();
            mitmStatus();
        });
    };

    window.mitmExportTraffic = function(fmt) {
        window.location.href = '/mitm-proxy/traffic/export?format=' + fmt;
    };

})();
</script>
{% endblock %}


Instruction: Create an AUTARCH web template for the 'Module Creator' page. It should extend base.html, have a page header, and use AUTARCH's CSS variables and UI patterns (sections, tab bars, data tables, buttons).

Input:
Template: module_creator.html
Sections: 0
Tabs: 0
Forms/API calls: 6
Has JavaScript: True

Output:
{% extends "base.html" %}
{% block title %}Module Creator - AUTARCH{% endblock %}

{% block content %}
<div class="page-header">
    <h1>Module Creator</h1>
    <p style="margin:0;font-size:0.85rem;color:var(--text-secondary)">
        Create, edit, validate, and manage AUTARCH modules
    </p>
</div>

<style>
#module-code {
    font-family: 'Courier New', monospace;
    font-size: 0.82rem;
    background: var(--bg-primary);
    color: var(--text-primary);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 0.75rem;
    width: 100%;
    min-height: 500px;
    resize: vertical;
    tab-size: 4;
    line-height: 1.5;
}
#module-code:focus {
    outline: none;
    border-color: var(--accent);
}
.mc-layout {
    display: grid;
    grid-template-columns: 1fr 340px;
    gap: 1.5rem;
    margin-top: 1rem;
}
@media (max-width: 960px) {
    .mc-layout { grid-template-columns: 1fr; }
}
.mc-editor, .mc-list {
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 1rem;
}
.mc-form-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.6rem 1rem;
    margin-bottom: 0.75rem;
}
.mc-form-grid .form-group { margin-bottom: 0; }
.mc-form-grid label {
    display: block;
    font-size: 0.78rem;
    color: var(--text-secondary);
    margin-bottom: 2px;
}
.mc-form-grid input, .mc-form-grid select {
    width: 100%;
    padding: 0.4rem 0.6rem;
    background: var(--bg-input);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    color: var(--text-primary);
    font-size: 0.82rem;
}
.mc-btn-row {
    display: flex;
    gap: 0.5rem;
    margin-top: 0.75rem;
    flex-wrap: wrap;
}
.mc-btn-row .btn { font-size: 0.82rem; padding: 0.4rem 0.9rem; }
.mc-status {
    margin-top: 0.75rem;
    padding: 0.6rem 0.8rem;
    border-radius: var(--radius);
    font-size: 0.8rem;
    display: none;
    white-space: pre-wrap;
}
.mc-status.success { display: block; background: rgba(34,197,94,0.12); color: #4ade80; border: 1px solid rgba(34,197,94,0.25); }
.mc-status.error { display: block; background: rgba(239,68,68,0.12); color: #f87171; border: 1px solid rgba(239,68,68,0.25); }
.mc-status.info { display: block; background: rgba(99,102,241,0.12); color: var(--accent-hover); border: 1px solid rgba(99,102,241,0.25); }
.mc-list h2 {
    font-size: 1rem;
    margin-bottom: 0.5rem;
    display: flex;
    align-items: center;
    justify-content: space-between;
}
.mc-modules-scroll {
    max-height: 620px;
    overflow-y: auto;
    padding-right: 0.25rem;
}
.mc-cat-group { margin-bottom: 0.75rem; }
.mc-cat-group h3 {
    font-size: 0.78rem;
    text-transform: uppercase;
    color: var(--text-muted);
    letter-spacing: 0.05em;
    margin-bottom: 0.3rem;
    padding-bottom: 0.2rem;
    border-bottom: 1px solid var(--border);
}
.mc-mod-item {
    padding: 0.4rem 0.5rem;
    border-radius: calc(var(--radius) - 2px);
    cursor: pointer;
    font-size: 0.8rem;
    transition: background 0.15s;
}
.mc-mod-item:hover { background: var(--bg-input); }
.mc-mod-item .mod-name { font-weight: 600; color: var(--text-primary); }
.mc-mod-item .mod-desc {
    font-size: 0.72rem;
    color: var(--text-secondary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.mc-mod-item .mod-meta {
    font-size: 0.68rem;
    color: var(--text-muted);
}
</style>

<div class="mc-layout">
    <!-- Left column: editor -->
    <div class="mc-editor">
        <div class="mc-form-grid">
            <div class="form-group">
                <label for="mod-name">Module Name</label>
                <input type="text" id="mod-name" placeholder="my_module" autocomplete="off">
            </div>
            <div class="form-group">
                <label for="mod-category">Category</label>
                <select id="mod-category">
                    <option value="defense">defense</option>
                    <option value="offense">offense</option>
                    <option value="counter">counter</option>
                    <option value="analyze">analyze</option>
                    <option value="osint">osint</option>
                    <option value="simulate">simulate</option>
                    <option value="core">core</option>
                    <option value="hardware">hardware</option>
                </select>
            </div>
            <div class="form-group">
                <label for="mod-desc">Description</label>
                <input type="text" id="mod-desc" placeholder="Short description of the module">
            </div>
            <div class="form-group">
                <label for="mod-author">Author</label>
                <input type="text" id="mod-author" value="darkHal">
            </div>
        </div>

        <div style="margin-bottom:0.5rem">
            <button class="btn btn-sm" onclick="loadTemplate()" style="font-size:0.78rem">Load Template</button>
        </div>

        <textarea id="module-code" rows="25" placeholder="# Module code will appear here...&#10;# Click 'Load Template' or select a module from the list." spellcheck="false"></textarea>

        <div class="mc-btn-row">
            <button class="btn" onclick="validateCode()">Validate</button>
            <button class="btn" onclick="createModule()" style="background:var(--accent);color:#fff">Create Module</button>
            <button class="btn" onclick="saveModule()">Save Changes</button>
        </div>

        <div id="mc-status" class="mc-status"></div>
    </div>

    <!-- Right column: module list -->
    <div class="mc-list">
        <h2>
            Modules
            <button class="btn btn-sm" onclick="refreshModuleList()" style="font-size:0.72rem;padding:0.25rem 0.6rem">Refresh</button>
        </h2>
        <div id="module-list" class="mc-modules-scroll">
            <p style="color:var(--text-muted);font-size:0.8rem">Loading...</p>
        </div>
    </div>
</div>

<script>
const API = '/module-creator';
const statusEl = document.getElementById('mc-status');
const codeEl = document.getElementById('module-code');
let currentEditName = null;

function showStatus(msg, type) {
    statusEl.className = 'mc-status ' + type;
    statusEl.textContent = msg;
}

function clearStatus() {
    statusEl.className = 'mc-status';
    statusEl.textContent = '';
}

/* Tab key support in textarea */
codeEl.addEventListener('keydown', function(e) {
    if (e.key === 'Tab') {
        e.preventDefault();
        const start = this.selectionStart;
        const end = this.selectionEnd;
        this.value = this.value.substring(0, start) + '    ' + this.value.substring(end);
        this.selectionStart = this.selectionEnd = start + 4;
    }
});

/* Load template skeleton for selected category */
function loadTemplate() {
    const cat = document.getElementById('mod-category').value;
    fetch(API + '/templates')
        .then(r => r.json())
        .then(templates => {
            const t = templates.find(x => x.category === cat);
            if (t) {
                codeEl.value = t.code;
                if (!document.getElementById('mod-desc').value) {
                    document.getElementById('mod-desc').value = t.description;
                }
                currentEditName = null;
                showStatus('Template loaded for category: ' + cat, 'info');
            }
        })
        .catch(err => showStatus('Failed to load templates: ' + err, 'error'));
}

/* Validate code syntax */
function validateCode() {
    const code = codeEl.value.trim();
    if (!code) { showStatus('No code to validate', 'error'); return; }
    fetch(API + '/validate', {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({code: code})
    })
    .then(r => r.json())
    .then(data => {
        if (data.valid) {
            showStatus('Validation passed. ' + (data.warnings || []).join('; '), 'success');
        } else {
            showStatus('Validation failed:\n' + (data.errors || []).join('\n'), 'error');
        }
    })
    .catch(err => showStatus('Validation request failed: ' + err, 'error'));
}

/* Create new module */
function createModule() {
    const name = document.getElementById('mod-name').value.trim();
    const category = document.getElementById('mod-category').value;
    const description = document.getElementById('mod-desc').value.trim();
    const author = document.getElementById('mod-author').value.trim() || 'darkHal';
    const code = codeEl.value;

    if (!name) { showStatus('Module name is required', 'error'); return; }
    if (!code.trim()) { showStatus('Module code is empty', 'error'); return; }

    fetch(API + '/create', {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({name, category, description, author, code})
    })
    .then(r => r.json())
    .then(data => {
        if (data.success) {
            showStatus(data.message, 'success');
            currentEditName = name;
            refreshModuleList();
        } else {
            showStatus(data.error || 'Creation failed', 'error');
        }
    })
    .catch(err => showStatus('Request failed: ' + err, 'error'));
}

/* Save changes to existing module */
function saveModule() {
    const name = currentEditName || document.getElementById('mod-name').value.trim();
    const code = codeEl.value;

    if (!name) { showStatus('No module selected to save. Enter a name or click a module from the list.', 'error'); return; }
    if (!code.trim()) { showStatus('Module code is empty', 'error'); return; }

    fetch(API + '/save', {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({name, code})
    })
    .then(r => r.json())
    .then(data => {
        if (data.success) {
            showStatus(data.message, 'success');
            refreshModuleList();
        } else {
            showStatus(data.error || 'Save failed', 'error');
        }
    })
    .catch(err => showStatus('Request failed: ' + err, 'error'));
}

/* Load a module into the editor */
function loadModule(name) {
    fetch(API + '/preview', {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({name: name})
    })
    .then(r => r.json())
    .then(data => {
        if (data.success) {
            codeEl.value = data.code;
            currentEditName = name;
            const m = data.metadata || {};
            document.getElementById('mod-name').value = name;
            if (m.category) document.getElementById('mod-category').value = m.category;
            if (m.description) document.getElementById('mod-desc').value = m.description;
            if (m.author) document.getElementById('mod-author').value = m.author;
            showStatus('Loaded module: ' + name, 'info');
        } else {
            showStatus(data.error || 'Failed to load module', 'error');
        }
    })
    .catch(err => showStatus('Failed to load module: ' + err, 'error'));
}

/* Refresh the module list */
function refreshModuleList() {
    const container = document.getElementById('module-list');
    container.innerHTML = '<p style="color:var(--text-muted);font-size:0.8rem">Loading...</p>';

    fetch(API + '/list')
        .then(r => r.json())
        .then(modules => {
            if (!modules.length) {
                container.innerHTML = '<p style="color:var(--text-muted);font-size:0.8rem">No modules found.</p>';
                return;
            }
            /* Group by category */
            const groups = {};
            modules.forEach(m => {
                const cat = m.category || 'unknown';
                if (!groups[cat]) groups[cat] = [];
                groups[cat].push(m);
            });

            let html = '';
            const catOrder = ['defense','offense','counter','analyze','osint','simulate','core','hardware','unknown'];
            catOrder.forEach(cat => {
                if (!groups[cat]) return;
                html += '<div class="mc-cat-group">';
                html += '<h3>' + cat + ' (' + groups[cat].length + ')</h3>';
                groups[cat].forEach(m => {
                    const desc = m.description ? m.description.substring(0, 60) : '';
                    const ver = m.version ? 'v' + m.version : '';
                    html += '<div class="mc-mod-item" onclick="loadModule(\'' + m.name.replace(/'/g, "\\'") + '\')">';
                    html += '<div class="mod-name">' + m.name + '</div>';
                    if (desc) html += '<div class="mod-desc">' + desc + '</div>';
                    html += '<div class="mod-meta">' + [ver, m.last_modified].filter(Boolean).join(' &middot; ') + '</div>';
                    html += '</div>';
                });
                html += '</div>';
            });

            /* Any categories not in catOrder */
            Object.keys(groups).forEach(cat => {
                if (catOrder.includes(cat)) return;
                html += '<div class="mc-cat-group">';
                html += '<h3>' + cat + ' (' + groups[cat].length + ')</h3>';
                groups[cat].forEach(m => {
                    const desc = m.description ? m.description.substring(0, 60) : '';
                    const ver = m.version ? 'v' + m.version : '';
                    html += '<div class="mc-mod-item" onclick="loadModule(\'' + m.name.replace(/'/g, "\\'") + '\')">';
                    html += '<div class="mod-name">' + m.name + '</div>';
                    if (desc) html += '<div class="mod-desc">' + desc + '</div>';
                    html += '<div class="mod-meta">' + [ver, m.last_modified].filter(Boolean).join(' &middot; ') + '</div>';
                    html += '</div>';
                });
                html += '</div>';
            });

            container.innerHTML = html;
        })
        .catch(err => {
            container.innerHTML = '<p style="color:var(--danger);font-size:0.8rem">Failed to load modules</p>';
        });
}

/* Initial load */
refreshModuleList();
</script>
{% endblock %}


Instruction: Create an AUTARCH web template for the 'Msf' page. It should extend base.html, have a page header, and use AUTARCH's CSS variables and UI patterns (sections, tab bars, data tables, buttons).

Input:
Template: msf.html
Sections: 3
Tabs: 0
Forms/API calls: 3
Has JavaScript: True

Output:
{% extends "base.html" %}
{% block title %}MSF Console - AUTARCH{% endblock %}

{% block content %}
<div class="page-header">
    <h1>MSF Console</h1>
</div>

<!-- Connection Bar -->
<div class="section" style="padding:0.75rem 1rem">
    <div style="display:flex;align-items:center;gap:0.75rem;flex-wrap:wrap">
        <div id="msf-conn-status" style="display:flex;align-items:center;gap:0.4rem;font-size:0.85rem">
            <span id="msf-conn-dot" class="status-dot inactive"></span>
            <span id="msf-conn-label">Checking...</span>
        </div>
        <button class="btn btn-sm" onclick="msfReconnect()">Reconnect</button>
        <span id="msf-conn-info" style="font-size:0.8rem;color:var(--text-secondary)"></span>
    </div>
</div>

<!-- Terminal -->
<div class="section">
    <div id="msf-terminal" style="background:#0a0a0a;color:#e0e0e0;font-family:monospace;font-size:0.85rem;border-radius:6px;padding:1rem;min-height:400px;max-height:60vh;overflow-y:auto;white-space:pre-wrap;word-break:break-all"></div>
    <div style="display:flex;gap:0.5rem;margin-top:0.5rem;align-items:center">
        <span style="color:var(--text-secondary);font-family:monospace;font-size:0.85rem">msf6 &gt;</span>
        <input type="text" id="msf-input" placeholder="Enter MSF console command..." style="flex:1;font-family:monospace;font-size:0.85rem" onkeypress="if(event.key==='Enter')msfSend()">
        <button class="btn btn-primary btn-sm" onclick="msfSend()">Send</button>
        <button class="btn btn-sm" onclick="msfClear()">Clear</button>
    </div>
    <div style="font-size:0.75rem;color:var(--text-muted);margin-top:0.4rem">
        Examples: <code>version</code> &nbsp; <code>search ssh</code> &nbsp; <code>use auxiliary/scanner/ssh/ssh_version</code> &nbsp; <code>sessions -l</code>
    </div>
</div>

<!-- Quick Commands -->
<div class="section">
    <h2>Quick Commands</h2>
    <div style="display:flex;gap:0.5rem;flex-wrap:wrap">
        <button class="btn btn-sm" onclick="msfQuick('version')">version</button>
        <button class="btn btn-sm" onclick="msfQuick('sessions -l')">sessions</button>
        <button class="btn btn-sm" onclick="msfQuick('jobs -l')">jobs</button>
        <button class="btn btn-sm" onclick="msfQuick('route')">routes</button>
        <button class="btn btn-sm" onclick="msfQuick('hosts')">hosts</button>
        <button class="btn btn-sm" onclick="msfQuick('services')">services</button>
        <button class="btn btn-sm" onclick="msfQuick('creds')">creds</button>
        <button class="btn btn-sm" onclick="msfQuick('vulns')">vulns</button>
        <button class="btn btn-sm" onclick="msfQuick('db_status')">db_status</button>
    </div>
</div>

<script>
const term = document.getElementById('msf-terminal');

function termPrint(text, cls) {
    const span = document.createElement('span');
    if (cls) span.className = cls;
    span.textContent = text;
    term.appendChild(span);
    term.scrollTop = term.scrollHeight;
}

function msfClear() {
    term.innerHTML = '';
}

async function msfCheckStatus() {
    try {
        const res = await fetch('/msf/status');
        const d = await res.json();
        const dot = document.getElementById('msf-conn-dot');
        const label = document.getElementById('msf-conn-label');
        const info = document.getElementById('msf-conn-info');
        if (d.connected) {
            dot.className = 'status-dot active';
            label.textContent = 'Connected';
            if (d.host) info.textContent = d.host + ':' + (d.port || 55553);
        } else {
            dot.className = 'status-dot inactive';
            label.textContent = 'Disconnected';
            info.textContent = '';
        }
    } catch(e) {}
}

async function msfReconnect() {
    document.getElementById('msf-conn-label').textContent = 'Connecting...';
    try {
        const res = await fetch('/msf/connect', {method:'POST'});
        const d = await res.json();
        msfCheckStatus();
        if (!d.connected) {
            termPrint('[error] ' + (d.error || d.message || 'Could not connect') + '\n', 'err');
        }
    } catch(e) {
        termPrint('[error] ' + e.message + '\n', 'err');
    }
}

async function msfSend() {
    const inp = document.getElementById('msf-input');
    const cmd = inp.value.trim();
    if (!cmd) return;
    inp.value = '';
    termPrint('msf6 > ' + cmd + '\n');
    await msfExec(cmd);
}

function msfQuick(cmd) {
    document.getElementById('msf-input').value = cmd;
    msfSend();
}

async function msfExec(cmd) {
    try {
        const res = await fetch('/msf/console/send', {
            method: 'POST',
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify({cmd})
        });
        const d = await res.json();
        if (d.error) {
            termPrint('[error] ' + d.error + '\n', 'err');
        } else {
            termPrint((d.output || '') + '\n');
        }
    } catch(e) {
        termPrint('[error] ' + e.message + '\n', 'err');
    }
}

document.addEventListener('DOMContentLoaded', function() {
    msfCheckStatus();
    termPrint('AUTARCH MSF Console — connected to msfrpcd via RPC\n');
    termPrint('Type commands below or use Quick Commands. Type "help" for MSF help.\n\n');
});
</script>
{% endblock %}


Instruction: Create an AUTARCH web template for the 'Net Mapper' page. It should extend base.html, have a page header, and use AUTARCH's CSS variables and UI patterns (sections, tab bars, data tables, buttons).

Input:
Template: net_mapper.html
Sections: 0
Tabs: 2
Forms/API calls: 8
Has JavaScript: True

Output:
{% extends "base.html" %}
{% block title %}Net Mapper — AUTARCH{% endblock %}
{% block content %}
<div class="page-header">
    <h1>Network Topology Mapper</h1>
    <p class="text-muted">Host discovery, service enumeration, topology visualization</p>
</div>

<div class="tabs">
    <button class="tab active" onclick="switchTab('discover')">Discover</button>
    <button class="tab" onclick="switchTab('map')">Map</button>
    <button class="tab" onclick="switchTab('scans')">Saved Scans</button>
</div>

<!-- Discover -->
<div id="tab-discover" class="tab-content active">
    <div class="card" style="max-width:700px">
        <h3>Host Discovery</h3>
        <div style="display:flex;gap:0.5rem;align-items:end">
            <div class="form-group" style="flex:1;margin:0">
                <label>Target (CIDR, range, or single IP)</label>
                <input type="text" id="disc-target" class="form-control" placeholder="192.168.1.0/24">
            </div>
            <select id="disc-method" class="form-control" style="width:130px">
                <option value="auto">Auto</option>
                <option value="nmap">Nmap</option>
                <option value="icmp">ICMP/TCP</option>
            </select>
            <button class="btn btn-primary" id="disc-btn" onclick="startDiscover()">Scan</button>
        </div>
        <div id="disc-status" style="margin-top:0.5rem"></div>
    </div>

    <div id="disc-results" style="margin-top:1rem">
        <div id="disc-empty" class="card" style="text-align:center;color:var(--text-muted)">Run a discovery scan to find hosts</div>
        <div id="disc-hosts" style="display:none">
            <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:0.5rem">
                <h3>Discovered Hosts (<span id="host-count">0</span>)</h3>
                <div style="display:flex;gap:0.5rem">
                    <button class="btn btn-sm" onclick="showTopology()">View Map</button>
                    <button class="btn btn-sm" onclick="saveScan()">Save Scan</button>
                </div>
            </div>
            <table class="data-table">
                <thead><tr><th>IP</th><th>Hostname</th><th>MAC</th><th>OS</th><th>Ports</th><th>Action</th></tr></thead>
                <tbody id="hosts-body"></tbody>
            </table>
        </div>
    </div>
</div>

<!-- Map -->
<div id="tab-map" class="tab-content" style="display:none">
    <div class="card">
        <h3>Network Topology</h3>
        <div id="topology-canvas" style="width:100%;height:500px;background:#0a0a12;border-radius:var(--radius);position:relative;overflow:hidden">
            <svg id="topo-svg" width="100%" height="100%"></svg>
        </div>
        <div id="topo-info" style="margin-top:0.5rem;font-size:0.8rem;color:var(--text-muted)"></div>
    </div>
</div>

<!-- Saved Scans -->
<div id="tab-scans" class="tab-content" style="display:none">
    <h3>Saved Scans</h3>
    <div id="scans-list"></div>
    <div class="card" style="margin-top:1rem">
        <h4>Compare Scans</h4>
        <div style="display:flex;gap:0.5rem;align-items:end">
            <select id="diff-s1" class="form-control" style="flex:1"></select>
            <span>vs</span>
            <select id="diff-s2" class="form-control" style="flex:1"></select>
            <button class="btn btn-primary" onclick="diffScans()">Compare</button>
        </div>
        <div id="diff-results" style="margin-top:0.5rem"></div>
    </div>
</div>

<style>
.node-circle{cursor:pointer;transition:r 0.2s}
.node-circle:hover{r:12}
.spinner-inline{display:inline-block;width:14px;height:14px;border:2px solid var(--border);border-top-color:var(--accent);border-radius:50%;animation:spin 0.8s linear infinite;vertical-align:middle;margin-right:6px}
@keyframes spin{to{transform:rotate(360deg)}}
</style>

<script>
let currentHosts=[];
let discPoll=null;

function switchTab(name){
    document.querySelectorAll('.tab').forEach((t,i)=>t.classList.toggle('active',['discover','map','scans'][i]===name));
    document.querySelectorAll('.tab-content').forEach(c=>c.style.display='none');
    document.getElementById('tab-'+name).style.display='block';
    if(name==='scans') loadScans();
}

function startDiscover(){
    const target=document.getElementById('disc-target').value.trim();
    if(!target) return;
    document.getElementById('disc-btn').disabled=true;
    document.getElementById('disc-status').innerHTML='<div class="spinner-inline"></div> Discovering hosts...';
    fetch('/net-mapper/discover',{method:'POST',headers:{'Content-Type':'application/json'},
        body:JSON.stringify({target,method:document.getElementById('disc-method').value})})
    .then(r=>r.json()).then(d=>{
        if(!d.ok){showDiscError(d.error);return}
        if(discPoll) clearInterval(discPoll);
        discPoll=setInterval(()=>{
            fetch('/net-mapper/discover/'+d.job_id).then(r=>r.json()).then(s=>{
                if(!s.done) return;
                clearInterval(discPoll);discPoll=null;
                document.getElementById('disc-btn').disabled=false;
                document.getElementById('disc-status').innerHTML='';
                currentHosts=s.hosts||[];
                renderHosts(currentHosts);
            });
        },2000);
    }).catch(e=>showDiscError(e.message));
}

function showDiscError(msg){
    document.getElementById('disc-btn').disabled=false;
    document.getElementById('disc-status').innerHTML='<span style="color:var(--danger)">'+esc(msg)+'</span>';
}

function renderHosts(hosts){
    document.getElementById('disc-empty').style.display='none';
    document.getElementById('disc-hosts').style.display='';
    document.getElementById('host-count').textContent=hosts.length;
    const body=document.getElementById('hosts-body');
    body.innerHTML=hosts.map(h=>`<tr>
        <td><strong>${esc(h.ip)}</strong></td>
        <td>${esc(h.hostname||'—')}</td>
        <td style="font-size:0.8rem">${esc(h.mac||'—')}</td>
        <td style="font-size:0.8rem">${esc(h.os_guess||'—')}</td>
        <td>${(h.ports||[]).length}</td>
        <td><button class="btn btn-sm" onclick="scanHost('${h.ip}')">Detail Scan</button></td>
    </tr>`).join('');
}

function scanHost(ip){
    fetch('/net-mapper/scan-host',{method:'POST',headers:{'Content-Type':'application/json'},
        body:JSON.stringify({ip})})
    .then(r=>r.json()).then(d=>{
        if(!d.ok){alert(d.error);return}
        const h=d.host;
        let msg=`${h.ip} — ${h.os_guess||'unknown OS'}\n\nOpen Ports:\n`;
        (h.ports||[]).forEach(p=>{msg+=`  ${p.port}/${p.protocol} ${p.service||''} ${p.version||''}\n`});
        alert(msg);
        // Update in current hosts
        const idx=currentHosts.findIndex(x=>x.ip===ip);
        if(idx>=0) currentHosts[idx]=h;
    });
}

function showTopology(){
    if(!currentHosts.length) return;
    fetch('/net-mapper/topology',{method:'POST',headers:{'Content-Type':'application/json'},
        body:JSON.stringify({hosts:currentHosts})})
    .then(r=>r.json()).then(d=>{
        if(!d.ok) return;
        renderTopology(d);
        switchTab('map');
    });
}

function renderTopology(data){
    const svg=document.getElementById('topo-svg');
    const w=svg.clientWidth||800,h=svg.clientHeight||500;
    svg.innerHTML='';
    const nodes=data.nodes.filter(n=>n.type!=='subnet');
    const colors={host:'#6366f1',web:'#22c55e',server:'#f59e0b',windows:'#3b82f6',subnet:'#444'};

    // Simple force-directed-ish layout
    nodes.forEach((n,i)=>{
        const angle=(i/nodes.length)*Math.PI*2;
        const radius=Math.min(w,h)*0.35;
        n.x=w/2+Math.cos(angle)*radius;
        n.y=h/2+Math.sin(angle)*radius;
    });

    // Edges
    data.edges.forEach(e=>{
        const from=nodes.find(n=>n.id===e.from);
        const to=nodes.find(n=>n.id===e.to);
        if(from&&to){
            const line=document.createElementNS('http://www.w3.org/2000/svg','line');
            line.setAttribute('x1',from.x);line.setAttribute('y1',from.y);
            line.setAttribute('x2',to.x);line.setAttribute('y2',to.y);
            line.setAttribute('stroke','#333');line.setAttribute('stroke-width','1');
            svg.appendChild(line);
        }
    });

    // Nodes
    nodes.forEach(n=>{
        const g=document.createElementNS('http://www.w3.org/2000/svg','g');
        const circle=document.createElementNS('http://www.w3.org/2000/svg','circle');
        circle.setAttribute('cx',n.x);circle.setAttribute('cy',n.y);
        circle.setAttribute('r','8');circle.setAttribute('fill',colors[n.type]||'#6366f1');
        circle.classList.add('node-circle');
        const text=document.createElementNS('http://www.w3.org/2000/svg','text');
        text.setAttribute('x',n.x);text.setAttribute('y',n.y+20);
        text.setAttribute('text-anchor','middle');text.setAttribute('fill','#888');
        text.setAttribute('font-size','10');text.textContent=n.label||n.ip;
        g.appendChild(circle);g.appendChild(text);svg.appendChild(g);
    });

    document.getElementById('topo-info').textContent=`${nodes.length} hosts across ${data.subnets.length} subnet(s)`;
}

function saveScan(){
    if(!currentHosts.length) return;
    const name=prompt('Scan name:');
    if(!name) return;
    fetch('/net-mapper/scans',{method:'POST',headers:{'Content-Type':'application/json'},
        body:JSON.stringify({name,hosts:currentHosts})})
    .then(r=>r.json()).then(d=>{if(d.ok) alert('Scan saved')});
}

function loadScans(){
    fetch('/net-mapper/scans').then(r=>r.json()).then(d=>{
        const scans=d.scans||[];
        const list=document.getElementById('scans-list');
        const s1=document.getElementById('diff-s1'),s2=document.getElementById('diff-s2');
        s1.innerHTML='';s2.innerHTML='';
        if(!scans.length){list.innerHTML='<div class="card" style="color:var(--text-muted)">No saved scans</div>';return}
        list.innerHTML=scans.map(s=>`<div class="card" style="margin-bottom:0.5rem;cursor:pointer" onclick="loadSavedScan('${esc(s.file)}')">
            <div style="display:flex;justify-content:space-between"><div><strong>${esc(s.name)}</strong> — ${s.host_count} hosts</div>
            <span style="font-size:0.8rem;color:var(--text-muted)">${(s.timestamp||'').slice(0,19)}</span></div></div>`).join('');
        scans.forEach(s=>{
            s1.innerHTML+=`<option value="${esc(s.file)}">${esc(s.name)} (${s.host_count})</option>`;
            s2.innerHTML+=`<option value="${esc(s.file)}">${esc(s.name)} (${s.host_count})</option>`;
        });
    });
}

function loadSavedScan(file){
    fetch('/net-mapper/scans/'+encodeURIComponent(file)).then(r=>r.json()).then(d=>{
        if(!d.ok) return;
        currentHosts=d.scan.hosts||[];
        renderHosts(currentHosts);
        switchTab('discover');
    });
}

function diffScans(){
    const s1=document.getElementById('diff-s1').value;
    const s2=document.getElementById('diff-s2').value;
    if(!s1||!s2) return;
    fetch('/net-mapper/diff',{method:'POST',headers:{'Content-Type':'application/json'},
        body:JSON.stringify({scan1:s1,scan2:s2})})
    .then(r=>r.json()).then(d=>{
        if(!d.ok){document.getElementById('diff-results').innerHTML=esc(d.error);return}
        let html=`<div style="margin-top:0.5rem;font-size:0.85rem">`;
        html+=`<div style="color:#22c55e"><strong>+ New hosts (${d.new_hosts.length}):</strong> ${d.new_hosts.join(', ')||'none'}</div>`;
        html+=`<div style="color:var(--danger)"><strong>- Removed (${d.removed_hosts.length}):</strong> ${d.removed_hosts.join(', ')||'none'}</div>`;
        html+=`<div style="color:var(--text-muted)">Unchanged: ${d.unchanged_hosts.length}</div></div>`;
        document.getElementById('diff-results').innerHTML=html;
    });
}

function esc(s){return s?String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;'):''}
</script>
{% endblock %}


Instruction: Create an AUTARCH web template for the 'Network' page. It should extend base.html, have a page header, and use AUTARCH's CSS variables and UI patterns (sections, tab bars, data tables, buttons).

Input:
Template: network.html
Sections: 5
Tabs: 3
Forms/API calls: 1
Has JavaScript: True

Output:
{% extends "base.html" %}
{% block title %}Network Security - AUTARCH{% endblock %}

{% block content %}
<div class="page-header">
    <h1>Network Security</h1>
    <p style="margin:0;font-size:0.85rem;color:var(--text-secondary)">
        Connection analysis, intrusion detection, rogue device scanning, and real-time monitoring.
    </p>
</div>

<!-- Tab Bar -->
<div class="tab-bar" id="network-tab-bar">
    <button class="tab active" onclick="networkTab('connections')">Connections</button>
    <button class="tab" onclick="networkTab('ids')">Intrusion Detection</button>
    <button class="tab" onclick="networkTab('rogue')">Rogue Devices</button>
    <button class="tab" onclick="networkTab('monitor')">Monitor</button>
</div>

<!-- ==================== CONNECTIONS TAB ==================== -->
<div class="network-tab-panel" id="network-tab-connections">

<div class="section">
    <h2>Active Connections</h2>
    <div style="display:flex;gap:0.5rem;flex-wrap:wrap;margin-bottom:1rem">
        <button class="btn btn-primary" onclick="scanConnections()">Scan Connections</button>
        <button class="btn" onclick="scanArpTable()">ARP Table</button>
        <button class="btn" onclick="scanInterfaces()">Interfaces</button>
    </div>
    <div id="conn-status" style="margin-bottom:0.5rem;font-size:0.85rem;color:var(--text-secondary)"></div>
    <div id="conn-results" style="overflow-x:auto"></div>
</div>

</div>

<!-- ==================== INTRUSION DETECTION TAB ==================== -->
<div class="network-tab-panel hidden" id="network-tab-ids">

<div class="section">
    <h2>Intrusion Detection System</h2>
    <div style="display:flex;gap:0.5rem;align-items:center;margin-bottom:1rem">
        <button class="btn btn-primary" onclick="runIdsScan()">Run IDS Scan</button>
        <span id="ids-overall" style="font-size:0.85rem"></span>
    </div>
    <div id="ids-results"></div>
</div>

</div>

<!-- ==================== ROGUE DEVICES TAB ==================== -->
<div class="network-tab-panel hidden" id="network-tab-rogue">

<div class="section">
    <h2>Rogue Device Detection</h2>
    <div style="display:flex;gap:0.5rem;align-items:center;margin-bottom:1rem">
        <button class="btn btn-primary" onclick="scanRogueDevices()">Scan for Rogues</button>
        <span id="rogue-summary" style="font-size:0.85rem;color:var(--text-secondary)"></span>
    </div>

    <div id="rogue-new" style="margin-bottom:1rem"></div>

    <h3 style="margin-top:1rem">Known Devices</h3>
    <div id="rogue-known" style="overflow-x:auto"></div>
</div>

</div>

<!-- ==================== MONITOR TAB ==================== -->
<div class="network-tab-panel hidden" id="network-tab-monitor">

<div class="section">
    <h2>Real-Time Connection Monitor</h2>
    <div style="display:flex;gap:0.5rem;align-items:center;margin-bottom:1rem">
        <button class="btn btn-primary" id="monitor-start-btn" onclick="startMonitor()">Start Monitor</button>
        <button class="btn btn-danger" id="monitor-stop-btn" onclick="stopMonitor()" disabled>Stop Monitor</button>
        <span id="monitor-status" style="font-size:0.85rem;color:var(--text-secondary)">Stopped</span>
    </div>
    <div id="monitor-feed" style="overflow-x:auto;max-height:500px;overflow-y:auto">
        <table class="data-table" style="font-size:0.8rem">
            <thead><tr><th>Time</th><th>Protocol</th><th>Local</th><th>Remote</th><th>Process</th></tr></thead>
            <tbody id="monitor-body"></tbody>
        </table>
    </div>
</div>

</div>

<!-- ==================== INTRUDER TRACE (always visible) ==================== -->
<div class="section" style="margin-top:2rem;border-top:1px solid var(--border);padding-top:1.5rem">
    <h2>Intruder Trace</h2>
    <p style="font-size:0.85rem;color:var(--text-secondary);margin-bottom:1rem">
        Trace an IP address: reverse DNS, GeoIP, whois, open ports, associated processes, connection history.
    </p>
    <div style="display:flex;gap:0.5rem;align-items:center;flex-wrap:wrap">
        <input type="text" id="trace-ip" class="form-control" placeholder="Enter IP address" style="max-width:250px">
        <button class="btn btn-primary" onclick="traceIntruder()">Trace</button>
    </div>
    <div id="trace-status" style="margin-top:0.5rem;font-size:0.85rem;color:var(--text-secondary)"></div>
    <div id="trace-results" style="margin-top:1rem"></div>
</div>

<style>
.hidden { display: none !important; }
.ids-group {
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 1rem;
    margin-bottom: 1rem;
}
.ids-group h4 {
    margin: 0 0 0.5rem 0;
    display: flex;
    align-items: center;
    gap: 0.5rem;
}
.severity-badge {
    display: inline-block;
    padding: 2px 8px;
    border-radius: 4px;
    font-size: 0.75rem;
    font-weight: 600;
    text-transform: uppercase;
}
.severity-critical { background: rgba(239,68,68,0.2); color: #ef4444; }
.severity-warning { background: rgba(234,179,8,0.2); color: #eab308; }
.severity-clean { background: rgba(34,197,94,0.2); color: #22c55e; }
.rogue-new-device {
    background: rgba(239,68,68,0.1);
    border: 1px solid rgba(239,68,68,0.3);
    border-radius: 8px;
    padding: 0.75rem 1rem;
    margin-bottom: 0.5rem;
    display: flex;
    justify-content: space-between;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.5rem;
}
.trace-section {
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 1rem;
    margin-bottom: 0.75rem;
}
.trace-section h4 { margin: 0 0 0.5rem 0; color: var(--accent); }
.trace-section pre {
    margin: 0;
    white-space: pre-wrap;
    word-break: break-all;
    font-size: 0.8rem;
    color: var(--text-secondary);
    max-height: 200px;
    overflow-y: auto;
}
</style>

<script>
/* ── Tab switching ── */
function networkTab(name) {
    var panels = document.querySelectorAll('.network-tab-panel');
    for (var i = 0; i < panels.length; i++) panels[i].classList.add('hidden');
    var tabs = document.querySelectorAll('#network-tab-bar .tab');
    for (var i = 0; i < tabs.length; i++) tabs[i].classList.remove('active');
    document.getElementById('network-tab-' + name).classList.remove('hidden');
    event.target.classList.add('active');
}

/* ── Helpers ── */
function postJSON(url, body) {
    return fetch(url, {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: body ? JSON.stringify(body) : '{}'
    }).then(function(r) { return r.json(); });
}

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

/* ── Connections ── */
function scanConnections() {
    var el = document.getElementById('conn-results');
    var st = document.getElementById('conn-status');
    st.textContent = 'Scanning connections...';
    el.innerHTML = '';
    postJSON('/network/connections').then(function(d) {
        if (!d.ok) { st.textContent = 'Error: ' + (d.error || 'unknown'); return; }
        st.textContent = d.count + ' connection(s) found';
        var html = '<table class="data-table" style="font-size:0.8rem"><thead><tr>' +
            '<th>Protocol</th><th>Local</th><th>Remote</th><th>State</th><th>Process</th></tr></thead><tbody>';
        for (var i = 0; i < d.connections.length; i++) {
            var c = d.connections[i];
            html += '<tr><td>' + escHtml(c.protocol) + '</td><td>' + escHtml(c.local) +
                '</td><td>' + escHtml(c.remote) + '</td><td>' + escHtml(c.state) +
                '</td><td>' + escHtml(c.process) + '</td></tr>';
        }
        html += '</tbody></table>';
        el.innerHTML = html;
    }).catch(function(e) { st.textContent = 'Error: ' + e; });
}

function scanArpTable() {
    var el = document.getElementById('conn-results');
    var st = document.getElementById('conn-status');
    st.textContent = 'Fetching ARP table...';
    el.innerHTML = '';
    postJSON('/network/arp-table').then(function(d) {
        if (!d.ok) { st.textContent = 'Error: ' + (d.error || 'unknown'); return; }
        st.textContent = d.count + ' ARP entries';
        var html = '<table class="data-table" style="font-size:0.8rem"><thead><tr>' +
            '<th>IP</th><th>MAC</th><th>Interface</th><th>State</th></tr></thead><tbody>';
        for (var i = 0; i < d.entries.length; i++) {
            var e = d.entries[i];
            html += '<tr><td>' + escHtml(e.ip) + '</td><td>' + escHtml(e.mac) +
                '</td><td>' + escHtml(e.dev) + '</td><td>' + escHtml(e.state) + '</td></tr>';
        }
        html += '</tbody></table>';
        el.innerHTML = html;
    }).catch(function(e) { st.textContent = 'Error: ' + e; });
}

function scanInterfaces() {
    var el = document.getElementById('conn-results');
    var st = document.getElementById('conn-status');
    st.textContent = 'Listing interfaces...';
    el.innerHTML = '';
    postJSON('/network/interfaces').then(function(d) {
        if (!d.ok) { st.textContent = 'Error: ' + (d.error || 'unknown'); return; }
        st.textContent = d.interfaces.length + ' interface(s)';
        var html = '<table class="data-table" style="font-size:0.8rem"><thead><tr>' +
            '<th>Name</th><th>State</th><th>MAC</th><th>MTU</th><th>Addresses</th></tr></thead><tbody>';
        for (var i = 0; i < d.interfaces.length; i++) {
            var ifc = d.interfaces[i];
            var addrs = '';
            if (ifc.addresses) {
                for (var j = 0; j < ifc.addresses.length; j++) {
                    if (j > 0) addrs += '<br>';
                    addrs += escHtml(ifc.addresses[j].address || '');
                }
            }
            html += '<tr><td><strong>' + escHtml(ifc.name) + '</strong></td><td>' + escHtml(ifc.state) +
                '</td><td>' + escHtml(ifc.mac) + '</td><td>' + escHtml(ifc.mtu) +
                '</td><td>' + addrs + '</td></tr>';
        }
        html += '</tbody></table>';
        el.innerHTML = html;
    }).catch(function(e) { st.textContent = 'Error: ' + e; });
}

/* ── IDS ── */
function severityBadge(sev) {
    return '<span class="severity-badge severity-' + sev + '">' + sev + '</span>';
}

function runIdsScan() {
    var el = document.getElementById('ids-results');
    var ov = document.getElementById('ids-overall');
    ov.innerHTML = 'Scanning...';
    el.innerHTML = '';
    postJSON('/network/ids/scan').then(function(d) {
        if (!d.ok) { ov.textContent = 'Scan failed'; return; }
        var r = d.results;
        ov.innerHTML = 'Overall: ' + severityBadge(r.overall);

        var groups = [
            {key: 'arp_spoof', title: 'ARP Spoof Detection'},
            {key: 'promiscuous', title: 'Promiscuous Mode'},
            {key: 'dhcp', title: 'Unauthorized DHCP'},
            {key: 'suspicious_conns', title: 'Suspicious Connections'},
            {key: 'raw_sockets', title: 'Raw Socket Processes'}
        ];

        var html = '';
        for (var i = 0; i < groups.length; i++) {
            var g = groups[i];
            var data = r[g.key];
            html += '<div class="ids-group">';
            html += '<h4>' + escHtml(g.title) + ' ' + severityBadge(data.severity) + '</h4>';
            html += '<p style="font-size:0.85rem;color:var(--text-secondary);margin:0 0 0.5rem 0">' + escHtml(data.details) + '</p>';
            if (data.alerts && data.alerts.length > 0) {
                html += '<ul style="margin:0;padding-left:1.2rem;font-size:0.8rem">';
                for (var j = 0; j < data.alerts.length; j++) {
                    var sev = data.severity === 'critical' ? 'color:#ef4444' : 'color:#eab308';
                    html += '<li style="' + sev + ';margin-bottom:0.25rem">' + escHtml(data.alerts[j].message) + '</li>';
                }
                html += '</ul>';
            }
            html += '</div>';
        }
        el.innerHTML = html;
    }).catch(function(e) { ov.textContent = 'Error: ' + e; });
}

/* ── Rogue Devices ── */
function scanRogueDevices() {
    var newEl = document.getElementById('rogue-new');
    var knownEl = document.getElementById('rogue-known');
    var sumEl = document.getElementById('rogue-summary');
    sumEl.textContent = 'Scanning...';
    newEl.innerHTML = '';
    knownEl.innerHTML = '';
    postJSON('/network/rogue-detect').then(function(d) {
        if (!d.ok) { sumEl.textContent = 'Error: ' + (d.error || 'unknown'); return; }
        var s = d.summary;
        sumEl.textContent = s.total + ' devices found, ' + s.known + ' known, ' + s.new + ' new, ' + s.spoofed + ' spoofed';

        // New/unauthorized devices
        if (d.new_devices.length > 0) {
            var html = '<h3 style="color:#ef4444;margin-bottom:0.5rem">New / Unknown Devices</h3>';
            for (var i = 0; i < d.new_devices.length; i++) {
                var dev = d.new_devices[i];
                html += '<div class="rogue-new-device">';
                html += '<div><strong>' + escHtml(dev.ip) + '</strong> &mdash; ' + escHtml(dev.mac) + '</div>';
                html += '<div style="display:flex;gap:0.5rem">';
                html += '<button class="btn btn-sm" onclick="trustDevice(\'' + escHtml(dev.ip) + '\',\'' + escHtml(dev.mac) + '\')">Trust</button>';
                html += '<button class="btn btn-danger btn-sm" onclick="blockDevice(\'' + escHtml(dev.ip) + '\')">Block</button>';
                html += '</div></div>';
            }
            newEl.innerHTML = html;
        }

        // Spoofed alerts
        if (d.spoofed.length > 0) {
            var shtml = '<h3 style="color:#ef4444;margin:1rem 0 0.5rem 0">Spoofed MAC Alerts</h3>';
            for (var i = 0; i < d.spoofed.length; i++) {
                shtml += '<div class="rogue-new-device">' + escHtml(d.spoofed[i].message) + '</div>';
            }
            newEl.innerHTML += shtml;
        }

        // Known devices table
        var known = d.known_devices;
        var keys = Object.keys(known);
        if (keys.length > 0) {
            var khtml = '<table class="data-table" style="font-size:0.8rem"><thead><tr>' +
                '<th>IP</th><th>MAC</th><th>Trusted</th><th>First Seen</th><th>Last Seen</th></tr></thead><tbody>';
            for (var i = 0; i < keys.length; i++) {
                var k = keys[i];
                var kd = known[k];
                khtml += '<tr><td>' + escHtml(k) + '</td><td>' + escHtml(kd.mac) +
                    '</td><td>' + (kd.trusted ? 'Yes' : 'No') +
                    '</td><td>' + escHtml(kd.first_seen || '') +
                    '</td><td>' + escHtml(kd.last_seen || '') + '</td></tr>';
            }
            khtml += '</tbody></table>';
            knownEl.innerHTML = khtml;
        } else {
            knownEl.innerHTML = '<p style="color:var(--text-secondary);font-size:0.85rem">No known devices yet. Scan and trust devices to build your baseline.</p>';
        }
    }).catch(function(e) { sumEl.textContent = 'Error: ' + e; });
}

function trustDevice(ip, mac) {
    postJSON('/network/rogue-detect/trust', {ip: ip, mac: mac}).then(function(d) {
        if (d.ok) scanRogueDevices();
        else alert('Error: ' + (d.error || 'unknown'));
    });
}

function blockDevice(ip) {
    if (!confirm('Block IP ' + ip + '?')) return;
    postJSON('/network/block-ip', {ip: ip, action: 'block'}).then(function(d) {
        alert(d.message || d.error || 'Done');
    });
}

/* ── Monitor ── */
var monitorSource = null;

function startMonitor() {
    document.getElementById('monitor-body').innerHTML = '';
    postJSON('/network/monitor/start').then(function(d) {
        if (!d.ok) { alert(d.error || 'Failed'); return; }
        document.getElementById('monitor-start-btn').disabled = true;
        document.getElementById('monitor-stop-btn').disabled = false;
        document.getElementById('monitor-status').textContent = 'Running...';
        document.getElementById('monitor-status').style.color = '#22c55e';

        monitorSource = new EventSource('/network/monitor/feed');
        monitorSource.onmessage = function(ev) {
            try {
                var data = JSON.parse(ev.data);
                if (data.done) { stopMonitor(); return; }
                var body = document.getElementById('monitor-body');
                var row = document.createElement('tr');
                var ts = data.timestamp ? data.timestamp.split('T')[1].split('.')[0] : '';
                row.innerHTML = '<td>' + escHtml(ts) + '</td><td>' + escHtml(data.protocol) +
                    '</td><td>' + escHtml(data.local) + '</td><td>' + escHtml(data.remote) +
                    '</td><td>' + escHtml(data.process) + '</td>';
                body.insertBefore(row, body.firstChild);
                // Cap displayed rows
                while (body.children.length > 200) body.removeChild(body.lastChild);
            } catch(e) {}
        };
        monitorSource.onerror = function() {
            document.getElementById('monitor-status').textContent = 'Connection lost';
            document.getElementById('monitor-status').style.color = '#ef4444';
        };
    });
}

function stopMonitor() {
    postJSON('/network/monitor/stop').then(function() {
        document.getElementById('monitor-start-btn').disabled = false;
        document.getElementById('monitor-stop-btn').disabled = true;
        document.getElementById('monitor-status').textContent = 'Stopped';
        document.getElementById('monitor-status').style.color = 'var(--text-secondary)';
    });
    if (monitorSource) { monitorSource.close(); monitorSource = null; }
}

/* ── Intruder Trace ── */
function traceIntruder() {
    var ip = document.getElementById('trace-ip').value.trim();
    if (!ip) { alert('Enter an IP address'); return; }
    var st = document.getElementById('trace-status');
    var el = document.getElementById('trace-results');
    st.textContent = 'Tracing ' + ip + '... this may take up to 30 seconds.';
    el.innerHTML = '';
    postJSON('/network/intruder-trace', {ip: ip}).then(function(d) {
        if (!d.ok) { st.textContent = 'Error: ' + (d.error || 'unknown'); return; }
        st.textContent = 'Trace complete for ' + ip;
        var t = d.trace;
        var sections = [
            {title: 'Reverse DNS', data: t.reverse_dns},
            {title: 'GeoIP', data: t.geoip},
            {title: 'Whois', data: t.whois},
            {title: 'Open Ports', data: t.open_ports},
            {title: 'Associated Processes', data: t.processes},
            {title: 'Connection History', data: t.connection_history}
        ];
        var html = '';
        for (var i = 0; i < sections.length; i++) {
            html += '<div class="trace-section"><h4>' + escHtml(sections[i].title) + '</h4>';
            html += '<pre>' + escHtml(sections[i].data) + '</pre></div>';
        }
        el.innerHTML = html;
    }).catch(function(e) { st.textContent = 'Error: ' + e; });
}
</script>
{% endblock %}