Instruction: Create an AUTARCH web template for the 'Sms Forge' 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: sms_forge.html
Sections: 11
Tabs: 3
Forms/API calls: 3
Has JavaScript: True

Output:
{% extends "base.html" %}
{% block title %}AUTARCH — SMS Forge{% endblock %}

{% block content %}
<div class="page-header">
    <h1>SMS/MMS Backup Forge</h1>
    <p style="margin:0;font-size:0.85rem;color:var(--text-secondary)">
        Create and modify SMS/MMS backup XML files (SMS Backup &amp; Restore format).
    </p>
</div>

<!-- Tab Bar -->
<div class="tab-bar">
    <button class="tab active" data-tab-group="forge" data-tab="messages" onclick="showTab('forge','messages')">Messages <span id="msg-badge" class="badge" style="display:none">0</span></button>
    <button class="tab" data-tab-group="forge" data-tab="conversations" onclick="showTab('forge','conversations')">Conversations</button>
    <button class="tab" data-tab-group="forge" data-tab="importexport" onclick="showTab('forge','importexport')">Import / Export</button>
    <button class="tab" data-tab-group="forge" data-tab="templates" onclick="showTab('forge','templates')">Templates</button>
</div>

<!-- ==================== TAB 1: MESSAGES ==================== -->
<div class="tab-content active" data-tab-group="forge" data-tab="messages">
<div class="section">
    <h2>Message List</h2>
    <div class="form-row" style="margin-bottom:12px;gap:8px;flex-wrap:wrap;align-items:flex-end">
        <div class="form-group" style="min-width:150px;flex:1">
            <label>Contact Filter</label>
            <select id="filter-contact" onchange="forgeLoadMessages()">
                <option value="">All contacts</option>
            </select>
        </div>
        <div class="form-group" style="min-width:150px;flex:1">
            <label>Keyword</label>
            <input type="text" id="filter-keyword" placeholder="Search messages..." onkeydown="if(event.key==='Enter')forgeLoadMessages()">
        </div>
        <div class="form-group" style="min-width:130px;flex:0.7">
            <label>Date From</label>
            <input type="datetime-local" id="filter-date-from">
        </div>
        <div class="form-group" style="min-width:130px;flex:0.7">
            <label>Date To</label>
            <input type="datetime-local" id="filter-date-to">
        </div>
        <div style="display:flex;gap:6px;padding-bottom:2px">
            <button class="btn btn-small" onclick="forgeLoadMessages()">Filter</button>
            <button class="btn btn-small" onclick="forgeClearFilters()">Clear</button>
        </div>
    </div>

    <!-- Chat bubble view -->
    <div id="forge-chat" class="forge-chat-container" style="max-height:500px;overflow-y:auto;padding:12px;background:var(--bg-input);border-radius:var(--radius);border:1px solid var(--border);min-height:120px">
        <div style="color:var(--text-muted);text-align:center;padding:40px 0">No messages loaded</div>
    </div>

    <div class="tool-actions" style="margin-top:12px">
        <button class="btn btn-primary btn-small" onclick="forgeShowAddSMS()">+ Add SMS</button>
        <button class="btn btn-small" onclick="forgeShowAddMMS()">+ Add MMS</button>
        <button class="btn btn-small" style="margin-left:auto;color:var(--danger)" onclick="forgeClearAll()">Clear All</button>
    </div>
</div>
</div>

<!-- ==================== TAB 2: CONVERSATIONS ==================== -->
<div class="tab-content" data-tab-group="forge" data-tab="conversations">

<div class="section">
    <h2>Generate from Template</h2>
    <p style="font-size:0.8rem;color:var(--text-muted);margin-bottom:12px">
        Generate a realistic conversation from a built-in or custom template.
    </p>
    <div class="form-row">
        <div class="form-group">
            <label>Phone Number</label>
            <input type="text" id="gen-address" placeholder="+15551234567">
        </div>
        <div class="form-group">
            <label>Contact Name</label>
            <input type="text" id="gen-contact" placeholder="John Smith">
        </div>
    </div>
    <div class="form-row">
        <div class="form-group">
            <label>Template</label>
            <select id="gen-template" onchange="forgeTemplateChanged()">
                <option value="">-- Select template --</option>
            </select>
        </div>
        <div class="form-group">
            <label>Start Date/Time</label>
            <input type="datetime-local" id="gen-start">
        </div>
    </div>
    <div id="gen-variables" style="margin-bottom:12px"></div>
    <div class="tool-actions">
        <button class="btn btn-primary" id="btn-generate" onclick="forgeGenerate()">Generate Conversation</button>
    </div>
    <pre class="output-panel" id="gen-output" style="display:none"></pre>
</div>

<div class="section">
    <h2>Manual Conversation Builder</h2>
    <p style="font-size:0.8rem;color:var(--text-muted);margin-bottom:12px">
        Build a conversation message by message with custom delays.
    </p>
    <div class="form-row">
        <div class="form-group">
            <label>Phone Number</label>
            <input type="text" id="conv-address" placeholder="+15551234567">
        </div>
        <div class="form-group">
            <label>Contact Name</label>
            <input type="text" id="conv-contact" placeholder="Jane Doe">
        </div>
        <div class="form-group">
            <label>Start Date/Time</label>
            <input type="datetime-local" id="conv-start">
        </div>
    </div>
    <div id="conv-builder-messages" style="margin-bottom:12px"></div>
    <div style="display:flex;gap:6px;margin-bottom:12px">
        <button class="btn btn-small" onclick="convAddMsg(1)">+ Received</button>
        <button class="btn btn-small" onclick="convAddMsg(2)">+ Sent</button>
    </div>
    <div id="conv-preview" class="forge-chat-container" style="max-height:300px;overflow-y:auto;padding:12px;background:var(--bg-input);border-radius:var(--radius);border:1px solid var(--border);min-height:60px;display:none"></div>
    <div class="tool-actions" style="margin-top:12px">
        <button class="btn btn-primary" onclick="convSubmit()">Add Conversation</button>
        <button class="btn btn-small" onclick="convClear()">Clear</button>
    </div>
    <pre class="output-panel" id="conv-output" style="display:none"></pre>
</div>

<div class="section">
    <h2>Bulk Contact Replace</h2>
    <div class="form-row">
        <div class="form-group">
            <label>Old Address</label>
            <input type="text" id="replace-old" placeholder="+15551234567">
        </div>
        <div class="form-group">
            <label>New Address</label>
            <input type="text" id="replace-new" placeholder="+15559876543">
        </div>
        <div class="form-group">
            <label>New Name (optional)</label>
            <input type="text" id="replace-name" placeholder="New Contact Name">
        </div>
    </div>
    <div class="tool-actions">
        <button class="btn btn-primary btn-small" onclick="forgeReplaceContact()">Replace Contact</button>
    </div>
    <pre class="output-panel" id="replace-output" style="display:none"></pre>
</div>

<div class="section">
    <h2>Timestamp Shift</h2>
    <div class="form-row">
        <div class="form-group">
            <label>Address (blank = all messages)</label>
            <input type="text" id="shift-address" placeholder="+15551234567">
        </div>
        <div class="form-group" style="max-width:180px">
            <label>Offset (minutes)</label>
            <input type="number" id="shift-offset" placeholder="60" value="0">
        </div>
    </div>
    <p style="font-size:0.75rem;color:var(--text-muted);margin-bottom:8px">
        Positive = forward in time, negative = backward.
        E.g. -1440 shifts back 1 day, 60 shifts forward 1 hour.
    </p>
    <div class="tool-actions">
        <button class="btn btn-primary btn-small" onclick="forgeShiftTimestamps()">Shift Timestamps</button>
    </div>
    <pre class="output-panel" id="shift-output" style="display:none"></pre>
</div>

</div>

<!-- ==================== TAB 3: IMPORT / EXPORT ==================== -->
<div class="tab-content" data-tab-group="forge" data-tab="importexport">

<div class="section">
    <h2>Import</h2>
    <p style="font-size:0.8rem;color:var(--text-muted);margin-bottom:12px">
        Import an existing SMS Backup &amp; Restore XML file or a CSV file.
    </p>
    <div class="form-group">
        <label>Upload File (XML or CSV)</label>
        <input type="file" id="import-file" accept=".xml,.csv" style="padding:8px">
    </div>
    <div class="tool-actions">
        <button class="btn btn-small" onclick="forgeValidate()">Validate XML</button>
        <button class="btn btn-primary" onclick="forgeImport()">Import</button>
    </div>
    <pre class="output-panel" id="import-output" style="display:none"></pre>
</div>

<div class="section">
    <h2>Merge Multiple Backups</h2>
    <p style="font-size:0.8rem;color:var(--text-muted);margin-bottom:12px">
        Upload multiple XML backups to merge. Duplicates are automatically removed.
    </p>
    <div class="form-group">
        <label>Upload Files (multiple XML)</label>
        <input type="file" id="merge-files" accept=".xml" multiple style="padding:8px">
    </div>
    <div class="tool-actions">
        <button class="btn btn-primary" onclick="forgeMerge()">Merge Backups</button>
    </div>
    <pre class="output-panel" id="merge-output" style="display:none"></pre>
</div>

<div class="section">
    <h2>Export</h2>
    <p style="font-size:0.8rem;color:var(--text-muted);margin-bottom:12px">
        Download the current message set as an SMS Backup &amp; Restore XML or CSV file.
    </p>
    <div class="form-row" style="align-items:flex-end">
        <div class="form-group" style="max-width:200px">
            <label>Format</label>
            <select id="export-format">
                <option value="xml">XML (SMS Backup &amp; Restore)</option>
                <option value="csv">CSV</option>
            </select>
        </div>
        <button class="btn btn-primary" onclick="forgeExport()">Download Export</button>
    </div>
</div>

<div class="section">
    <h2>Backup Statistics</h2>
    <div id="stats-panel" style="color:var(--text-secondary)">
        <em>Click Refresh to load stats.</em>
    </div>
    <div class="tool-actions" style="margin-top:8px">
        <button class="btn btn-small" onclick="forgeLoadStats()">Refresh Stats</button>
    </div>
</div>

</div>

<!-- ==================== TAB 4: TEMPLATES ==================== -->
<div class="tab-content" data-tab-group="forge" data-tab="templates">

<div class="section">
    <h2>Conversation Templates</h2>
    <p style="font-size:0.8rem;color:var(--text-muted);margin-bottom:12px">
        Built-in and custom templates for generating realistic SMS conversations.
    </p>
    <div id="templates-list"></div>
</div>

<div class="section">
    <h2>Custom Template Editor</h2>
    <p style="font-size:0.8rem;color:var(--text-muted);margin-bottom:12px">
        Create a custom template in JSON format. Fields: name, description, variables (array), messages (array of {body, type, delay_minutes}).
    </p>
    <div class="form-group" style="max-width:300px">
        <label>Template Key</label>
        <input type="text" id="tmpl-key" placeholder="my_template">
    </div>
    <div class="form-group">
        <label>Template JSON</label>
        <textarea id="tmpl-json" rows="12" placeholder='{
  "name": "My Template",
  "description": "A custom conversation template",
  "variables": ["contact", "topic"],
  "messages": [
    {"body": "Hey {contact}, want to discuss {topic}?", "type": 2, "delay_minutes": 0},
    {"body": "Sure, what about it?", "type": 1, "delay_minutes": 5}
  ]
}'></textarea>
    </div>
    <div class="tool-actions">
        <button class="btn btn-primary" onclick="forgeSaveTemplate()">Save Template</button>
    </div>
    <pre class="output-panel" id="tmpl-output" style="display:none"></pre>
</div>

</div>

<!-- ==================== ADD SMS MODAL ==================== -->
<div id="modal-sms" class="forge-modal" style="display:none">
    <div class="forge-modal-content">
        <div class="forge-modal-header">
            <h3 id="modal-sms-title">Add SMS</h3>
            <button onclick="forgeCloseModal('modal-sms')" style="background:none;border:none;color:var(--text-secondary);font-size:1.2rem;cursor:pointer">&times;</button>
        </div>
        <div class="form-group">
            <label>Phone Number</label>
            <input type="text" id="sms-address" placeholder="+15551234567">
        </div>
        <div class="form-group">
            <label>Contact Name</label>
            <input type="text" id="sms-contact" placeholder="John Smith">
        </div>
        <div class="form-group">
            <label>Message Body</label>
            <textarea id="sms-body" rows="4" placeholder="Enter message text..."></textarea>
        </div>
        <div class="form-row">
            <div class="form-group">
                <label>Type</label>
                <select id="sms-type">
                    <option value="1">Received</option>
                    <option value="2">Sent</option>
                    <option value="3">Draft</option>
                    <option value="4">Outbox</option>
                    <option value="5">Failed</option>
                    <option value="6">Queued</option>
                </select>
            </div>
            <div class="form-group">
                <label>Timestamp</label>
                <input type="datetime-local" id="sms-timestamp">
            </div>
        </div>
        <input type="hidden" id="sms-edit-index" value="">
        <div class="tool-actions">
            <button class="btn btn-primary" id="btn-sms-save" onclick="forgeSaveSMS()">Add SMS</button>
            <button class="btn btn-small" onclick="forgeCloseModal('modal-sms')">Cancel</button>
        </div>
    </div>
</div>

<!-- ==================== ADD MMS MODAL ==================== -->
<div id="modal-mms" class="forge-modal" style="display:none">
    <div class="forge-modal-content">
        <div class="forge-modal-header">
            <h3>Add MMS</h3>
            <button onclick="forgeCloseModal('modal-mms')" style="background:none;border:none;color:var(--text-secondary);font-size:1.2rem;cursor:pointer">&times;</button>
        </div>
        <div class="form-group">
            <label>Phone Number</label>
            <input type="text" id="mms-address" placeholder="+15551234567">
        </div>
        <div class="form-group">
            <label>Contact Name</label>
            <input type="text" id="mms-contact" placeholder="John Smith">
        </div>
        <div class="form-group">
            <label>Message Body</label>
            <textarea id="mms-body" rows="3" placeholder="Enter MMS text..."></textarea>
        </div>
        <div class="form-row">
            <div class="form-group">
                <label>Type</label>
                <select id="mms-type">
                    <option value="1">Received</option>
                    <option value="2">Sent</option>
                    <option value="3">Draft</option>
                    <option value="4">Outbox</option>
                </select>
            </div>
            <div class="form-group">
                <label>Timestamp</label>
                <input type="datetime-local" id="mms-timestamp">
            </div>
        </div>
        <div class="form-group">
            <label>Attachment (base64 data, optional)</label>
            <textarea id="mms-attachment-data" rows="2" placeholder="Paste base64 data or leave empty"></textarea>
        </div>
        <div class="form-row">
            <div class="form-group">
                <label>Attachment Filename</label>
                <input type="text" id="mms-attachment-name" placeholder="image.jpg">
            </div>
            <div class="form-group">
                <label>Content Type</label>
                <input type="text" id="mms-attachment-ct" placeholder="image/jpeg">
            </div>
        </div>
        <div class="tool-actions">
            <button class="btn btn-primary" onclick="forgeSaveMMS()">Add MMS</button>
            <button class="btn btn-small" onclick="forgeCloseModal('modal-mms')">Cancel</button>
        </div>
    </div>
</div>

<style>
/* ── Chat Bubble Styles ─────────────────────────────────────────── */
.forge-chat-container{display:flex;flex-direction:column;gap:6px}
.forge-bubble{max-width:75%;padding:8px 12px;border-radius:12px;font-size:0.85rem;line-height:1.45;word-wrap:break-word;position:relative}
.forge-bubble .bubble-meta{font-size:0.65rem;color:var(--text-muted);margin-top:4px;display:flex;justify-content:space-between;align-items:center;gap:8px}
.forge-bubble .bubble-actions{display:none;gap:4px}
.forge-bubble:hover .bubble-actions{display:flex}
.forge-bubble .bubble-actions button{background:none;border:none;color:var(--text-muted);cursor:pointer;font-size:0.7rem;padding:1px 4px;border-radius:3px}
.forge-bubble .bubble-actions button:hover{background:rgba(255,255,255,0.1);color:var(--text-primary)}
.forge-bubble-received{align-self:flex-start;background:#2a2d3e;border-bottom-left-radius:4px;color:var(--text-primary)}
.forge-bubble-sent{align-self:flex-end;background:var(--accent);border-bottom-right-radius:4px;color:#fff}
.forge-bubble-sent .bubble-meta{color:rgba(255,255,255,0.6)}
.forge-bubble-sent .bubble-actions button{color:rgba(255,255,255,0.5)}
.forge-bubble-sent .bubble-actions button:hover{color:#fff;background:rgba(255,255,255,0.15)}
.forge-bubble-mms{border:1px solid var(--border)}
.forge-bubble-mms .mms-tag{display:inline-block;background:rgba(255,255,255,0.1);border-radius:3px;padding:0 4px;font-size:0.65rem;margin-right:4px;vertical-align:middle}
.forge-contact-label{font-size:0.7rem;color:var(--text-muted);margin-bottom:2px;padding-left:4px}

/* ── Modal ──────────────────────────────────────────────────────── */
.forge-modal{position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.6);z-index:1000;display:flex;align-items:center;justify-content:center}
.forge-modal-content{background:var(--bg-card);border:1px solid var(--border);border-radius:var(--radius);padding:24px;width:90%;max-width:520px;max-height:90vh;overflow-y:auto}
.forge-modal-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:16px}
.forge-modal-header h3{margin:0;font-size:1.1rem}

/* ── Template cards ─────────────────────────────────────────────── */
.tmpl-card{background:var(--bg-input);border:1px solid var(--border);border-radius:var(--radius);padding:14px;margin-bottom:10px}
.tmpl-card h4{margin:0 0 4px 0;font-size:0.95rem;color:var(--text-primary)}
.tmpl-card .tmpl-desc{font-size:0.8rem;color:var(--text-secondary);margin-bottom:8px}
.tmpl-card .tmpl-vars{font-size:0.75rem;color:var(--text-muted)}
.tmpl-card .tmpl-preview{margin-top:8px;padding:8px;background:var(--bg-primary);border-radius:6px;font-size:0.78rem;color:var(--text-secondary);max-height:120px;overflow-y:auto}
.tmpl-card .tmpl-tag{display:inline-block;background:var(--accent);color:#fff;border-radius:3px;padding:1px 6px;font-size:0.65rem;margin-left:6px;vertical-align:middle}
.tmpl-card .tmpl-tag.custom{background:var(--danger)}

/* ── Stats ──────────────────────────────────────────────────────── */
.stats-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:10px;margin-bottom:12px}
.stat-card{background:var(--bg-input);border:1px solid var(--border);border-radius:var(--radius);padding:12px;text-align:center}
.stat-card .stat-value{font-size:1.6rem;font-weight:700;color:var(--accent)}
.stat-card .stat-label{font-size:0.75rem;color:var(--text-muted);margin-top:2px}

/* ── Badge ──────────────────────────────────────────────────────── */
.badge{background:var(--accent);color:#fff;border-radius:10px;padding:1px 7px;font-size:0.7rem;margin-left:4px;vertical-align:middle}

/* ── Conversation builder messages ──────────────────────────────── */
.conv-msg-row{display:flex;gap:8px;align-items:center;margin-bottom:6px;padding:6px 8px;background:var(--bg-input);border-radius:6px;border:1px solid var(--border)}
.conv-msg-row textarea{flex:1;min-height:32px;resize:vertical;font-size:0.82rem}
.conv-msg-row select,.conv-msg-row input[type="number"]{width:80px;font-size:0.82rem}
.conv-msg-row button{background:none;border:none;color:var(--danger);cursor:pointer;font-size:1rem;padding:2px 6px}
</style>

<script>
/* ── State ──────────────────────────────────────────────────────── */
var forgeMessages = [];
var forgeTemplates = {};
var convBuilderMsgs = [];

/* ── Init ───────────────────────────────────────────────────────── */
document.addEventListener('DOMContentLoaded', function() {
    forgeLoadMessages();
    forgeLoadTemplatesList();
    forgeLoadTemplateDropdown();
});

/* ── Message Loading ────────────────────────────────────────────── */
function forgeLoadMessages() {
    var params = new URLSearchParams();
    var addr = document.getElementById('filter-contact').value;
    var kw = document.getElementById('filter-keyword').value;
    var df = document.getElementById('filter-date-from').value;
    var dt = document.getElementById('filter-date-to').value;
    if (addr) params.set('address', addr);
    if (kw) params.set('keyword', kw);
    if (df) params.set('date_from', String(new Date(df).getTime()));
    if (dt) params.set('date_to', String(new Date(dt).getTime()));
    var qs = params.toString();
    fetchJSON('/sms-forge/messages' + (qs ? '?' + qs : '')).then(function(data) {
        forgeMessages = data.messages || [];
        forgeRenderChat();
        forgeUpdateContactFilter();
        var badge = document.getElementById('msg-badge');
        if (badge) { badge.textContent = forgeMessages.length; badge.style.display = forgeMessages.length ? 'inline' : 'none'; }
    });
}

function forgeClearFilters() {
    document.getElementById('filter-contact').value = '';
    document.getElementById('filter-keyword').value = '';
    document.getElementById('filter-date-from').value = '';
    document.getElementById('filter-date-to').value = '';
    forgeLoadMessages();
}

function forgeUpdateContactFilter() {
    var sel = document.getElementById('filter-contact');
    var cur = sel.value;
    var contacts = {};
    forgeMessages.forEach(function(m) {
        if (m.address && !contacts[m.address]) contacts[m.address] = m.contact_name || m.address;
    });
    var html = '<option value="">All contacts</option>';
    Object.keys(contacts).sort().forEach(function(addr) {
        var selected = addr === cur ? ' selected' : '';
        html += '<option value="' + escapeHtml(addr) + '"' + selected + '>' + escapeHtml(contacts[addr]) + ' (' + escapeHtml(addr) + ')</option>';
    });
    sel.innerHTML = html;
}

/* ── Chat Rendering ─────────────────────────────────────────────── */
function forgeRenderChat() {
    var container = document.getElementById('forge-chat');
    if (!forgeMessages.length) {
        container.innerHTML = '<div style="color:var(--text-muted);text-align:center;padding:40px 0">No messages loaded</div>';
        return;
    }
    var html = '';
    var lastContact = '';
    forgeMessages.forEach(function(m, vi) {
        var idx = (m.index !== undefined) ? m.index : vi;
        var isSent = (m.msg_kind === 'mms') ? (m.msg_box === 2) : (m.type === 2);
        var cls = isSent ? 'forge-bubble-sent' : 'forge-bubble-received';
        var body = m.body || '';
        if (m.msg_kind === 'mms' && !body) {
            (m.parts || []).forEach(function(p) { if (p.ct === 'text/plain' && p.text !== 'null') body = p.text; });
        }
        var contactLabel = '';
        if (!isSent && m.contact_name && m.contact_name !== lastContact) {
            contactLabel = '<div class="forge-contact-label">' + escapeHtml(m.contact_name || m.address) + '</div>';
            lastContact = m.contact_name;
        } else if (isSent) {
            lastContact = '';
        }
        var mmsTag = m.msg_kind === 'mms' ? '<span class="mms-tag">MMS</span>' : '';
        var mmsCls = m.msg_kind === 'mms' ? ' forge-bubble-mms' : '';
        var date = m.readable_date || '';
        html += contactLabel;
        html += '<div class="forge-bubble ' + cls + mmsCls + '" data-idx="' + idx + '">';
        html += mmsTag + escapeHtml(body);
        html += '<div class="bubble-meta"><span>' + escapeHtml(date) + '</span>';
        html += '<span class="bubble-actions">';
        html += '<button onclick="forgeEditMsg(' + idx + ')" title="Edit">edit</button>';
        html += '<button onclick="forgeDeleteMsg(' + idx + ')" title="Delete">del</button>';
        html += '</span></div></div>';
    });
    container.innerHTML = html;
    container.scrollTop = container.scrollHeight;
}

/* ── Add SMS ────────────────────────────────────────────────────── */
function forgeShowAddSMS() {
    document.getElementById('sms-address').value = '';
    document.getElementById('sms-contact').value = '';
    document.getElementById('sms-body').value = '';
    document.getElementById('sms-type').value = '1';
    document.getElementById('sms-timestamp').value = '';
    document.getElementById('sms-edit-index').value = '';
    document.getElementById('modal-sms-title').textContent = 'Add SMS';
    document.getElementById('btn-sms-save').textContent = 'Add SMS';
    document.getElementById('modal-sms').style.display = 'flex';
}

function forgeEditMsg(idx) {
    fetchJSON('/sms-forge/messages').then(function(data) {
        var msg = null;
        (data.messages || []).forEach(function(m) { if (m.index === idx) msg = m; });
        if (!msg) return;
        document.getElementById('sms-address').value = msg.address || '';
        document.getElementById('sms-contact').value = msg.contact_name || '';
        document.getElementById('sms-body').value = msg.body || '';
        document.getElementById('sms-type').value = String(msg.type || msg.msg_box || 1);
        if (msg.date) {
            var d = new Date(msg.date);
            var iso = d.getFullYear() + '-' + String(d.getMonth()+1).padStart(2,'0') + '-' + String(d.getDate()).padStart(2,'0') + 'T' + String(d.getHours()).padStart(2,'0') + ':' + String(d.getMinutes()).padStart(2,'0');
            document.getElementById('sms-timestamp').value = iso;
        }
        document.getElementById('sms-edit-index').value = String(idx);
        document.getElementById('modal-sms-title').textContent = 'Edit Message #' + idx;
        document.getElementById('btn-sms-save').textContent = 'Save Changes';
        document.getElementById('modal-sms').style.display = 'flex';
    });
}

function forgeSaveSMS() {
    var editIdx = document.getElementById('sms-edit-index').value;
    var ts = document.getElementById('sms-timestamp').value;
    var timestamp = ts ? new Date(ts).getTime() : null;

    if (editIdx !== '') {
        var payload = {};
        payload.body = document.getElementById('sms-body').value;
        payload.contact_name = document.getElementById('sms-contact').value;
        if (timestamp) payload.timestamp = timestamp;
        fetchJSON('/sms-forge/message/' + editIdx, {
            method: 'PUT',
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify(payload)
        }).then(function(r) {
            forgeCloseModal('modal-sms');
            forgeLoadMessages();
        });
    } else {
        var data = {
            address: document.getElementById('sms-address').value,
            body: document.getElementById('sms-body').value,
            type: parseInt(document.getElementById('sms-type').value),
            contact_name: document.getElementById('sms-contact').value,
        };
        if (timestamp) data.timestamp = timestamp;
        postJSON('/sms-forge/sms', data).then(function(r) {
            forgeCloseModal('modal-sms');
            forgeLoadMessages();
        });
    }
}

function forgeCloseModal(id) {
    document.getElementById(id).style.display = 'none';
}

/* ── Add MMS ────────────────────────────────────────────────────── */
function forgeShowAddMMS() {
    document.getElementById('mms-address').value = '';
    document.getElementById('mms-contact').value = '';
    document.getElementById('mms-body').value = '';
    document.getElementById('mms-type').value = '1';
    document.getElementById('mms-timestamp').value = '';
    document.getElementById('mms-attachment-data').value = '';
    document.getElementById('mms-attachment-name').value = '';
    document.getElementById('mms-attachment-ct').value = 'image/jpeg';
    document.getElementById('modal-mms').style.display = 'flex';
}

function forgeSaveMMS() {
    var ts = document.getElementById('mms-timestamp').value;
    var timestamp = ts ? new Date(ts).getTime() : null;
    var attachments = [];
    var attData = document.getElementById('mms-attachment-data').value.trim();
    var attName = document.getElementById('mms-attachment-name').value.trim();
    var attCt = document.getElementById('mms-attachment-ct').value.trim();
    if (attData && attName) {
        attachments.push({data: attData, filename: attName, content_type: attCt || 'application/octet-stream'});
    }
    var data = {
        address: document.getElementById('mms-address').value,
        body: document.getElementById('mms-body').value,
        msg_box: parseInt(document.getElementById('mms-type').value),
        contact_name: document.getElementById('mms-contact').value,
        attachments: attachments,
    };
    if (timestamp) data.timestamp = timestamp;
    postJSON('/sms-forge/mms', data).then(function(r) {
        forgeCloseModal('modal-mms');
        forgeLoadMessages();
    });
}

/* ── Delete ──────────────────────────────────────────────────────── */
function forgeDeleteMsg(idx) {
    if (!confirm('Delete message #' + idx + '?')) return;
    fetchJSON('/sms-forge/message/' + idx, {method: 'DELETE'}).then(function(r) {
        forgeLoadMessages();
    });
}

function forgeClearAll() {
    if (!confirm('Clear ALL messages? This cannot be undone.')) return;
    postJSON('/sms-forge/clear', {}).then(function() {
        forgeLoadMessages();
    });
}

/* ── Generate from Template ─────────────────────────────────────── */
function forgeLoadTemplateDropdown() {
    fetchJSON('/sms-forge/templates').then(function(data) {
        forgeTemplates = data;
        var sel = document.getElementById('gen-template');
        var html = '<option value="">-- Select template --</option>';
        Object.keys(data).forEach(function(key) {
            html += '<option value="' + escapeHtml(key) + '">' + escapeHtml(data[key].name) + '</option>';
        });
        sel.innerHTML = html;
    });
}

function forgeTemplateChanged() {
    var key = document.getElementById('gen-template').value;
    var container = document.getElementById('gen-variables');
    if (!key || !forgeTemplates[key]) { container.innerHTML = ''; return; }
    var tmpl = forgeTemplates[key];
    var vars = tmpl.variables || [];
    if (!vars.length) { container.innerHTML = '<p style="font-size:0.8rem;color:var(--text-muted)">No variables for this template.</p>'; return; }
    var html = '<div class="form-row" style="flex-wrap:wrap;gap:8px">';
    vars.forEach(function(v) {
        html += '<div class="form-group" style="min-width:140px;flex:1"><label>' + escapeHtml(v) + '</label>';
        html += '<input type="text" id="gen-var-' + escapeHtml(v) + '" placeholder="' + escapeHtml(v) + '"></div>';
    });
    html += '</div>';
    container.innerHTML = html;
}

function forgeGenerate() {
    var btn = document.getElementById('btn-generate');
    setLoading(btn, true);
    var key = document.getElementById('gen-template').value;
    if (!key) { renderOutput('gen-output', 'Please select a template.'); document.getElementById('gen-output').style.display='block'; setLoading(btn,false); return; }
    var tmpl = forgeTemplates[key];
    var variables = {};
    (tmpl.variables || []).forEach(function(v) {
        var el = document.getElementById('gen-var-' + v);
        if (el) variables[v] = el.value;
    });
    var ts = document.getElementById('gen-start').value;
    var data = {
        address: document.getElementById('gen-address').value,
        contact_name: document.getElementById('gen-contact').value,
        template: key,
        variables: variables,
    };
    if (ts) data.start_timestamp = new Date(ts).getTime();
    postJSON('/sms-forge/generate', data).then(function(r) {
        setLoading(btn, false);
        var out = document.getElementById('gen-output');
        out.style.display = 'block';
        out.textContent = r.ok ? 'Generated ' + (r.added || 0) + ' messages.' : 'Error: ' + (r.error || 'unknown');
        forgeLoadMessages();
    }).catch(function(e) { setLoading(btn, false); });
}

/* ── Manual Conversation Builder ────────────────────────────────── */
function convAddMsg(type) {
    convBuilderMsgs.push({body: '', type: type, delay_minutes: 5});
    convRender();
}

function convRender() {
    var container = document.getElementById('conv-builder-messages');
    var html = '';
    convBuilderMsgs.forEach(function(m, i) {
        var typeLabel = m.type === 2 ? 'Sent' : 'Received';
        html += '<div class="conv-msg-row">';
        html += '<select onchange="convBuilderMsgs[' + i + '].type=parseInt(this.value);convRenderPreview()">';
        html += '<option value="1"' + (m.type===1?' selected':'') + '>IN</option>';
        html += '<option value="2"' + (m.type===2?' selected':'') + '>OUT</option></select>';
        html += '<textarea placeholder="Message text..." oninput="convBuilderMsgs[' + i + '].body=this.value;convRenderPreview()">' + escapeHtml(m.body) + '</textarea>';
        html += '<input type="number" value="' + m.delay_minutes + '" min="0" placeholder="min" title="Delay (minutes)" onchange="convBuilderMsgs[' + i + '].delay_minutes=parseInt(this.value)||0">';
        html += '<button onclick="convBuilderMsgs.splice(' + i + ',1);convRender()" title="Remove">&times;</button>';
        html += '</div>';
    });
    container.innerHTML = html;
    convRenderPreview();
}

function convRenderPreview() {
    var preview = document.getElementById('conv-preview');
    if (!convBuilderMsgs.length) { preview.style.display = 'none'; return; }
    preview.style.display = 'block';
    var html = '';
    convBuilderMsgs.forEach(function(m) {
        if (!m.body) return;
        var cls = m.type === 2 ? 'forge-bubble-sent' : 'forge-bubble-received';
        html += '<div class="forge-bubble ' + cls + '">' + escapeHtml(m.body) + '</div>';
    });
    preview.innerHTML = html || '<div style="color:var(--text-muted);padding:12px;text-align:center">Type messages above to preview</div>';
}

function convSubmit() {
    if (!convBuilderMsgs.length) return;
    var ts = document.getElementById('conv-start').value;
    var data = {
        address: document.getElementById('conv-address').value,
        contact_name: document.getElementById('conv-contact').value,
        messages: convBuilderMsgs,
    };
    if (ts) data.start_timestamp = new Date(ts).getTime();
    postJSON('/sms-forge/conversation', data).then(function(r) {
        var out = document.getElementById('conv-output');
        out.style.display = 'block';
        out.textContent = r.ok ? 'Added ' + (r.added || 0) + ' messages.' : 'Error: ' + (r.error || 'unknown');
        forgeLoadMessages();
    });
}

function convClear() {
    convBuilderMsgs = [];
    convRender();
    document.getElementById('conv-output').style.display = 'none';
}

/* ── Replace Contact ────────────────────────────────────────────── */
function forgeReplaceContact() {
    var data = {
        old_address: document.getElementById('replace-old').value,
        new_address: document.getElementById('replace-new').value,
        new_name: document.getElementById('replace-name').value || null,
    };
    postJSON('/sms-forge/replace-contact', data).then(function(r) {
        var out = document.getElementById('replace-output');
        out.style.display = 'block';
        out.textContent = r.ok ? 'Updated ' + r.updated + ' messages.' : 'Error: ' + (r.error || 'unknown');
        forgeLoadMessages();
    });
}

/* ── Shift Timestamps ───────────────────────────────────────────── */
function forgeShiftTimestamps() {
    var data = {
        address: document.getElementById('shift-address').value || null,
        offset_minutes: parseInt(document.getElementById('shift-offset').value) || 0,
    };
    postJSON('/sms-forge/shift-timestamps', data).then(function(r) {
        var out = document.getElementById('shift-output');
        out.style.display = 'block';
        out.textContent = r.ok ? 'Shifted ' + r.shifted + ' messages by ' + r.offset_minutes + ' minutes.' : 'Error: ' + (r.error || 'unknown');
        forgeLoadMessages();
    });
}

/* ── Import / Export ────────────────────────────────────────────── */
function forgeImport() {
    var input = document.getElementById('import-file');
    if (!input.files.length) { renderOutput('import-output', 'No file selected.'); document.getElementById('import-output').style.display='block'; return; }
    var fd = new FormData();
    fd.append('file', input.files[0]);
    fetch('/sms-forge/import', {method: 'POST', body: fd}).then(function(r){return r.json();}).then(function(r) {
        var out = document.getElementById('import-output');
        out.style.display = 'block';
        if (r.ok) {
            out.textContent = 'Imported ' + (r.added || r.count || 0) + ' messages. Total: ' + (r.total || '?');
        } else {
            out.textContent = 'Error: ' + (r.error || 'unknown');
        }
        forgeLoadMessages();
    });
}

function forgeValidate() {
    var input = document.getElementById('import-file');
    if (!input.files.length) { renderOutput('import-output', 'No file selected.'); document.getElementById('import-output').style.display='block'; return; }
    var fd = new FormData();
    fd.append('file', input.files[0]);
    fetch('/sms-forge/validate', {method: 'POST', body: fd}).then(function(r){return r.json();}).then(function(r) {
        var out = document.getElementById('import-output');
        out.style.display = 'block';
        if (r.valid) {
            out.textContent = 'Valid SMS Backup & Restore XML (' + r.element_count + ' elements).';
        } else {
            var txt = 'Validation issues:\n';
            (r.issues || []).forEach(function(iss) { txt += '  - ' + iss + '\n'; });
            if (r.error) txt += '\nError: ' + r.error;
            out.textContent = txt;
        }
    });
}

function forgeMerge() {
    var input = document.getElementById('merge-files');
    if (!input.files.length) { renderOutput('merge-output', 'No files selected.'); document.getElementById('merge-output').style.display='block'; return; }
    var fd = new FormData();
    for (var i = 0; i < input.files.length; i++) fd.append('files', input.files[i]);
    fetch('/sms-forge/merge', {method: 'POST', body: fd}).then(function(r){return r.json();}).then(function(r) {
        var out = document.getElementById('merge-output');
        out.style.display = 'block';
        if (r.ok) {
            out.textContent = 'Merged: ' + r.total + ' total messages (' + r.added + ' new).';
            if (r.errors) r.errors.forEach(function(e) { out.textContent += '\n  Warning: ' + e; });
        } else {
            out.textContent = 'Error: ' + (r.error || 'unknown');
        }
        forgeLoadMessages();
    });
}

function forgeExport() {
    var fmt = document.getElementById('export-format').value;
    window.location.href = '/sms-forge/export/' + fmt;
}

/* ── Stats ──────────────────────────────────────────────────────── */
function forgeLoadStats() {
    fetchJSON('/sms-forge/stats').then(function(s) {
        var html = '<div class="stats-grid">';
        html += '<div class="stat-card"><div class="stat-value">' + s.total + '</div><div class="stat-label">Total Messages</div></div>';
        html += '<div class="stat-card"><div class="stat-value">' + s.sms_count + '</div><div class="stat-label">SMS</div></div>';
        html += '<div class="stat-card"><div class="stat-value">' + s.mms_count + '</div><div class="stat-label">MMS</div></div>';
        html += '<div class="stat-card"><div class="stat-value">' + s.sent + '</div><div class="stat-label">Sent</div></div>';
        html += '<div class="stat-card"><div class="stat-value">' + s.received + '</div><div class="stat-label">Received</div></div>';
        html += '<div class="stat-card"><div class="stat-value">' + (s.contacts ? s.contacts.length : 0) + '</div><div class="stat-label">Contacts</div></div>';
        html += '</div>';
        if (s.date_range) {
            html += '<p style="font-size:0.8rem;color:var(--text-secondary)"><strong>Date range:</strong> ' + escapeHtml(s.date_range.earliest_readable) + ' &mdash; ' + escapeHtml(s.date_range.latest_readable) + '</p>';
        }
        if (s.contacts && s.contacts.length) {
            html += '<table style="width:100%;font-size:0.8rem;margin-top:8px"><thead><tr style="color:var(--text-muted)"><th style="text-align:left">Address</th><th style="text-align:left">Name</th><th style="text-align:right">Count</th></tr></thead><tbody>';
            s.contacts.forEach(function(c) {
                html += '<tr><td>' + escapeHtml(c.address) + '</td><td>' + escapeHtml(c.name) + '</td><td style="text-align:right">' + c.count + '</td></tr>';
            });
            html += '</tbody></table>';
        }
        document.getElementById('stats-panel').innerHTML = html;
    });
}

/* ── Templates List (Tab 4) ─────────────────────────────────────── */
function forgeLoadTemplatesList() {
    fetchJSON('/sms-forge/templates').then(function(data) {
        var container = document.getElementById('templates-list');
        var html = '';
        Object.keys(data).forEach(function(key) {
            var t = data[key];
            var tag = t.builtin ? '<span class="tmpl-tag">built-in</span>' : '<span class="tmpl-tag custom">custom</span>';
            html += '<div class="tmpl-card">';
            html += '<h4>' + escapeHtml(t.name) + tag + '</h4>';
            html += '<div class="tmpl-desc">' + escapeHtml(t.description) + '</div>';
            if (t.variables && t.variables.length) {
                html += '<div class="tmpl-vars">Variables: ' + t.variables.map(function(v){return '<code>{'+ escapeHtml(v) +'}</code>';}).join(', ') + '</div>';
            }
            if (t.messages && t.messages.length) {
                html += '<div class="tmpl-preview">';
                t.messages.forEach(function(m) {
                    var dir = m.type === 2 ? '&rarr;' : '&larr;';
                    var delay = m.delay_minutes ? ' <span style="color:var(--text-muted)">(+' + m.delay_minutes + 'min)</span>' : '';
                    html += '<div>' + dir + ' ' + escapeHtml(m.body) + delay + '</div>';
                });
                html += '</div>';
            }
            html += '<div style="font-size:0.75rem;color:var(--text-muted);margin-top:6px">' + t.message_count + ' messages</div>';
            html += '</div>';
        });
        container.innerHTML = html || '<p style="color:var(--text-muted)">No templates available.</p>';
    });
}

function forgeSaveTemplate() {
    var key = document.getElementById('tmpl-key').value.trim();
    var jsonStr = document.getElementById('tmpl-json').value.trim();
    var out = document.getElementById('tmpl-output');
    out.style.display = 'block';
    if (!key) { out.textContent = 'Please enter a template key.'; return; }
    var tmpl;
    try { tmpl = JSON.parse(jsonStr); } catch(e) { out.textContent = 'Invalid JSON: ' + e.message; return; }
    postJSON('/sms-forge/templates/save', {key: key, template: tmpl}).then(function(r) {
        if (r.ok) {
            out.textContent = 'Template "' + key + '" saved. Use it in the Conversations tab.';
            forgeLoadTemplatesList();
            forgeLoadTemplateDropdown();
        } else {
            out.textContent = 'Error: ' + (r.error || 'unknown');
        }
    }).catch(function(e) {
        out.textContent = 'Error saving template: ' + e.message;
    });
}
</script>
{% endblock %}


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

Output:
{% extends "base.html" %}
{% block title %}Social Engineering — AUTARCH{% endblock %}
{% block content %}
<div class="page-header">
    <h1>Social Engineering Toolkit</h1>
    <p class="text-muted">Credential harvesting, pretexts, QR phishing, USB payloads, vishing scripts</p>
</div>

<!-- Tabs -->
<div class="tabs">
    <button class="tab active" onclick="switchTab('harvest')">Harvest</button>
    <button class="tab" onclick="switchTab('pretexts')">Pretexts</button>
    <button class="tab" onclick="switchTab('qr')">QR Codes</button>
    <button class="tab" onclick="switchTab('campaigns')">Campaigns</button>
</div>

<!-- ══════════════════════ HARVEST TAB ══════════════════════ -->
<div id="tab-harvest" class="tab-content active">
    <!-- Clone Section -->
    <div class="card" style="margin-bottom:1rem">
        <h3>Clone Login Page</h3>
        <p style="color:var(--text-secondary);font-size:0.85rem;margin-bottom:0.75rem">
            Fetch a login page, rewrite form actions to capture credentials through AUTARCH.
        </p>
        <div style="display:flex;gap:0.5rem;align-items:end">
            <div style="flex:1">
                <label class="form-label">Target URL</label>
                <input type="text" id="clone-url" class="form-control" placeholder="https://login.example.com/signin">
            </div>
            <button id="clone-btn" class="btn btn-primary" onclick="clonePage()">Clone Page</button>
        </div>
        <div id="clone-result" style="margin-top:0.75rem;display:none" class="card"
             style="background:var(--bg-input);padding:0.75rem;border-radius:var(--radius)"></div>
    </div>

    <!-- Cloned Pages Table -->
    <div class="card" style="margin-bottom:1rem">
        <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:0.75rem">
            <h3>Cloned Pages</h3>
            <button class="btn btn-sm" onclick="loadPages()">Refresh</button>
        </div>
        <div id="pages-list">
            <p style="color:var(--text-muted)">Loading...</p>
        </div>
    </div>

    <!-- Captures Log -->
    <div class="card">
        <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:0.75rem">
            <h3>Captured Credentials</h3>
            <div style="display:flex;gap:0.5rem">
                <select id="cap-filter" class="form-control" style="width:180px;font-size:0.85rem" onchange="loadCaptures()">
                    <option value="">All Pages</option>
                </select>
                <button class="btn btn-sm" onclick="loadCaptures()">Refresh</button>
                <button class="btn btn-sm" style="color:var(--danger)" onclick="clearCaptures()">Clear</button>
            </div>
        </div>
        <div id="captures-list">
            <p style="color:var(--text-muted)">No captures yet.</p>
        </div>
    </div>
</div>

<!-- ══════════════════════ PRETEXTS TAB ══════════════════════ -->
<div id="tab-pretexts" class="tab-content" style="display:none">
    <!-- Pretext Templates -->
    <div class="card" style="margin-bottom:1rem">
        <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:0.75rem">
            <h3>Pretext Templates</h3>
            <select id="pretext-cat" class="form-control" style="width:180px;font-size:0.85rem" onchange="loadPretexts()">
                <option value="">All Categories</option>
                <option value="it_support">IT Support</option>
                <option value="hr">HR</option>
                <option value="vendor">Vendor</option>
                <option value="delivery">Delivery</option>
                <option value="executive">Executive</option>
                <option value="financial">Financial</option>
            </select>
        </div>
        <div id="pretexts-list"></div>
    </div>

    <!-- USB Payload Generator -->
    <div class="card" style="margin-bottom:1rem">
        <h3>USB Payload Generator</h3>
        <p style="color:var(--text-secondary);font-size:0.85rem;margin-bottom:0.75rem">
            Generate USB drop payloads for physical social engineering assessments.
        </p>
        <div style="display:grid;grid-template-columns:1fr 1fr;gap:0.75rem">
            <div>
                <label class="form-label">Payload Type</label>
                <select id="usb-type" class="form-control">
                    <option value="autorun">Autorun.inf (Legacy)</option>
                    <option value="powershell_cradle">PowerShell Download Cradle</option>
                    <option value="hid_script">HID Script (Rubber Ducky)</option>
                    <option value="bat_file">BAT File Dropper</option>
                    <option value="lnk_dropper">LNK Shortcut Dropper</option>
                    <option value="html_smuggling">HTML Smuggling</option>
                </select>
            </div>
            <div>
                <label class="form-label">Payload URL</label>
                <input type="text" id="usb-url" class="form-control" placeholder="http://10.0.0.1:8080/payload">
            </div>
            <div>
                <label class="form-label">Executable Name</label>
                <input type="text" id="usb-exec" class="form-control" placeholder="setup.exe">
            </div>
            <div>
                <label class="form-label">Label / Title</label>
                <input type="text" id="usb-label" class="form-control" placeholder="Removable Disk">
            </div>
        </div>
        <button class="btn btn-primary" style="margin-top:0.75rem" onclick="generateUSB()">Generate Payload</button>
        <div id="usb-output" style="display:none;margin-top:0.75rem">
            <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:0.5rem">
                <strong id="usb-name"></strong>
                <button class="btn btn-sm" onclick="copyUSB()">Copy</button>
            </div>
            <pre id="usb-payload" style="background:#0a0a0a;color:#0f0;font-family:monospace;font-size:0.8rem;
                padding:1rem;border-radius:var(--radius);white-space:pre-wrap;max-height:300px;overflow-y:auto"></pre>
        </div>
    </div>

    <!-- Vishing Scripts -->
    <div class="card">
        <h3>Vishing Scripts</h3>
        <p style="color:var(--text-secondary);font-size:0.85rem;margin-bottom:0.75rem">
            Call flow scripts for voice-based social engineering.
        </p>
        <div style="display:grid;grid-template-columns:1fr 1fr;gap:0.75rem">
            <div>
                <label class="form-label">Scenario</label>
                <select id="vish-scenario" class="form-control">
                    <option value="it_helpdesk">IT Help Desk</option>
                    <option value="bank_fraud">Bank Fraud Alert</option>
                    <option value="vendor_support">Vendor Tech Support</option>
                    <option value="ceo_urgent">CEO Urgent Request</option>
                </select>
            </div>
            <div>
                <label class="form-label">Target Name</label>
                <input type="text" id="vish-target" class="form-control" placeholder="John Smith">
            </div>
            <div>
                <label class="form-label">Caller Name</label>
                <input type="text" id="vish-caller" class="form-control" placeholder="Mike Johnson">
            </div>
            <div>
                <label class="form-label">Phone Number</label>
                <input type="text" id="vish-phone" class="form-control" placeholder="(555) 123-4567">
            </div>
        </div>
        <button class="btn btn-primary" style="margin-top:0.75rem" onclick="generateVishing()">Generate Script</button>
        <div id="vish-output" style="display:none;margin-top:0.75rem">
            <div id="vish-content" style="background:var(--bg-input);padding:1rem;border-radius:var(--radius);
                font-size:0.85rem;line-height:1.6;max-height:500px;overflow-y:auto"></div>
        </div>
    </div>
</div>

<!-- ══════════════════════ QR CODES TAB ══════════════════════ -->
<div id="tab-qr" class="tab-content" style="display:none">
    <div class="card" style="max-width:700px">
        <h3>QR Code Generator</h3>
        <p style="color:var(--text-secondary);font-size:0.85rem;margin-bottom:0.75rem">
            Generate QR codes for phishing URLs, credential harvesting pages, or payload delivery.
        </p>
        <div style="display:grid;grid-template-columns:1fr 1fr;gap:0.75rem">
            <div style="grid-column:span 2">
                <label class="form-label">URL to Encode</label>
                <input type="text" id="qr-url" class="form-control" placeholder="https://secure-login.company.local/verify">
            </div>
            <div>
                <label class="form-label">Label (optional)</label>
                <input type="text" id="qr-label" class="form-control" placeholder="Scan for WiFi access">
            </div>
            <div>
                <label class="form-label">Size (px)</label>
                <select id="qr-size" class="form-control">
                    <option value="200">200</option>
                    <option value="300" selected>300</option>
                    <option value="400">400</option>
                    <option value="500">500</option>
                    <option value="600">600</option>
                </select>
            </div>
        </div>
        <button id="qr-btn" class="btn btn-primary" style="margin-top:0.75rem" onclick="generateQR()">Generate QR Code</button>

        <!-- QR Code Preview -->
        <div id="qr-preview" style="display:none;margin-top:1rem;text-align:center">
            <div style="background:#fff;display:inline-block;padding:1rem;border-radius:var(--radius)">
                <img id="qr-img" src="" alt="QR Code" style="max-width:100%">
            </div>
            <div style="margin-top:0.75rem">
                <span id="qr-url-label" style="color:var(--text-secondary);font-size:0.85rem"></span>
            </div>
            <div style="margin-top:0.5rem;display:flex;gap:0.5rem;justify-content:center">
                <button class="btn btn-sm" onclick="downloadQR()">Download</button>
                <button class="btn btn-sm" onclick="copyQRDataURL()">Copy Data URL</button>
            </div>
        </div>
    </div>
</div>

<!-- ══════════════════════ CAMPAIGNS TAB ══════════════════════ -->
<div id="tab-campaigns" class="tab-content" style="display:none">
    <!-- Create Campaign Form -->
    <div class="card" style="margin-bottom:1rem">
        <h3>Create Campaign</h3>
        <div style="display:grid;grid-template-columns:1fr 1fr;gap:0.75rem">
            <div>
                <label class="form-label">Campaign Name</label>
                <input type="text" id="camp-name" class="form-control" placeholder="Q1 Phishing Assessment">
            </div>
            <div>
                <label class="form-label">Vector</label>
                <select id="camp-vector" class="form-control">
                    <option value="email">Email</option>
                    <option value="qr">QR Code</option>
                    <option value="usb">USB Drop</option>
                    <option value="vishing">Vishing</option>
                    <option value="physical">Physical</option>
                    <option value="smishing">SMS / Smishing</option>
                </select>
            </div>
            <div style="grid-column:span 2">
                <label class="form-label">Targets (comma-separated emails or identifiers)</label>
                <textarea id="camp-targets" class="form-control" rows="3"
                    placeholder="user1@company.com, user2@company.com, user3@company.com"></textarea>
            </div>
            <div style="grid-column:span 2">
                <label class="form-label">Pretext / Notes</label>
                <input type="text" id="camp-pretext" class="form-control" placeholder="Password reset pretext targeting engineering team">
            </div>
        </div>
        <button class="btn btn-primary" style="margin-top:0.75rem" onclick="createCampaign()">Create Campaign</button>
    </div>

    <!-- Campaign List -->
    <div class="card">
        <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:0.75rem">
            <h3>Campaigns</h3>
            <button class="btn btn-sm" onclick="loadCampaigns()">Refresh</button>
        </div>
        <div id="campaigns-list">
            <p style="color:var(--text-muted)">Loading...</p>
        </div>
    </div>

    <!-- Campaign Detail View -->
    <div id="campaign-detail" class="card" style="margin-top:1rem;display:none">
        <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:0.75rem">
            <h3>Campaign: <span id="detail-name" style="color:var(--accent)"></span></h3>
            <button class="btn btn-sm" onclick="document.getElementById('campaign-detail').style.display='none'">Close</button>
        </div>
        <div id="detail-content"></div>
    </div>
</div>

<!-- ═══════════════════ STYLES ═══════════════════ -->
<style>
.tabs{display:flex;gap:0;border-bottom:2px solid var(--border);margin-bottom:1.5rem}
.tab{background:none;border:none;color:var(--text-secondary);padding:0.5rem 1.25rem;cursor:pointer;
    font-size:0.9rem;border-bottom:2px solid transparent;margin-bottom:-2px;transition:all 0.2s}
.tab:hover{color:var(--text-primary)}
.tab.active{color:var(--accent);border-bottom-color:var(--accent)}
.card{background:var(--bg-card);border:1px solid var(--border);border-radius:var(--radius);padding:1.25rem}
.form-label{display:block;font-size:0.8rem;color:var(--text-secondary);margin-bottom:0.25rem}
.form-control{width:100%;padding:0.5rem 0.75rem;background:var(--bg-input);border:1px solid var(--border);
    border-radius:var(--radius);color:var(--text-primary);font-size:0.85rem}
.form-control:focus{outline:none;border-color:var(--accent)}
.btn{padding:0.5rem 1rem;border-radius:var(--radius);border:1px solid var(--border);
    background:var(--bg-input);color:var(--text-primary);cursor:pointer;font-size:0.85rem;transition:all 0.2s}
.btn:hover{border-color:var(--accent);color:var(--accent)}
.btn-primary{background:var(--accent);color:#fff;border-color:var(--accent)}
.btn-primary:hover{background:var(--accent-hover);border-color:var(--accent-hover);color:#fff}
.btn-sm{padding:0.3rem 0.75rem;font-size:0.8rem}
.pretext-card{background:var(--bg-input);border:1px solid var(--border);border-radius:var(--radius);padding:1rem;margin-bottom:0.75rem}
.pretext-card h4{margin:0 0 0.5rem;color:var(--accent)}
.pretext-card .subject{font-size:0.85rem;color:var(--text-secondary);margin-bottom:0.5rem}
.pretext-card .body{font-size:0.8rem;color:var(--text-muted);white-space:pre-line;max-height:120px;overflow:hidden}
.pretext-card .notes{font-size:0.75rem;color:#f59e0b;margin-top:0.5rem;font-style:italic}
.cap-row{padding:0.5rem 0.75rem;border-bottom:1px solid var(--border);font-size:0.85rem}
.cap-row:last-child{border-bottom:none}
.cap-creds{font-family:monospace;color:var(--accent);font-size:0.8rem}
.camp-row{display:grid;grid-template-columns:2fr 1fr 1fr 1fr 1fr 1fr auto;gap:0.5rem;
    padding:0.6rem 0.75rem;border-bottom:1px solid var(--border);font-size:0.85rem;align-items:center}
.camp-row:last-child{border-bottom:none}
.camp-header{font-weight:600;color:var(--text-secondary);font-size:0.8rem;border-bottom:2px solid var(--border)}
.badge{display:inline-block;padding:2px 8px;border-radius:4px;font-size:0.75rem;font-weight:600}
.badge-active{background:rgba(34,197,94,0.15);color:#22c55e}
.badge-email{background:rgba(99,102,241,0.15);color:var(--accent)}
.badge-qr{background:rgba(245,158,11,0.15);color:#f59e0b}
.badge-usb{background:rgba(239,68,68,0.15);color:var(--danger)}
.badge-vishing{background:rgba(168,85,247,0.15);color:#a855f7}
.badge-physical{background:rgba(59,130,246,0.15);color:#3b82f6}
.badge-smishing{background:rgba(20,184,166,0.15);color:#14b8a6}
.page-row{display:grid;grid-template-columns:2fr 2fr 1.5fr 1fr auto;gap:0.5rem;
    padding:0.6rem 0.75rem;border-bottom:1px solid var(--border);font-size:0.85rem;align-items:center}
.page-row:last-child{border-bottom:none}
.page-header-row{font-weight:600;color:var(--text-secondary);font-size:0.8rem;border-bottom:2px solid var(--border)}
.vish-section{margin-bottom:1rem}
.vish-section h4{color:var(--accent);margin:0 0 0.5rem;font-size:0.9rem}
.vish-section p,.vish-section li{font-size:0.85rem;line-height:1.6}
.vish-section ul{margin:0;padding-left:1.5rem}
.vish-objection{margin-bottom:0.5rem}
.vish-objection strong{color:var(--text-primary)}
.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>

<!-- ═══════════════════ JAVASCRIPT ═══════════════════ -->
<script>
const API='/social-eng';
let currentQRData=null;

/* ── Tab switching ─────────────────────────────── */
function switchTab(name){
    document.querySelectorAll('.tab').forEach((t,i)=>t.classList.toggle('active',
        ['harvest','pretexts','qr','campaigns'][i]===name));
    document.querySelectorAll('.tab-content').forEach(c=>c.style.display='none');
    document.getElementById('tab-'+name).style.display='block';
    if(name==='harvest'){loadPages();loadCaptures();}
    if(name==='pretexts') loadPretexts();
    if(name==='campaigns') loadCampaigns();
}

/* ── Helpers ───────────────────────────────────── */
function esc(s){
    if(s===null||s===undefined) return '';
    var d=document.createElement('div');d.textContent=String(s);return d.innerHTML;
}
function setLoading(btn,loading){
    if(!btn) return;
    if(loading){btn._origText=btn.textContent;btn.disabled=true;btn.innerHTML='<span class="spinner-inline"></span>Working...';}
    else{btn.disabled=false;btn.textContent=btn._origText||'Submit';}
}

/* ── Page Cloning ──────────────────────────────── */
function clonePage(){
    var url=document.getElementById('clone-url').value.trim();
    if(!url) return;
    var btn=document.getElementById('clone-btn');
    setLoading(btn,true);

    postJSON(API+'/clone',{url:url}).then(function(d){
        setLoading(btn,false);
        var div=document.getElementById('clone-result');
        div.style.display='';
        if(d.ok){
            div.innerHTML='<span style="color:#22c55e">Page cloned successfully!</span><br>'+
                '<span style="color:var(--text-secondary);font-size:0.85rem">Page ID: <strong>'+esc(d.page_id)+
                '</strong> | Domain: '+esc(d.domain)+' | Size: '+d.size+' bytes</span>';
            loadPages();
        } else {
            div.innerHTML='<span style="color:var(--danger)">Error: '+esc(d.error)+'</span>';
        }
    }).catch(function(e){setLoading(btn,false);});
}

/* ── Cloned Pages List ─────────────────────────── */
function loadPages(){
    fetchJSON(API+'/pages').then(function(d){
        var div=document.getElementById('pages-list');
        var pages=d.pages||[];
        if(!pages.length){
            div.innerHTML='<p style="color:var(--text-muted)">No cloned pages yet. Clone a page above to get started.</p>';
            return;
        }
        var html='<div class="page-row page-header-row"><span>Page ID</span><span>Source URL</span><span>Date</span><span>Captures</span><span>Actions</span></div>';
        pages.forEach(function(p){
            var date=p.cloned_at?(p.cloned_at.substring(0,10)):'—';
            var srcUrl=p.source_url||'—';
            if(srcUrl.length>40) srcUrl=srcUrl.substring(0,40)+'...';
            html+='<div class="page-row">'+
                '<span style="font-family:monospace;color:var(--accent)">'+esc(p.id)+'</span>'+
                '<span title="'+esc(p.source_url)+'">'+esc(srcUrl)+'</span>'+
                '<span>'+esc(date)+'</span>'+
                '<span style="font-weight:600">'+(p.captures_count||0)+'</span>'+
                '<span><button class="btn btn-sm" onclick="viewPage(\''+esc(p.id)+'\')">View</button> '+
                '<button class="btn btn-sm" style="color:var(--danger)" onclick="deletePage(\''+esc(p.id)+'\')">Delete</button></span>'+
                '</div>';
        });
        div.innerHTML=html;

        // Update capture filter dropdown
        var sel=document.getElementById('cap-filter');
        var cur=sel.value;
        sel.innerHTML='<option value="">All Pages</option>';
        pages.forEach(function(p){
            sel.innerHTML+='<option value="'+esc(p.id)+'">'+esc(p.id)+' ('+esc(p.domain||p.source_url||'')+')</option>';
        });
        sel.value=cur;
    });
}

function viewPage(pid){
    fetchJSON(API+'/pages/'+pid).then(function(d){
        if(!d.ok) return alert(d.error||'Page not found');
        var w=window.open('','_blank','width=900,height=700');
        w.document.write(d.html);
        w.document.close();
    });
}

function deletePage(pid){
    if(!confirm('Delete cloned page '+pid+'?')) return;
    fetch(API+'/pages/'+pid,{method:'DELETE'}).then(function(r){return r.json();}).then(function(d){
        if(d.ok) loadPages();
        else alert(d.error||'Delete failed');
    });
}

/* ── Captures ──────────────────────────────────── */
function loadCaptures(){
    var pageId=document.getElementById('cap-filter').value;
    var url=API+'/captures';
    if(pageId) url+='?page_id='+encodeURIComponent(pageId);

    fetchJSON(url).then(function(d){
        var div=document.getElementById('captures-list');
        var caps=d.captures||[];
        if(!caps.length){
            div.innerHTML='<p style="color:var(--text-muted)">No credentials captured yet.</p>';
            return;
        }
        var html='';
        caps.forEach(function(c){
            var ts=(c.timestamp||'').substring(0,19).replace('T',' ');
            var creds=c.credentials||{};
            var credParts=[];
            Object.keys(creds).forEach(function(k){
                var v=creds[k];
                // Mask password-like fields
                if(/pass|pwd|secret|token/i.test(k)){
                    v=v.substring(0,2)+'***'+v.substring(v.length-1);
                }
                credParts.push(esc(k)+'='+esc(v));
            });
            html+='<div class="cap-row">'+
                '<div style="display:flex;justify-content:space-between;align-items:center">'+
                '<span style="color:var(--text-secondary);font-size:0.8rem">'+esc(ts)+' | Page: '+
                '<span style="color:var(--accent)">'+esc(c.page_id)+'</span> | IP: '+esc(c.ip)+'</span>'+
                '<span style="font-size:0.75rem;color:var(--text-muted)" title="'+esc(c.user_agent)+'">'+
                esc((c.user_agent||'').substring(0,50))+'</span></div>'+
                '<div class="cap-creds" style="margin-top:0.25rem">'+credParts.join(' &nbsp; ')+'</div>'+
                '</div>';
        });
        div.innerHTML=html;
    });
}

function clearCaptures(){
    if(!confirm('Clear all captured credentials?')) return;
    var pageId=document.getElementById('cap-filter').value;
    var url=API+'/captures';
    if(pageId) url+='?page_id='+encodeURIComponent(pageId);
    fetch(url,{method:'DELETE'}).then(function(r){return r.json();}).then(function(d){
        loadCaptures();
    });
}

/* ── Pretexts ──────────────────────────────────── */
function loadPretexts(){
    var cat=document.getElementById('pretext-cat').value;
    var url=API+'/pretexts';
    if(cat) url+='?category='+encodeURIComponent(cat);

    fetchJSON(url).then(function(d){
        var div=document.getElementById('pretexts-list');
        var pretexts=d.pretexts||{};
        var html='';
        Object.keys(pretexts).forEach(function(cat){
            html+='<h4 style="color:var(--text-secondary);margin:1rem 0 0.5rem;text-transform:capitalize">'+
                esc(cat.replace(/_/g,' '))+'</h4>';
            pretexts[cat].forEach(function(p){
                var bodyPreview=(p.body||'').substring(0,200).replace(/\n/g,' ');
                html+='<div class="pretext-card">'+
                    '<div style="display:flex;justify-content:space-between;align-items:start">'+
                    '<h4>'+esc(p.name)+'</h4>'+
                    '<button class="btn btn-sm" onclick="copyPretext(this)" data-subject="'+esc(p.subject)+
                    '" data-body="'+esc(p.body)+'">Copy</button></div>'+
                    '<div class="subject"><strong>Subject:</strong> '+esc(p.subject)+'</div>'+
                    '<div class="body">'+esc(bodyPreview)+'...</div>'+
                    (p.pretext_notes?'<div class="notes">Notes: '+esc(p.pretext_notes)+'</div>':'')+
                    '</div>';
            });
        });
        if(!html) html='<p style="color:var(--text-muted)">No templates found.</p>';
        div.innerHTML=html;
    });
}

function copyPretext(btn){
    var text='Subject: '+btn.dataset.subject+'\n\n'+btn.dataset.body;
    navigator.clipboard.writeText(text).then(function(){
        btn.textContent='Copied!';
        setTimeout(function(){btn.textContent='Copy';},1500);
    });
}

/* ── USB Payload ───────────────────────────────── */
function generateUSB(){
    var type=document.getElementById('usb-type').value;
    var params={};
    var url=document.getElementById('usb-url').value.trim();
    if(url) params.payload_url=url;
    var exec=document.getElementById('usb-exec').value.trim();
    if(exec) params.executable=exec;
    var label=document.getElementById('usb-label').value.trim();
    if(label){params.label=label;params.title=label;}

    postJSON(API+'/usb',{type:type,params:params}).then(function(d){
        var div=document.getElementById('usb-output');
        if(d.ok){
            div.style.display='';
            document.getElementById('usb-name').textContent=d.name+' — '+d.description;
            document.getElementById('usb-payload').textContent=d.payload;
        } else {
            alert(d.error||'Failed to generate payload');
        }
    });
}

function copyUSB(){
    var text=document.getElementById('usb-payload').textContent;
    navigator.clipboard.writeText(text).then(function(){});
}

/* ── Vishing Script ────────────────────────────── */
function generateVishing(){
    var scenario=document.getElementById('vish-scenario').value;
    var url=API+'/vishing/'+scenario+'?';
    var target=document.getElementById('vish-target').value.trim();
    if(target) url+='target_name='+encodeURIComponent(target)+'&';
    var caller=document.getElementById('vish-caller').value.trim();
    if(caller) url+='caller_name='+encodeURIComponent(caller)+'&';
    var phone=document.getElementById('vish-phone').value.trim();
    if(phone) url+='phone='+encodeURIComponent(phone);

    fetchJSON(url).then(function(d){
        var div=document.getElementById('vish-output');
        if(!d.ok){alert(d.error||'Error');return;}
        div.style.display='';

        var html='<div class="vish-section"><h4>Opening</h4><p>'+esc(d.opening)+'</p></div>';

        html+='<div class="vish-section"><h4>Key Questions</h4><ul>';
        (d.key_questions||[]).forEach(function(q){
            html+='<li>'+esc(q)+'</li>';
        });
        html+='</ul></div>';

        html+='<div class="vish-section"><h4>Credential Extraction</h4><p>'+esc(d.credential_extraction)+'</p></div>';

        html+='<div class="vish-section"><h4>Objection Handling</h4>';
        var obj=d.objection_handling||{};
        Object.keys(obj).forEach(function(k){
            html+='<div class="vish-objection"><strong>"'+esc(k.replace(/_/g,' '))+'":</strong> '+esc(obj[k])+'</div>';
        });
        html+='</div>';

        html+='<div class="vish-section"><h4>Closing</h4><p>'+esc(d.closing)+'</p></div>';

        document.getElementById('vish-content').innerHTML=html;
    });
}

/* ── QR Code ───────────────────────────────────── */
function generateQR(){
    var url=document.getElementById('qr-url').value.trim();
    if(!url) return;
    var btn=document.getElementById('qr-btn');
    setLoading(btn,true);

    postJSON(API+'/qr',{
        url:url,
        label:document.getElementById('qr-label').value.trim(),
        size:parseInt(document.getElementById('qr-size').value)
    }).then(function(d){
        setLoading(btn,false);
        if(!d.ok){alert(d.error||'Failed');return;}
        currentQRData=d;
        var preview=document.getElementById('qr-preview');
        preview.style.display='';
        document.getElementById('qr-img').src=d.data_url;
        document.getElementById('qr-url-label').textContent=d.url+(d.label?' — '+d.label:'');
    }).catch(function(){setLoading(btn,false);});
}

function downloadQR(){
    if(!currentQRData) return;
    var a=document.createElement('a');
    a.href=currentQRData.data_url;
    a.download='qr_'+(currentQRData.label||'code')+'.png';
    a.click();
}

function copyQRDataURL(){
    if(!currentQRData) return;
    navigator.clipboard.writeText(currentQRData.data_url).then(function(){});
}

/* ── Campaigns ─────────────────────────────────── */
function createCampaign(){
    var name=document.getElementById('camp-name').value.trim();
    if(!name){alert('Campaign name required');return;}
    var vector=document.getElementById('camp-vector').value;
    var targetsStr=document.getElementById('camp-targets').value.trim();
    var targets=targetsStr?targetsStr.split(',').map(function(t){return t.trim();}).filter(Boolean):[];
    var pretext=document.getElementById('camp-pretext').value.trim();

    postJSON(API+'/campaign',{name:name,vector:vector,targets:targets,pretext:pretext}).then(function(d){
        if(d.ok){
            document.getElementById('camp-name').value='';
            document.getElementById('camp-targets').value='';
            document.getElementById('camp-pretext').value='';
            loadCampaigns();
        } else {
            alert(d.error||'Failed to create campaign');
        }
    });
}

function loadCampaigns(){
    fetchJSON(API+'/campaigns').then(function(d){
        var div=document.getElementById('campaigns-list');
        var camps=d.campaigns||[];
        if(!camps.length){
            div.innerHTML='<p style="color:var(--text-muted)">No campaigns yet. Create one above.</p>';
            return;
        }
        var html='<div class="camp-row camp-header"><span>Name</span><span>Vector</span><span>Created</span>'+
            '<span>Targets</span><span>Clicks</span><span>Success</span><span>Actions</span></div>';
        camps.forEach(function(c){
            var stats=c.stats||{};
            var total=stats.total_targets||0;
            var captured=stats.captured||0;
            var rate=total>0?Math.round(captured/total*100)+'%':'—';
            var date=(c.created_at||'').substring(0,10);
            var vectorClass='badge badge-'+(c.vector||'email');
            html+='<div class="camp-row">'+
                '<span style="font-weight:600">'+esc(c.name)+'</span>'+
                '<span><span class="'+vectorClass+'">'+esc(c.vector)+'</span></span>'+
                '<span>'+esc(date)+'</span>'+
                '<span>'+total+'</span>'+
                '<span>'+(stats.clicked||0)+'</span>'+
                '<span>'+rate+'</span>'+
                '<span>'+
                '<button class="btn btn-sm" onclick="viewCampaign(\''+esc(c.id)+'\')">View</button> '+
                '<button class="btn btn-sm" style="color:var(--danger)" onclick="deleteCampaign(\''+esc(c.id)+'\')">Delete</button>'+
                '</span></div>';
        });
        div.innerHTML=html;
    });
}

function viewCampaign(cid){
    fetchJSON(API+'/campaign/'+cid).then(function(d){
        if(!d.ok){alert(d.error);return;}
        var c=d.campaign;
        var detail=document.getElementById('campaign-detail');
        detail.style.display='';
        document.getElementById('detail-name').textContent=c.name;

        var stats=c.stats||{};
        var html='<div style="display:grid;grid-template-columns:repeat(4,1fr);gap:0.75rem;margin-bottom:1rem">';
        html+='<div style="text-align:center;padding:0.75rem;background:var(--bg-input);border-radius:var(--radius)">'+
            '<div style="font-size:1.5rem;font-weight:700;color:var(--accent)">'+(stats.total_targets||0)+'</div>'+
            '<div style="font-size:0.8rem;color:var(--text-secondary)">Targets</div></div>';
        html+='<div style="text-align:center;padding:0.75rem;background:var(--bg-input);border-radius:var(--radius)">'+
            '<div style="font-size:1.5rem;font-weight:700;color:#22c55e">'+(stats.sent||0)+'</div>'+
            '<div style="font-size:0.8rem;color:var(--text-secondary)">Sent</div></div>';
        html+='<div style="text-align:center;padding:0.75rem;background:var(--bg-input);border-radius:var(--radius)">'+
            '<div style="font-size:1.5rem;font-weight:700;color:#f59e0b">'+(stats.clicked||0)+'</div>'+
            '<div style="font-size:0.8rem;color:var(--text-secondary)">Clicked</div></div>';
        html+='<div style="text-align:center;padding:0.75rem;background:var(--bg-input);border-radius:var(--radius)">'+
            '<div style="font-size:1.5rem;font-weight:700;color:var(--danger)">'+(stats.captured||0)+'</div>'+
            '<div style="font-size:0.8rem;color:var(--text-secondary)">Captured</div></div>';
        html+='</div>';

        // Info
        html+='<div style="margin-bottom:1rem;font-size:0.85rem">'+
            '<strong>Vector:</strong> <span class="badge badge-'+esc(c.vector)+'">'+esc(c.vector)+'</span> &nbsp; '+
            '<strong>Status:</strong> <span class="badge badge-active">'+esc(c.status)+'</span> &nbsp; '+
            '<strong>Created:</strong> '+esc((c.created_at||'').substring(0,10))+
            (c.pretext?' &nbsp; <strong>Pretext:</strong> '+esc(c.pretext):'')+
            '</div>';

        // Targets
        if(c.targets&&c.targets.length){
            html+='<h4 style="margin-bottom:0.5rem">Targets</h4>';
            html+='<div style="font-family:monospace;font-size:0.8rem;background:var(--bg-input);padding:0.75rem;border-radius:var(--radius)">';
            c.targets.forEach(function(t){
                html+=esc(t)+'<br>';
            });
            html+='</div>';
        }

        // Event Timeline
        if(c.events&&c.events.length){
            html+='<h4 style="margin:1rem 0 0.5rem">Event Timeline</h4>';
            html+='<div style="max-height:200px;overflow-y:auto">';
            c.events.forEach(function(ev){
                var ts=(ev.timestamp||'').substring(0,19).replace('T',' ');
                html+='<div style="padding:0.3rem 0;font-size:0.8rem;border-bottom:1px solid var(--border)">'+
                    '<span style="color:var(--text-muted)">'+esc(ts)+'</span> '+
                    '<span class="badge badge-active">'+esc(ev.type)+'</span> '+
                    esc(ev.detail||'')+'</div>';
            });
            html+='</div>';
        }

        document.getElementById('detail-content').innerHTML=html;
    });
}

function deleteCampaign(cid){
    if(!confirm('Delete this campaign?')) return;
    fetch(API+'/campaign/'+cid,{method:'DELETE'}).then(function(r){return r.json();}).then(function(d){
        if(d.ok){
            loadCampaigns();
            document.getElementById('campaign-detail').style.display='none';
        } else {
            alert(d.error||'Delete failed');
        }
    });
}

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