<?php
require_once __DIR__ . '/../includes/auth.php';
requireAuth();
$user = currentUser();
$config = getConfig();
$baseUrl = rtrim($config['base_url'] ?? '', '/');
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>TrackPro — Dashboard</title>
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500&family=Outfit:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="../assets/css/admin.css">
</head>
<body>
<!-- Sidebar -->
<aside class="sidebar" id="sidebar">
    <div class="sidebar-header">
        <div class="logo">
            <div class="logo-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/></svg></div>
            <span class="logo-text">Track<em>Pro</em></span>
        </div>
    </div>
    <nav class="sidebar-nav">
        <a class="nav-item active" data-page="dashboard" onclick="showPage('dashboard')">
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/></svg>
            Dashboard
        </a>
        <a class="nav-item" data-page="links" onclick="showPage('links')">
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M10 13a5 5 0 007.54.54l3-3a5 5 0 00-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 00-7.54-.54l-3 3a5 5 0 007.07 7.07l1.71-1.71"/></svg>
            Tracking Links
        </a>
        <a class="nav-item" data-page="sessions" onclick="showPage('sessions')">
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M17 21v-2a4 4 0 00-4-4H5a4 4 0 00-4-4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 00-3-3.87"/><path d="M16 3.13a4 4 0 010 7.75"/></svg>
            Sessions
        </a>
        <a class="nav-item" data-page="export" onclick="showPage('export')">
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
            Export
        </a>
        <a class="nav-item" data-page="alerts" onclick="showPage('alerts')">
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 8A6 6 0 006 8c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 01-3.46 0"/></svg>
            Alerts <span class="nav-badge" id="alert-badge" style="display:none">0</span>
        </a>
        <a class="nav-item" data-page="live" onclick="showPage('live')">
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="2"/><path d="M16.24 7.76a6 6 0 010 8.49"/><path d="M7.76 16.24a6 6 0 010-8.49"/><path d="M19.07 4.93a10 10 0 010 14.14"/><path d="M4.93 19.07a10 10 0 010-14.14"/></svg>
            Live Feed
        </a>
        <a class="nav-item" data-page="geomap" onclick="showPage('geomap')">
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="10" r="3"/><path d="M12 21.7C17.3 17 20 13 20 10a8 8 0 10-16 0c0 3 2.7 7 8 11.7z"/></svg>
            Geo Map
        </a>
        <a class="nav-item" data-page="visitors" onclick="showPage('visitors')">
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M16 21v-2a4 4 0 00-4-4H5a4 4 0 00-4-4v2"/><circle cx="8.5" cy="7" r="4"/><path d="M20 8v6"/><path d="M23 11h-6"/></svg>
            Visitors
        </a>
        <a class="nav-item" data-page="intelligence" onclick="showPage('intelligence')" style="border-top:1px solid rgba(99,102,241,.1);margin-top:.5rem;padding-top:.8rem">
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2a10 10 0 110 20 10 10 0 010-20z"/><path d="M12 16v-4"/><path d="M12 8h.01"/><path d="M2 12h2"/><path d="M20 12h2"/><path d="M12 2v2"/><path d="M12 20v2"/></svg>
            Intelligence
        </a>
        <?php if (hasRole('superadmin')): ?>
        <a class="nav-item" data-page="users" onclick="showPage('users')">
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M16 21v-2a4 4 0 00-4-4H6a4 4 0 00-4-4v2"/><circle cx="9" cy="7" r="4"/><line x1="19" y1="8" x2="19" y2="14"/><line x1="22" y1="11" x2="16" y2="11"/></svg>
            Users
        </a>
        <a class="nav-item" data-page="webhooks" onclick="showPage('webhooks')">
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 12h-4l-3 9L9 3l-3 9H2"/></svg>
            Webhooks
        </a>
        <a class="nav-item" data-page="audit" onclick="showPage('audit')">
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/></svg>
            Audit Log
        </a>
        <a class="nav-item" data-page="settings" onclick="showPage('settings')">
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 01-2.83 2.83l-.06-.06a1.65 1.65 0 00-1.82-.33 1.65 1.65 0 00-1 1.51V21a2 2 0 01-4 0v-.09A1.65 1.65 0 009 19.4a1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 01-2.83-2.83l.06-.06A1.65 1.65 0 004.68 15a1.65 1.65 0 00-1.51-1H3a2 2 0 010-4h.09A1.65 1.65 0 004.6 9a1.65 1.65 0 00-.33-1.82l-.06-.06a2 2 0 012.83-2.83l.06.06A1.65 1.65 0 009 4.68a1.65 1.65 0 001-1.51V3a2 2 0 014 0v.09a1.65 1.65 0 001 1.51 1.65 1.65 0 001.82-.33l.06-.06a2 2 0 012.83 2.83l-.06.06A1.65 1.65 0 0019.4 9a1.65 1.65 0 001.51 1H21a2 2 0 010 4h-.09a1.65 1.65 0 00-1.51 1z"/></svg>
            Settings
        </a>
        <?php endif; ?>
    </nav>
    <div class="sidebar-footer">
        <div class="user-badge">
            <div class="user-avatar"><?= strtoupper(substr($user['username'], 0, 1)) ?></div>
            <div class="user-info">
                <div class="user-name"><?= htmlspecialchars($user['username']) ?></div>
                <div class="user-role"><?= htmlspecialchars($user['role']) ?></div>
            </div>
        </div>
        <a href="logout.php" class="logout-btn" title="Sign Out">
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 21H5a2 2 0 01-2-2V5a2 2 0 012-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg>
        </a>
    </div>
</aside>

<!-- Mobile header -->
<header class="mobile-header">
    <button class="hamburger" onclick="document.getElementById('sidebar').classList.toggle('open')">
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="18" x2="21" y2="18"/></svg>
    </button>
    <span class="logo-text">Track<em>Pro</em></span>
</header>

<!-- Main Content -->
<main class="main-content">

<!-- ═══ DASHBOARD PAGE ═══ -->
<div class="page active" id="page-dashboard">
    <div class="page-header">
        <h1>Dashboard</h1>
        <div class="header-actions">
            <button class="btn btn-primary" onclick="showPage('links');document.getElementById('create-link-modal').classList.add('open')">
                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" height="16"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
                New Link
            </button>
        </div>
    </div>

    <div class="stats-grid" id="stats-grid">
        <div class="stat-card"><div class="stat-icon blue"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M10 13a5 5 0 007.54.54l3-3a5 5 0 00-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 00-7.54-.54l-3 3a5 5 0 007.07 7.07l1.71-1.71"/></svg></div><div class="stat-body"><div class="stat-value" id="stat-total-links">—</div><div class="stat-label">Total Links</div></div></div>
        <div class="stat-card"><div class="stat-icon green"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg></div><div class="stat-body"><div class="stat-value" id="stat-today-sessions">—</div><div class="stat-label">Today's Hits</div></div></div>
        <div class="stat-card"><div class="stat-icon purple"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M17 21v-2a4 4 0 00-4-4H5a4 4 0 00-4-4v2"/><circle cx="9" cy="7" r="4"/></svg></div><div class="stat-body"><div class="stat-value" id="stat-unique-visitors">—</div><div class="stat-label">Unique Visitors</div></div></div>
        <div class="stat-card"><div class="stat-icon red"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg></div><div class="stat-body"><div class="stat-value" id="stat-high-risk">—</div><div class="stat-label">High Risk</div></div></div>
        <div class="stat-card"><div class="stat-icon" style="background:rgba(245,158,11,.12);color:#f59e0b"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg></div><div class="stat-body"><div class="stat-value" id="stat-vpn">—</div><div class="stat-label">VPN Sessions</div></div></div>
        <div class="stat-card"><div class="stat-icon" style="background:rgba(239,68,68,.12);color:#ef4444"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/></svg></div><div class="stat-body"><div class="stat-value" id="stat-tor">—</div><div class="stat-label">Tor Sessions</div></div></div>
        <div class="stat-card"><div class="stat-icon" style="background:rgba(16,185,129,.12);color:#10b981"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0118 0z"/><circle cx="12" cy="10" r="3"/></svg></div><div class="stat-body"><div class="stat-value" id="stat-gps">—</div><div class="stat-label">GPS Collected</div></div></div>
    </div>

    <div class="grid-2">
        <div class="card">
            <div class="card-header"><h3>Sessions (30 days)</h3></div>
            <div class="card-body"><canvas id="chart-timeline" height="200"></canvas></div>
        </div>
        <div class="card">
            <div class="card-header"><h3>Devices</h3></div>
            <div class="card-body"><canvas id="chart-devices" height="200"></canvas></div>
        </div>
    </div>

    <div class="grid-2">
        <div class="card">
            <div class="card-header"><h3>Top Countries</h3></div>
            <div class="card-body" id="top-countries-list"></div>
        </div>
        <div class="card">
            <div class="card-header"><h3>Browsers</h3></div>
            <div class="card-body"><canvas id="chart-browsers" height="200"></canvas></div>
        </div>
    </div>
</div>

<!-- ═══ LINKS PAGE ═══ -->
<div class="page" id="page-links">
    <div class="page-header">
        <h1>Tracking Links</h1>
        <div class="header-actions">
            <input type="text" class="search-input" placeholder="Search links..." id="links-search" oninput="debounce(loadLinks,400)()">
            <button class="btn btn-primary" onclick="openCreateLink()">
                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" height="16"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
                Create Link
            </button>
        </div>
    </div>
    <div class="card">
        <div class="table-wrap">
            <table class="data-table" id="links-table">
                <thead><tr><th>UID</th><th>Title</th><th>Mode</th><th>Priority</th><th>Hits</th><th>Status</th><th>Tags</th><th>Created</th><th>Actions</th></tr></thead>
                <tbody id="links-tbody"></tbody>
            </table>
        </div>
        <div class="table-footer" id="links-pagination"></div>
    </div>

    <!-- Create Link Modal -->
    <div class="modal-overlay" id="create-link-modal">
        <div class="modal">
            <div class="modal-header">
                <h3>Create Tracking Link</h3>
                <button class="modal-close" onclick="this.closest('.modal-overlay').classList.remove('open')">&times;</button>
            </div>
            <div class="modal-body">
                <div class="field"><label>Title *</label><input type="text" id="link-title" placeholder="e.g. Phishing Investigation #42"></div>
                <div class="field"><label>Description</label><textarea id="link-desc" rows="2" placeholder="Optional notes"></textarea></div>
                <div class="field">
                    <label>Landing Mode</label>
                    <select id="link-mode" onchange="toggleRedirectField()">
                        <option value="neutral">Neutral Landing Page</option>
                        <option value="redirect">Redirect to URL</option>
                        <option value="custom">Custom HTML</option>
                    </select>
                </div>
                <div class="field" id="field-dest" style="display:none"><label>Destination URL</label><input type="url" id="link-dest" placeholder="https://example.com"></div>
                <div class="field" id="field-custom" style="display:none"><label>Custom HTML</label><textarea id="link-custom" rows="4" placeholder="<h1>Custom page</h1>"></textarea></div>
                <div class="grid-2">
                    <div class="field"><label>Expires At (optional)</label><input type="datetime-local" id="link-expires"></div>
                    <div class="field"><label>Max Hits (optional)</label><input type="number" id="link-maxhits" min="1" placeholder="Unlimited"></div>
                </div>
                <div class="grid-2">
                    <div class="field"><label>Team</label><input type="text" id="link-team" placeholder="e.g. Security, Fraud"></div>
                    <div class="field"><label>Priority</label><select id="link-priority"><option value="low">Low</option><option value="medium" selected>Medium</option><option value="high">High</option><option value="critical">Critical</option></select></div>
                </div>
                <div class="field"><label>Tags (comma-separated)</label><input type="text" id="link-tags" placeholder="phishing, investigation, case-42"></div>
                <div class="field"><label>Password Protection (optional)</label><input type="password" id="link-password" placeholder="Leave empty for no password"></div>
                <div class="field"><label>Internal Notes</label><textarea id="link-notes" rows="2" placeholder="Private investigator notes"></textarea></div>
                <div style="border-top:1px solid rgba(99,102,241,.1);margin:1.2rem 0;padding-top:1.2rem">
                    <h4 style="font-size:.85rem;color:#8896b3;margin-bottom:1rem">📱 Link Preview (Open Graph)</h4>
                    <p style="font-size:.75rem;color:#5b6b8a;margin-bottom:1rem">These control how the link appears when shared on WhatsApp, Facebook, Telegram, Twitter, etc.</p>
                    <div class="field"><label>Preview Title</label><input type="text" id="link-og-title" placeholder="e.g. Check out this article!"></div>
                    <div class="field"><label>Preview Description</label><textarea id="link-og-desc" rows="2" placeholder="A short description shown below the title"></textarea></div>
                    <div class="field"><label>Preview Image</label>
                        <div style="display:flex;gap:.5rem;align-items:flex-start">
                            <div style="flex:1">
                                <input type="file" id="link-og-file" accept="image/jpeg,image/png,image/gif,image/webp" onchange="uploadOgImage(this)" style="font-size:.82rem">
                                <input type="hidden" id="link-og-image">
                                <p style="font-size:.7rem;color:#5b6b8a;margin-top:.3rem">Recommended: 1200×630px. Max 5MB. JPG/PNG/GIF/WEBP</p>
                            </div>
                            <div id="og-preview-thumb" style="width:80px;height:80px;border-radius:8px;background:var(--bg-input);border:1px dashed rgba(99,102,241,.2);display:flex;align-items:center;justify-content:center;overflow:hidden;flex-shrink:0">
                                <span style="font-size:.65rem;color:#5b6b8a">No image</span>
                            </div>
                        </div>
                    </div>
                    <div class="field"><label>Site Name (optional)</label><input type="text" id="link-og-sitename" placeholder="e.g. CNN, TechCrunch, Your Brand"></div>
                </div>
                <div style="border-top:1px solid rgba(99,102,241,.1);margin:1.2rem 0;padding-top:1.2rem">
                    <h4 style="font-size:.85rem;color:#8896b3;margin-bottom:1rem">🎭 Link Cloaking</h4>
                    <p style="font-size:.75rem;color:#5b6b8a;margin-bottom:1rem">Make the link appear as a legitimate page in the browser tab.</p>
                    <div class="field"><label>Tab Title</label><input type="text" id="link-cloak-title" placeholder="e.g. CNN Breaking News - Latest Updates"></div>
                    <div class="field"><label>Favicon URL</label><input type="url" id="link-cloak-favicon" placeholder="e.g. https://www.google.com/favicon.ico"></div>
                </div>
                <div style="border-top:1px solid rgba(99,102,241,.1);margin:1.2rem 0;padding-top:1.2rem">
                    <h4 style="font-size:.85rem;color:#8896b3;margin-bottom:1rem">📧 Pixel Tracking</h4>
                    <label style="display:flex;align-items:center;gap:.5rem;font-size:.82rem;color:var(--text-secondary)">
                        <input type="checkbox" id="link-pixel-enabled"> Enable 1x1 pixel tracking for this link
                    </label>
                    <p style="font-size:.72rem;color:#5b6b8a;margin-top:.4rem">After creating, use the pixel button on the link row to copy the pixel URL for embedding in emails.</p>
                </div>
            </div>
            <div class="modal-footer">
                <button class="btn btn-ghost" onclick="this.closest('.modal-overlay').classList.remove('open')">Cancel</button>
                <button class="btn btn-primary" onclick="createLink()">Create Link</button>
            </div>
        </div>
    </div>
</div>

<!-- ═══ SESSIONS PAGE ═══ -->
<div class="page" id="page-sessions">
    <div class="page-header">
        <h1>Sessions</h1>
        <div class="header-actions">
            <select class="search-input" id="sessions-link-filter" onchange="loadSessions()">
                <option value="">All Links</option>
            </select>
            <select class="search-input" id="sessions-risk-filter" onchange="loadSessions()">
                <option value="">All Risk Levels</option>
                <option value="50">High Risk (50+)</option>
                <option value="30">Medium+ (30+)</option>
            </select>
        </div>
    </div>
    <div class="card">
        <div class="table-wrap">
            <table class="data-table">
                <thead><tr><th>Time</th><th>Link</th><th>IP</th><th>Location</th><th>Device</th><th>Browser</th><th>Risk</th><th>Flags</th><th></th></tr></thead>
                <tbody id="sessions-tbody"></tbody>
            </table>
        </div>
        <div class="table-footer" id="sessions-pagination"></div>
    </div>

    <!-- Session Detail Modal -->
    <div class="modal-overlay" id="session-detail-modal">
        <div class="modal modal-lg">
            <div class="modal-header"><h3>Session Details</h3><button class="modal-close" onclick="this.closest('.modal-overlay').classList.remove('open')">&times;</button></div>
            <div class="modal-body" id="session-detail-body">Loading...</div>
        </div>
    </div>
</div>

<!-- ═══ EXPORT PAGE ═══ -->
<div class="page" id="page-export">
    <div class="page-header"><h1>Export Data</h1></div>
    <div class="card" style="max-width:600px">
        <div class="card-body">
            <div class="field"><label>Link (optional)</label><select id="export-link"><option value="">All Links</option></select></div>
            <div class="grid-2">
                <div class="field"><label>From Date</label><input type="date" id="export-from"></div>
                <div class="field"><label>To Date</label><input type="date" id="export-to"></div>
            </div>
            <div class="field"><label>Format</label>
                <div class="radio-group">
                    <label class="radio"><input type="radio" name="export-fmt" value="csv" checked> CSV</label>
                    <label class="radio"><input type="radio" name="export-fmt" value="json"> JSON</label>
                </div>
            </div>
            <button class="btn btn-primary" onclick="exportData()" style="margin-top:1rem">Download Export</button>
        </div>
    </div>
</div>

<!-- ═══ USERS PAGE ═══ -->
<div class="page" id="page-users">
    <div class="page-header">
        <h1>User Management</h1>
        <button class="btn btn-primary" onclick="document.getElementById('create-user-modal').classList.add('open')">Add User</button>
    </div>
    <div class="card">
        <div class="table-wrap">
            <table class="data-table">
                <thead><tr><th>Username</th><th>Email</th><th>Role</th><th>Status</th><th>Last Login</th></tr></thead>
                <tbody id="users-tbody"></tbody>
            </table>
        </div>
    </div>
    <div class="modal-overlay" id="create-user-modal">
        <div class="modal">
            <div class="modal-header"><h3>Add User</h3><button class="modal-close" onclick="this.closest('.modal-overlay').classList.remove('open')">&times;</button></div>
            <div class="modal-body">
                <div class="field"><label>Username</label><input type="text" id="new-username"></div>
                <div class="field"><label>Email</label><input type="email" id="new-email"></div>
                <div class="field"><label>Password</label><input type="password" id="new-password"></div>
                <div class="field"><label>Role</label><select id="new-role"><option value="viewer">Viewer</option><option value="analyst">Analyst</option><option value="admin">Admin</option><option value="superadmin">Super Admin</option></select></div>
            </div>
            <div class="modal-footer">
                <button class="btn btn-ghost" onclick="this.closest('.modal-overlay').classList.remove('open')">Cancel</button>
                <button class="btn btn-primary" onclick="createUser()">Create User</button>
            </div>
        </div>
    </div>
</div>

<!-- ═══ SETTINGS PAGE ═══ -->
<div class="page" id="page-settings">
    <div class="page-header"><h1>Settings</h1></div>
    <div class="card" style="max-width:600px">
        <div class="card-body" id="settings-form"></div>
    </div>
</div>

<!-- ═══ LIVE FEED PAGE ═══ -->
<div class="page" id="page-live">
    <div class="page-header">
        <h1>Live Session Feed</h1>
        <div style="display:flex;align-items:center;gap:1rem">
            <div id="live-counter" style="background:rgba(16,185,129,.12);color:#10b981;padding:.4rem 1rem;border-radius:20px;font-size:.85rem;font-weight:600">● 0 active</div>
            <label style="font-size:.8rem;color:var(--text-muted);display:flex;align-items:center;gap:.4rem"><input type="checkbox" id="live-auto" checked onchange="toggleLive()"> Auto-refresh</label>
        </div>
    </div>
    <div class="card"><div class="table-wrap"><table class="data-table"><thead><tr><th>Time</th><th>Link</th><th>IP</th><th>Location</th><th>Device</th><th>Risk</th></tr></thead><tbody id="live-tbody"></tbody></table></div></div>
</div>

<!-- ═══ GEO MAP PAGE ═══ -->
<div class="page" id="page-geomap">
    <div class="page-header"><h1>Visitor Map</h1></div>
    <div class="card" style="padding:0;overflow:hidden;border-radius:12px">
        <div id="geomap" style="height:600px;width:100%;background:#0c1223"></div>
    </div>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.min.css">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.min.js"></script>
</div>

<!-- ═══ ALERTS PAGE ═══ -->
<div class="page" id="page-alerts">
    <div class="page-header">
        <h1>Alerts</h1>
        <button class="btn btn-ghost" onclick="markAllRead()">Mark All Read</button>
    </div>
    <div class="card"><div class="table-wrap"><table class="data-table"><thead><tr><th>Time</th><th>Severity</th><th>Type</th><th>Title</th><th>Message</th><th></th></tr></thead><tbody id="alerts-tbody"></tbody></table></div></div>
</div>

<!-- ═══ VISITORS PAGE ═══ -->
<div class="page" id="page-visitors">
    <div class="page-header"><h1>Visitor Correlation</h1></div>
    <div class="card"><div class="table-wrap"><table class="data-table"><thead><tr><th>Fingerprint</th><th>IP</th><th>Sessions</th><th>First Seen</th><th>Last Seen</th><th>Risk</th></tr></thead><tbody id="visitors-tbody"></tbody></table></div></div>
</div>

<!-- ═══ WEBHOOKS PAGE ═══ -->
<div class="page" id="page-webhooks">
    <div class="page-header">
        <h1>Webhooks</h1>
        <button class="btn btn-primary" onclick="document.getElementById('create-webhook-modal').classList.add('open')">Add Webhook</button>
    </div>
    <div class="card"><div class="table-wrap"><table class="data-table"><thead><tr><th>Name</th><th>URL</th><th>Events</th><th>Status</th><th>Last Triggered</th></tr></thead><tbody id="webhooks-tbody"></tbody></table></div></div>
    <div class="modal-overlay" id="create-webhook-modal"><div class="modal"><div class="modal-header"><h3>Add Webhook</h3><button class="modal-close" onclick="this.closest('.modal-overlay').classList.remove('open')">&times;</button></div><div class="modal-body">
        <div class="field"><label>Name</label><input type="text" id="wh-name" placeholder="e.g. Slack Alert"></div>
        <div class="field"><label>URL</label><input type="url" id="wh-url" placeholder="https://hooks.slack.com/..."></div>
        <div class="field"><label>Events (comma-separated)</label><input type="text" id="wh-events" placeholder="new_session,high_risk,tor_access"></div>
        <div class="field"><label>Secret (optional)</label><input type="text" id="wh-secret" placeholder="Signing secret"></div>
    </div><div class="modal-footer"><button class="btn btn-ghost" onclick="this.closest('.modal-overlay').classList.remove('open')">Cancel</button><button class="btn btn-primary" onclick="createWebhook()">Create</button></div></div></div>
</div>

<!-- ═══ AUDIT LOG PAGE ═══ -->
<div class="page" id="page-audit">
    <div class="page-header"><h1>Audit Log</h1></div>
    <div class="card"><div class="table-wrap"><table class="data-table"><thead><tr><th>Time</th><th>User</th><th>Action</th><th>Entity</th><th>IP</th><th>Details</th></tr></thead><tbody id="audit-tbody"></tbody></table></div></div>
</div>

<div class="page" id="page-intelligence">
    <div class="page-header">
        <h1>Intelligence Engine</h1>
        <div style="display:flex;gap:.5rem">
            <button class="btn btn-primary" onclick="runProfileAll()" id="btn-profile-all">Profile All Visitors</button>
            <button class="btn btn-ghost" onclick="runCorrelation()" id="btn-correlate">Run Identity Correlation</button>
        </div>
    </div>
    <div id="intel-status" style="display:none;padding:.8rem 1.2rem;margin-bottom:1rem;border-radius:8px;font-size:.85rem"></div>
    <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:1rem;margin-bottom:1.5rem" id="intel-stats"></div>
    <div style="display:grid;grid-template-columns:1fr 1fr;gap:1rem;margin-bottom:1.5rem">
        <div class="card" style="padding:1.2rem"><h3 style="font-size:.9rem;margin-bottom:1rem;color:var(--text-secondary)">Device Tier Distribution</h3><div id="intel-tiers"></div></div>
        <div class="card" style="padding:1.2rem"><h3 style="font-size:.9rem;margin-bottom:1rem;color:var(--text-secondary)">Tech Proficiency Levels</h3><div id="intel-tech"></div></div>
        <div class="card" style="padding:1.2rem"><h3 style="font-size:.9rem;margin-bottom:1rem;color:var(--text-secondary)">Visitor Intent</h3><div id="intel-intent"></div></div>
        <div class="card" style="padding:1.2rem"><h3 style="font-size:.9rem;margin-bottom:1rem;color:var(--text-secondary)">Privacy Consciousness</h3><div id="intel-privacy"></div></div>
        <div class="card" style="padding:1.2rem"><h3 style="font-size:.9rem;margin-bottom:1rem;color:var(--text-secondary)">Platform Ecosystem</h3><div id="intel-ecosystem"></div></div>
        <div class="card" style="padding:1.2rem"><h3 style="font-size:.9rem;margin-bottom:1rem;color:var(--text-secondary)">Engagement Levels</h3><div id="intel-engagement"></div></div>
    </div>
    <div class="card">
        <h3 style="font-size:.9rem;padding:1rem 1.2rem 0;color:var(--text-secondary)">Recent Visitor Profiles</h3>
        <div class="table-wrap"><table class="data-table"><thead><tr><th>Visitor</th><th>Device Tier</th><th>Tech Level</th><th>Intent</th><th>Privacy</th><th>Ecosystem</th><th>Engagement</th><th>Age Range</th><th>Profession</th><th>Confidence</th><th></th></tr></thead><tbody id="intel-profiles-tbody"></tbody></table></div>
    </div>
</div>

</main>

<script>
const BASE = <?= json_encode($baseUrl) ?>;
const API = BASE + '/api/router.php?route=';
const ROLE = <?= json_encode($user['role']) ?>;
let currentPage = 'dashboard';

// ─── Navigation ──────────────────────────────────────────────────────────────
function showPage(page) {
    document.querySelectorAll('.page').forEach(p => p.classList.remove('active'));
    document.querySelectorAll('.nav-item').forEach(n => n.classList.remove('active'));
    document.getElementById('page-' + page)?.classList.add('active');
    document.querySelector(`[data-page="${page}"]`)?.classList.add('active');
    document.getElementById('sidebar')?.classList.remove('open');
    currentPage = page;

    if (page === 'dashboard') loadDashboard();
    if (page === 'links') loadLinks();
    if (page === 'sessions') { loadLinkFilter(); loadSessions(); }
    if (page === 'export') loadLinkFilter();
    if (page === 'users') loadUsers();
    if (page === 'settings') loadSettings();
    if (page === 'alerts') loadAlerts();
    if (page === 'visitors') loadVisitors();
    if (page === 'webhooks') loadWebhooks();
    if (page === 'audit') loadAudit();
    if (page === 'live') { loadLive(); startLive(); }
    if (page === 'geomap') loadGeoMap();
    if (page === 'intelligence') loadIntelligence();
}

// ─── API Helper ──────────────────────────────────────────────────────────────
async function api(route, method = 'GET', body = null) {
    const opts = { method, headers: { 'Content-Type': 'application/json' } };
    if (body) opts.body = JSON.stringify(body);
    const res = await fetch(API + route, opts);
    if (res.headers.get('content-type')?.includes('json')) return res.json();
    return res;
}

function debounce(fn, ms) {
    let t;
    return (...args) => { clearTimeout(t); t = setTimeout(() => fn(...args), ms); };
}

function timeAgo(dt) {
    const s = Math.floor((Date.now() - new Date(dt)) / 1000);
    if (s < 60) return s + 's ago';
    if (s < 3600) return Math.floor(s/60) + 'm ago';
    if (s < 86400) return Math.floor(s/3600) + 'h ago';
    return Math.floor(s/86400) + 'd ago';
}

function riskBadge(score) {
    if (score === null || score === undefined) return '<span class="badge badge-gray">N/A</span>';
    const s = parseFloat(score);
    if (s >= 50) return `<span class="badge badge-red">${s.toFixed(0)}</span>`;
    if (s >= 25) return `<span class="badge badge-yellow">${s.toFixed(0)}</span>`;
    return `<span class="badge badge-green">${s.toFixed(0)}</span>`;
}

function flagIcons(session) {
    let flags = '';
    if (session.is_vpn == 1) flags += '<span class="flag flag-yellow" title="VPN">VPN</span>';
    if (session.is_tor == 1) flags += '<span class="flag flag-red" title="Tor">TOR</span>';
    if (session.is_proxy == 1) flags += '<span class="flag flag-orange" title="Proxy">PRX</span>';
    if (session.is_bot == 1) flags += '<span class="flag flag-purple" title="Bot">BOT</span>';
    if (session.webdriver == 1 || session.automation_detected == 1) flags += '<span class="flag flag-red" title="Automation">AUTO</span>';
    if (session.webrtc_local_ip && session.is_vpn == 1) flags += '<span class="flag flag-orange" title="WebRTC Leak">LEAK</span>';
    if (session.gps_source === 'browser') flags += '<span class="flag flag-green" title="GPS">GPS</span>';
    if (session.ad_blocker_detected == 1) flags += '<span class="flag flag-yellow" title="Ad Blocker">ADB</span>';
    if (session.incognito_detected == 1) flags += '<span class="flag flag-purple" title="Incognito">INC</span>';
    if (session.in_app_browser && session.in_app_browser !== 'none') flags += '<span class="flag flag-blue" title="In-App: '+esc(session.in_app_browser)+'">APP</span>';
    return flags || '<span class="flag flag-green">OK</span>';
}

// ─── Dashboard ───────────────────────────────────────────────────────────────
async function loadDashboard() {
    const d = await api('dashboard/stats');

    document.getElementById('stat-total-links').textContent = d.total_links || 0;
    document.getElementById('stat-today-sessions').textContent = d.today_sessions || 0;
    document.getElementById('stat-unique-visitors').textContent = d.unique_visitors || 0;
    document.getElementById('stat-high-risk').textContent = d.high_risk || 0;
    document.getElementById('stat-vpn').textContent = d.vpn_sessions || 0;
    document.getElementById('stat-tor').textContent = d.tor_sessions || 0;
    document.getElementById('stat-gps').textContent = d.gps_collected || 0;

    // Alert badge
    if (d.unread_alerts > 0) {
        const badge = document.getElementById('alert-badge');
        if (badge) { badge.textContent = d.unread_alerts; badge.style.display = 'inline'; }
    }

    // Timeline chart
    if (d.sessions_timeline?.length && window.Chart) {
        const ctx = document.getElementById('chart-timeline').getContext('2d');
        if (window._chartTimeline) window._chartTimeline.destroy();
        window._chartTimeline = new Chart(ctx, {
            type: 'line',
            data: {
                labels: d.sessions_timeline.map(r => r.date.slice(5)),
                datasets: [{
                    label: 'Sessions',
                    data: d.sessions_timeline.map(r => r.count),
                    borderColor: '#6366f1',
                    backgroundColor: 'rgba(99,102,241,.1)',
                    fill: true, tension: .4, pointRadius: 2,
                }]
            },
            options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } }, scales: { x: { grid: { color: 'rgba(255,255,255,.04)' }, ticks: { color: '#5b6b8a', maxTicksLimit: 10 } }, y: { grid: { color: 'rgba(255,255,255,.04)' }, ticks: { color: '#5b6b8a' }, beginAtZero: true } } }
        });
    }

    // Devices chart
    if (d.device_types?.length && window.Chart) {
        const ctx = document.getElementById('chart-devices').getContext('2d');
        if (window._chartDevices) window._chartDevices.destroy();
        const colors = ['#6366f1','#3b82f6','#10b981','#f59e0b','#ef4444','#8b5cf6','#ec4899'];
        window._chartDevices = new Chart(ctx, {
            type: 'doughnut',
            data: {
                labels: d.device_types.map(r => r.device_type),
                datasets: [{ data: d.device_types.map(r => r.count), backgroundColor: colors }]
            },
            options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'bottom', labels: { color: '#8896b3', padding: 15 } } } }
        });
    }

    // Top countries
    const cl = document.getElementById('top-countries-list');
    if (d.top_countries?.length) {
        const max = d.top_countries[0].count;
        cl.innerHTML = d.top_countries.map(c => `
            <div class="country-row">
                <span class="country-name">${c.country_code || '??'} ${c.country || 'Unknown'}</span>
                <div class="country-bar"><div class="country-fill" style="width:${(c.count/max*100).toFixed(0)}%"></div></div>
                <span class="country-count">${c.count}</span>
            </div>`).join('');
    } else { cl.innerHTML = '<p style="color:#5b6b8a">No data yet</p>'; }

    // Browsers
    if (d.browsers?.length && window.Chart) {
        const ctx = document.getElementById('chart-browsers').getContext('2d');
        if (window._chartBrowsers) window._chartBrowsers.destroy();
        window._chartBrowsers = new Chart(ctx, {
            type: 'bar',
            data: {
                labels: d.browsers.map(r => r.browser_name),
                datasets: [{ data: d.browsers.map(r => r.count), backgroundColor: 'rgba(99,102,241,.6)', borderRadius: 4 }]
            },
            options: { responsive: true, maintainAspectRatio: false, indexAxis: 'y', plugins: { legend: { display: false } }, scales: { x: { grid: { color: 'rgba(255,255,255,.04)' }, ticks: { color: '#5b6b8a' } }, y: { grid: { display: false }, ticks: { color: '#8896b3' } } } }
        });
    }
}

// ─── Links ───────────────────────────────────────────────────────────────────
let linksPage = 1;
async function loadLinks(page = 1) {
    linksPage = page;
    const search = document.getElementById('links-search')?.value || '';
    const d = await api(`links&page=${page}&search=${encodeURIComponent(search)}`);
    const tbody = document.getElementById('links-tbody');
    const siteBase = location.origin + BASE;

    tbody.innerHTML = (d.links || []).map(l => `
        <tr>
            <td><code class="uid-code">${l.link_uid}</code></td>
            <td><strong>${esc(l.title)}</strong>${l.team ? '<br><span style="font-size:.72rem;color:var(--text-muted)">'+esc(l.team)+'</span>' : ''}</td>
            <td><span class="badge badge-blue">${l.landing_mode}</span></td>
            <td><span class="badge badge-${l.priority==='critical'?'red':l.priority==='high'?'yellow':'gray'}">${l.priority||'medium'}</span></td>
            <td>${l.hit_count}</td>
            <td>${l.is_active == 1 ? '<span class="badge badge-green">Active</span>' : '<span class="badge badge-gray">Revoked</span>'}${l.password_protected==1?' 🔒':''}</td>
            <td>${(l.tags||[]).map(t=>'<span class="badge badge-gray" style="margin:.1rem;font-size:.65rem">'+esc(t)+'</span>').join('')}</td>
            <td>${timeAgo(l.created_at)}</td>
            <td class="actions">
                <button class="btn-icon" title="Copy Link" onclick="copyLink('${siteBase}/t/${l.link_uid}')"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="15" height="15"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg></button>
                <button class="btn-icon" title="QR Code" onclick="viewQR(${l.id})"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="15" height="15"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/><rect x="14" y="14" width="3" height="3"/><rect x="18" y="14" width="3" height="3"/><rect x="14" y="18" width="3" height="3"/><rect x="18" y="18" width="3" height="3"/></svg></button>
                <button class="btn-icon" title="Funnel" onclick="viewFunnel(${l.id})"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="15" height="15"><polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3"/></svg></button>
                <button class="btn-icon" title="Pixel URL" onclick="copyLink('${siteBase}/tracking/pixel.php?uid=${l.link_uid}')"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="15" height="15"><rect x="2" y="2" width="20" height="20" rx="5" ry="5"/><path d="M16 11.37A4 4 0 1112.63 8 4 4 0 0116 11.37z"/><line x1="17.5" y1="6.5" x2="17.51" y2="6.5"/></svg></button>
                <button class="btn-icon" title="View Sessions" onclick="viewLinkSessions(${l.id})"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="15" height="15"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg></button>
                ${l.is_active == 1 ? `<button class="btn-icon btn-danger" title="Revoke" onclick="revokeLink(${l.id})"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="15" height="15"><circle cx="12" cy="12" r="10"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/></svg></button>` : ''}
            </td>
        </tr>`).join('') || '<tr><td colspan="9" style="text-align:center;color:#5b6b8a;padding:2rem">No tracking links yet</td></tr>';
}

function openCreateLink() {
    document.getElementById('create-link-modal').classList.add('open');
    document.getElementById('link-title').value = '';
    document.getElementById('link-desc').value = '';
    document.getElementById('link-mode').value = 'neutral';
    toggleRedirectField();
}

function toggleRedirectField() {
    const mode = document.getElementById('link-mode').value;
    document.getElementById('field-dest').style.display = mode === 'redirect' ? 'block' : 'none';
    document.getElementById('field-custom').style.display = mode === 'custom' ? 'block' : 'none';
}

async function createLink() {
    const body = {
        title: document.getElementById('link-title').value,
        description: document.getElementById('link-desc').value,
        landing_mode: document.getElementById('link-mode').value,
        destination_url: document.getElementById('link-dest').value || null,
        custom_html: document.getElementById('link-custom').value || null,
        expires_at: document.getElementById('link-expires').value || null,
        max_hits: document.getElementById('link-maxhits').value || null,
        team: document.getElementById('link-team').value || null,
        priority: document.getElementById('link-priority').value || 'medium',
        tags: (document.getElementById('link-tags').value || '').split(',').map(t=>t.trim()).filter(Boolean),
        password: document.getElementById('link-password').value || null,
        notes: document.getElementById('link-notes').value || null,
        og_title: document.getElementById('link-og-title').value || null,
        og_description: document.getElementById('link-og-desc').value || null,
        og_image: document.getElementById('link-og-image').value || null,
        og_site_name: document.getElementById('link-og-sitename').value || null,
        cloak_title: document.getElementById('link-cloak-title').value || null,
        cloak_favicon: document.getElementById('link-cloak-favicon').value || null,
        pixel_enabled: document.getElementById('link-pixel-enabled').checked ? 1 : 0,
    };
    if (!body.title) return alert('Title is required');
    const res = await api('links', 'POST', body);
    if (res.success) {
        document.getElementById('create-link-modal').classList.remove('open');
        const fullUrl = location.origin + BASE + '/t/' + res.link_uid;
        prompt('Link created! Copy your tracking URL:', fullUrl);
        loadLinks();
    } else {
        alert(res.error || 'Error creating link');
    }
}

async function revokeLink(id) {
    if (!confirm('Revoke this link? It will stop tracking.')) return;
    await api(`links/${id}`, 'DELETE');
    loadLinks(linksPage);
}

function copyLink(url) {
    navigator.clipboard.writeText(url).then(() => {
        const toast = document.createElement('div');
        toast.className = 'toast';
        toast.textContent = 'Link copied!';
        document.body.appendChild(toast);
        setTimeout(() => toast.remove(), 2000);
    });
}

function viewLinkSessions(linkId) {
    showPage('sessions');
    document.getElementById('sessions-link-filter').value = linkId;
    loadSessions();
}

async function uploadOgImage(input) {
    const file = input.files[0];
    if (!file) return;
    const thumb = document.getElementById('og-preview-thumb');
    thumb.innerHTML = '<div class="sp" style="width:24px;height:24px;border-width:2px"></div>';

    const formData = new FormData();
    formData.append('image', file);

    try {
        const res = await fetch(API + 'upload/og-image', { method: 'POST', body: formData });
        const data = await res.json();
        if (data.success) {
            document.getElementById('link-og-image').value = data.url;
            thumb.innerHTML = '<img src="' + data.url + '" style="width:100%;height:100%;object-fit:cover">';
        } else {
            thumb.innerHTML = '<span style="font-size:.6rem;color:#ef4444">' + (data.error || 'Error') + '</span>';
        }
    } catch (e) {
        thumb.innerHTML = '<span style="font-size:.6rem;color:#ef4444">Upload failed</span>';
    }
}

// ─── Sessions ────────────────────────────────────────────────────────────────
let sessionsPage = 1;
async function loadSessions(page = 1) {
    sessionsPage = page;
    const linkId = document.getElementById('sessions-link-filter')?.value || '';
    const riskMin = document.getElementById('sessions-risk-filter')?.value || '';
    let url = `sessions&page=${page}&limit=20`;
    if (linkId) url += `&link_id=${linkId}`;
    if (riskMin) url += `&risk_min=${riskMin}`;

    const d = await api(url);
    const tbody = document.getElementById('sessions-tbody');
    tbody.innerHTML = (d.sessions || []).map(s => `
        <tr>
            <td title="${s.started_at}">${timeAgo(s.started_at)}</td>
            <td><code class="uid-code">${s.link_uid || ''}</code></td>
            <td><span class="mono">${s.ip_address || '—'}</span></td>
            <td>${s.country_code || ''} ${s.city || s.country || '—'}</td>
            <td>${s.device_type || '—'} / ${s.os_name || ''} ${s.os_version || ''}</td>
            <td>${s.browser_name || '—'} ${s.browser_version || ''}</td>
            <td>${riskBadge(s.risk_score)}</td>
            <td>${flagIcons(s)}</td>
            <td><button class="btn-icon" onclick="viewSession(${s.id})" title="Details"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="15" height="15"><polyline points="9 18 15 12 9 6"/></svg></button></td>
        </tr>`).join('') || '<tr><td colspan="9" style="text-align:center;color:#5b6b8a;padding:2rem">No sessions recorded</td></tr>';

    // Pagination
    const total = d.total || 0;
    const pages = Math.ceil(total / 20);
    const pag = document.getElementById('sessions-pagination');
    pag.innerHTML = `<span>${total} sessions</span><div>` +
        (page > 1 ? `<button class="btn btn-sm" onclick="loadSessions(${page-1})">Prev</button>` : '') +
        ` Page ${page}/${pages || 1} ` +
        (page < pages ? `<button class="btn btn-sm" onclick="loadSessions(${page+1})">Next</button>` : '') +
        '</div>';
}

async function viewSession(id) {
    document.getElementById('session-detail-modal').classList.add('open');
    document.getElementById('session-detail-body').innerHTML = '<div style="text-align:center;padding:2rem;color:#5b6b8a">Loading session data...</div>';

    const d = await api(`sessions/${id}`);
    if (d.error) { document.getElementById('session-detail-body').innerHTML = '<div style="text-align:center;padding:2rem;color:#ef4444">Error: '+esc(d.error)+'</div>'; return; }
    try {
    const n = d.network_data || {};
    const l = d.location_data || {};
    const dev = d.device_data || {};
    const b = d.browser_data || {};
    const f = d.fingerprint_data || {};
    const a = d.attribution_data || {};
    const r = d.risk_data || {};
    const beh = d.behavioral_data || [];

    document.getElementById('session-detail-body').innerHTML = `
        <div class="detail-grid">
            <div class="detail-section">
                <h4>Network & IP</h4>
                <dl>
                    <dt>IP Address</dt><dd>${n.ip_address || '—'}</dd>
                    <dt>IP Version</dt><dd>IPv${n.ip_version || '?'}</dd>
                    <dt>ISP</dt><dd>${n.isp || '—'}</dd>
                    <dt>ASN</dt><dd>${n.asn || '—'}</dd>
                    <dt>Organization</dt><dd>${n.as_org || '—'}</dd>
                    <dt>Network Type</dt><dd>${n.network_type || '—'}</dd>
                    <dt>VPN</dt><dd>${n.is_vpn == 1 ? '⚠ Yes' : 'No'}</dd>
                    <dt>Tor</dt><dd>${n.is_tor == 1 ? '🔴 Yes' : 'No'}</dd>
                    <dt>Proxy</dt><dd>${n.is_proxy == 1 ? '⚠ Yes' : 'No'}</dd>
                    <dt>WebRTC Local IP</dt><dd>${n.webrtc_local_ip || '—'}</dd>
                    <dt>WebRTC IPv6</dt><dd>${n.webrtc_ipv6 || '—'}</dd>
                    <dt>Connection</dt><dd>${n.effective_type || '—'} / ${n.connection_type || '—'}</dd>
                    <dt>Downlink</dt><dd>${n.downlink_mbps ? n.downlink_mbps+' Mbps' : '—'}</dd>
                    <dt>RTT</dt><dd>${n.rtt_ms ? n.rtt_ms+'ms' : '—'}</dd>
                    <dt>STUN Google</dt><dd>${n.stun_google_ip || '—'}</dd>
                    <dt>STUN Twilio</dt><dd>${n.stun_twilio_ip || '—'}</dd>
                    <dt>STUN Mozilla</dt><dd>${n.stun_mozilla_ip || '—'}</dd>
                    <dt>DNS Resolve</dt><dd>${n.dns_resolve_ms ? n.dns_resolve_ms+'ms' : '—'}</dd>
                    <dt>Nav DNS</dt><dd>${n.nav_dns_ms != null ? n.nav_dns_ms+'ms' : '—'}</dd>
                    <dt>Nav TCP</dt><dd>${n.nav_tcp_ms != null ? n.nav_tcp_ms+'ms' : '—'}</dd>
                    <dt>Nav TLS</dt><dd>${n.nav_tls_ms != null ? n.nav_tls_ms+'ms' : '—'}</dd>
                    <dt>Nav TTFB</dt><dd>${n.nav_ttfb_ms != null ? n.nav_ttfb_ms+'ms' : '—'}</dd>
                    <dt>Nav Download</dt><dd>${n.nav_download_ms != null ? n.nav_download_ms+'ms' : '—'}</dd>
                    <dt>DOM Ready</dt><dd>${n.nav_dom_ready_ms != null ? n.nav_dom_ready_ms+'ms' : '—'}</dd>
                    <dt>Page Load</dt><dd>${n.nav_load_ms != null ? n.nav_load_ms+'ms' : '—'}</dd>
                    <dt>Transfer Size</dt><dd>${n.nav_transfer_size ? Math.round(n.nav_transfer_size/1024)+'KB' : '—'}</dd>
                </dl>
            </div>
            <div class="detail-section">
                <h4>Location</h4>
                <dl>
                    <dt>Country</dt><dd>${l.country || '—'} (${l.country_code || '—'})</dd>
                    <dt>Region</dt><dd>${l.region || '—'}</dd>
                    <dt>City</dt><dd>${l.city || '—'}</dd>
                    <dt>Postal Code</dt><dd>${l.postal_code || '—'}</dd>
                    <dt>Coordinates</dt><dd>${l.latitude || '—'}, ${l.longitude || '—'}</dd>
                    <dt>Timezone</dt><dd>${l.timezone || '—'}</dd>
                    <dt>Language</dt><dd>${l.language || '—'}</dd>
                    <dt>GPS Lat/Lng</dt><dd>${l.gps_latitude ? l.gps_latitude+', '+l.gps_longitude : '—'}</dd>
                    <dt>GPS Accuracy</dt><dd>${l.gps_accuracy ? l.gps_accuracy+'m' : '—'}</dd>
                    <dt>GPS Source</dt><dd>${l.gps_source || 'none'}</dd>
                    <dt>GPS Consent</dt><dd>${l.gps_consent == 1 ? '✓ Granted' : '✗ Not granted'}</dd>
                </dl>
            </div>
            <div class="detail-section">
                <h4>Device & Hardware</h4>
                <dl>
                    <dt>Type</dt><dd>${dev.device_type || '—'}</dd>
                    <dt>OS</dt><dd>${dev.os_name || '—'} ${dev.os_version || ''}</dd>
                    <dt>CPU Arch</dt><dd>${dev.cpu_arch || '—'}</dd>
                    <dt>GPU</dt><dd>${dev.gpu_renderer || '—'}</dd>
                    <dt>Screen</dt><dd>${dev.screen_width || '?'}×${dev.screen_height || '?'}</dd>
                    <dt>Viewport</dt><dd>${dev.viewport_width || '?'}×${dev.viewport_height || '?'}</dd>
                    <dt>Pixel Ratio</dt><dd>${dev.pixel_ratio || '—'}</dd>
                    <dt>Color Depth</dt><dd>${dev.color_depth || '—'}bit</dd>
                    <dt>Touch</dt><dd>${dev.touch_support == 1 ? 'Yes ('+dev.max_touch_points+' points)' : 'No'}</dd>
                    <dt>Battery</dt><dd>${dev.battery_level != null ? dev.battery_level+'%'+(dev.battery_charging == 1 ? ' ⚡' : '') : '—'}</dd>
                    <dt>Concurrency</dt><dd>${f.hardware_concurrency || '—'}</dd>
                    <dt>Memory</dt><dd>${f.device_memory ? f.device_memory+'GB' : '—'}</dd>
                    <dt>Media Cameras</dt><dd>${dev.media_devices_camera ?? '—'}</dd>
                    <dt>Media Mics</dt><dd>${dev.media_devices_mic ?? '—'}</dd>
                    <dt>Media Speakers</dt><dd>${dev.media_devices_speaker ?? '—'}</dd>
                    <dt>Webdriver</dt><dd>${dev.webdriver == 1 ? '🔴 Yes' : 'No'}</dd>
                    <dt>Automation</dt><dd>${dev.automation_detected == 1 ? '🔴 Detected' : 'No'}</dd>
                    <dt>DevTools Open</dt><dd>${dev.devtools_open == 1 ? '⚠ Yes' : 'No'}</dd>
                    <dt>Storage Quota</dt><dd>${dev.storage_quota_mb ? dev.storage_quota_mb+'MB' : '—'}</dd>
                    <dt>Incognito</dt><dd>${dev.incognito_detected == 1 ? '🟣 Detected' : 'No'}</dd>
                    <dt>Dark Mode</dt><dd>${dev.dark_mode == 1 ? '🌙 Yes' : '☀️ No'}</dd>
                    <dt>Bandwidth</dt><dd>${dev.bandwidth_kbps ? dev.bandwidth_kbps+' kbps' : '—'}</dd>
                    <dt>Extensions</dt><dd>${dev.extensions_count ? dev.extensions_count+' found: '+esc(dev.extensions_detected||'') : 'None detected'}</dd>
                    <dt>Orientation</dt><dd>${dev.screen_orientation || '—'}</dd>
                    <dt>Color Gamut</dt><dd>${dev.color_gamut || 'srgb'}</dd>
                    <dt>HDR Support</dt><dd>${dev.hdr_support == 1 ? 'Yes' : 'No'}</dd>
                    <dt>Reduced Motion</dt><dd>${dev.reduced_motion == 1 ? 'Yes' : 'No'}</dd>
                    <dt>Vibration</dt><dd>${dev.vibration_support == 1 ? 'Yes' : 'No'}</dd>
                    <dt>Gamepads</dt><dd>${dev.gamepad_count || 0}</dd>
                    <dt>Bluetooth</dt><dd>${dev.bluetooth_available == 1 ? 'Available' : 'No'}</dd>
                    <dt>NFC</dt><dd>${dev.nfc_available == 1 ? 'Available' : 'No'}</dd>
                    <dt>HID</dt><dd>${dev.hid_available == 1 ? 'Available' : 'No'}</dd>
                    <dt>JS Heap Limit</dt><dd>${dev.max_ram_mb ? dev.max_ram_mb+'MB' : '—'}</dd>
                </dl>
            </div>
            <div class="detail-section">
                <h4>Browser</h4>
                <dl>
                    <dt>Browser</dt><dd>${b.browser_name || '—'} ${b.browser_version || ''}</dd>
                    <dt>Engine</dt><dd>${b.browser_engine || '—'}</dd>
                    <dt>DNT</dt><dd>${b.dnt_flag == 1 ? 'Enabled' : b.dnt_flag == 0 ? 'Disabled' : '—'}</dd>
                    <dt>Cookies</dt><dd>${b.cookies_enabled == 1 ? 'Yes' : 'No'}</dd>
                    <dt>Ad Blocker</dt><dd>${b.ad_blocker_detected == 1 ? '⚠ Detected' : 'No'}</dd>
                    <dt>Perm: Geolocation</dt><dd>${b.perm_geolocation || '—'}</dd>
                    <dt>Perm: Notifications</dt><dd>${b.perm_notifications || '—'}</dd>
                    <dt>Perm: Camera</dt><dd>${b.perm_camera || '—'}</dd>
                    <dt>Perm: Microphone</dt><dd>${b.perm_microphone || '—'}</dd>
                    <dt>WebGL Version</dt><dd>${b.webgl_version || '—'}</dd>
                    <dt>Max Texture</dt><dd>${b.max_texture_size || '—'}</dd>
                    <dt>ServiceWorker</dt><dd>${b.service_worker_support == 1 ? 'Yes' : 'No'}</dd>
                    <dt>WebAssembly</dt><dd>${b.webassembly_support == 1 ? 'Yes' : 'No'}</dd>
                    <dt>Languages</dt><dd>${esc(b.languages_list || navigator.language || '—')}</dd>
                    <dt>Privacy Control</dt><dd>${b.gpc_flag == 1 ? '🛡 GPC Enabled' : 'Off'}</dd>
                    <dt>In-App Browser</dt><dd>${b.in_app_browser && b.in_app_browser !== 'none' ? '<span class="badge badge-purple" style="text-transform:capitalize">'+esc(b.in_app_browser)+'</span>' : 'No'}</dd>
                    <dt>HTTP Version</dt><dd>${b.http_version || '—'}</dd>
                    <dt>Page Visible</dt><dd>${b.page_visible_at_load == 1 ? 'Yes' : 'Background'}</dd>
                    <dt>User Agent</dt><dd class="ua-cell">${esc(b.user_agent || '—')}</dd>
                </dl>
            </div>
            <div class="detail-section">
                <h4>Fingerprints</h4>
                <dl>
                    <dt>Combined</dt><dd><code>${f.combined_fingerprint || '—'}</code></dd>
                    <dt>Canvas</dt><dd><code>${f.canvas_hash || '—'}</code></dd>
                    <dt>WebGL</dt><dd><code>${f.webgl_hash || '—'}</code></dd>
                    <dt>Audio</dt><dd><code>${f.audio_hash || '—'}</code></dd>
                    <dt>Fonts</dt><dd><code>${f.font_hash || '—'}</code></dd>
                    <dt>Plugins</dt><dd><code>${f.plugin_hash || '—'}</code></dd>
                </dl>
            </div>
            <div class="detail-section">
                <h4>Risk Analysis</h4>
                <dl>
                    <dt>Trust Score</dt><dd>${riskBadge(100 - (r.fraud_score || 0))}</dd>
                    <dt>Fraud Score</dt><dd>${riskBadge(r.fraud_score)}</dd>
                    <dt>Risk Score</dt><dd>${riskBadge(r.risk_score)}</dd>
                    <dt>Bot Likelihood</dt><dd>${riskBadge(r.bot_likelihood)}</dd>
                    <dt>Emulator</dt><dd>${r.is_emulator == 1 ? '⚠ Yes' : 'No'}</dd>
                    <dt>Factors</dt><dd>${(() => { try { return r.risk_factors ? JSON.parse(r.risk_factors).join(', ') || 'None' : '—'; } catch(e) { return esc(r.risk_factors || '—'); } })()}</dd>
                    <dt>TZ Mismatch</dt><dd>${r.timezone_mismatch == 1 ? '⚠ IP: '+(r.ip_timezone||'?')+' / Browser: '+(r.browser_timezone||'?') : 'No'}</dd>
                    <dt>Lang Mismatch</dt><dd>${r.language_mismatch == 1 ? '⚠ IP: '+(r.ip_country||'?')+' / Browser: '+(r.browser_language||'?') : 'No'}</dd>
                </dl>
            </div>
            <div class="detail-section">
                <h4>Screenshots & Replay</h4>
                <div style="display:flex;gap:.5rem;flex-wrap:wrap">
                    ${d.has_screenshot ? '<button class="btn btn-ghost" onclick="viewScreenshot('+id+')">📸 View Screenshot</button>' : '<span style="color:#5b6b8a;font-size:.82rem">No screenshot</span>'}
                    ${d.has_heatmap ? '<button class="btn btn-ghost" onclick="viewHeatmap('+id+')">🔥 View Heatmap</button>' : '<span style="color:#5b6b8a;font-size:.82rem">No heatmap</span>'}
                </div>
            </div>
            <div class="detail-section">
                <h4>Attribution</h4>
                <dl>
                    <dt>Referrer</dt><dd>${a.referrer_domain || '—'}</dd>
                    <dt>UTM Source</dt><dd>${a.utm_source || '—'}</dd>
                    <dt>UTM Medium</dt><dd>${a.utm_medium || '—'}</dd>
                    <dt>UTM Campaign</dt><dd>${a.utm_campaign || '—'}</dd>
                    <dt>Campaign ID</dt><dd>${a.campaign_id || '—'}</dd>
                </dl>
            </div>
            <div class="detail-section">
                <h4>Behavioral</h4>
                <dl>
                    ${beh.map(ev => {
                        const ed = typeof ev.event_data === 'string' ? JSON.parse(ev.event_data) : ev.event_data;
                        return `<dt>Duration</dt><dd>${ed?.session_duration || 0}s</dd>
                                <dt>Scroll Depth</dt><dd>${ed?.max_scroll || 0}%</dd>
                                <dt>Clicks</dt><dd>${ed?.click_count || 0}</dd>
                                <dt>Keystrokes</dt><dd>${ed?.keystroke_count || 0}</dd>
                                <dt>Keystroke Avg</dt><dd>${ed?.keystroke_avg_interval ? ed.keystroke_avg_interval+'ms' : '—'}</dd>
                                <dt>Rage Clicks</dt><dd>${ed?.rage_click_count || 0}</dd>
                                <dt>Tab Switches</dt><dd>${ed?.tab_switches || 0}</dd>
                                <dt>Copy Events</dt><dd>${ed?.copy_events || 0}</dd>
                                <dt>Paste Events</dt><dd>${ed?.paste_events || 0}</dd>
                                <dt>Right Clicks</dt><dd>${ed?.right_clicks || 0}</dd>
                                <dt>Touch Gestures</dt><dd>${ed?.touch_gestures || 0}</dd>
                                <dt>Idle Time</dt><dd>${ed?.idle_seconds || 0}s</dd>
                                <dt>Exit Intent</dt><dd>${ed?.exit_intent_count || 0}</dd>
                                <dt>Copied Text</dt><dd>${ed?.copied_text ? '<code style="word-break:break-all;font-size:.7rem">'+esc(ed.copied_text.substring(0,200))+'</code>' : '—'}</dd>`;
                    }).join('') || '<dt>Data</dt><dd>No behavioral data</dd>'}
                </dl>
            </div>
            <div class="detail-section">
                <h4>Motion Sensors</h4>
                ${d.motion_data ? '<dl>' +
                    '<dt>Accelerometer</dt><dd>'+(d.motion_data.has_accelerometer==1?'Yes':'No')+'</dd>'+
                    '<dt>Gyroscope</dt><dd>'+(d.motion_data.has_gyroscope==1?'Yes':'No')+'</dd>'+
                    '<dt>Magnetometer</dt><dd>'+(d.motion_data.has_magnetometer==1?'Yes':'No')+'</dd>'+
                    '<dt>Ambient Light</dt><dd>'+(d.motion_data.has_ambient_light==1?'Yes':'No')+'</dd>'+
                    '<dt>Motion Detected</dt><dd>'+(d.motion_data.motion_detected==1?'<span class="badge badge-green">Yes (real device)</span>':'No')+'</dd>'+
                    '<dt>Max Acceleration</dt><dd>'+(d.motion_data.max_accel_magnitude||'—')+'</dd>'+
                    '<dt>Orientation Changes</dt><dd>'+(d.motion_data.orientation_changes||0)+'</dd>'+
                '</dl>' : '<p style="color:#5b6b8a;font-size:.82rem">No motion data</p>'}
            </div>
            <div class="detail-section">
                <h4>Social & Platform Detection</h4>
                ${(d.social_detection && d.social_detection.length) ? (() => {
                    // Group by confidence for better display
                    const definite=[], high=[], medium=[], low=[], unknown=[];
                    d.social_detection.forEach(s => {
                        let raw = {};
                        try { raw = typeof s.raw_data === 'string' ? JSON.parse(s.raw_data) : (s.raw_data || {}); } catch(e){}
                        const item = {...s, raw};
                        if(s.confidence==='definite') definite.push(item);
                        else if(s.confidence==='high') high.push(item);
                        else if(s.confidence==='medium') medium.push(item);
                        else if(s.logged_in==1) medium.push(item);
                        else if(raw.status==='not_visited') unknown.push(item);
                        else low.push(item);
                    });
                    let html='<div style="display:flex;flex-wrap:wrap;gap:.4rem;margin-bottom:.8rem">';
                    // Show detected platforms as badges
                    [...definite,...high,...medium].forEach(s=>{
                        const colors={definite:'#10b981',high:'#3b82f6',medium:'#f59e0b'};
                        const c=colors[s.confidence]||'#f59e0b';
                        html+='<span style="display:inline-flex;align-items:center;gap:.3rem;padding:.2rem .6rem;background:'+c+'15;color:'+c+';border-radius:6px;font-size:.78rem;font-weight:600;text-transform:capitalize">'+esc(s.platform.replace(/_/g,' '))+(s.raw.load_time?' <span style="opacity:.6;font-weight:400">'+s.raw.load_time+'ms</span>':'')+'</span>';
                    });
                    if(definite.length+high.length+medium.length===0) html+='<span style="color:#5b6b8a;font-size:.82rem">No platforms detected with confidence</span>';
                    html+='</div>';
                    // Detailed list
                    html+='<dl style="font-size:.78rem">';
                    [...definite,...high,...medium,...low].slice(0,20).forEach(s=>{
                        const icon=s.confidence==='definite'?'🟣':s.confidence==='high'?'🟢':s.confidence==='medium'?'🟡':s.logged_in==1?'🟢':'⚪';
                        const statusText=s.confidence==='definite'?'In-App (definite)':s.confidence==='high'?'Cached (likely active)':s.confidence==='medium'?'Probable':s.logged_in==1?'Detected':'Unknown';
                        const methods=[];
                        if(s.raw.method)methods.push(s.raw.method.replace(/_/g,' '));
                        if(s.raw.sdk_detected)methods.push('SDK found');
                        if(s.raw.resource_found)methods.push('cached resource');
                        if(s.raw.auth_time)methods.push('auth '+s.raw.auth_time+'ms');
                        html+='<dt style="text-transform:capitalize">'+icon+' '+esc(s.platform.replace(/_/g,' '))+'</dt><dd>'+statusText+(methods.length?' <span style="color:var(--text-muted)">via '+methods.join(', ')+'</span>':'')+(s.raw.load_time?' <span style="color:var(--text-muted)">('+s.raw.load_time+'ms)</span>':'')+'</dd>';
                    });
                    if(unknown.length) html+='<dt style="color:#5b6b8a">+'+unknown.length+' not detected</dt><dd></dd>';
                    html+='</dl>';
                    return html;
                })() : '<p style="color:#5b6b8a;font-size:.82rem">No social detection data</p>'}
            </div>
            ${d.visitor_identity ? `<div class="detail-section">
                <h4>Visitor Identity</h4>
                <dl>
                    <dt>Identity Hash</dt><dd><code>${d.visitor_identity.identity_hash?.substring(0,16) || '—'}...</code></dd>
                    <dt>Total Sessions</dt><dd>${d.visitor_identity.session_count || 1}</dd>
                    <dt>First Seen</dt><dd>${d.visitor_identity.first_seen || '—'}</dd>
                    <dt>Last Seen</dt><dd>${d.visitor_identity.last_seen || '—'}</dd>
                    <dt>Risk Level</dt><dd>${d.visitor_identity.risk_level || 'low'}</dd>
                </dl>
                ${d.related_sessions?.length ? '<h4 style="margin-top:1rem">Related Sessions</h4><div style="max-height:150px;overflow-y:auto">' + d.related_sessions.map(rs => '<div style="padding:.3rem 0;font-size:.8rem;color:var(--text-muted)">' + rs.started_at + ' — ' + esc(rs.link_title || '') + '</div>').join('') + '</div>' : ''}
            </div>` : ''}
        </div>`;
    } catch(e) { document.getElementById('session-detail-body').innerHTML = '<div style="text-align:center;padding:2rem;color:#ef4444">Render error: '+e.message+'</div>'; }
}

async function loadLinkFilter() {
    const d = await api('links&limit=100');
    const options = '<option value="">All Links</option>' +
        (d.links || []).map(l => `<option value="${l.id}">${esc(l.title)} (${l.link_uid})</option>`).join('');
    ['sessions-link-filter', 'export-link'].forEach(id => {
        const el = document.getElementById(id);
        if (el) el.innerHTML = options;
    });
}

// ─── Export ──────────────────────────────────────────────────────────────────
async function exportData() {
    const format = document.querySelector('input[name="export-fmt"]:checked')?.value || 'csv';
    const body = {
        format,
        link_id: document.getElementById('export-link')?.value || null,
        date_from: document.getElementById('export-from')?.value || null,
        date_to: document.getElementById('export-to')?.value || null,
    };

    if (format === 'csv') {
        const form = document.createElement('form');
        form.method = 'POST';
        form.action = API + 'export';
        form.target = '_blank';
        Object.entries(body).forEach(([k, v]) => {
            if (v !== null) {
                const input = document.createElement('input');
                input.type = 'hidden'; input.name = k; input.value = v;
                form.appendChild(input);
            }
        });
        document.body.appendChild(form);
        form.submit();
        form.remove();
    } else {
        const res = await api('export', 'POST', body);
        const blob = new Blob([JSON.stringify(res, null, 2)], { type: 'application/json' });
        const url = URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = url; a.download = `trackpro_export_${Date.now()}.json`;
        a.click();
        URL.revokeObjectURL(url);
    }
}

// ─── Users ───────────────────────────────────────────────────────────────────
async function loadUsers() {
    const d = await api('users');
    document.getElementById('users-tbody').innerHTML = (d.users || []).map(u => `
        <tr>
            <td><strong>${esc(u.username)}</strong></td>
            <td>${esc(u.email)}</td>
            <td><span class="badge badge-blue">${u.role}</span></td>
            <td>${u.is_active == 1 ? '<span class="badge badge-green">Active</span>' : '<span class="badge badge-red">Disabled</span>'}</td>
            <td>${u.last_login ? timeAgo(u.last_login) : 'Never'}</td>
        </tr>`).join('');
}

async function createUser() {
    const body = {
        username: document.getElementById('new-username').value,
        email: document.getElementById('new-email').value,
        password: document.getElementById('new-password').value,
        role: document.getElementById('new-role').value,
    };
    const res = await api('users', 'POST', body);
    if (res.success) {
        document.getElementById('create-user-modal').classList.remove('open');
        loadUsers();
    } else {
        alert(res.error || 'Error');
    }
}

// ─── Settings ────────────────────────────────────────────────────────────────
async function loadSettings() {
    const d = await api('settings');
    const settingsMeta = {
        site_name: { label: 'Site Name', type: 'text' },
        data_retention_days: { label: 'Data Retention (days)', type: 'number' },
        require_consent: { label: 'Require Consent Banner', type: 'select', options: { '1': 'Yes', '0': 'No' } },
        session_timeout_minutes: { label: 'Session Timeout (min)', type: 'number' },
        enable_behavioral: { label: 'Behavioral Tracking', type: 'select', options: { '1': 'Enabled', '0': 'Disabled' } },
        enable_fingerprinting: { label: 'Device Fingerprinting', type: 'select', options: { '1': 'Enabled', '0': 'Disabled' } },
    };

    let html = '';
    for (const [key, meta] of Object.entries(settingsMeta)) {
        const val = d[key] || '';
        if (meta.type === 'select') {
            html += `<div class="field"><label>${meta.label}</label><select id="setting-${key}">`;
            for (const [ov, ol] of Object.entries(meta.options)) {
                html += `<option value="${ov}" ${val == ov ? 'selected' : ''}>${ol}</option>`;
            }
            html += `</select></div>`;
        } else {
            html += `<div class="field"><label>${meta.label}</label><input type="${meta.type}" id="setting-${key}" value="${esc(val)}"></div>`;
        }
    }
    html += '<button class="btn btn-primary" onclick="saveSettings()" style="margin-top:1rem">Save Settings</button>';
    document.getElementById('settings-form').innerHTML = html;
}

async function saveSettings() {
    const body = {};
    document.querySelectorAll('[id^="setting-"]').forEach(el => {
        body[el.id.replace('setting-', '')] = el.value;
    });
    const res = await api('settings', 'POST', body);
    if (res.success) {
        const toast = document.createElement('div');
        toast.className = 'toast'; toast.textContent = 'Settings saved!';
        document.body.appendChild(toast);
        setTimeout(() => toast.remove(), 2000);
    }
}

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

// ─── Alerts ──────────────────────────────────────────────────────────────────
async function loadAlerts() {
    const d = await api('alerts');
    document.getElementById('alerts-tbody').innerHTML = (d.alerts || []).map(a => `
        <tr style="${a.is_read==0?'background:rgba(99,102,241,.04)':''}">
            <td>${timeAgo(a.created_at)}</td>
            <td>${a.severity==='critical'?'<span class="badge badge-red">CRITICAL</span>':a.severity==='warning'?'<span class="badge badge-yellow">WARNING</span>':'<span class="badge badge-blue">INFO</span>'}</td>
            <td>${esc(a.type)}</td><td><strong>${esc(a.title)}</strong></td>
            <td style="max-width:300px;white-space:normal;font-size:.8rem">${esc(a.message||'')}</td>
            <td>${a.is_read==0?'<button class="btn-icon" onclick="markRead('+a.id+')">✓</button>':''}</td>
        </tr>`).join('')||'<tr><td colspan="6" style="text-align:center;color:#5b6b8a;padding:2rem">No alerts</td></tr>';
}
async function markRead(id){await api(`alerts/${id}/read`,'POST');loadAlerts();}
async function markAllRead(){await api('alerts/read-all','POST');loadAlerts();}

async function loadVisitors() {
    const d = await api('visitors');
    document.getElementById('visitors-tbody').innerHTML = (d.visitors||[]).map(v=>`
        <tr><td><code class="uid-code">${(v.fingerprint||'—').substring(0,12)}</code></td>
            <td><span class="mono">${v.ip_address||'—'}</span></td>
            <td><strong>${v.session_count}</strong></td>
            <td>${timeAgo(v.first_seen)}</td><td>${timeAgo(v.last_seen)}</td>
            <td><span class="badge badge-${v.risk_level==='critical'?'red':v.risk_level==='high'?'yellow':'green'}">${v.risk_level}</span></td>
        </tr>`).join('')||'<tr><td colspan="6" style="text-align:center;color:#5b6b8a;padding:2rem">No visitor data</td></tr>';
}

async function loadWebhooks() {
    const d = await api('webhooks');
    document.getElementById('webhooks-tbody').innerHTML = (d.webhooks||[]).map(w=>`
        <tr><td><strong>${esc(w.name)}</strong></td>
            <td style="max-width:250px;overflow:hidden;text-overflow:ellipsis"><span class="mono" style="font-size:.78rem">${esc(w.url)}</span></td>
            <td>${JSON.parse(w.events||'[]').map(e=>'<span class="badge badge-blue" style="margin:.1rem">'+e+'</span>').join('')}</td>
            <td>${w.is_active==1?'<span class="badge badge-green">Active</span>':'<span class="badge badge-gray">Off</span>'}</td>
            <td>${w.last_triggered?timeAgo(w.last_triggered):'Never'}</td></tr>`).join('')||'<tr><td colspan="5" style="text-align:center;color:#5b6b8a;padding:2rem">No webhooks</td></tr>';
}
async function createWebhook(){
    const res=await api('webhooks','POST',{name:document.getElementById('wh-name').value,url:document.getElementById('wh-url').value,events:document.getElementById('wh-events').value.split(',').map(s=>s.trim()),secret:document.getElementById('wh-secret').value||null});
    if(res.success){document.getElementById('create-webhook-modal').classList.remove('open');loadWebhooks();}else alert(res.error||'Error');
}

async function loadAudit() {
    const d = await api('audit');
    document.getElementById('audit-tbody').innerHTML = (d.logs||[]).map(l=>`
        <tr><td>${timeAgo(l.created_at)}</td><td>${esc(l.username||'System')}</td>
            <td><span class="badge badge-blue">${esc(l.action)}</span></td>
            <td>${l.entity_type?esc(l.entity_type)+' #'+l.entity_id:'—'}</td>
            <td><span class="mono">${l.ip_address||'—'}</span></td>
            <td style="max-width:200px;overflow:hidden;text-overflow:ellipsis;font-size:.75rem">${l.details||'—'}</td></tr>`).join('')||'<tr><td colspan="6" style="text-align:center;color:#5b6b8a;padding:2rem">No entries</td></tr>';
}

// ─── Live Session Feed ───────────────────────────────────────────────────────
let liveInterval=null;
async function loadLive(){
    const d=await api('live');
    document.getElementById('live-counter').innerHTML='● '+d.active_now+' active';
    document.getElementById('live-tbody').innerHTML=(d.sessions||[]).map(s=>{
        const risk=s.risk_score||0;const rb=risk>=70?'red':risk>=40?'yellow':'green';
        return '<tr><td style="white-space:nowrap">'+timeAgo(s.started_at)+'</td><td><code class="uid-code">'+(s.link_uid||'')+'</code> '+esc(s.link_title||'')+'</td><td><span class="mono">'+(s.ip_address||'')+'</span>'+(s.is_vpn==1?' <span class="flag flag-yellow">VPN</span>':'')+(s.is_tor==1?' <span class="flag flag-red">TOR</span>':'')+'</td><td>'+(s.city?esc(s.city)+', ':'')+esc(s.country_code||'')+'</td><td>'+esc(s.device_type||'')+' '+esc(s.os_name||'')+' '+esc(s.browser_name||'')+'</td><td><span class="badge badge-'+rb+'">'+risk+'</span></td></tr>';
    }).join('')||'<tr><td colspan="6" style="text-align:center;color:#5b6b8a;padding:2rem">No recent sessions</td></tr>';
}
function startLive(){if(liveInterval)clearInterval(liveInterval);if(document.getElementById('live-auto')?.checked)liveInterval=setInterval(loadLive,5000);}
function toggleLive(){if(document.getElementById('live-auto').checked)startLive();else if(liveInterval){clearInterval(liveInterval);liveInterval=null;}}

// ─── Geo Map ─────────────────────────────────────────────────────────────────
let geoMapInstance=null;
async function loadGeoMap(){
    const d=await api('geomap');
    if(!window.L){setTimeout(loadGeoMap,500);return;}
    if(geoMapInstance){geoMapInstance.remove();geoMapInstance=null;}
    geoMapInstance=L.map('geomap').setView([20,0],2);
    L.tileLayer('https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png',{maxZoom:18,attribution:'CARTO'}).addTo(geoMapInstance);
    (d.pins||[]).forEach(p=>{
        const lat=p.gps_latitude||p.latitude;const lng=p.gps_longitude||p.longitude;
        if(!lat||!lng)return;
        const risk=p.risk_score||0;const color=risk>=70?'#ef4444':risk>=40?'#f59e0b':'#10b981';
        L.circleMarker([lat,lng],{radius:6,fillColor:color,color:'#fff',weight:1,opacity:.8,fillOpacity:.7}).addTo(geoMapInstance)
            .bindPopup('<b>'+esc(p.link_title||'')+'</b><br>'+esc(p.city||'')+', '+esc(p.country||'')+'<br>IP: '+esc(p.ip_address||'')+'<br>Risk: '+risk+(p.is_vpn==1?'<br><b style="color:#f59e0b">VPN</b>':'')+(p.is_tor==1?'<br><b style="color:#ef4444">TOR</b>':''));
    });
    setTimeout(()=>geoMapInstance?.invalidateSize(),200);
}

// ─── Screenshot Viewer ───────────────────────────────────────────────────────
async function viewScreenshot(sid){
    const d=await api('sessions/'+sid+'/screenshot');
    if(d.error){alert('No screenshot available');return;}
    const w=window.open('','Screenshot','width=800,height=600');
    w.document.write('<html><head><title>Screenshot</title><style>body{margin:0;background:#0a0e17;display:flex;align-items:center;justify-content:center;min-height:100vh}img{max-width:100%;max-height:100vh}</style></head><body><img src="'+d.image_data+'"></body></html>');
}

// ─── Heatmap Viewer ──────────────────────────────────────────────────────────
async function viewHeatmap(sid){
    const d=await api('sessions/'+sid+'/heatmap');
    if(d.error||!d.points){alert('No heatmap data');return;}
    const w=window.open('','Heatmap','width='+d.viewport_w+',height='+d.viewport_h);
    const pts=d.points;
    w.document.write('<html><head><title>Mouse Heatmap</title><style>body{margin:0;background:#0a0e17}canvas{display:block}</style></head><body><canvas id="hm"></canvas><script>const c=document.getElementById("hm");c.width='+d.viewport_w+';c.height='+d.viewport_h+';const ctx=c.getContext("2d");const pts='+JSON.stringify(pts)+';ctx.lineWidth=2;ctx.strokeStyle="rgba(99,102,241,0.4)";ctx.beginPath();pts.forEach((p,i)=>{if(i===0)ctx.moveTo(p[0],p[1]);else ctx.lineTo(p[0],p[1]);});ctx.stroke();pts.forEach(p=>{const g=ctx.createRadialGradient(p[0],p[1],0,p[0],p[1],15);g.addColorStop(0,"rgba(239,68,68,0.3)");g.addColorStop(1,"rgba(239,68,68,0)");ctx.fillStyle=g;ctx.fillRect(p[0]-15,p[1]-15,30,30);});<\/script></body></html>');
}

// ─── QR Code ─────────────────────────────────────────────────────────────────
async function viewQR(linkId){
    const d=await api('links/'+linkId+'/qr?size=400');
    if(d.qr_url){
        const w=window.open('','QR Code','width=500,height=550');
        w.document.write('<html><head><title>QR Code</title><style>body{margin:0;background:#fff;display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:100vh;font-family:system-ui}img{margin:1rem}p{color:#666;font-size:.85rem;word-break:break-all;max-width:400px;text-align:center}</style></head><body><h3>Scan to Track</h3><img src="'+d.qr_url+'" width="400" height="400"><p>'+esc(d.track_url)+'</p></body></html>');
    }
}

// ─── Funnel View ─────────────────────────────────────────────────────────────
async function viewFunnel(linkId){
    const d=await api('links/'+linkId+'/funnel');
    if(d.error){alert(d.error);return;}
    const steps=[['Total Hits',d.total_hits],['Humans (non-bot)',d.humans],['Stayed 5s+',d.stayed_5s],['Stayed 30s+',d.stayed_30s],['GPS Granted',d.gps_granted],['Pixel Opens',d.pixel_opens]];
    let html='<div style="padding:1.5rem"><h3 style="margin-bottom:1rem">Funnel: '+esc(d.link_title)+'</h3>';
    const max=Math.max(d.total_hits,1);
    steps.forEach(([label,val])=>{
        const pct=Math.round(val/max*100);
        html+='<div style="margin:.5rem 0"><div style="display:flex;justify-content:space-between;font-size:.82rem;margin-bottom:.2rem"><span>'+label+'</span><span style="font-weight:600">'+val+' ('+pct+'%)</span></div><div style="height:24px;background:rgba(99,102,241,.08);border-radius:6px;overflow:hidden"><div style="height:100%;width:'+pct+'%;background:linear-gradient(90deg,var(--primary),#818cf8);border-radius:6px;transition:width .5s"></div></div></div>';
    });
    html+='</div>';
    const overlay=document.createElement('div');
    overlay.className='modal-overlay open';
    overlay.innerHTML='<div class="modal" style="max-width:500px"><div class="modal-header"><h3>Funnel Analysis</h3><button class="modal-close" onclick="this.closest(\'.modal-overlay\').remove()">&times;</button></div><div class="modal-body" style="padding:0">'+html+'</div></div>';
    document.body.appendChild(overlay);
}

// ─── Intelligence Engine ─────────────────────────────────────────────────────
async function loadIntelligence(){
    const d=await api('intelligence/overview');
    // Stats cards
    const statCards=[
        {v:d.total_visitors,l:'Total Visitors',c:'#6366f1'},
        {v:d.profiled,l:'Profiled',c:'#10b981'},
        {v:d.clusters,l:'Identity Clusters',c:'#f59e0b'},
    ];
    document.getElementById('intel-stats').innerHTML=statCards.map(s=>
        '<div class="card" style="padding:1rem;text-align:center"><div style="font-size:1.8rem;font-weight:700;color:'+s.c+'">'+s.v+'</div><div style="font-size:.78rem;color:var(--text-muted);margin-top:.3rem">'+s.l+'</div></div>'
    ).join('');

    // Distribution bars helper
    const distBar=(items,colors)=>items.map(i=>{
        const pct=d.profiled>0?Math.round(i.cnt/d.profiled*100):0;
        const c=colors[i[Object.keys(i)[0]]]||'#5b6b8a';
        return '<div style="display:flex;align-items:center;gap:.5rem;margin:.4rem 0"><span style="font-size:.78rem;color:var(--text-secondary);min-width:80px;text-transform:capitalize">'+(i[Object.keys(i)[0]]||'—')+'</span><div style="flex:1;height:20px;background:rgba(99,102,241,.06);border-radius:4px;overflow:hidden"><div style="height:100%;width:'+pct+'%;background:'+c+';border-radius:4px;transition:width .5s"></div></div><span style="font-size:.75rem;color:var(--text-muted);min-width:45px;text-align:right">'+i.cnt+' ('+pct+'%)</span></div>';
    }).join('')||'<p style="color:#5b6b8a;font-size:.82rem">No data yet — click "Profile All Visitors"</p>';

    const tierColors={budget:'#ef4444',mid:'#f59e0b',premium:'#3b82f6',ultra:'#10b981'};
    const techColors={novice:'#ef4444',basic:'#f59e0b',intermediate:'#eab308',advanced:'#3b82f6',expert:'#10b981'};
    const intentColors={casual:'#94a3b8',researching:'#06b6d4',comparing:'#f59e0b',ready_to_act:'#10b981',returning:'#6366f1',automated:'#ef4444'};
    const privacyColors={none:'#94a3b8',low:'#10b981',moderate:'#f59e0b',high:'#f97316',paranoid:'#ef4444'};
    const ecoColors={google:'#4285f4',apple:'#a3aaae',microsoft:'#00a1f1',meta:'#0668e1',other:'#94a3b8',mixed:'#8b5cf6'};
    const engColors={bounce:'#ef4444',glance:'#f59e0b',reader:'#eab308',engaged:'#3b82f6',power_user:'#10b981'};

    document.getElementById('intel-tiers').innerHTML=distBar(d.tiers||[],tierColors);
    document.getElementById('intel-tech').innerHTML=distBar(d.tech_levels||[],techColors);
    document.getElementById('intel-intent').innerHTML=distBar(d.intents||[],intentColors);
    document.getElementById('intel-privacy').innerHTML=distBar(d.privacy||[],privacyColors);
    document.getElementById('intel-ecosystem').innerHTML=distBar(d.ecosystems||[],ecoColors);
    document.getElementById('intel-engagement').innerHTML=distBar(d.engagement||[],engColors);

    // Recent profiles table
    document.getElementById('intel-profiles-tbody').innerHTML=(d.recent_profiles||[]).map(p=>{
        const tierBadge={budget:'badge-red',mid:'badge-yellow',premium:'badge-blue',ultra:'badge-green'};
        const techBadge={novice:'badge-red',basic:'badge-yellow',intermediate:'badge-yellow',advanced:'badge-blue',expert:'badge-green'};
        const intents=JSON.parse(p.interest_tags||'[]');
        return '<tr>'+
            '<td><span class="mono" style="font-size:.75rem">'+(p.ip_address||'—')+'</span><br><span style="font-size:.7rem;color:var(--text-muted)">'+p.session_count+' sessions</span></td>'+
            '<td><span class="badge '+(tierBadge[p.device_tier]||'badge-blue')+'" style="text-transform:capitalize">'+p.device_tier+'</span><br><span style="font-size:.7rem;color:var(--text-muted)">Score: '+p.device_tier_score+'</span></td>'+
            '<td><span class="badge '+(techBadge[p.tech_level]||'badge-blue')+'" style="text-transform:capitalize">'+p.tech_level+'</span></td>'+
            '<td style="text-transform:capitalize">'+p.intent.replace(/_/g,' ')+'</td>'+
            '<td style="text-transform:capitalize">'+p.privacy_consciousness+'</td>'+
            '<td style="text-transform:capitalize">'+p.primary_ecosystem+'</td>'+
            '<td style="text-transform:capitalize">'+p.engagement_level.replace(/_/g,' ')+'</td>'+
            '<td>'+(p.likely_age_range||'—')+'</td>'+
            '<td style="text-transform:capitalize">'+(p.likely_profession||'—')+'</td>'+
            '<td><span class="badge badge-blue">'+p.confidence_score+'%</span></td>'+
            '<td><button class="btn-icon" title="Full Intel" onclick="viewVisitorIntel('+p.visitor_id+')"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="15" height="15"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg></button></td>'+
        '</tr>';
    }).join('')||'<tr><td colspan="11" style="text-align:center;color:#5b6b8a;padding:2rem">No profiles yet — click "Profile All Visitors" to start</td></tr>';
}

async function runProfileAll(){
    const btn=document.getElementById('btn-profile-all');
    btn.disabled=true;btn.textContent='Profiling...';
    const st=document.getElementById('intel-status');
    st.style.display='block';st.style.background='rgba(99,102,241,.1)';st.style.color='#818cf8';
    st.textContent='Running behavioral profiling on all visitors...';
    const d=await api('intelligence/profile-all','POST');
    st.style.background='rgba(16,185,129,.1)';st.style.color='#10b981';
    st.textContent='Profiled '+d.profiled+' visitors'+(d.errors?' ('+d.errors+' errors'+(d.error_details?.length?': '+d.error_details[0]:'')+')':'');
    btn.disabled=false;btn.textContent='Profile All Visitors';
    loadIntelligence();
}

async function runCorrelation(){
    const btn=document.getElementById('btn-correlate');
    btn.disabled=true;btn.textContent='Correlating...';
    const st=document.getElementById('intel-status');
    st.style.display='block';st.style.background='rgba(245,158,11,.1)';st.style.color='#f59e0b';
    st.textContent='Running identity correlation across all visitors...';
    const d=await api('intelligence/correlate','POST');
    st.style.background='rgba(16,185,129,.1)';st.style.color='#10b981';
    st.textContent='Found '+d.clusters_found+' identity clusters, linked '+d.visitors_linked+' visitors';
    btn.disabled=false;btn.textContent='Run Identity Correlation';
    loadIntelligence();
}

async function viewVisitorIntel(vid){
    const d=await api('visitors/'+vid+'/intelligence');
    const p=d.profile||{};const id=d.identity||{};
    const interests=JSON.parse(p.interest_tags||'[]');
    const tierSignals=JSON.parse(p.device_tier_signals||'[]');
    const techSignals=JSON.parse(p.tech_level_signals||'[]');
    const privacySignals=JSON.parse(p.privacy_signals||'[]');
    const intentSignals=JSON.parse(p.intent_signals||'[]');
    const ecoSignals=JSON.parse(p.ecosystem_signals||'[]');

    let html='<div style="padding:1.5rem"><div class="detail-grid" style="grid-template-columns:1fr 1fr">';

    // Profile Summary
    html+='<div class="detail-section"><h4>Visitor Profile</h4><dl>';
    html+='<dt>Identity</dt><dd><code style="font-size:.7rem">'+(id.identity_hash||'').substring(0,16)+'...</code></dd>';
    html+='<dt>Sessions</dt><dd><strong>'+(id.session_count||1)+'</strong></dd>';
    html+='<dt>First Seen</dt><dd>'+(id.first_seen||'—')+'</dd>';
    html+='<dt>Last Seen</dt><dd>'+(id.last_seen||'—')+'</dd>';
    html+='<dt>Confidence</dt><dd><span class="badge badge-blue">'+(p.confidence_score||0)+'%</span></dd>';
    html+='</dl></div>';

    // Classifications
    html+='<div class="detail-section"><h4>AI Classifications</h4><dl>';
    html+='<dt>Device Tier</dt><dd><span class="badge badge-blue" style="text-transform:capitalize">'+(p.device_tier||'—')+'</span> ('+( p.device_tier_score||0)+')</dd>';
    html+='<dt>Tech Level</dt><dd><span class="badge badge-green" style="text-transform:capitalize">'+(p.tech_level||'—')+'</span> ('+(p.tech_level_score||0)+')</dd>';
    html+='<dt>Intent</dt><dd style="text-transform:capitalize">'+(p.intent||'—').replace(/_/g,' ')+'</dd>';
    html+='<dt>Engagement</dt><dd style="text-transform:capitalize">'+(p.engagement_level||'—').replace(/_/g,' ')+'</dd>';
    html+='<dt>Privacy Level</dt><dd style="text-transform:capitalize">'+(p.privacy_consciousness||'—')+'</dd>';
    html+='<dt>Ecosystem</dt><dd style="text-transform:capitalize">'+(p.primary_ecosystem||'—')+'</dd>';
    html+='<dt>Age Range</dt><dd>'+(p.likely_age_range||'—')+'</dd>';
    html+='<dt>Profession</dt><dd style="text-transform:capitalize">'+(p.likely_profession||'—')+'</dd>';
    html+='<dt>Visit Pattern</dt><dd style="text-transform:capitalize">'+(p.visit_pattern||'—').replace(/_/g,' ')+'</dd>';
    html+='<dt>Avg Duration</dt><dd>'+(p.avg_session_duration||0)+'s</dd>';
    html+='</dl></div>';

    // Evidence signals
    html+='<div class="detail-section"><h4>Evidence Signals</h4>';
    const showSignals=(label,sigs,color)=>{
        if(sigs.length)html+='<div style="margin:.5rem 0"><span style="font-size:.72rem;color:var(--text-muted)">'+label+':</span><div style="display:flex;flex-wrap:wrap;gap:.3rem;margin-top:.3rem">'+sigs.map(s=>'<span style="padding:.15rem .5rem;background:'+color+'15;color:'+color+';border-radius:4px;font-size:.7rem">'+esc(s)+'</span>').join('')+'</div></div>';
    };
    showSignals('Device',tierSignals,'#3b82f6');
    showSignals('Tech',techSignals,'#10b981');
    showSignals('Intent',intentSignals,'#f59e0b');
    showSignals('Privacy',privacySignals,'#ef4444');
    showSignals('Ecosystem',ecoSignals,'#8b5cf6');
    html+='</div>';

    // Interests
    html+='<div class="detail-section"><h4>Inferred Interests</h4>';
    if(interests.length)html+='<div style="display:flex;flex-wrap:wrap;gap:.4rem">'+interests.map(i=>'<span style="padding:.25rem .7rem;background:rgba(99,102,241,.1);color:#818cf8;border-radius:6px;font-size:.78rem;text-transform:capitalize">'+esc(i)+'</span>').join('')+'</div>';
    else html+='<p style="color:#5b6b8a;font-size:.82rem">No interests detected</p>';
    html+='</div>';

    // Identity cluster
    if(d.cluster&&d.cluster.length){
        html+='<div class="detail-section" style="grid-column:1/-1"><h4>Identity Cluster (Linked Visitors)</h4><div class="table-wrap"><table class="data-table"><thead><tr><th>Linked Visitor</th><th>Match Type</th><th>Confidence</th><th>Evidence</th><th>Sessions</th><th>Last Seen</th></tr></thead><tbody>';
        d.cluster.forEach(c=>{
            const ev=JSON.parse(c.match_evidence||'[]');
            html+='<tr><td><code style="font-size:.7rem">'+(c.fingerprint||'').substring(0,12)+'</code> <span class="mono" style="font-size:.72rem">'+(c.ip_address||'')+'</span></td><td><span class="badge badge-blue" style="text-transform:capitalize">'+esc(c.match_type)+'</span></td><td><strong>'+c.match_confidence+'%</strong></td><td>'+ev.map(e=>'<span class="badge badge-yellow" style="font-size:.65rem">'+e+'</span>').join(' ')+'</td><td>'+c.session_count+'</td><td>'+esc(c.last_seen||'')+'</td></tr>';
        });
        html+='</tbody></table></div></div>';
    }

    // Session history
    if(d.sessions&&d.sessions.length){
        html+='<div class="detail-section" style="grid-column:1/-1"><h4>Session History</h4><div class="table-wrap" style="max-height:250px;overflow-y:auto"><table class="data-table"><thead><tr><th>Time</th><th>Link</th><th>IP</th><th>Location</th><th>Device</th><th>Duration</th><th>Risk</th></tr></thead><tbody>';
        d.sessions.forEach(s=>{
            html+='<tr><td style="white-space:nowrap;font-size:.78rem">'+esc(s.started_at||'')+'</td><td>'+esc(s.link_title||s.link_uid||'')+'</td><td><span class="mono" style="font-size:.72rem">'+esc(s.ip_address||'')+'</span></td><td>'+esc(s.city||'')+' '+esc(s.country_code||'')+'</td><td>'+esc(s.device_type||'')+' '+esc(s.os_name||'')+' '+esc(s.browser_name||'')+'</td><td>'+(s.duration_seconds||0)+'s</td><td>'+riskBadge(s.risk_score)+'</td></tr>';
        });
        html+='</tbody></table></div></div>';
    }

    html+='</div></div>';

    const overlay=document.createElement('div');overlay.className='modal-overlay open';
    overlay.innerHTML='<div class="modal" style="max-width:900px;max-height:90vh;overflow-y:auto"><div class="modal-header"><h3>Visitor Intelligence Report</h3><button class="modal-close" onclick="this.closest(\'.modal-overlay\').remove()">&times;</button></div><div class="modal-body" style="padding:0">'+html+'</div></div>';
    document.body.appendChild(overlay);
}

// ─── Init ────────────────────────────────────────────────────────────────────
loadDashboard();
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.1/chart.umd.min.js"></script>
<script>if(window.Chart) loadDashboard();</script>
</body>
</html>
