Instruction: Create an AUTARCH web template for the 'Reverse Eng' 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: reverse_eng.html
Sections: 16
Tabs: 3
Forms/API calls: 0
Has JavaScript: True

Output:
{% extends "base.html" %}
{% block title %}AUTARCH — Reverse Engineering{% endblock %}

{% block content %}
<div class="page-header">
    <h1>Reverse Engineering</h1>
    <p style="margin:0;font-size:0.85rem;color:var(--text-secondary)">
        Binary analysis, disassembly, YARA scanning, hex viewing, and packer detection.
    </p>
</div>

<!-- Tab Bar -->
<div class="tab-bar">
    <button class="tab active" data-tab-group="re" data-tab="analyze" onclick="showTab('re','analyze')">Analyze</button>
    <button class="tab" data-tab-group="re" data-tab="disasm" onclick="showTab('re','disasm')">Disasm</button>
    <button class="tab" data-tab-group="re" data-tab="yara" onclick="showTab('re','yara')">YARA</button>
    <button class="tab" data-tab-group="re" data-tab="hex" onclick="showTab('re','hex')">Hex View</button>
</div>

<!-- ==================== ANALYZE TAB ==================== -->
<div class="tab-content active" data-tab-group="re" data-tab="analyze">

<div class="section">
    <h2>Binary Analysis</h2>
    <p style="font-size:0.8rem;color:var(--text-muted);margin-bottom:12px">
        Comprehensive file analysis: type detection, hashes, entropy, strings, imports, exports, and packer detection.
    </p>
    <div class="form-row">
        <div class="form-group" style="flex:1">
            <label>File Path</label>
            <input type="text" id="analyze-file" placeholder="/path/to/binary.exe">
        </div>
        <div class="form-group" style="max-width:160px;align-self:flex-end">
            <button id="btn-analyze" class="btn btn-primary" onclick="reAnalyze()">Analyze</button>
        </div>
    </div>
</div>

<!-- Analysis Results (hidden until analysis runs) -->
<div id="analyze-results" style="display:none">

    <!-- File Info Card -->
    <div class="section">
        <h2>File Info</h2>
        <table class="data-table" id="analyze-info-table">
            <tbody>
                <tr><td style="width:140px">Name</td><td id="a-name" style="font-weight:600"></td></tr>
                <tr><td>Type</td><td id="a-type"></td></tr>
                <tr><td>Architecture</td><td id="a-arch"></td></tr>
                <tr><td>Size</td><td id="a-size"></td></tr>
                <tr><td>Modified</td><td id="a-modified"></td></tr>
                <tr><td>MD5</td><td id="a-md5" style="font-family:monospace;font-size:0.85rem"></td></tr>
                <tr><td>SHA1</td><td id="a-sha1" style="font-family:monospace;font-size:0.85rem"></td></tr>
                <tr><td>SHA256</td><td id="a-sha256" style="font-family:monospace;font-size:0.85rem;word-break:break-all"></td></tr>
            </tbody>
        </table>
    </div>

    <!-- Entropy -->
    <div class="section">
        <h2>Entropy</h2>
        <div style="display:flex;align-items:center;gap:16px;margin-bottom:12px">
            <span style="font-size:1.2rem;font-weight:600" id="a-entropy-value"></span>
            <div style="flex:1;height:20px;background:var(--bg-input);border-radius:4px;overflow:hidden">
                <div id="a-entropy-bar" style="height:100%;border-radius:4px;transition:width 0.4s"></div>
            </div>
            <span id="a-entropy-label" style="font-size:0.8rem;font-weight:600;padding:2px 8px;border-radius:4px"></span>
        </div>
        <table class="data-table" id="a-sections-table" style="display:none">
            <thead>
                <tr><th>Section</th><th>Raw Size</th><th>Entropy</th><th style="width:40%">Bar</th><th>Status</th></tr>
            </thead>
            <tbody id="a-sections-body"></tbody>
        </table>
    </div>

    <!-- Imports -->
    <div class="section" id="a-imports-section" style="display:none">
        <h2>Imports <span id="a-imports-count" style="font-size:0.8rem;color:var(--text-muted)"></span></h2>
        <div id="a-imports-container"></div>
    </div>

    <!-- Exports -->
    <div class="section" id="a-exports-section" style="display:none">
        <h2>Exports <span id="a-exports-count" style="font-size:0.8rem;color:var(--text-muted)"></span></h2>
        <table class="data-table" id="a-exports-table">
            <thead><tr><th>Name</th><th>Ordinal</th><th>Address</th></tr></thead>
            <tbody id="a-exports-body"></tbody>
        </table>
    </div>

    <!-- Packer Detection -->
    <div class="section" id="a-packer-section" style="display:none">
        <h2>Packer Detection</h2>
        <div id="a-packer-result"></div>
    </div>

    <!-- Strings Preview -->
    <div class="section">
        <h2>Strings <span id="a-strings-count" style="font-size:0.8rem;color:var(--text-muted)"></span></h2>
        <div class="form-row" style="margin-bottom:8px">
            <input type="text" id="a-strings-filter" placeholder="Filter strings..." style="max-width:300px" oninput="reFilterStrings()">
        </div>
        <div id="a-strings-container" style="max-height:300px;overflow-y:auto;font-family:monospace;font-size:0.82rem;background:var(--bg-primary);padding:8px;border-radius:var(--radius);border:1px solid var(--border)"></div>
    </div>

</div>

<!-- Compare Section -->
<div class="section" style="margin-top:24px;border-top:1px solid var(--border);padding-top:24px">
    <h2>Binary Comparison</h2>
    <div class="form-row">
        <div class="form-group">
            <label>File 1</label>
            <input type="text" id="cmp-file1" placeholder="/path/to/binary1">
        </div>
        <div class="form-group">
            <label>File 2</label>
            <input type="text" id="cmp-file2" placeholder="/path/to/binary2">
        </div>
        <div class="form-group" style="max-width:120px;align-self:flex-end">
            <button id="btn-compare" class="btn btn-primary btn-small" onclick="reCompare()">Compare</button>
        </div>
    </div>
    <pre class="output-panel" id="cmp-result" style="display:none"></pre>
</div>

</div>

<!-- ==================== DISASM TAB ==================== -->
<div class="tab-content" data-tab-group="re" data-tab="disasm">

<div class="section">
    <h2>Disassembly</h2>
    <p style="font-size:0.8rem;color:var(--text-muted);margin-bottom:12px">
        Disassemble binary data from a file section or raw hex bytes.
    </p>

    <!-- Input Mode Toggle -->
    <div style="margin-bottom:12px">
        <button class="btn btn-small" id="disasm-mode-file" onclick="reDisasmMode('file')" style="opacity:1">From File</button>
        <button class="btn btn-small" id="disasm-mode-hex" onclick="reDisasmMode('hex')" style="opacity:0.5">From Hex Bytes</button>
    </div>

    <!-- File Mode -->
    <div id="disasm-file-inputs">
        <div class="form-row">
            <div class="form-group" style="flex:1">
                <label>File Path</label>
                <input type="text" id="disasm-file" placeholder="/path/to/binary">
            </div>
            <div class="form-group" style="max-width:140px">
                <label>Section</label>
                <input type="text" id="disasm-section" placeholder=".text" value=".text">
            </div>
            <div class="form-group" style="max-width:120px">
                <label>Offset</label>
                <input type="number" id="disasm-offset" value="0" min="0">
            </div>
        </div>
    </div>

    <!-- Hex Mode -->
    <div id="disasm-hex-inputs" style="display:none">
        <div class="form-group">
            <label>Hex Bytes</label>
            <textarea id="disasm-hex" rows="4" placeholder="48 89 5c 24 08 48 89 6c 24 10 48 89 74 24 18 ..." style="font-family:monospace"></textarea>
        </div>
    </div>

    <div class="form-row">
        <div class="form-group" style="max-width:140px">
            <label>Architecture</label>
            <select id="disasm-arch">
                <option value="x64">x86-64</option>
                <option value="x86">x86 (32-bit)</option>
                <option value="arm">ARM</option>
                <option value="arm64">ARM64</option>
            </select>
        </div>
        <div class="form-group" style="max-width:120px">
            <label>Count</label>
            <input type="number" id="disasm-count" value="100" min="1" max="5000">
        </div>
        <div class="form-group" style="max-width:160px;align-self:flex-end">
            <button id="btn-disasm" class="btn btn-primary" onclick="reDisassemble()">Disassemble</button>
        </div>
    </div>
</div>

<!-- Disassembly Output -->
<div class="section" id="disasm-results" style="display:none">
    <h2>Disassembly Output <span id="disasm-count-label" style="font-size:0.8rem;color:var(--text-muted)"></span></h2>
    <div style="max-height:600px;overflow-y:auto;background:var(--bg-primary);border:1px solid var(--border);border-radius:var(--radius)">
        <table class="data-table" style="margin:0">
            <thead><tr><th style="width:100px">Address</th><th style="width:180px">Bytes</th><th style="width:100px">Mnemonic</th><th>Operands</th></tr></thead>
            <tbody id="disasm-body"></tbody>
        </table>
    </div>
</div>

</div>

<!-- ==================== YARA TAB ==================== -->
<div class="tab-content" data-tab-group="re" data-tab="yara">

<div class="section">
    <h2>YARA Scanner</h2>
    <p style="font-size:0.8rem;color:var(--text-muted);margin-bottom:12px">
        Scan files against YARA rules for malware signatures, patterns, and indicators of compromise.
    </p>
    <div class="form-row">
        <div class="form-group" style="flex:1">
            <label>File Path to Scan</label>
            <input type="text" id="yara-file" placeholder="/path/to/suspect_binary">
        </div>
    </div>
    <div class="form-row">
        <div class="form-group" style="max-width:250px">
            <label>Rule Source</label>
            <select id="yara-source" onchange="reYaraSourceChange()">
                <option value="all">All Built-in Rules</option>
                <option value="select">Select Rule File</option>
                <option value="custom">Custom Rule (inline)</option>
            </select>
        </div>
        <div class="form-group" style="flex:1;display:none" id="yara-select-group">
            <label>Rule File</label>
            <select id="yara-rulefile"><option value="">Loading...</option></select>
        </div>
    </div>
    <div class="form-group" id="yara-custom-group" style="display:none">
        <label>Custom YARA Rule</label>
        <textarea id="yara-custom" rows="8" placeholder='rule example_rule {
    strings:
        $a = "malware"
        $b = { 6A 40 68 00 30 00 00 }
    condition:
        $a or $b
}' style="font-family:monospace;font-size:0.85rem"></textarea>
    </div>
    <div class="tool-actions">
        <button id="btn-yara" class="btn btn-primary" onclick="reYaraScan()">Scan</button>
        <button class="btn btn-small" onclick="reYaraLoadRules()">Refresh Rules</button>
    </div>
</div>

<!-- YARA Results -->
<div class="section" id="yara-results" style="display:none">
    <h2>Scan Results <span id="yara-match-count" style="font-size:0.8rem;color:var(--text-muted)"></span></h2>
    <div id="yara-results-container"></div>
</div>

<!-- Available Rules -->
<div class="section">
    <h2>Available Rules</h2>
    <div id="yara-rules-list" style="font-size:0.85rem;color:var(--text-secondary)">
        <em>Click "Refresh Rules" to load the list of available YARA rule files.</em>
    </div>
</div>

</div>

<!-- ==================== HEX VIEW TAB ==================== -->
<div class="tab-content" data-tab-group="re" data-tab="hex">

<div class="section">
    <h2>Hex Viewer</h2>
    <p style="font-size:0.8rem;color:var(--text-muted);margin-bottom:12px">
        View raw binary content in hex and ASCII format.
    </p>
    <div class="form-row">
        <div class="form-group" style="flex:1">
            <label>File Path</label>
            <input type="text" id="hex-file" placeholder="/path/to/binary">
        </div>
        <div class="form-group" style="max-width:120px">
            <label>Offset</label>
            <input type="number" id="hex-offset" value="0" min="0">
        </div>
        <div class="form-group" style="max-width:120px">
            <label>Length</label>
            <input type="number" id="hex-length" value="512" min="16" max="65536">
        </div>
        <div class="form-group" style="max-width:100px;align-self:flex-end">
            <button id="btn-hex" class="btn btn-primary" onclick="reHexDump()">Load</button>
        </div>
    </div>
    <div style="display:flex;gap:8px;margin-top:4px">
        <button class="btn btn-small" onclick="reHexNav(-1)" title="Previous page">Prev</button>
        <button class="btn btn-small" onclick="reHexNav(1)" title="Next page">Next</button>
        <span id="hex-info" style="font-size:0.8rem;color:var(--text-muted);align-self:center"></span>
    </div>
</div>

<!-- Hex Dump Display -->
<div class="section" id="hex-dump-section" style="display:none">
    <pre id="hex-dump-output" style="font-family:'Cascadia Code','Fira Code','Courier New',monospace;font-size:0.82rem;line-height:1.6;background:var(--bg-primary);padding:12px;border:1px solid var(--border);border-radius:var(--radius);overflow-x:auto;max-height:600px;overflow-y:auto;white-space:pre;tab-size:8"></pre>
</div>

<!-- Hex Search -->
<div class="section">
    <h2>Hex Search</h2>
    <div class="form-row">
        <div class="form-group" style="flex:1">
            <label>Hex Pattern</label>
            <input type="text" id="hex-search-pattern" placeholder="4D 5A 90 00  or  4d5a9000" style="font-family:monospace">
        </div>
        <div class="form-group" style="max-width:120px;align-self:flex-end">
            <button id="btn-hex-search" class="btn btn-primary btn-small" onclick="reHexSearch()">Search</button>
        </div>
    </div>
    <div id="hex-search-results" style="display:none">
        <p style="font-size:0.85rem;margin-bottom:8px"><strong id="hex-search-count"></strong></p>
        <div id="hex-search-list" style="max-height:200px;overflow-y:auto;font-family:monospace;font-size:0.82rem;background:var(--bg-primary);padding:8px;border-radius:var(--radius);border:1px solid var(--border)"></div>
    </div>
</div>

</div>

<!-- ==================== JAVASCRIPT ==================== -->
<script>
/* -- globals for hex pagination -- */
var _reStrings = [];
var _hexCurrentOffset = 0;
var _hexCurrentLength = 512;
var _hexCurrentFile = '';

/* ==================== ANALYZE ==================== */
function reAnalyze() {
    var btn = document.getElementById('btn-analyze');
    var file = document.getElementById('analyze-file').value.trim();
    if (!file) return;
    setLoading(btn, true);
    document.getElementById('analyze-results').style.display = 'none';
    postJSON('/reverse-eng/analyze', {file: file}).then(function(r) {
        setLoading(btn, false);
        if (r.error) { alert(r.error); return; }
        document.getElementById('analyze-results').style.display = '';
        /* File info */
        document.getElementById('a-name').textContent = r.name || '';
        document.getElementById('a-type').textContent = (r.file_type||{}).type || 'unknown';
        document.getElementById('a-arch').textContent = r.architecture || 'unknown';
        document.getElementById('a-size').textContent = (r.size_human||'') + ' (' + (r.size||0).toLocaleString() + ' bytes)';
        document.getElementById('a-modified').textContent = r.modified || '';
        document.getElementById('a-md5').textContent = (r.hashes||{}).md5 || '';
        document.getElementById('a-sha1').textContent = (r.hashes||{}).sha1 || '';
        document.getElementById('a-sha256').textContent = (r.hashes||{}).sha256 || '';
        /* Entropy */
        var ent = r.entropy || 0;
        document.getElementById('a-entropy-value').textContent = ent.toFixed(4);
        var pct = (ent / 8) * 100;
        var bar = document.getElementById('a-entropy-bar');
        bar.style.width = pct + '%';
        var lbl = document.getElementById('a-entropy-label');
        if (ent > 7.0) { bar.style.background = 'var(--danger)'; lbl.textContent = 'HIGH'; lbl.style.background = 'rgba(239,68,68,0.2)'; lbl.style.color = 'var(--danger)'; }
        else if (ent > 6.0) { bar.style.background = '#f59e0b'; lbl.textContent = 'MEDIUM'; lbl.style.background = 'rgba(245,158,11,0.2)'; lbl.style.color = '#f59e0b'; }
        else { bar.style.background = '#22c55e'; lbl.textContent = 'LOW'; lbl.style.background = 'rgba(34,197,94,0.2)'; lbl.style.color = '#22c55e'; }
        /* Section entropy */
        var se = r.section_entropy || [];
        var stbl = document.getElementById('a-sections-table');
        var sbody = document.getElementById('a-sections-body');
        sbody.innerHTML = '';
        if (se.length > 0) {
            stbl.style.display = '';
            se.forEach(function(s) {
                var color = s.entropy > 7.0 ? 'var(--danger)' : (s.entropy > 6.0 ? '#f59e0b' : '#22c55e');
                var w = (s.entropy / 8) * 100;
                var status = s.packed ? '<span style="color:var(--danger);font-weight:600">PACKED</span>' : '<span style="color:#22c55e">Normal</span>';
                sbody.innerHTML += '<tr><td style="font-family:monospace">' + escapeHtml(s.name) + '</td><td>' + (s.size||0).toLocaleString() + '</td><td style="font-weight:600;color:' + color + '">' + s.entropy.toFixed(2) + '</td><td><div style="height:14px;background:var(--bg-input);border-radius:3px;overflow:hidden"><div style="height:100%;width:' + w + '%;background:' + color + ';border-radius:3px"></div></div></td><td>' + status + '</td></tr>';
            });
        } else {
            stbl.style.display = 'none';
        }
        /* Imports */
        var imports = r.imports || [];
        var impSec = document.getElementById('a-imports-section');
        var impCont = document.getElementById('a-imports-container');
        impCont.innerHTML = '';
        if (imports.length > 0) {
            impSec.style.display = '';
            var totalFuncs = 0;
            imports.forEach(function(lib) { totalFuncs += (lib.functions||[]).length; });
            document.getElementById('a-imports-count').textContent = '(' + imports.length + ' libraries, ' + totalFuncs + ' functions)';
            imports.forEach(function(lib, idx) {
                var fns = lib.functions || [];
                var id = 'imp-lib-' + idx;
                var html = '<div style="margin-bottom:6px"><button class="btn btn-small" onclick="var e=document.getElementById(\'' + id + '\');e.style.display=e.style.display===\'none\'?\'\':\'none\'" style="font-family:monospace;font-size:0.82rem;text-align:left;width:100%;justify-content:flex-start">' + escapeHtml(lib.library || '?') + ' <span style="color:var(--text-muted)">(' + fns.length + ')</span></button>';
                html += '<div id="' + id + '" style="display:none;padding:4px 0 4px 20px;font-family:monospace;font-size:0.8rem;color:var(--text-secondary);max-height:200px;overflow-y:auto">';
                fns.forEach(function(f) {
                    html += '<div>' + escapeHtml(f.name || '') + '</div>';
                });
                html += '</div></div>';
                impCont.innerHTML += html;
            });
        } else {
            impSec.style.display = 'none';
        }
        /* Exports */
        var exports = r.exports || [];
        var expSec = document.getElementById('a-exports-section');
        var expBody = document.getElementById('a-exports-body');
        expBody.innerHTML = '';
        if (exports.length > 0) {
            expSec.style.display = '';
            document.getElementById('a-exports-count').textContent = '(' + exports.length + ')';
            exports.forEach(function(e) {
                expBody.innerHTML += '<tr><td style="font-family:monospace;font-size:0.85rem">' + escapeHtml(e.name||'') + '</td><td>' + (e.ordinal||'') + '</td><td style="font-family:monospace">' + escapeHtml(e.address||'') + '</td></tr>';
            });
        } else {
            expSec.style.display = 'none';
        }
        /* Packer */
        var packer = r.packer || {};
        var pkSec = document.getElementById('a-packer-section');
        var pkRes = document.getElementById('a-packer-result');
        pkSec.style.display = '';
        if (packer.detected) {
            var ph = '';
            (packer.detections||[]).forEach(function(d) {
                var confColor = d.confidence > 70 ? 'var(--danger)' : (d.confidence > 40 ? '#f59e0b' : 'var(--text-secondary)');
                ph += '<div style="padding:8px;background:var(--bg-primary);border:1px solid var(--border);border-radius:var(--radius);margin-bottom:8px">';
                ph += '<div style="font-weight:600;color:var(--danger)">' + escapeHtml(d.packer) + ' <span style="color:' + confColor + ';font-size:0.85rem">(' + d.confidence + '% confidence)</span></div>';
                ph += '<div style="font-size:0.82rem;color:var(--text-secondary);margin-top:4px">' + escapeHtml(d.description||'') + '</div>';
                (d.evidence||[]).forEach(function(ev) {
                    ph += '<div style="font-size:0.8rem;color:var(--text-muted);padding-left:12px;font-family:monospace">' + escapeHtml(ev) + '</div>';
                });
                ph += '</div>';
            });
            pkRes.innerHTML = ph;
        } else {
            pkRes.innerHTML = '<div style="padding:8px;color:#22c55e">No packer detected. Entropy: ' + (packer.overall_entropy||0).toFixed(4) + '</div>';
        }
        /* Strings */
        _reStrings = r.strings_preview || [];
        document.getElementById('a-strings-count').textContent = '(' + (r.strings_count||0).toLocaleString() + ' total, showing first ' + _reStrings.length + ')';
        reFilterStrings();
    }).catch(function(e) {
        setLoading(btn, false);
        alert('Analysis failed: ' + e);
    });
}

function reFilterStrings() {
    var filter = (document.getElementById('a-strings-filter').value || '').toLowerCase();
    var container = document.getElementById('a-strings-container');
    var html = '';
    var shown = 0;
    _reStrings.forEach(function(s) {
        if (filter && s.string.toLowerCase().indexOf(filter) === -1) return;
        if (shown >= 500) return;
        var enc = s.encoding === 'unicode' ? '<span style="color:#f59e0b">U</span>' : '<span style="color:var(--text-muted)">A</span>';
        html += '<div style="display:flex;gap:12px;border-bottom:1px solid var(--border);padding:2px 4px"><span style="color:var(--text-muted);min-width:80px">0x' + s.offset.toString(16).padStart(8, '0') + '</span>' + enc + ' <span>' + escapeHtml(s.string) + '</span></div>';
        shown++;
    });
    if (!html) html = '<div style="color:var(--text-muted);padding:8px">No strings match filter.</div>';
    container.innerHTML = html;
}

/* ==================== COMPARE ==================== */
function reCompare() {
    var btn = document.getElementById('btn-compare');
    var f1 = document.getElementById('cmp-file1').value.trim();
    var f2 = document.getElementById('cmp-file2').value.trim();
    if (!f1 || !f2) return;
    setLoading(btn, true);
    var out = document.getElementById('cmp-result');
    out.style.display = 'none';
    postJSON('/reverse-eng/compare', {file1: f1, file2: f2}).then(function(r) {
        setLoading(btn, false);
        out.style.display = '';
        if (r.error) { out.textContent = 'Error: ' + r.error; return; }
        var lines = [];
        lines.push('File 1: ' + r.file1.name + '  (' + r.file1.size.toLocaleString() + ' bytes, entropy ' + r.file1.entropy.toFixed(2) + ')');
        lines.push('File 2: ' + r.file2.name + '  (' + r.file2.size.toLocaleString() + ' bytes, entropy ' + r.file2.entropy.toFixed(2) + ')');
        lines.push('');
        if (r.identical) { lines.push('RESULT: Files are IDENTICAL'); }
        else {
            lines.push('Different bytes: ' + r.diff_bytes.toLocaleString() + ' (' + r.diff_percentage + '%)');
            lines.push('Diff regions: ' + r.diff_regions_total);
            if (r.section_diffs && r.section_diffs.length > 0) {
                lines.push('');
                lines.push('Section Diffs:');
                r.section_diffs.forEach(function(s) {
                    var marker = s.status === 'unchanged' ? '  ' : (s.status === 'modified' ? '* ' : (s.status === 'added' ? '+ ' : '- '));
                    var detail = s.size_file1 !== undefined ? '  (' + s.size_file1 + ' vs ' + s.size_file2 + ')' : '';
                    lines.push('  ' + marker + s.name + '  [' + s.status + ']' + detail);
                });
            }
        }
        lines.push('');
        lines.push('MD5  file1: ' + r.file1.hashes.md5);
        lines.push('MD5  file2: ' + r.file2.hashes.md5);
        out.textContent = lines.join('\n');
    }).catch(function(e) {
        setLoading(btn, false);
        out.style.display = '';
        out.textContent = 'Error: ' + e;
    });
}

/* ==================== DISASSEMBLE ==================== */
var _disasmFileMode = true;

function reDisasmMode(mode) {
    _disasmFileMode = (mode === 'file');
    document.getElementById('disasm-file-inputs').style.display = _disasmFileMode ? '' : 'none';
    document.getElementById('disasm-hex-inputs').style.display = _disasmFileMode ? 'none' : '';
    document.getElementById('disasm-mode-file').style.opacity = _disasmFileMode ? '1' : '0.5';
    document.getElementById('disasm-mode-hex').style.opacity = _disasmFileMode ? '0.5' : '1';
}

function reDisassemble() {
    var btn = document.getElementById('btn-disasm');
    var body = {};
    body.arch = document.getElementById('disasm-arch').value;
    body.count = parseInt(document.getElementById('disasm-count').value) || 100;
    if (_disasmFileMode) {
        body.file = document.getElementById('disasm-file').value.trim();
        body.section = document.getElementById('disasm-section').value.trim() || '.text';
        body.offset = parseInt(document.getElementById('disasm-offset').value) || 0;
        if (!body.file) return;
    } else {
        body.hex = document.getElementById('disasm-hex').value.trim();
        if (!body.hex) return;
    }
    setLoading(btn, true);
    document.getElementById('disasm-results').style.display = 'none';
    postJSON('/reverse-eng/disassemble', body).then(function(r) {
        setLoading(btn, false);
        if (r.error) { alert(r.error); return; }
        var insts = r.instructions || [];
        if (insts.length > 0 && insts[0].error) { alert(insts[0].error); return; }
        document.getElementById('disasm-results').style.display = '';
        document.getElementById('disasm-count-label').textContent = '(' + insts.length + ' instructions)';
        var tbody = document.getElementById('disasm-body');
        tbody.innerHTML = '';
        insts.forEach(function(inst) {
            var row = '<tr>';
            row += '<td style="font-family:monospace;color:var(--text-muted)">' + escapeHtml(inst.address||'') + '</td>';
            row += '<td style="font-family:monospace;font-size:0.8rem;color:var(--text-muted)">' + escapeHtml(inst.bytes_hex||'') + '</td>';
            row += '<td style="font-family:monospace;font-weight:600;color:var(--accent)">' + escapeHtml(inst.mnemonic||'') + '</td>';
            /* Highlight registers and immediates in operands */
            var ops = escapeHtml(inst.op_str||'');
            ops = ops.replace(/\b(rax|rbx|rcx|rdx|rsi|rdi|rbp|rsp|r8|r9|r10|r11|r12|r13|r14|r15|eax|ebx|ecx|edx|esi|edi|ebp|esp|ax|bx|cx|dx|al|bl|cl|dl|ah|bh|ch|dh|cs|ds|es|fs|gs|ss|rip|eip|ip)\b/gi, '<span style="color:#22c55e">$1</span>');
            ops = ops.replace(/\b(0x[0-9a-fA-F]+)\b/g, '<span style="color:#f59e0b">$1</span>');
            row += '<td style="font-family:monospace;font-size:0.85rem">' + ops + '</td>';
            row += '</tr>';
            tbody.innerHTML += row;
        });
    }).catch(function(e) {
        setLoading(btn, false);
        alert('Disassembly failed: ' + e);
    });
}

/* ==================== YARA ==================== */
function reYaraSourceChange() {
    var src = document.getElementById('yara-source').value;
    document.getElementById('yara-select-group').style.display = src === 'select' ? '' : 'none';
    document.getElementById('yara-custom-group').style.display = src === 'custom' ? '' : 'none';
    if (src === 'select') reYaraLoadRules();
}

function reYaraLoadRules() {
    fetchJSON('/reverse-eng/yara/rules').then(function(r) {
        var rules = r.rules || [];
        var sel = document.getElementById('yara-rulefile');
        sel.innerHTML = '';
        if (rules.length === 0) {
            sel.innerHTML = '<option value="">(no rules found)</option>';
        } else {
            rules.forEach(function(rule) {
                sel.innerHTML += '<option value="' + escapeHtml(rule.path) + '">' + escapeHtml(rule.name) + ' (' + rule.size + ' bytes)</option>';
            });
        }
        /* Also update rules list section */
        var list = document.getElementById('yara-rules-list');
        if (rules.length === 0) {
            list.innerHTML = '<em>No YARA rule files found. Place .yar/.yara files in data/reverse_eng/yara_rules/</em>';
        } else {
            var html = '<table class="data-table"><thead><tr><th>Name</th><th>Size</th><th>Modified</th></tr></thead><tbody>';
            rules.forEach(function(rule) {
                html += '<tr><td style="font-family:monospace">' + escapeHtml(rule.name) + '</td><td>' + rule.size + '</td><td>' + escapeHtml(rule.modified||'') + '</td></tr>';
            });
            html += '</tbody></table>';
            list.innerHTML = html;
        }
    });
}

function reYaraScan() {
    var btn = document.getElementById('btn-yara');
    var file = document.getElementById('yara-file').value.trim();
    if (!file) return;
    var body = {file: file};
    var src = document.getElementById('yara-source').value;
    if (src === 'select') {
        body.rules_path = document.getElementById('yara-rulefile').value;
    } else if (src === 'custom') {
        body.rules_string = document.getElementById('yara-custom').value;
    }
    setLoading(btn, true);
    document.getElementById('yara-results').style.display = 'none';
    postJSON('/reverse-eng/yara/scan', body).then(function(r) {
        setLoading(btn, false);
        var sec = document.getElementById('yara-results');
        sec.style.display = '';
        var container = document.getElementById('yara-results-container');
        if (r.error) {
            document.getElementById('yara-match-count').textContent = '';
            container.innerHTML = '<div style="color:var(--danger);padding:8px">' + escapeHtml(r.error) + '</div>';
            return;
        }
        var matches = r.matches || [];
        document.getElementById('yara-match-count').textContent = '(' + matches.length + ' matches, engine: ' + (r.engine||'') + ')';
        if (matches.length === 0) {
            container.innerHTML = '<div style="padding:8px;color:#22c55e">No YARA rules matched this file.</div>';
            return;
        }
        var html = '';
        matches.forEach(function(m) {
            html += '<div style="padding:10px;background:var(--bg-primary);border:1px solid var(--danger);border-radius:var(--radius);margin-bottom:8px">';
            html += '<div style="font-weight:600;color:var(--danger)">' + escapeHtml(m.rule||'') + '</div>';
            if (m.namespace) html += '<div style="font-size:0.8rem;color:var(--text-muted)">Namespace: ' + escapeHtml(m.namespace) + '</div>';
            if (m.tags && m.tags.length) html += '<div style="font-size:0.8rem;color:var(--text-muted)">Tags: ' + m.tags.map(escapeHtml).join(', ') + '</div>';
            if (m.meta && Object.keys(m.meta).length) {
                html += '<div style="font-size:0.8rem;color:var(--text-muted);margin-top:4px">';
                Object.keys(m.meta).forEach(function(k) {
                    html += '<div>' + escapeHtml(k) + ': ' + escapeHtml(String(m.meta[k])) + '</div>';
                });
                html += '</div>';
            }
            var strs = m.strings || [];
            if (strs.length > 0) {
                html += '<div style="margin-top:6px;font-family:monospace;font-size:0.82rem">';
                strs.forEach(function(s) {
                    html += '<div style="color:var(--text-secondary)">0x' + (s.offset||0).toString(16).padStart(8, '0') + ' ' + escapeHtml(s.identifier||'') + ' = ' + escapeHtml(s.data||'') + '</div>';
                });
                html += '</div>';
            }
            html += '</div>';
        });
        container.innerHTML = html;
    }).catch(function(e) {
        setLoading(btn, false);
        alert('YARA scan failed: ' + e);
    });
}

/* ==================== HEX VIEW ==================== */
function reHexDump() {
    var btn = document.getElementById('btn-hex');
    _hexCurrentFile = document.getElementById('hex-file').value.trim();
    _hexCurrentOffset = parseInt(document.getElementById('hex-offset').value) || 0;
    _hexCurrentLength = parseInt(document.getElementById('hex-length').value) || 512;
    if (!_hexCurrentFile) return;
    setLoading(btn, true);
    postJSON('/reverse-eng/hex', {file: _hexCurrentFile, offset: _hexCurrentOffset, length: _hexCurrentLength}).then(function(r) {
        setLoading(btn, false);
        if (r.error) { alert(r.error); return; }
        document.getElementById('hex-dump-section').style.display = '';
        var pre = document.getElementById('hex-dump-output');
        /* Color-coded hex dump */
        var lines = r.lines || [];
        var html = '';
        lines.forEach(function(l) {
            html += '<span style="color:var(--accent)">' + escapeHtml(l.offset) + '</span>  ';
            /* Color non-zero bytes differently */
            var hexParts = l.hex.split(' ');
            hexParts.forEach(function(h, i) {
                if (h === '' && i > 0) { html += ' '; return; } /* double-space separator */
                if (h === '00') html += '<span style="color:var(--text-muted)">' + h + '</span> ';
                else html += '<span style="color:var(--text-primary)">' + h + '</span> ';
            });
            /* Pad to fixed width */
            var padLen = 49 - l.hex.length;
            if (padLen > 0) html += ' '.repeat(padLen);
            html += ' <span style="color:#22c55e">|' + escapeHtml(l.ascii) + '|</span>\n';
        });
        pre.innerHTML = html;
        document.getElementById('hex-info').textContent = 'Offset: 0x' + _hexCurrentOffset.toString(16) + ' | Showing: ' + r.length + ' bytes | File: ' + r.file_size.toLocaleString() + ' bytes total';
    }).catch(function(e) {
        setLoading(btn, false);
        alert('Hex dump failed: ' + e);
    });
}

function reHexNav(direction) {
    if (!_hexCurrentFile) return;
    _hexCurrentOffset += direction * _hexCurrentLength;
    if (_hexCurrentOffset < 0) _hexCurrentOffset = 0;
    document.getElementById('hex-offset').value = _hexCurrentOffset;
    reHexDump();
}

function reHexSearch() {
    var btn = document.getElementById('btn-hex-search');
    var file = document.getElementById('hex-file').value.trim();
    var pattern = document.getElementById('hex-search-pattern').value.trim();
    if (!file || !pattern) return;
    setLoading(btn, true);
    document.getElementById('hex-search-results').style.display = 'none';
    postJSON('/reverse-eng/hex/search', {file: file, pattern: pattern}).then(function(r) {
        setLoading(btn, false);
        if (r.error) { alert(r.error); return; }
        document.getElementById('hex-search-results').style.display = '';
        document.getElementById('hex-search-count').textContent = r.total + ' matches found (pattern: ' + r.pattern + ')';
        var list = document.getElementById('hex-search-list');
        var matches = r.matches || [];
        if (matches.length === 0) {
            list.innerHTML = '<div style="color:var(--text-muted)">No matches found.</div>';
            return;
        }
        var html = '';
        matches.forEach(function(m) {
            html += '<div style="cursor:pointer;padding:2px 4px;border-bottom:1px solid var(--border)" onclick="document.getElementById(\'hex-offset\').value=' + m.offset + ';reHexDump()" title="Click to jump to offset">';
            html += '<span style="color:var(--accent)">' + escapeHtml(m.offset_hex) + '</span>';
            html += ' <span style="color:var(--text-muted)">' + escapeHtml(m.context) + '</span>';
            html += '</div>';
        });
        list.innerHTML = html;
    }).catch(function(e) {
        setLoading(btn, false);
        alert('Hex search failed: ' + e);
    });
}

/* ==================== INIT ==================== */
document.addEventListener('DOMContentLoaded', function() {
    /* Pre-load YARA rules list on tab switch */
});
</script>
{% endblock %}


Instruction: Create an AUTARCH web template for the 'Revshell' 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: revshell.html
Sections: 0
Tabs: 0
Forms/API calls: 2
Has JavaScript: True

Output:
{% extends "base.html" %}
{% block title %}Reverse Shell — AUTARCH{% endblock %}

{% block content %}
<div class="page-header">
    <h1>Reverse Shell</h1>
    <p>Remote device shell via Archon Companion App</p>
</div>

<!-- Listener Control -->
<div class="card">
    <div class="card-header">
        <h3>Listener</h3>
        <span id="listenerStatus" class="status-badge {% if running %}status-good{% else %}status-bad{% endif %}">
            {{ 'Running' if running else 'Stopped' }}
        </span>
    </div>
    <div class="card-body">
        <div class="form-row">
            <div class="form-group">
                <label>Port</label>
                <input type="number" id="listenerPort" value="{{ port }}" class="form-input" style="width:100px">
            </div>
            <div class="form-group">
                <label>Token</label>
                <input type="text" id="listenerToken" value="{{ token }}" class="form-input" style="width:300px" readonly>
            </div>
            <div class="form-group" style="padding-top:24px">
                <button onclick="rsListenerStart()" class="btn btn-primary" id="btnStart">Start</button>
                <button onclick="rsListenerStop()" class="btn btn-danger" id="btnStop">Stop</button>
                <button onclick="rsRefreshSessions()" class="btn btn-secondary">Refresh</button>
            </div>
        </div>
        <p class="help-text">
            On the Archon app: Modules tab → Reverse Shell → CONNECT.<br>
            The device will connect back to this listener on port <strong id="portDisplay">{{ port }}</strong>.
        </p>
    </div>
</div>

<!-- Sessions -->
<div class="card">
    <div class="card-header">
        <h3>Sessions</h3>
        <span id="sessionCount" class="status-badge">{{ sessions|length }}</span>
    </div>
    <div class="card-body">
        <div id="sessionList">
            {% if sessions %}
                {% for s in sessions %}
                <div class="session-card" data-sid="{{ s.session_id }}">
                    <div class="session-info">
                        <strong>{{ s.device }}</strong>
                        <span class="dim">Android {{ s.android }} | UID {{ s.uid }}</span>
                        <span class="dim">Connected {{ s.connected_at }} | {{ s.commands_executed }} cmds</span>
                    </div>
                    <div class="session-actions">
                        <button onclick="rsSelectSession('{{ s.session_id }}')" class="btn btn-primary btn-sm">Select</button>
                        <button onclick="rsDisconnect('{{ s.session_id }}')" class="btn btn-danger btn-sm">Disconnect</button>
                    </div>
                </div>
                {% endfor %}
            {% else %}
                <p class="dim">No active sessions. Start the listener and connect from the Archon app.</p>
            {% endif %}
        </div>
    </div>
</div>

<!-- Terminal -->
<div class="card" id="terminalCard" style="display:none">
    <div class="card-header">
        <h3>Terminal — <span id="termDevice">?</span></h3>
        <div>
            <button onclick="rsSysinfo()" class="btn btn-secondary btn-sm">SysInfo</button>
            <button onclick="rsScreenshot()" class="btn btn-secondary btn-sm">Screenshot</button>
            <button onclick="rsPackages()" class="btn btn-secondary btn-sm">Packages</button>
            <button onclick="rsProcesses()" class="btn btn-secondary btn-sm">Processes</button>
            <button onclick="rsNetstat()" class="btn btn-secondary btn-sm">Netstat</button>
            <button onclick="rsLogcat()" class="btn btn-secondary btn-sm">Logcat</button>
        </div>
    </div>
    <div class="card-body">
        <div id="termOutput" class="terminal-output"></div>
        <div class="terminal-input-row">
            <span class="terminal-prompt">shell@device:/ $</span>
            <input type="text" id="termInput" class="terminal-input"
                   placeholder="Type command and press Enter"
                   onkeydown="if(event.key==='Enter')rsExecCmd()">
            <button onclick="rsExecCmd()" class="btn btn-primary btn-sm">Run</button>
        </div>
    </div>
</div>

<!-- File Transfer -->
<div class="card" id="fileCard" style="display:none">
    <div class="card-header">
        <h3>File Transfer</h3>
    </div>
    <div class="card-body">
        <div class="form-row">
            <div class="form-group">
                <label>Download from device</label>
                <input type="text" id="downloadPath" class="form-input" placeholder="/sdcard/file.txt">
                <button onclick="rsDownload()" class="btn btn-secondary btn-sm" style="margin-top:4px">Download</button>
            </div>
            <div class="form-group">
                <label>Upload to device</label>
                <input type="file" id="uploadFile" class="form-input">
                <input type="text" id="uploadPath" class="form-input" placeholder="/data/local/tmp/file" style="margin-top:4px">
                <button onclick="rsUpload()" class="btn btn-secondary btn-sm" style="margin-top:4px">Upload</button>
            </div>
        </div>
    </div>
</div>

<!-- Screenshot Preview -->
<div class="card" id="screenshotCard" style="display:none">
    <div class="card-header">
        <h3>Screenshot</h3>
    </div>
    <div class="card-body" style="text-align:center">
        <img id="screenshotImg" style="max-width:100%;max-height:600px;border:1px solid var(--border)">
    </div>
</div>

<style>
.session-card {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 10px;
    margin-bottom: 8px;
    background: var(--surface-dark);
    border: 1px solid var(--border);
    border-radius: 4px;
}
.session-card.selected {
    border-color: var(--primary);
}
.session-info { display: flex; flex-direction: column; gap: 2px; }
.session-actions { display: flex; gap: 6px; }
.terminal-output {
    background: #0a0a0a;
    border: 1px solid var(--border);
    border-radius: 4px;
    padding: 12px;
    font-family: 'Courier New', monospace;
    font-size: 13px;
    color: #00ff41;
    min-height: 300px;
    max-height: 500px;
    overflow-y: auto;
    white-space: pre-wrap;
    word-break: break-all;
}
.terminal-input-row {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 8px;
    background: #0a0a0a;
    border: 1px solid var(--border);
    border-radius: 4px;
    padding: 8px 12px;
}
.terminal-prompt {
    color: #00ff41;
    font-family: 'Courier New', monospace;
    font-size: 13px;
    white-space: nowrap;
}
.terminal-input {
    flex: 1;
    background: transparent;
    border: none;
    color: #fff;
    font-family: 'Courier New', monospace;
    font-size: 13px;
    outline: none;
}
.dim { color: var(--text-secondary); font-size: 12px; }
.form-row { display: flex; gap: 16px; flex-wrap: wrap; }
.form-group { display: flex; flex-direction: column; }
.form-group label { font-size: 12px; color: var(--text-secondary); margin-bottom: 4px; }
.help-text { font-size: 12px; color: var(--text-secondary); margin-top: 8px; }
.status-badge {
    padding: 2px 8px;
    border-radius: 10px;
    font-size: 12px;
    font-weight: bold;
}
.status-good { background: #1a3a1a; color: #00ff41; }
.status-bad { background: #3a1a1a; color: #ff4444; }
.term-error { color: #ff4444; }
.term-info { color: #4488ff; }
.term-cmd { color: #888; }
</style>

<script>
let currentSession = null;
const cmdHistory = [];
let historyIdx = -1;

// ── Listener ───────────────────────────────────────────

function rsListenerStart() {
    const port = document.getElementById('listenerPort').value;
    fetchJSON('/revshell/listener/start', {port: parseInt(port)}, r => {
        if (r.success) {
            document.getElementById('listenerToken').value = r.token;
            document.getElementById('portDisplay').textContent = port;
            showStatus('listenerStatus', true, 'Running');
            termLog('Listener started on port ' + port, 'info');
        } else {
            termLog('Failed: ' + r.message, 'error');
        }
    });
}

function rsListenerStop() {
    fetchJSON('/revshell/listener/stop', {}, r => {
        showStatus('listenerStatus', false, 'Stopped');
        termLog('Listener stopped', 'info');
        document.getElementById('sessionList').innerHTML = '<p class="dim">No active sessions.</p>';
    });
}

function rsRefreshSessions() {
    fetchJSON('/revshell/sessions', {}, r => {
        const list = document.getElementById('sessionList');
        const count = document.getElementById('sessionCount');
        if (!r.sessions || r.sessions.length === 0) {
            list.innerHTML = '<p class="dim">No active sessions.</p>';
            count.textContent = '0';
            return;
        }
        count.textContent = r.sessions.length;
        list.innerHTML = r.sessions.map(s => `
            <div class="session-card ${currentSession === s.session_id ? 'selected' : ''}" data-sid="${s.session_id}">
                <div class="session-info">
                    <strong>${esc(s.device)}</strong>
                    <span class="dim">Android ${esc(s.android)} | UID ${s.uid} | ${s.commands_executed} cmds | ${formatUptime(s.uptime)}</span>
                </div>
                <div class="session-actions">
                    <button onclick="rsSelectSession('${s.session_id}')" class="btn btn-primary btn-sm">Select</button>
                    <button onclick="rsDisconnect('${s.session_id}')" class="btn btn-danger btn-sm">Disconnect</button>
                </div>
            </div>
        `).join('');
    });
}

// ── Session ────────────────────────────────────────────

function rsSelectSession(sid) {
    currentSession = sid;
    document.getElementById('terminalCard').style.display = '';
    document.getElementById('fileCard').style.display = '';
    document.querySelectorAll('.session-card').forEach(el => {
        el.classList.toggle('selected', el.dataset.sid === sid);
    });

    fetchJSON(`/revshell/session/${sid}/info`, {}, r => {
        if (r.success) {
            document.getElementById('termDevice').textContent =
                r.session.device + ' (Android ' + r.session.android + ')';
        }
    });

    clearTerm();
    termLog('Session selected: ' + sid, 'info');
    document.getElementById('termInput').focus();
}

function rsDisconnect(sid) {
    if (!confirm('Disconnect this session?')) return;
    fetchJSON(`/revshell/session/${sid}/disconnect`, {}, r => {
        termLog(r.message, r.success ? 'info' : 'error');
        if (sid === currentSession) {
            currentSession = null;
            document.getElementById('terminalCard').style.display = 'none';
            document.getElementById('fileCard').style.display = 'none';
        }
        rsRefreshSessions();
    });
}

// ── Terminal ───────────────────────────────────────────

function rsExecCmd() {
    if (!currentSession) { termLog('No session selected', 'error'); return; }
    const input = document.getElementById('termInput');
    const cmd = input.value.trim();
    if (!cmd) return;

    cmdHistory.unshift(cmd);
    historyIdx = -1;
    input.value = '';

    termLog('$ ' + cmd, 'cmd');
    fetchJSON(`/revshell/session/${currentSession}/execute`, {cmd: cmd}, r => {
        if (r.stdout) termLog(r.stdout);
        if (r.stderr) termLog(r.stderr, 'error');
        if (r.exit_code !== 0) termLog('[exit: ' + r.exit_code + ']', 'error');
    });
}

document.addEventListener('keydown', e => {
    const input = document.getElementById('termInput');
    if (document.activeElement !== input) return;
    if (e.key === 'ArrowUp' && cmdHistory.length > 0) {
        historyIdx = Math.min(historyIdx + 1, cmdHistory.length - 1);
        input.value = cmdHistory[historyIdx];
        e.preventDefault();
    } else if (e.key === 'ArrowDown') {
        historyIdx = Math.max(historyIdx - 1, -1);
        input.value = historyIdx >= 0 ? cmdHistory[historyIdx] : '';
        e.preventDefault();
    }
});

// ── Quick Actions ──────────────────────────────────────

function rsSysinfo() {
    if (!currentSession) return;
    termLog('--- System Info ---', 'info');
    fetchJSON(`/revshell/session/${currentSession}/sysinfo`, {}, r => {
        termLog(r.stdout || r.stderr || r.message);
    });
}

function rsScreenshot() {
    if (!currentSession) return;
    termLog('Capturing screenshot...', 'info');
    const img = document.getElementById('screenshotImg');
    img.src = `/revshell/session/${currentSession}/screenshot/view?t=` + Date.now();
    document.getElementById('screenshotCard').style.display = '';
    img.onerror = () => termLog('Screenshot failed', 'error');
    img.onload = () => termLog('Screenshot captured', 'info');
}

function rsPackages() {
    if (!currentSession) return;
    termLog('--- Installed Packages ---', 'info');
    fetchJSON(`/revshell/session/${currentSession}/packages`, {}, r => {
        termLog(r.stdout || r.stderr || r.message);
    });
}

function rsProcesses() {
    if (!currentSession) return;
    termLog('--- Processes ---', 'info');
    fetchJSON(`/revshell/session/${currentSession}/processes`, {}, r => {
        termLog(r.stdout || r.stderr || r.message);
    });
}

function rsNetstat() {
    if (!currentSession) return;
    termLog('--- Network Connections ---', 'info');
    fetchJSON(`/revshell/session/${currentSession}/netstat`, {}, r => {
        termLog(r.stdout || r.stderr || r.message);
    });
}

function rsLogcat() {
    if (!currentSession) return;
    termLog('--- Logcat (last 50 lines) ---', 'info');
    fetchJSON(`/revshell/session/${currentSession}/logcat`, {lines: 50}, r => {
        termLog(r.stdout || r.stderr || r.message);
    });
}

// ── File Transfer ──────────────────────────────────────

function rsDownload() {
    if (!currentSession) return;
    const path = document.getElementById('downloadPath').value.trim();
    if (!path) { termLog('No path specified', 'error'); return; }
    termLog('Downloading: ' + path, 'info');
    fetchJSON(`/revshell/session/${currentSession}/download`, {path: path}, r => {
        termLog(r.success ? 'Saved to: ' + r.path : 'Failed: ' + r.message, r.success ? 'info' : 'error');
    });
}

function rsUpload() {
    if (!currentSession) return;
    const fileInput = document.getElementById('uploadFile');
    const remotePath = document.getElementById('uploadPath').value.trim();
    if (!fileInput.files[0] || !remotePath) {
        termLog('Select a file and specify remote path', 'error');
        return;
    }
    termLog('Uploading to: ' + remotePath, 'info');
    const fd = new FormData();
    fd.append('file', fileInput.files[0]);
    fd.append('path', remotePath);
    fetch(`/revshell/session/${currentSession}/upload`, {method:'POST', body:fd})
        .then(r => r.json())
        .then(r => termLog(r.success ? r.stdout : 'Failed: ' + r.stderr, r.success ? 'info' : 'error'));
}

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

function termLog(text, type) {
    const out = document.getElementById('termOutput');
    if (!out) return;
    const span = document.createElement('span');
    if (type === 'error') span.className = 'term-error';
    else if (type === 'info') span.className = 'term-info';
    else if (type === 'cmd') span.className = 'term-cmd';
    span.textContent = text + '\n';
    out.appendChild(span);
    out.scrollTop = out.scrollHeight;
}

function clearTerm() {
    const out = document.getElementById('termOutput');
    if (out) out.innerHTML = '';
}

function showStatus(id, good, text) {
    const el = document.getElementById(id);
    el.textContent = text;
    el.className = 'status-badge ' + (good ? 'status-good' : 'status-bad');
}

function formatUptime(sec) {
    if (sec < 60) return sec + 's';
    if (sec < 3600) return Math.floor(sec/60) + 'm';
    return Math.floor(sec/3600) + 'h ' + Math.floor((sec%3600)/60) + 'm';
}

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

function fetchJSON(url, data, cb) {
    fetch(url, {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify(data)
    }).then(r => r.json()).then(cb).catch(e => {
        if (typeof termLog === 'function') termLog('Request failed: ' + e, 'error');
    });
}

// Auto-refresh sessions every 10s
setInterval(rsRefreshSessions, 10000);
</script>
{% endblock %}


Instruction: Create an AUTARCH web template for the 'Rfid Tools' 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: rfid_tools.html
Sections: 7
Tabs: 2
Forms/API calls: 0
Has JavaScript: True

Output:
{% extends "base.html" %}
{% block title %}AUTARCH — RFID/NFC Tools{% endblock %}

{% block content %}
<div class="page-header">
    <h1>RFID/NFC Tools</h1>
    <p style="margin:0;font-size:0.85rem;color:var(--text-secondary)">
        Proxmark3 interface for RFID/NFC scanning, cloning, and card management.
    </p>
</div>

<!-- Tab Bar -->
<div class="tab-bar">
    <button class="tab active" data-tab-group="rfid" data-tab="scan" onclick="showTab('rfid','scan')">Scan</button>
    <button class="tab" data-tab-group="rfid" data-tab="clone" onclick="showTab('rfid','clone')">Clone</button>
    <button class="tab" data-tab-group="rfid" data-tab="cards" onclick="showTab('rfid','cards')">Cards</button>
</div>

<!-- ==================== SCAN TAB ==================== -->
<div class="tab-content active" data-tab-group="rfid" data-tab="scan">

<div class="section">
    <h2>Tools Status</h2>
    <div class="stats-grid" style="grid-template-columns:repeat(auto-fit,minmax(140px,1fr))">
        <div class="stat-card">
            <div class="stat-label">Proxmark3</div>
            <div class="stat-value small">
                <span class="status-dot" id="rfid-pm3-dot"></span>
                <span id="rfid-pm3-status">Checking...</span>
            </div>
        </div>
        <div class="stat-card">
            <div class="stat-label">libnfc</div>
            <div class="stat-value small">
                <span class="status-dot" id="rfid-libnfc-dot"></span>
                <span id="rfid-libnfc-status">Checking...</span>
            </div>
        </div>
    </div>
</div>

<div class="section">
    <h2>Scan for Cards</h2>
    <div class="tool-actions" style="margin-bottom:12px">
        <button id="btn-lf-search" class="btn btn-primary" onclick="rfidLFSearch()">LF Search (125kHz)</button>
        <button id="btn-hf-search" class="btn btn-primary" onclick="rfidHFSearch()">HF Search (13.56MHz)</button>
        <button id="btn-nfc-scan" class="btn btn-primary" onclick="rfidNFCScan()">NFC Scan</button>
    </div>
    <pre class="output-panel scrollable" id="rfid-scan-output" style="max-height:250px"></pre>
</div>

<div class="section">
    <h2>Last Read Card</h2>
    <table class="data-table" style="max-width:500px">
        <tbody>
            <tr><td>Type</td><td id="rfid-last-type">--</td></tr>
            <tr><td>ID / UID</td><td id="rfid-last-id" style="font-family:monospace">--</td></tr>
            <tr><td>Frequency</td><td id="rfid-last-freq">--</td></tr>
            <tr><td>Technology</td><td id="rfid-last-tech">--</td></tr>
        </tbody>
    </table>
</div>

</div>

<!-- ==================== CLONE TAB ==================== -->
<div class="tab-content" data-tab-group="rfid" data-tab="clone">

<div class="section">
    <h2>EM410x Clone</h2>
    <p style="font-size:0.8rem;color:var(--text-muted);margin-bottom:8px">
        Clone an EM410x LF card by writing a known card ID to a T55x7 blank.
    </p>
    <div class="input-row">
        <input type="text" id="rfid-em-id" placeholder="Card ID (hex, e.g. 0102030405)" maxlength="10">
        <button id="btn-em-clone" class="btn btn-primary" onclick="rfidEMClone()">Clone EM410x</button>
    </div>
    <pre class="output-panel" id="rfid-em-output" style="min-height:0"></pre>
</div>

<div class="section">
    <h2>MIFARE Classic</h2>
    <div class="tool-actions" style="margin-bottom:12px">
        <button id="btn-mf-dump" class="btn btn-primary" onclick="rfidMFDump()">Dump MIFARE Card</button>
    </div>
    <pre class="output-panel" id="rfid-mf-dump-output" style="min-height:0"></pre>

    <h3 style="margin-top:16px">Clone from Dump</h3>
    <div class="input-row">
        <input type="text" id="rfid-mf-dump-path" placeholder="Path to dump file (e.g. /tmp/card.mfd)">
        <button id="btn-mf-clone" class="btn btn-primary" onclick="rfidMFClone()">Clone from Dump</button>
    </div>
    <pre class="output-panel" id="rfid-mf-clone-output" style="min-height:0"></pre>

    <h3 style="margin-top:16px">Default Keys</h3>
    <div class="output-panel" id="rfid-default-keys" style="font-family:monospace;font-size:0.8rem">
        FFFFFFFFFFFF (factory default)<br>
        A0A1A2A3A4A5 (MAD key)<br>
        D3F7D3F7D3F7 (NFC NDEF)<br>
        000000000000 (null key)<br>
        B0B1B2B3B4B5 (common transport)<br>
        4D3A99C351DD (Mifare Application Directory)
    </div>
</div>

</div>

<!-- ==================== CARDS TAB ==================== -->
<div class="tab-content" data-tab-group="rfid" data-tab="cards">

<div class="section">
    <h2>Saved Cards</h2>
    <table class="data-table">
        <thead><tr><th>Name</th><th>Type</th><th>ID / UID</th><th>Saved</th><th>Action</th></tr></thead>
        <tbody id="rfid-cards-table">
            <tr><td colspan="5" class="empty-state">No saved cards yet. Scan and save cards from the Scan tab.</td></tr>
        </tbody>
    </table>
</div>

<div class="section">
    <h2>Card Dumps</h2>
    <div class="tool-actions" style="margin-bottom:12px">
        <button class="btn btn-small" onclick="rfidRefreshDumps()">Refresh</button>
    </div>
    <table class="data-table">
        <thead><tr><th>Filename</th><th>Size</th><th>Date</th><th>Action</th></tr></thead>
        <tbody id="rfid-dumps-table">
            <tr><td colspan="4" class="empty-state">No dumps found.</td></tr>
        </tbody>
    </table>
</div>

</div>

<script>
function esc(s) { return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;'); }

/* ── Status ── */
function rfidCheckStatus() {
    fetchJSON('/rfid/status').then(function(data) {
        var pm3Dot = document.getElementById('rfid-pm3-dot');
        var pm3Txt = document.getElementById('rfid-pm3-status');
        var nfcDot = document.getElementById('rfid-libnfc-dot');
        var nfcTxt = document.getElementById('rfid-libnfc-status');
        if (pm3Dot) pm3Dot.className = 'status-dot ' + (data.proxmark3 ? 'active' : 'inactive');
        if (pm3Txt) pm3Txt.textContent = data.proxmark3 ? 'Connected' : 'Not found';
        if (nfcDot) nfcDot.className = 'status-dot ' + (data.libnfc ? 'active' : 'inactive');
        if (nfcTxt) nfcTxt.textContent = data.libnfc ? 'Available' : 'Not installed';
    }).catch(function() {
        document.getElementById('rfid-pm3-status').textContent = 'Error';
        document.getElementById('rfid-libnfc-status').textContent = 'Error';
    });
}

/* ── Scan ── */
function rfidLFSearch() {
    var btn = document.getElementById('btn-lf-search');
    setLoading(btn, true);
    postJSON('/rfid/scan', {mode: 'lf'}).then(function(data) {
        setLoading(btn, false);
        if (data.error) { renderOutput('rfid-scan-output', 'Error: ' + data.error); return; }
        renderOutput('rfid-scan-output', data.output || 'No card detected.');
        if (data.card) rfidUpdateLastCard(data.card);
    }).catch(function() { setLoading(btn, false); });
}

function rfidHFSearch() {
    var btn = document.getElementById('btn-hf-search');
    setLoading(btn, true);
    postJSON('/rfid/scan', {mode: 'hf'}).then(function(data) {
        setLoading(btn, false);
        if (data.error) { renderOutput('rfid-scan-output', 'Error: ' + data.error); return; }
        renderOutput('rfid-scan-output', data.output || 'No card detected.');
        if (data.card) rfidUpdateLastCard(data.card);
    }).catch(function() { setLoading(btn, false); });
}

function rfidNFCScan() {
    var btn = document.getElementById('btn-nfc-scan');
    setLoading(btn, true);
    postJSON('/rfid/scan', {mode: 'nfc'}).then(function(data) {
        setLoading(btn, false);
        if (data.error) { renderOutput('rfid-scan-output', 'Error: ' + data.error); return; }
        renderOutput('rfid-scan-output', data.output || 'No NFC tag detected.');
        if (data.card) rfidUpdateLastCard(data.card);
    }).catch(function() { setLoading(btn, false); });
}

function rfidUpdateLastCard(card) {
    document.getElementById('rfid-last-type').textContent = card.type || '--';
    document.getElementById('rfid-last-id').textContent = card.id || '--';
    document.getElementById('rfid-last-freq').textContent = card.frequency || '--';
    document.getElementById('rfid-last-tech').textContent = card.technology || '--';
}

/* ── Clone ── */
function rfidEMClone() {
    var id = document.getElementById('rfid-em-id').value.trim();
    if (!id) return;
    var btn = document.getElementById('btn-em-clone');
    setLoading(btn, true);
    postJSON('/rfid/clone/em410x', {card_id: id}).then(function(data) {
        setLoading(btn, false);
        renderOutput('rfid-em-output', data.message || data.error || 'Done');
    }).catch(function() { setLoading(btn, false); });
}

function rfidMFDump() {
    var btn = document.getElementById('btn-mf-dump');
    setLoading(btn, true);
    postJSON('/rfid/dump/mifare', {}).then(function(data) {
        setLoading(btn, false);
        renderOutput('rfid-mf-dump-output', data.output || data.error || 'No output');
    }).catch(function() { setLoading(btn, false); });
}

function rfidMFClone() {
    var path = document.getElementById('rfid-mf-dump-path').value.trim();
    if (!path) return;
    var btn = document.getElementById('btn-mf-clone');
    setLoading(btn, true);
    postJSON('/rfid/clone/mifare', {dump_path: path}).then(function(data) {
        setLoading(btn, false);
        renderOutput('rfid-mf-clone-output', data.message || data.error || 'Done');
    }).catch(function() { setLoading(btn, false); });
}

/* ── Cards ── */
function rfidLoadCards() {
    fetchJSON('/rfid/cards').then(function(data) {
        var tb = document.getElementById('rfid-cards-table');
        if (!data.cards || !data.cards.length) {
            tb.innerHTML = '<tr><td colspan="5" class="empty-state">No saved cards yet.</td></tr>';
            return;
        }
        var html = '';
        data.cards.forEach(function(c) {
            html += '<tr><td>' + esc(c.name) + '</td><td>' + esc(c.type) + '</td>'
                + '<td style="font-family:monospace">' + esc(c.id) + '</td>'
                + '<td>' + esc(c.saved_date) + '</td>'
                + '<td><button class="btn btn-danger btn-small" onclick="rfidDeleteCard(\'' + esc(c.id) + '\')">Delete</button></td></tr>';
        });
        tb.innerHTML = html;
    });
}

function rfidDeleteCard(id) {
    if (!confirm('Delete this saved card?')) return;
    postJSON('/rfid/cards/delete', {id: id}).then(function(data) {
        if (data.success) rfidLoadCards();
    });
}

function rfidRefreshDumps() {
    fetchJSON('/rfid/dumps').then(function(data) {
        var tb = document.getElementById('rfid-dumps-table');
        if (!data.dumps || !data.dumps.length) {
            tb.innerHTML = '<tr><td colspan="4" class="empty-state">No dumps found.</td></tr>';
            return;
        }
        var html = '';
        data.dumps.forEach(function(d) {
            html += '<tr><td>' + esc(d.filename) + '</td><td>' + esc(d.size) + '</td>'
                + '<td>' + esc(d.date) + '</td>'
                + '<td><button class="btn btn-danger btn-small" onclick="rfidDeleteDump(\'' + esc(d.filename) + '\')">Delete</button></td></tr>';
        });
        tb.innerHTML = html;
    });
}

function rfidDeleteDump(filename) {
    if (!confirm('Delete dump file "' + filename + '"?')) return;
    postJSON('/rfid/dumps/delete', {filename: filename}).then(function(data) {
        if (data.success) rfidRefreshDumps();
    });
}

/* ── Init ── */
document.addEventListener('DOMContentLoaded', function() {
    rfidCheckStatus();
    rfidLoadCards();
    rfidRefreshDumps();
});
</script>
{% endblock %}