<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{{title}} - MemScope Memory Analysis</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
    <script src="https://d3js.org/d3.v7.min.js"></script>
    <style>
        :root {
            --primary: #3b82f6;
            --secondary: #64748b;
            --accent: #f59e0b;
            --danger: #ef4444;
            --success: #10b981;
            --info: #3b82f6;
            --warning: #f59e0b;
            --cycle-edge: #ef4444;
            --relationship-clone: #10b981;
            --relationship-ownership: #dc2626;
            --relationship-borrow: #3b82f6;
            --relationship-arc: #8b5cf6;
            --relationship-reference: #64748b;
            --bg: #f8fafc;
            --bg2: #ffffff;
            --bg3: #f1f5f9;
            --text: #1e293b;
            --text2: #64748b;
            --border: #e2e8f0;
        }

        [data-theme="dark"] {
            --primary: #60a5fa;
            --secondary: #94a3b8;
            --accent: #fbbf24;
            --danger: #f87171;
            --success: #34d399;
            --warning: #fbbf24;
            --bg: #0f172a;
            --bg2: #1e293b;
            --bg3: #334155;
            --text: #f1f5f9;
            --text2: #94a3b8;
            --border: #334155;
        }

        * { box-sizing: border-box; }

        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            background: var(--bg);
            color: var(--text);
            margin: 0;
            padding: 0;
            line-height: 1.6;
            transition: background 0.3s, color 0.3s;
        }

        .container { max-width: 1600px; margin: 0 auto; padding: 16px; }

        .header {
            background: var(--bg2);
            border-bottom: 1px solid var(--border);
            padding: 16px 0;
            position: sticky;
            top: 0;
            z-index: 100;
            transition: background 0.3s, border-color 0.3s;
        }

        .header-content {
            display: flex;
            justify-content: space-between;
            align-items: center;
            flex-wrap: wrap;
            gap: 12px;
        }

        .header h1 {
            color: var(--primary);
            font-size: 1.5rem;
            font-weight: 700;
            margin: 0;
        }

        .header-info { color: var(--text2); font-size: 0.85rem; }

        .theme-toggle {
            background: var(--bg3);
            border: 1px solid var(--border);
            color: var(--text);
            padding: 8px 16px;
            border-radius: 8px;
            cursor: pointer;
            font-size: 0.9rem;
            display: flex;
            align-items: center;
            gap: 8px;
            transition: all 0.2s;
        }

        .theme-toggle:hover { background: var(--primary); color: white; }

        .mode-tabs {
            display: flex;
            gap: 4px;
            background: var(--bg2);
            padding: 4px;
            border-radius: 8px;
            margin: 16px 0;
            overflow-x: auto;
            border: 1px solid var(--border);
        }

        .mode-tab {
            padding: 8px 16px;
            border-radius: 6px;
            border: none;
            background: transparent;
            color: var(--text2);
            cursor: pointer;
            font-size: 0.9rem;
            white-space: nowrap;
            transition: all 0.2s;
        }

        .mode-tab:hover { background: var(--bg3); color: var(--text); }
        .mode-tab.active { background: var(--primary); color: white; }

        .stats-grid {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
            gap: 12px;
            margin-bottom: 20px;
        }

        .stat-card {
            background: var(--bg2);
            border: 1px solid var(--border);
            border-radius: 10px;
            padding: 16px;
            text-align: center;
            transition: transform 0.2s, border-color 0.2s, background 0.3s;
        }

        .stat-card:hover { transform: translateY(-2px); border-color: var(--primary); }
        .stat-value { font-size: 1.75rem; font-weight: 700; color: var(--primary); }
        .stat-label { color: var(--text2); font-size: 0.8rem; margin-top: 4px; }
        .stat-card.danger .stat-value { color: var(--danger); }
        .stat-card.warning .stat-value { color: var(--warning); }
        .stat-card.success .stat-value { color: var(--success); }

        .diagnosis-grid {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
            gap: 12px;
        }

        .diagnosis-item {
            display: flex;
            align-items: flex-start;
            gap: 12px;
            padding: 12px 16px;
            background: var(--bg);
            border-radius: 8px;
            border-left: 4px solid var(--border);
        }

        .diagnosis-item.critical { border-left-color: var(--danger); background: rgba(239, 68, 68, 0.05); }
        .diagnosis-item.warning { border-left-color: var(--warning); background: rgba(245, 158, 11, 0.05); }
        .diagnosis-item.info { border-left-color: var(--info); background: rgba(59, 130, 246, 0.05); }
        .diagnosis-item.success { border-left-color: var(--success); background: rgba(16, 185, 129, 0.05); }

        .diagnosis-icon { font-size: 1.25rem; flex-shrink: 0; }
        .diagnosis-content { flex: 1; }
        .diagnosis-title { font-weight: 600; font-size: 0.9rem; margin-bottom: 2px; }
        .diagnosis-item.critical .diagnosis-title { color: var(--danger); }
        .diagnosis-item.warning .diagnosis-title { color: var(--warning); }
        .diagnosis-item.info .diagnosis-title { color: var(--info); }
        .diagnosis-item.success .diagnosis-title { color: var(--success); }
        .diagnosis-desc { font-size: 0.8rem; color: var(--text2); }

        .card {
            background: var(--bg2);
            border: 1px solid var(--border);
            border-radius: 10px;
            padding: 20px;
            margin-bottom: 20px;
            transition: background 0.3s, border-color 0.3s;
        }

        .card-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 16px;
            border-bottom: 2px solid var(--primary);
            padding-bottom: 10px;
        }

        .card-title { color: var(--primary); font-size: 1.2rem; font-weight: 600; margin: 0; }

        .mode-section { display: none; }
        .mode-section.active { display: block; }

        table { width: 100%; border-collapse: collapse; font-size: 0.9rem; }

        th, td {
            padding: 10px 12px;
            text-align: left;
            border-bottom: 1px solid var(--border);
        }

        th { background: var(--bg); color: var(--text2); font-weight: 600; position: sticky; top: 0; }
        tr:hover { background: rgba(59, 130, 246, 0.1); }

        .badge {
            display: inline-block;
            padding: 2px 8px;
            border-radius: 4px;
            font-size: 0.75rem;
            font-weight: 600;
        }

        .badge-danger { background: rgba(239, 68, 68, 0.2); color: var(--danger); }
        .badge-warning { background: rgba(245, 158, 11, 0.2); color: var(--warning); }
        .badge-success { background: rgba(16, 185, 129, 0.2); color: var(--success); }
        .badge-info { background: rgba(59, 130, 246, 0.2); color: var(--primary); }

        .chart-container { position: relative; height: 300px; margin: 16px 0; }
        .chart-container.small { height: 200px; }

        .detail-panel {
            background: var(--bg);
            border: 1px solid var(--border);
            border-radius: 8px;
            padding: 16px;
            margin-top: 16px;
        }

        .detail-row {
            display: flex;
            justify-content: space-between;
            padding: 8px 0;
            border-bottom: 1px solid var(--border);
        }

        .detail-row:last-child { border-bottom: none; }
        .detail-label { color: var(--text2); }
        .detail-value { font-weight: 600; font-family: monospace; }

        .timeline { position: relative; padding-left: 24px; }

        .timeline::before {
            content: '';
            position: absolute;
            left: 8px;
            top: 0;
            bottom: 0;
            width: 2px;
            background: var(--border);
        }

        .timeline-item {
            position: relative;
            padding: 12px 0;
        }

        .timeline-item::before {
            content: '';
            position: absolute;
            left: -20px;
            top: 16px;
            width: 10px;
            height: 10px;
            border-radius: 50%;
            background: var(--primary);
        }

        .timeline-item.leaked::before { background: var(--danger); }
        .timeline-item.active::before { background: var(--success); }

        .search-box {
            background: var(--bg);
            border: 1px solid var(--border);
            border-radius: 6px;
            padding: 8px 12px;
            color: var(--text);
            width: 100%;
            max-width: 300px;
            margin-bottom: 12px;
        }

        .search-box:focus { outline: none; border-color: var(--primary); }

        .table-wrapper { max-height: 500px; overflow-y: auto; border-radius: 8px; }

        .empty-state { text-align: center; padding: 40px; color: var(--text2); }

        .tab-nav {
            display: flex;
            gap: 4px;
            margin-bottom: 16px;
            border-bottom: 1px solid var(--border);
        }

        .tab-btn {
            padding: 8px 16px;
            border: none;
            background: transparent;
            color: var(--text2);
            cursor: pointer;
            border-bottom: 2px solid transparent;
            margin-bottom: -1px;
        }

        .tab-btn:hover { color: var(--text); }
        .tab-btn.active { color: var(--primary); border-bottom-color: var(--primary); }

        .tab-content { display: none; }
        .tab-content.active { display: block; }

        .risk-low { color: var(--success); }
        .risk-medium { color: var(--warning); }
        .risk-high { color: var(--danger); }

        .graph-container {
            width: 100%;
            height: 400px;
            background: var(--bg);
            border-radius: 8px;
            overflow: hidden;
        }

        .node circle { cursor: pointer; transition: all 0.2s; }
        .node circle:hover { filter: brightness(1.2); }
        .node text { font-size: 10px; fill: var(--text); pointer-events: none; }
        .link { stroke-opacity: 0.6; }

        .tooltip {
            position: absolute;
            background: var(--bg2);
            border: 1px solid var(--border);
            border-radius: 6px;
            padding: 8px 12px;
            font-size: 0.85rem;
            pointer-events: none;
            z-index: 1000;
            box-shadow: 0 4px 12px rgba(0,0,0,0.15);
        }

        .legend-item {
            display: flex;
            align-items: center;
            gap: 8px;
            padding: 4px 8px;
        }

        .legend-color {
            width: 12px;
            height: 12px;
            border-radius: 50%;
        }

        .flex-center { display: flex; align-items: center; justify-content: center; }
        .flex-between { display: flex; align-items: center; justify-content: space-between; }
        .flex-gap-2 { display: flex; align-items: center; gap: 8px; }
        .flex-gap-3 { display: flex; align-items: center; gap: 12px; }
        .flex-gap-4 { display: flex; align-items: center; gap: 16px; }
        .flex-wrap { flex-wrap: wrap; }
        .mb-3 { margin-bottom: 12px; }
        .mt-3 { margin-top: 12px; }
        .mt-4 { margin-top: 16px; }
        .gap-3 { gap: 12px; }
        .gap-4 { gap: 16px; }
        .gap-2 { gap: 8px; }
        .grid-auto { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 8px; }
        .grid-auto-sm { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 8px; }
        .grid-auto-lg { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px; }
        .flex-float-start { display: flex; align-items: flex-start; }
        .flex-1 { flex: 1; }
        .text-primary { color: var(--primary); }
        .font-bold { font-weight: 600; }
        .mb-2 { margin-bottom: 8px; }
        .mb-4 { margin-bottom: 16px; }
        .mt-5 { margin-top: 20px; }
        .text-sm { font-size: 0.9rem; }
        .text-secondary { color: var(--text2); font-size: 0.9rem; }
        .text-small { font-size: 0.85rem; }
        .text-mono { font-family: monospace; }

        .btn-sm { padding: 4px 12px; background: var(--bg3); border: 1px solid var(--border); border-radius: 6px; cursor: pointer; color: var(--text); }
        .btn-sm:hover { background: var(--bg2); }

        .input-sm { background: var(--bg3); border: 1px solid var(--border); padding: 4px 8px; border-radius: 4px; color: var(--text); cursor: pointer; }

        .table-scroll { max-height: 400px; overflow-y: auto; border-radius: 8px; }
        .table-scroll-sm { max-height: 300px; overflow-y: auto; border-radius: 8px; }

        .pagination { display: none; justify-content: center; align-items: center; gap: 8px; margin-top: 12px; padding: 12px; }

        .panel-border { border: 1px solid var(--border); border-radius: 8px; padding: 16px; }
        .panel-primary { border-left: 4px solid var(--primary); }
        .panel-danger { border-left: 4px solid var(--danger); }

        .grid-auto { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 8px; }
        .grid-auto-sm { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 8px; }

        .timeline-item { margin-bottom: 8px; padding: 8px; border-radius: 4px; }
        .timeline-item.alloc { background: rgba(16, 185, 129, 0.1); border-left: 3px solid #10b981; }
        .timeline-item.dealloc { background: rgba(59, 130, 246, 0.1); border-left: 3px solid #3b82f6; }
        .timeline-item.leak { background: rgba(239, 68, 68, 0.1); border-left: 3px solid #ef4444; }

        @media (max-width: 768px) {
            .stats-grid { grid-template-columns: repeat(2, 1fr); }
            .header-content { flex-direction: column; align-items: flex-start; }
        }
    </style>
</head>
<body>
    <div class="header">
        <div class="container header-content">
            <div>
                <h1>🧠 MemScope Memory Analysis</h1>
                <div class="header-info">{{title}} | {{export_timestamp}}</div>
            </div>
            <div class="flex-gap-3">
                <div class="header-info">{{os_name}} | {{architecture}} | {{cpu_cores}} cores</div>
                <button class="theme-toggle" onclick="toggleTheme()">
                    <span id="theme-icon">🌙</span>
                    <span id="theme-text">Dark</span>
                </button>
            </div>
        </div>
    </div>

    <div class="container">
        <div class="mode-tabs">
            <button class="mode-tab active" onclick="showMode('overview')">📊 Overview</button>
            <button class="mode-tab" onclick="showMode('thread')">🧵 Thread View</button>
            <button class="mode-tab" onclick="showMode('task')">⚡ Async View</button>
            <button class="mode-tab" onclick="showMode('taskgraph')">🌳 Task Graph</button>
            <button class="mode-tab" onclick="showMode('variable')">📦 Variable View</button>
            <button class="mode-tab" onclick="showMode('passport')">🛂 Memory Passport</button>
            <button class="mode-tab" onclick="showMode('timetravel')">⏪ Time Travel</button>
            <button class="mode-tab" onclick="showMode('unsafe')">⚠️ Unsafe/FFI</button>
        </div>

        <div class="stats-grid">
            <div class="stat-card" id="healthScoreCard" style="background: linear-gradient(135deg, var(--bg2) 0%, var(--bg3) 100%); border: 2px solid var(--primary);">
                <div class="stat-value" id="healthScore" style="font-size: 2.5rem;">{{health_score}}</div>
                <div class="stat-label">🛡️ Health Score</div>
                <div id="healthStatus" style="font-size: 0.75rem; margin-top: 4px; color: var(--text2);">{{health_status}}</div>
            </div>
            <div class="stat-card">
                <div class="stat-value">{{total_allocations}}</div>
                <div class="stat-label">📦 Allocations</div>
            </div>
            <div class="stat-card">
                <div class="stat-value">{{total_memory}}</div>
                <div class="stat-label">💾 Memory</div>
            </div>
            <div class="stat-card">
                <div class="stat-value">{{peak_memory}}</div>
                <div class="stat-label">📈 Peak</div>
            </div>
            <div class="stat-card danger" id="leakCard">
                <div class="stat-value">{{leak_count}}</div>
                <div class="stat-label">🔴 Leaks</div>
            </div>
            <div class="stat-card" style="background: linear-gradient(135deg, #8b5cf620 0%, #8b5cf640 100%); border: 1px solid #8b5cf6;">
                <div class="stat-value" style="color: #8b5cf6;">{{passport_count}}</div>
                <div class="stat-label">🛂 Passports</div>
            </div>
            <div class="stat-card warning">
                <div class="stat-value">{{unsafe_count}}</div>
                <div class="stat-label">⚠️ Unsafe</div>
            </div>
            <div class="stat-card">
                <div class="stat-value">{{ffi_count}}</div>
                <div class="stat-label">🔗 FFI Calls</div>
            </div>
        </div>

        <!-- Overview Mode -->
        <div id="mode-overview" class="mode-section active">
            <!-- Quick Health Dashboard -->
            <div class="card" style="background: linear-gradient(135deg, #3b82f610 0%, #3b82f620 100%); border: 2px solid var(--primary); margin-bottom: 16px;">
                <div class="card-header" style="margin-bottom: 12px;">
                    <h2 class="card-title">🎯 Quick Health Dashboard</h2>
                    <div class="flex-gap-2">
                        <span class="badge badge-info" id="quickStatusBadge">Analyzing...</span>
                        <span class="text-secondary" style="font-size: 0.85rem;">Last Updated: {{export_timestamp}}</span>
                    </div>
                </div>
                
                <!-- Critical Alerts -->
                <div id="criticalAlerts" style="margin-bottom: 12px;"></div>
                
                <!-- Compact Stats Grid -->
                <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 8px;">
                    <div class="stat-card" style="padding: 8px;">
                        <div style="display: flex; justify-content: space-between; align-items: center;">
                            <div>
                                <div class="stat-value" style="font-size: 1.5rem;" id="quickHealthScore">{{health_score}}</div>
                                <div class="stat-label" style="font-size: 0.75rem;">🛡️ Health</div>
                            </div>
                            <div id="healthTrend" style="font-size: 1.5rem;">✅</div>
                        </div>
                    </div>
                    <div class="stat-card danger" style="padding: 8px;" id="quickLeakCard">
                        <div style="display: flex; justify-content: space-between; align-items: center;">
                            <div>
                                <div class="stat-value" style="font-size: 1.5rem;">{{leak_count}}</div>
                                <div class="stat-label" style="font-size: 0.75rem;">🔴 Leaks</div>
                            </div>
                            <div id="leakTrend" style="font-size: 1.5rem;">⚠️</div>
                        </div>
                    </div>
                    <div class="stat-card" style="padding: 8px; background: linear-gradient(135deg, #10b98120 0%, #10b98140 100%); border: 1px solid #10b981;">
                        <div style="display: flex; justify-content: space-between; align-items: center;">
                            <div>
                                <div class="stat-value" style="font-size: 1.5rem; color: #10b981;" id="quickActiveTasks">0</div>
                                <div class="stat-label" style="font-size: 0.75rem;">🟢 Active Tasks</div>
                            </div>
                            <div style="font-size: 1.5rem;">⚡</div>
                        </div>
                    </div>
                    <div class="stat-card warning" style="padding: 8px;">
                        <div style="display: flex; justify-content: space-between; align-items: center;">
                            <div>
                                <div class="stat-value" style="font-size: 1.5rem;">{{unsafe_count}}</div>
                                <div class="stat-label" style="font-size: 0.75rem;">⚠️ Unsafe</div>
                            </div>
                            <div id="unsafeTrend" style="font-size: 1.5rem;">⚡</div>
                        </div>
                    </div>
                    <div class="stat-card" style="padding: 8px;">
                        <div style="display: flex; justify-content: space-between; align-items: center;">
                            <div>
                                <div class="stat-value" style="font-size: 1.5rem;">{{ffi_count}}</div>
                                <div class="stat-label" style="font-size: 0.75rem;">🔗 FFI</div>
                            </div>
                            <div style="font-size: 1.5rem;">🌐</div>
                        </div>
                    </div>
                    <div class="stat-card" style="padding: 8px; background: linear-gradient(135deg, #8b5cf620 0%, #8b5cf640 100%); border: 1px solid #8b5cf6;">
                        <div style="display: flex; justify-content: space-between; align-items: center;">
                            <div>
                                <div class="stat-value" style="font-size: 1.5rem; color: #8b5cf6;">{{passport_count}}</div>
                                <div class="stat-label" style="font-size: 0.75rem;">🛂 Passports</div>
                            </div>
                            <div style="font-size: 1.5rem;">📋</div>
                        </div>
                    </div>
                </div>
                <div id="eventSummaryCard" style="margin-top: 12px; display: none;"></div>
            </div>

            <!-- Auto Diagnosis -->
            <div class="card" style="background: linear-gradient(135deg, #fef2f220 0%, #fef2f240 100%); border: 2px solid #fef2f2;">
                <div class="card-header">
                    <h2 class="card-title">🔬 Auto Diagnosis</h2>
                    <span class="badge" id="diagnosisBadge" style="background: var(--primary); color: white;">Analysis Complete</span>
                </div>
                <div id="diagnosisContent" class="diagnosis-grid">
                    <!-- Dynamic diagnosis content will be rendered here -->
                </div>
            </div>

            <div class="card" style="background: linear-gradient(135deg, var(--bg2) 0%, var(--bg3) 100%); border: 2px solid var(--primary);">
                <div class="card-header">
                    <h2 class="card-title">🛡️ Memory Safety Analysis</h2>
                    <span class="badge badge-info" id="safetyBadge">{{safe_code_percent}}% Safe</span>
                </div>
                <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px; margin-bottom: 16px;">
                    <div class="detail-panel" style="border-left: 4px solid var(--success);">
                        <div style="display: flex; align-items: center; gap: 8px;">
                            <span style="font-size: 1.5rem;">✅</span>
                            <div>
                                <div style="font-weight: 600; color: var(--success);">Safe Operations</div>
                                <div id="safeOpsCount" style="font-size: 1.25rem; font-weight: 700;">{{safe_ops_count}}</div>
                            </div>
                        </div>
                    </div>
                    <div class="detail-panel" style="border-left: 4px solid var(--warning);">
                        <div style="display: flex; align-items: center; gap: 8px;">
                            <span style="font-size: 1.5rem;">⚡</span>
                            <div>
                                <div style="font-weight: 600; color: var(--warning);">Rust-FFI Crossings</div>
                                <div id="ffiCrossCount" style="font-size: 1.25rem; font-weight: 700;">{{ffi_count}}</div>
                            </div>
                        </div>
                    </div>
                    <div class="detail-panel" style="border-left: 4px solid var(--danger);">
                        <div style="display: flex; align-items: center; gap: 8px;">
                            <span style="font-size: 1.5rem;">🚨</span>
                            <div>
                                <div style="font-weight: 600; color: var(--danger);">Potential Issues</div>
                                <div id="potentialIssues" style="font-size: 1.25rem; font-weight: 700;">{{high_risk_count}}</div>
                            </div>
                        </div>
                    </div>
                </div>
                <div class="chart-container">
                    <canvas id="memoryChart"></canvas>
                </div>
            </div>

            <div class="card">
                <div class="card-header">
                    <h2 class="card-title">📊 Type Intelligence</h2>
                    <span class="text-secondary" style="font-size: 0.85rem;">Automatic type inference with confidence scores</span>
                </div>
                <div class="chart-container">
                    <canvas id="typeChart"></canvas>
                </div>
            </div>

            <div class="card">
                <div class="card-header">
                    <h2 class="card-title">📋 Active Allocations</h2>
                    <div class="flex-gap-3">
                        <span class="badge badge-info" id="allocCount">0 items</span>
                        <button class="btn-sm" onclick="Dashboard.allocations.toggle()" id="toggleAllocBtn">▼ Show All</button>
                    </div>
                </div>
                <input type="text" class="search-box" id="searchAllocations" placeholder="Search by name or type..." oninput="filterTable('allocationsTable', this.value)">
                <div class="table-wrapper table-scroll" id="allocationsWrapper">
                    <table id="allocationsTable">
                        <thead>
                            <tr>
                                <th>Address</th>
                                <th>Variable</th>
                                <th>Type</th>
                                <th>Size</th>
                                <th>Source</th>
                                <th>Thread</th>
                                <th>Lifetime</th>
                                <th>Status</th>
                            </tr>
                        </thead>
                        <tbody id="allocationsBody"></tbody>
                    </table>
                </div>
                <div id="allocationsPagination" class="pagination">
                    <button class="btn-sm" onclick="Dashboard.allocations.prevPage()" id="prevAllocBtn">← Prev</button>
                    <span class="text-secondary" id="allocPageInfo">Page 1 of 1</span>
                    <button class="btn-sm" onclick="Dashboard.allocations.nextPage()" id="nextAllocBtn">Next →</button>
                </div>
            </div>
        </div>

        <!-- Thread Mode -->
        <div id="mode-thread" class="mode-section">
            <div class="card" style="background: linear-gradient(135deg, #10b98110 0%, #10b98120 100%); border: 2px solid #10b98160;">
                <div class="card-header">
                    <h2 class="card-title" style="color: #10b981;">🧵 Thread Activity Dashboard</h2>
                    <span class="badge" style="background: #10b98130; color: #10b981;" id="threadCount">0 threads</span>
                </div>
                
                <!-- Thread Activity Timeline -->
                <div style="background: var(--bg2); border-radius: 8px; padding: 16px; margin-bottom: 16px; border: 1px solid var(--border);">
                    <div style="display: flex; align-items: center; gap: 12px;">
                        <span style="font-size: 2rem;">📊</span>
                        <div>
                            <div style="font-weight: 600; color: var(--primary);">Real-time Thread Activity</div>
                            <div style="color: var(--text2); font-size: 0.85rem;">
                                Monitor memory allocation patterns across all threads. Each bar represents memory activity over time.
                            </div>
                        </div>
                    </div>
                </div>
                
                <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; margin-bottom: 16px;">
                    <div class="detail-panel" style="border-left: 4px solid #10b981; text-align: center;">
                        <div style="font-size: 0.75rem; color: var(--text2);">🟢 Active Threads</div>
                        <div class="stat-value" style="color: #10b981; font-size: 1.5rem;" id="activeThreadCount">0</div>
                    </div>
                    <div class="detail-panel" style="border-left: 4px solid #3b82f6; text-align: center;">
                        <div style="font-size: 0.75rem; color: var(--text2);">💾 Total Memory</div>
                        <div class="stat-value" style="color: #3b82f6; font-size: 1.5rem;" id="totalThreadMemory">0 B</div>
                    </div>
                    <div class="detail-panel" style="border-left: 4px solid #f59e0b; text-align: center;">
                        <div style="font-size: 0.75rem; color: var(--text2);">🏔️ Peak Memory</div>
                        <div class="stat-value" style="color: #f59e0b; font-size: 1.5rem;" id="peakThreadMemory">0 B</div>
                    </div>
                    <div class="detail-panel" style="border-left: 4px solid #8b5cf6; text-align: center;">
                        <div style="font-size: 0.75rem; color: var(--text2);">📦 Total Allocs</div>
                        <div class="stat-value" style="color: #8b5cf6; font-size: 1.5rem;" id="totalThreadAllocs">0</div>
                    </div>
                </div>
            </div>

            <!-- Thread Relationship Graph -->
            <div class="card" style="background: linear-gradient(135deg, #8b5cf610 0%, #8b5cf620 100%); border: 2px solid #8b5cf660;">
                <div class="card-header">
                    <h2 class="card-title" style="color: #8b5cf6;">🕸️ Thread Relationship Graph</h2>
                    <div class="flex-gap-2">
                        <span class="badge badge-info" id="threadGraphEdgeCount">0 edges</span>
                    </div>
                </div>
                <div style="background: var(--bg2); border-radius: 8px; padding: 12px; margin-bottom: 12px; border: 1px solid var(--border);">
                    <div style="display: flex; align-items: center; gap: 12px; flex-wrap: wrap;">
                        <div class="legend-item"><div class="legend-color" style="background: #10b981;"></div>Normal</div>
                        <div class="legend-item"><div class="legend-color" style="background: #f59e0b;"></div>Leak Risk</div>
                        <div class="legend-item"><div class="legend-color" style="background: #ef4444;"></div>High Risk</div>
                        <div class="legend-item"><div class="legend-color" style="background: #8b5cf6;"></div>Mixed</div>
                        <div class="legend-item" style="margin-left: 12px;"><div style="width: 20px; height: 2px; background: #3b82f6;"></div>Shared Type</div>
                        <div class="legend-item"><div style="width: 20px; height: 2px; background: #10b981; stroke-dasharray: 4,2;"></div>Shared Variable</div>
                        <div class="legend-item"><div style="width: 20px; height: 2px; background: #ef4444;"></div>Clone/Transfer</div>
                        <button class="btn-sm" onclick="Dashboard.threads.graph.resetZoom()">🔍 Reset</button>
                    </div>
                </div>
                <div class="graph-container" id="threadGraphContainer" style="border: 1px solid var(--border); border-radius: 8px; background: var(--bg); min-height: 300px;"></div>
            </div>

            <!-- Thread Detail Panel -->
            <div id="threadDetailPanel" class="card" style="display: none; background: linear-gradient(135deg, var(--bg) 0%, var(--bg3) 100%); border: 2px solid var(--primary);">
                <div class="card-header">
                    <h3 style="color: var(--primary); margin: 0;" id="tdTitle">🧵 Thread Details</h3>
                    <button class="btn-sm" onclick="Dashboard.threads.hideDetail()">✕ Close</button>
                </div>
                <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 12px;">
                    <div class="detail-panel" id="tdMemByType"></div>
                    <div class="detail-panel" id="tdTopVariables"></div>
                </div>
                <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 12px;">
                    <div class="detail-panel" id="tdTopSources"></div>
                    <div class="detail-panel" id="tdUnsafeReports"></div>
                </div>
            </div>

            <!-- Thread Memory Heatmap -->
            <div class="card">
                <div class="card-header">
                    <h2 class="card-title">🔥 Thread Memory Heatmap</h2>
                    <div class="flex-gap-2">
                        <button class="btn-sm" onclick="Dashboard.threads.toggleHeatmap('memory')">💾 Memory</button>
                        <button class="btn-sm" onclick="Dashboard.threads.toggleHeatmap('allocs')">📦 Allocations</button>
                    </div>
                </div>
                <div id="threadHeatmapContainer" style="min-height: 200px; background: var(--bg); border-radius: 8px; padding: 16px;"></div>
            </div>

            <!-- Thread Activity Timeline -->
            <div class="card">
                <div class="card-header">
                    <h2 class="card-title">📅 Thread Activity Timeline</h2>
                </div>
                <div id="threadTimelineContainer" style="min-height: 250px; background: var(--bg); border-radius: 8px; padding: 16px;"></div>
            </div>

            <!-- Thread Distribution Chart -->
            <div class="card">
                <div class="card-header">
                    <h2 class="card-title">📊 Memory Distribution</h2>
                </div>
                <div class="chart-container" style="height: 200px;">
                    <canvas id="threadChart"></canvas>
                </div>
            </div>

            <div class="card">
                <div class="card-header">
                    <h2 class="card-title">📋 Thread Details</h2>
                    <div class="flex-gap-2">
                        <label class="text-secondary">Sort by:</label>
                        <select id="threadSortOrder" onchange="Dashboard.threads.sort()" class="input-sm">
                            <option value="alloc-desc">Allocations ↓</option>
                            <option value="alloc-asc">Allocations ↑</option>
                            <option value="memory-desc">Memory ↓</option>
                            <option value="memory-asc">Memory ↑</option>
                            <option value="thread-id">Thread ID</option>
                        </select>
                    </div>
                </div>
                <div class="table-wrapper">
                    <table>
                        <thead>
                            <tr>
                                <th>Thread ID</th>
                                <th>Summary</th>
                                <th>Allocations</th>
                                <th>Current Memory</th>
                                <th>Peak Memory</th>
                                <th>Status</th>
                            </tr>
                        </thead>
                        <tbody id="threadTableBody"></tbody>
                    </table>
                </div>
            </div>
        </div>

        <!-- Task Mode (Allocations) -->
        <div id="mode-task" class="mode-section">
            <!-- Async Overview Dashboard -->
            <div class="card" style="background: linear-gradient(135deg, #3b82f610 0%, #3b82f620 100%); border: 2px solid var(--primary);">
                <div class="card-header">
                    <h2 class="card-title">⚡ Async Task Dashboard</h2>
                    <span class="badge badge-info" id="asyncTaskCount">0 tasks</span>
                </div>
                
                <!-- Quick Stats -->
                <div class="stats-grid" style="margin-bottom: 16px;">
                    <div class="stat-card">
                        <div class="stat-value" id="activeTaskCount">0</div>
                        <div class="stat-label">🟢 Active Tasks</div>
                    </div>
                    <div class="stat-card success">
                        <div class="stat-value" id="completedTaskCount">0</div>
                        <div class="stat-label">✅ Completed</div>
                    </div>
                    <div class="stat-card warning">
                        <div class="stat-value" id="leakTaskCount">0</div>
                        <div class="stat-label">⚠️ Potential Leaks</div>
                    </div>
                    <div class="stat-card">
                        <div class="stat-value" id="avgEfficiency">0%</div>
                        <div class="stat-label">📊 Avg Efficiency</div>
                    </div>
                    <div class="stat-card">
                        <div class="stat-value" id="totalAsyncMemory">0 B</div>
                        <div class="stat-label">💾 Total Memory</div>
                    </div>
                    <div class="stat-card">
                        <div class="stat-value" id="avgDuration">0ms</div>
                        <div class="stat-label">⏱️ Avg Duration</div>
                    </div>
                </div>
            </div>

            <!-- Task Execution Timeline -->
            <div class="card">
                <div class="card-header">
                    <h2 class="card-title">📅 Task Execution Timeline</h2>
                    <div class="flex-gap-2">
                        <button class="btn-sm" onclick="Dashboard.tasks.toggleView('timeline')">📊 Timeline</button>
                        <button class="btn-sm" onclick="Dashboard.tasks.toggleView('gantt')">📈 Gantt Chart</button>
                    </div>
                </div>
                <div id="taskTimelineContainer" style="min-height: 300px; background: var(--bg); border-radius: 8px; padding: 16px;">
                    <div id="taskTimeline"></div>
                </div>
            </div>

            <!-- Resource Consumption Heatmap -->
            <div class="card">
                <div class="card-header">
                    <h2 class="card-title">🔥 Resource Consumption Heatmap</h2>
                </div>
                <div class="chart-container" style="height: 250px;">
                    <canvas id="asyncHeatmapChart"></canvas>
                </div>
            </div>

            <!-- Task Performance Analysis -->
            <div class="card">
                <div class="card-header">
                    <h2 class="card-title">📊 Task Performance Analysis</h2>
                </div>
                <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 16px;">
                    <div class="chart-container small">
                        <canvas id="asyncDurationChart"></canvas>
                    </div>
                    <div class="chart-container small">
                        <canvas id="asyncEfficiencyChart"></canvas>
                    </div>
                </div>
            </div>

            <!-- Personalized Task Cards -->
            <div class="card">
                <div class="card-header">
                    <h2 class="card-title">🎯 Task Details</h2>
                    <div class="flex-gap-2">
                        <label class="text-secondary">Sort by:</label>
                        <select id="taskSortOrder" onchange="Dashboard.tasks.sort()" class="input-sm">
                            <option value="memory-desc">Memory ↓</option>
                            <option value="memory-asc">Memory ↑</option>
                            <option value="peak-desc">Peak ↓</option>
                            <option value="efficiency-desc">Efficiency ↓</option>
                            <option value="duration-desc">Duration ↓</option>
                            <option value="allocations-desc">Allocs ↓</option>
                        </select>
                        <select id="taskFilterStatus" onchange="Dashboard.tasks.filter()" class="input-sm">
                            <option value="all">All Tasks</option>
                            <option value="active">Active Only</option>
                            <option value="completed">Completed Only</option>
                            <option value="leak">Potential Leaks</option>
                        </select>
                    </div>
                </div>
                <div id="taskCardsContainer" style="display: grid; grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); gap: 12px;"></div>
            </div>
        </div>

        <!-- Task Graph Mode -->
        <div id="mode-taskgraph" class="mode-section">
            <div class="card" style="background: linear-gradient(135deg, #10b98110 0%, #10b98120 100%); border: 2px solid #10b98160;">
                <div class="card-header">
                    <h2 class="card-title" style="color: #10b981;">🌳 Task Relationship Graph</h2>
                    <span class="badge" style="background: #10b98130; color: #10b981;" id="taskGraphCount">0 tasks</span>
                </div>
                <div style="background: linear-gradient(135deg, #10b98110 0%, #10b98120 100%); border-radius: 8px; padding: 12px; margin-bottom: 12px; border: 1px solid #10b98140;">
                    <div style="display: flex; align-items: center; gap: 12px;">
                        <span style="font-size: 1.5rem;">🌳</span>
                        <div>
                            <div style="font-weight: 600; color: #10b981;">Task Hierarchy Visualization</div>
                            <div style="color: var(--text2); font-size: 0.85rem;">
                                Click on a task to see its memory usage • Tree view shows parent-child relationships
                            </div>
                        </div>
                    </div>
                </div>
                <div class="graph-container" id="taskGraphContainer" style="border: 1px solid var(--border); border-radius: 8px; background: var(--bg); min-height: 400px;">
                    <div id="taskGraphEmptyState" class="empty-state" style="display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100%; padding: 40px;">
                        <div style="font-size: 3rem; margin-bottom: 16px;">🌳</div>
                        <div style="font-size: 1.1rem; font-weight: 600; margin-bottom: 8px;">No Task Data Available</div>
                        <div style="color: var(--text2); font-size: 0.9rem; max-width: 400px; text-align: center;">
                            Task tracking requires integration with TaskIdRegistry. Use the API to spawn tasks and track memory allocations per task.
                        </div>
                    </div>
                    <div id="taskGraphSvg" style="display: none;"></div>
                </div>
                <div id="taskGraphDetails" class="card" style="display: none; margin-top: 16px; background: linear-gradient(135deg, var(--bg2) 0%, var(--bg3) 100%);">
                    <div class="card-header">
                        <h2 class="card-title">📊 Task Memory Details</h2>
                        <button class="btn-sm" onclick="document.getElementById('taskGraphDetails').style.display='none'">✕ Close</button>
                    </div>
                    <div id="taskGraphDetailsContent"></div>
                </div>
            </div>
        </div>

        <!-- Variable Mode -->
        <div id="mode-variable" class="mode-section">
            <div class="card" style="background: linear-gradient(135deg, var(--bg2) 0%, var(--bg3) 100%);">
                <div class="card-header">
                    <h2 class="card-title">🔗 Variable Relationship Graph</h2>
                    <span class="badge badge-info" id="relationshipCount">0 relationships</span>
                </div>
                <div style="background: linear-gradient(135deg, #3b82f610 0%, #3b82f620 100%); border-radius: 8px; padding: 12px; margin-bottom: 12px; border: 1px solid #3b82f640;">
                    <div style="display: flex; align-items: center; gap: 12px;">
                        <span style="font-size: 1.5rem;">🕸️</span>
                        <div>
                            <div style="font-weight: 600; color: var(--primary);">Ownership & Borrowing Visualization</div>
                            <div style="color: var(--text2); font-size: 0.85rem;">
                                Drag nodes to explore • Red dashed lines = circular references • Node size = allocation size
                            </div>
                        </div>
                    </div>
                </div>
                <div class="flex-gap-4 flex-wrap mb-3">
                    <div class="legend-item"><div class="legend-color" style="background: #10b981; width: 12px; height: 12px; border-radius: 50%;"></div>Owner/Clone</div>
                    <div class="legend-item"><div class="legend-color" style="background: #3b82f6; width: 12px; height: 12px; border-radius: 50%;"></div>Immutable &</div>
                    <div class="legend-item"><div class="legend-color" style="background: #f59e0b; width: 12px; height: 12px; border-radius: 50%;"></div>Mutable &mut</div>
                    <div class="legend-item"><div class="legend-color" style="background: #8b5cf6; width: 12px; height: 12px; border-radius: 50%;"></div>Arc/Rc</div>
                    <div class="legend-item"><div class="legend-color" style="background: #06b6d4; width: 12px; height: 12px; border-radius: 50%;"></div>Variable Evolution</div>
                    <div class="legend-item"><div class="legend-color" style="background: #ef4444; width: 20px; height: 2px; border-style: dashed;"></div>Circular Ref</div>
                </div>
                <div class="graph-container" id="relationshipGraph" style="border: 1px solid var(--border); border-radius: 8px; background: var(--bg);"></div>
                <div style="display: flex; gap: 8px; margin-top: 8px;">
                    <button class="btn-sm" onclick="Dashboard.variableGraph.resetZoom()">🔍 Reset View</button>
                    <button class="btn-sm" onclick="Dashboard.variableGraph.highlightCycles()">🔴 Highlight Cycles</button>
                </div>
            </div>

            <div class="card">
                <div class="card-header">
                    <h2 class="card-title">📋 Relationship List</h2>
                    <div class="flex-gap-3">
                        <span class="badge badge-info" id="relationshipCount2">0 relationships</span>
                        <select id="relSortOrder" onchange="Dashboard.relationships.sort()" class="input-sm">
                            <option value="strength-desc">Strength ↓</option>
                            <option value="strength-asc">Strength ↑</option>
                            <option value="type">Type A-Z</option>
                            <option value="source">Source A-Z</option>
                        </select>
                        <button class="btn-sm" onclick="Dashboard.relationships.toggle()" id="toggleRelBtn">▼ Expand</button>
                    </div>
                </div>
                <div class="table-wrapper table-scroll-sm" id="relTableWrapper">
                    <table>
                        <thead>
                            <tr>
                                <th>Source</th>
                                <th>Target</th>
                                <th>Relationship</th>
                                <th>Type</th>
                                <th>Strength</th>
                            </tr>
                        </thead>
                        <tbody id="relationshipsBody"></tbody>
                    </table>
                </div>
            </div>
        </div>

        <!-- Passport Mode -->
        <div id="mode-passport" class="mode-section">
            <div class="card" style="background: linear-gradient(135deg, #8b5cf610 0%, #8b5cf620 100%); border: 2px solid #8b5cf6;">
                <div class="card-header">
                    <h2 class="card-title" style="color: #8b5cf6;">🛂 Memory Passport™</h2>
                    <span class="badge" style="background: #8b5cf630; color: #8b5cf6;" id="passportBadge">0 passports</span>
                </div>
                <div style="background: var(--bg2); border-radius: 8px; padding: 16px; margin-bottom: 16px; border: 1px solid var(--border);">
                    <div style="display: flex; align-items: center; gap: 12px;">
                        <span style="font-size: 2rem;">💡</span>
                        <div>
                            <div style="font-weight: 600; color: var(--primary);">What is Memory Passport?</div>
                            <div style="color: var(--text2); font-size: 0.85rem;">
                                Every memory allocation gets a "passport" that tracks its entire lifecycle - from birth to death.
                                Like a visa, it records every border crossing between Rust and FFI, detecting potential leaks before they happen.
                            </div>
                        </div>
                    </div>
                </div>
                <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 12px; margin-bottom: 16px;">
                    <div class="detail-panel" style="border-left: 4px solid #10b981; text-align: center;">
                        <div style="font-size: 0.75rem; color: var(--text2);">✅ Clean</div>
                        <div class="stat-value" style="color: #10b981; font-size: 1.5rem;" id="cleanPassCount">{{clean_passport_count}}</div>
                    </div>
                    <div class="detail-panel" style="border-left: 4px solid #f59e0b; text-align: center;">
                        <div style="font-size: 0.75rem; color: var(--text2);">⚡ Active</div>
                        <div class="stat-value" style="color: #f59e0b; font-size: 1.5rem;" id="activePassCount">{{active_passport_count}}</div>
                    </div>
                    <div class="detail-panel" style="border-left: 4px solid #ef4444; text-align: center;">
                        <div style="font-size: 0.75rem; color: var(--text2);">🚨 Leaked</div>
                        <div class="stat-value" style="color: #ef4444; font-size: 1.5rem;" id="leakedPassCount">{{leaked_passport_count}}</div>
                    </div>
                    <div class="detail-panel" style="border-left: 4px solid #8b5cf6; text-align: center;">
                        <div style="font-size: 0.75rem; color: var(--text2);">🔗 FFI Tracked</div>
                        <div class="stat-value" style="color: #8b5cf6; font-size: 1.5rem;" id="ffiPassCount">{{ffi_tracked_count}}</div>
                    </div>
                </div>
                <div class="tab-nav">
                    <button class="tab-btn active" onclick="showTab('passport-overview')">📊 Risk Analysis</button>
                    <button class="tab-btn" onclick="showTab('passport-list')">📋 Passport List</button>
                    <button class="tab-btn" onclick="showTab('passport-timeline')">⏱️ Timeline</button>
                </div>
                <div id="tab-passport-overview" class="tab-content active">
                    <div class="chart-container">
                        <canvas id="passportChart"></canvas>
                    </div>
                    <div class="grid-auto-lg gap-4 mt-4">
                        <div class="detail-panel" style="border-left: 4px solid var(--danger);">
                            <div class="detail-label">🔴 High Risk</div>
                            <div class="stat-value risk-high" id="highRiskCount">0</div>
                        </div>
                        <div class="detail-panel" style="border-left: 4px solid var(--warning);">
                            <div class="detail-label">🟡 Medium Risk</div>
                            <div class="stat-value risk-medium" id="mediumRiskCount">0</div>
                        </div>
                        <div class="detail-panel" style="border-left: 4px solid var(--success);">
                            <div class="detail-label">🟢 Low Risk</div>
                            <div class="stat-value risk-low" id="lowRiskCount">0</div>
                        </div>
                    </div>
                </div>
                <div id="tab-passport-list" class="tab-content">
                    <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px;">
                        <span class="text-secondary" style="font-size: 0.85rem;">Click to expand each passport for details</span>
                        <button class="btn-sm" onclick="toggleSection('passportCardContainer', this)">▼ Expand All</button>
                    </div>
                    <div id="passportCardContainer" style="display: grid; grid-template-columns: repeat(auto-fill, minmax(350px, 1fr)); gap: 16px; max-height: 600px; overflow-y: auto;"></div>
                </div>
                <div id="tab-passport-timeline" class="tab-content">
                    <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px;">
                        <span class="text-secondary" style="font-size: 0.85rem;">Timeline of passport events</span>
                        <button class="btn-sm" onclick="toggleSection('passportTimeline', this)">▼ Expand All</button>
                    </div>
                    <div id="passportTimeline" class="timeline" style="max-height: 500px; overflow-y: auto;"></div>
                </div>
            </div>
        </div>

        <!-- Time Travel Mode -->
        <div id="mode-timetravel" class="mode-section">
            <div class="card" style="background: linear-gradient(135deg, #8b5cf610 0%, #8b5cf620 100%); border: 2px solid #8b5cf660;">
                <div class="card-header">
                    <h2 class="card-title" style="color: #8b5cf6;">⏪ Time Travel Debugging</h2>
                    <span class="badge" style="background: #8b5cf630; color: #8b5cf6;">Interactive Timeline</span>
                </div>
                
                <div style="background: var(--bg2); border-radius: 8px; padding: 16px; margin-bottom: 16px; border: 1px solid var(--border);">
                    <div style="display: flex; align-items: center; gap: 12px;">
                        <span style="font-size: 2rem;">🎯</span>
                        <div>
                            <div style="font-weight: 600; color: var(--primary);">Time Travel Debugging</div>
                            <div style="color: var(--text2); font-size: 0.85rem;">
                                Navigate through memory events like a time machine. Use the slider to explore allocation history.
                            </div>
                        </div>
                    </div>
                </div>
                
                <!-- Stats Summary -->
                <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 12px; margin-bottom: 16px;">
                    <div class="detail-panel" style="border-left: 4px solid #10b981; text-align: center;">
                        <div style="font-size: 0.75rem; color: var(--text2);">📦 Total Events</div>
                        <div class="stat-value" style="color: #10b981; font-size: 1.25rem;" id="ttTotalEvents">0</div>
                    </div>
                    <div class="detail-panel" style="border-left: 4px solid #3b82f6; text-align: center;">
                        <div style="font-size: 0.75rem; color: var(--text2);">🟢 Active</div>
                        <div class="stat-value" style="color: #3b82f6; font-size: 1.25rem;" id="ttActiveCount">0</div>
                    </div>
                    <div class="detail-panel" style="border-left: 4px solid #f59e0b; text-align: center;">
                        <div style="font-size: 0.75rem; color: var(--text2);">✅ Freed</div>
                        <div class="stat-value" style="color: #f59e0b; font-size: 1.25rem;" id="ttFreedCount">0</div>
                    </div>
                    <div class="detail-panel" style="border-left: 4px solid #ef4444; text-align: center;">
                        <div style="font-size: 0.75rem; color: var(--text2);">🚨 Leaked</div>
                        <div class="stat-value" style="color: #ef4444; font-size: 1.25rem;" id="ttLeakedCount">0</div>
                    </div>
                </div>
            </div>

            <!-- Allocation Timeline Chart -->
            <div class="card">
                <div class="card-header">
                    <h2 class="card-title">📈 Allocation Timeline</h2>
                </div>
                <div class="chart-container" style="height: 200px;">
                    <canvas id="ttTimelineChart"></canvas>
                </div>
            </div>

            <!-- Lifecycle Bars -->
            <div class="card">
                <div class="card-header">
                    <h2 class="card-title">⏳ Allocation Lifecycle Bars</h2>
                    <div class="flex-gap-2">
                        <select id="lcThreadFilter" onchange="Dashboard.timetravel.renderLifecycleTimeline()" class="input-sm">
                            <option value="all">All Threads</option>
                        </select>
                        <select id="lcTypeFilter" onchange="Dashboard.timetravel.renderLifecycleTimeline()" class="input-sm">
                            <option value="all">All Types</option>
                        </select>
                        <select id="lcStatusFilter" onchange="Dashboard.timetravel.renderLifecycleTimeline()" class="input-sm">
                            <option value="all">All Status</option>
                            <option value="active">Active Only</option>
                            <option value="leaked">Leaked Only</option>
                            <option value="freed">Freed Only</option>
                        </select>
                        <span class="badge badge-info" id="lcBarCount">0 bars</span>
                    </div>
                </div>
                <div id="lifecycleTimelineContainer" style="width: 100%; min-height: 300px; background: var(--bg); border-radius: 8px; overflow: hidden;"></div>
            </div>

            <!-- Time Slice Snapshots -->
            <div class="card">
                <div class="card-header">
                    <h2 class="card-title">⏱ Time Slice Snapshot</h2>
                    <span class="badge badge-info">Drag to explore memory at a point in time</span>
                </div>
                <div style="padding: 0 4px; margin-bottom: 12px;">
                    <input type="range" id="tsTimeSlider" min="0" max="100" value="100" oninput="Dashboard.timetravel.renderTimeSlice()" style="width: 100%; accent-color: var(--primary);">
                    <div class="flex-between" style="font-size: 0.75rem; color: var(--text2);">
                        <span id="tsTimeStart">T+0</span>
                        <span id="tsTimeCurrent">T+0</span>
                        <span id="tsTimeEnd">T+0</span>
                    </div>
                </div>
                <div id="tsStatsGrid" style="display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 8px;">
                    <div class="detail-panel" style="border-left: 4px solid #3b82f6; text-align: center;">
                        <div style="font-size: 0.7rem; color: var(--text2);">📦 Active Allocs</div>
                        <div class="stat-value" style="color: #3b82f6; font-size: 1.2rem;" id="tsAllocCount">0</div>
                    </div>
                    <div class="detail-panel" style="border-left: 4px solid #10b981; text-align: center;">
                        <div style="font-size: 0.7rem; color: var(--text2);">💾 Live Bytes</div>
                        <div class="stat-value" style="color: #10b981; font-size: 1.2rem;" id="tsLiveBytes">0 B</div>
                    </div>
                    <div class="detail-panel" style="border-left: 4px solid #f59e0b; text-align: center;">
                        <div style="font-size: 0.7rem; color: var(--text2);">📈 Peak Bytes</div>
                        <div class="stat-value" style="color: #f59e0b; font-size: 1.2rem;" id="tsPeakBytes">0 B</div>
                    </div>
                    <div class="detail-panel" style="border-left: 4px solid #ef4444; text-align: center;">
                        <div style="font-size: 0.7rem; color: var(--text2);">🚨 Leak Candidates</div>
                        <div class="stat-value" style="color: #ef4444; font-size: 1.2rem;" id="tsLeakCandidates">0</div>
                    </div>
                    <div class="detail-panel" style="border-left: 4px solid #8b5cf6; text-align: center;">
                        <div style="font-size: 0.7rem; color: var(--text2);">⚠️ Unsafe Boundaries</div>
                        <div class="stat-value" style="color: #8b5cf6; font-size: 1.2rem;" id="tsUnsafeCount">0</div>
                    </div>
                </div>
                <div style="margin-top: 12px; display: grid; grid-template-columns: 1fr 1fr; gap: 12px;">
                    <div class="detail-panel" style="border-left: 4px solid #3b82f6;">
                        <div class="detail-label">💾 Memory by Thread</div>
                        <div id="tsMemByThread" style="font-size: 0.8rem; margin-top: 4px;"></div>
                    </div>
                    <div class="detail-panel" style="border-left: 4px solid #10b981;">
                        <div class="detail-label">📦 Memory by Type</div>
                        <div id="tsMemByType" style="font-size: 0.8rem; margin-top: 4px;"></div>
                    </div>
                </div>
            </div>

            <div class="card">
                <div class="card-header">
                    <h2 class="card-title">🔍 Event Browser</h2>
                    <div class="flex-gap-2">
                        <input type="text" class="search-box" placeholder="🔍 Search..." id="timetravelSearch" oninput="Dashboard.timetravel.filter()" style="width: 200px;">
                        <select id="ttSortOrder" onchange="Dashboard.timetravel.sort()" class="input-sm">
                            <option value="time-asc">⏱️ Oldest First</option>
                            <option value="time-desc">⏱️ Newest First</option>
                            <option value="size-desc">📦 Largest</option>
                            <option value="lifetime-desc">⏳ Longest Life</option>
                        </select>
                        <button class="btn-sm" onclick="toggleTable('timetravelTableWrapper', this)">▼ Expand</button>
                    </div>
                </div>
                <div class="table-wrapper" id="timetravelTableWrapper" style="max-height: 400px; overflow-y: auto;">
                    <table>
                        <thead>
                            <tr>
                                <th>#</th>
                                <th>Address</th>
                                <th>Variable</th>
                                <th>Size</th>
                                <th>Provenance</th>
                                <th>Evidence</th>
                                <th>Source</th>
                                <th>Lifetime</th>
                                <th>Status</th>
                                <th>Action</th>
                            </tr>
                        </thead>
                        <tbody id="timetravelBody"></tbody>
                    </table>
                </div>
            </div>

            <div id="timetravelDetail" class="card" style="display: none; background: linear-gradient(135deg, var(--bg) 0%, var(--bg3) 100%); border: 2px solid var(--primary);">
                <div class="card-header">
                    <h3 style="color: var(--primary); margin: 0;">📍 Allocation Details: <span id="ttAddress"></span></h3>
                </div>
                <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; margin-bottom: 16px;">
                    <div class="detail-panel" style="border-left: 4px solid var(--primary);">
                        <div class="detail-label">Variable</div>
                        <div id="ttVarName" class="detail-value">-</div>
                    </div>
                    <div class="detail-panel" style="border-left: 4px solid var(--info);">
                        <div class="detail-label">Type</div>
                        <div id="ttType" class="detail-value">-</div>
                    </div>
                    <div class="detail-panel" style="border-left: 4px solid var(--warning);">
                        <div class="detail-label">Size</div>
                        <div id="ttSize" class="detail-value">-</div>
                    </div>
                    <div class="detail-panel" style="border-left: 4px solid var(--success);">
                        <div class="detail-label">Status</div>
                        <div id="ttStatus" class="detail-value">-</div>
                    </div>
                    <div class="detail-panel" style="border-left: 4px solid #8b5cf6;">
                        <div class="detail-label">Provenance</div>
                        <div id="ttProvenance" class="detail-value">-</div>
                    </div>
                </div>
                <h4 style="color: var(--text2); margin-bottom: 8px; margin-top: 12px;">📐 Type Layout</h4>
                <div id="ttLayout" style="display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: 8px; margin-bottom: 16px;"></div>
                <h4 style="color: var(--text2); margin-bottom: 12px;">🔄 Lifecycle Events</h4>
                <div class="timeline" id="ttTimeline"></div>
            </div>
        </div>

        <!-- Unsafe/FFI Mode -->
        <div id="mode-unsafe" class="mode-section">
            <div class="card" style="background: linear-gradient(135deg, #ef444410 0%, #ef444420 100%); border: 2px solid #ef444460;">
                <div class="card-header">
                    <h2 class="card-title" style="color: #ef4444;">🛡️ Memory Safety Guardian</h2>
                    <span class="badge" style="background: #ef444430; color: #ef4444;" id="unsafeBadge">0 issues</span>
                </div>
                
                <div style="background: var(--bg2); border-radius: 8px; padding: 16px; margin-bottom: 16px; border: 1px solid var(--border);">
                    <div style="display: flex; align-items: center; gap: 12px;">
                        <span style="font-size: 2rem;">🔒</span>
                        <div>
                            <div style="font-weight: 600; color: var(--primary);">Rust's Promise: Memory Safety</div>
                            <div style="color: var(--text2); font-size: 0.85rem;">
                                Track every unsafe block and FFI call. Know exactly where your code steps outside Rust's safety guarantees.
                            </div>
                        </div>
                    </div>
                </div>
                
                <!-- Safety Score Gauge -->
                <div style="display: grid; grid-template-columns: 1fr 2fr; gap: 16px; margin-bottom: 16px;">
                    <div style="background: var(--bg); border-radius: 8px; padding: 16px; text-align: center;">
                        <div style="font-size: 0.85rem; color: var(--text2); margin-bottom: 8px;">Safety Score</div>
                        <div id="safetyGauge" style="position: relative; width: 120px; height: 60px; margin: 0 auto;">
                            <svg viewBox="0 0 120 60" style="width: 100%; height: 100%;">
                                <path d="M 10 55 A 50 50 0 0 1 110 55" fill="none" stroke="#374151" stroke-width="8" stroke-linecap="round"/>
                                <path id="safetyArc" d="M 10 55 A 50 50 0 0 1 110 55" fill="none" stroke="#10b981" stroke-width="8" stroke-linecap="round" stroke-dasharray="157" stroke-dashoffset="0"/>
                            </svg>
                            <div style="position: absolute; bottom: 0; left: 50%; transform: translateX(-50%); font-size: 1.5rem; font-weight: bold;" id="safetyScoreValue">100%</div>
                        </div>
                    </div>
                    <div style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px;">
                        <div class="detail-panel" style="border-left: 4px solid #10b981; text-align: center;">
                            <div style="font-size: 0.75rem; color: var(--text2);">✅ Safe Code</div>
                            <div class="stat-value" style="color: #10b981; font-size: 1.25rem;" id="safeCodePercent">{{safe_code_percent}}%</div>
                        </div>
                        <div class="detail-panel" style="border-left: 4px solid #f59e0b; text-align: center;">
                            <div style="font-size: 0.75rem; color: var(--text2);">⚠️ Unsafe Blocks</div>
                            <div class="stat-value" style="color: #f59e0b; font-size: 1.25rem;" id="unsafeBlockCount">{{unsafe_count}}</div>
                        </div>
                        <div class="detail-panel" style="border-left: 4px solid #3b82f6; text-align: center;">
                            <div style="font-size: 0.75rem; color: var(--text2);">🔗 FFI Calls</div>
                            <div class="stat-value" style="color: #3b82f6; font-size: 1.25rem;" id="ffiCallCount">{{ffi_count}}</div>
                        </div>
                        <div class="detail-panel" style="border-left: 4px solid #ef4444; text-align: center;">
                            <div style="font-size: 0.75rem; color: var(--text2);">🚨 Critical</div>
                            <div class="stat-value" style="color: #ef4444; font-size: 1.25rem;" id="criticalIssueCount">{{high_risk_count}}</div>
                        </div>
                    </div>
                </div>
            </div>

            <!-- Risk Heatmap -->
            <div class="card">
                <div class="card-header">
                    <h2 class="card-title">🔥 Risk Heatmap</h2>
                    <div class="flex-gap-2">
                        <button class="btn-sm" onclick="Dashboard.unsafe.toggleHeatmap('risk')">⚠️ Risk Level</button>
                        <button class="btn-sm" onclick="Dashboard.unsafe.toggleHeatmap('type')">📦 By Type</button>
                    </div>
                </div>
                <div id="unsafeHeatmapContainer" style="min-height: 200px; background: var(--bg); border-radius: 8px; padding: 16px;"></div>
            </div>

            <!-- Unsafe Call-Stack View -->
            <div class="card">
                <div class="card-header">
                    <h2 class="card-title">📋 Unsafe Call-Stack View</h2>
                    <div class="flex-gap-2">
                        <span class="badge badge-danger" id="callStackGroupCount">0 groups</span>
                        <button class="btn-sm" onclick="toggleSection('callStackContainer', this)">▼ Expand</button>
                    </div>
                </div>
                <div style="background: linear-gradient(135deg, #ef444410 0%, #ef444420 100%); border-radius: 8px; padding: 12px; margin-bottom: 12px; border: 1px solid #ef444430;">
                    <div style="display: flex; align-items: center; gap: 12px;">
                        <span style="font-size: 1.5rem;">🪜</span>
                        <div style="color: var(--text2); font-size: 0.85rem;">
                            Unsafe reports grouped by source location. Each group shows the call-chain of boundary crossings and lifecycle events.
                            <a href="#" onclick="Dashboard.unsafe.renderCallStackView(); return false;" style="color: var(--primary);">Refresh</a>
                        </div>
                    </div>
                </div>
                <div id="callStackContainer" style="max-height: 450px; overflow-y: auto;"></div>
            </div>

            <!-- Enhanced FFI Boundary Flow -->
            <div class="card" style="background: linear-gradient(135deg, #8b5cf610 0%, #8b5cf620 100%); border: 2px solid #8b5cf640;">
                <div class="card-header">
                    <h2 class="card-title" style="color: #8b5cf6;">🔀 FFI Boundary Flow</h2>
                    <div class="flex-gap-2">
                        <span class="badge badge-info" id="ffiFlowCount">0 flows</span>
                        <span class="badge" style="background: #ef444430; color: #ef4444;" id="ffiMismatchCount">0 mismatches</span>
                    </div>
                </div>
                <div style="background: linear-gradient(135deg, #8b5cf610 0%, #8b5cf620 100%); border-radius: 8px; padding: 12px; margin-bottom: 12px; border: 1px solid #8b5cf640;">
                    <div style="display: flex; align-items: center; gap: 12px; flex-wrap: wrap;">
                        <span style="font-size: 1.5rem;">🛂</span>
                        <div style="color: var(--text2); font-size: 0.85rem; flex: 1;">
                            Memory crossing Rust ↔ FFI boundaries. Direction, ownership transfer, and mismatched allocation/deallocation sides are highlighted.
                        </div>
                        <button class="btn-sm" onclick="Dashboard.unsafe.renderFfiBoundaryFlow()">🔄 Refresh</button>
                    </div>
                </div>
                <div id="ffiBoundaryFlowContainer" style="min-height: 120px; background: var(--bg); border-radius: 8px; padding: 16px;"></div>
                <div style="margin-top: 12px; display: flex; gap: 8px; flex-wrap: wrap; padding: 8px 0;" id="ffiFlowLegend"></div>
            </div>
            <div class="card">
                <div class="card-header">
                    <h2 class="card-title">📊 Risk Distribution</h2>
                </div>
                <div class="chart-container" style="height: 200px;">
                    <canvas id="unsafeChart"></canvas>
                </div>
            </div>

            <div class="card">
                <div class="card-header">
                    <h2 class="card-title">🎯 Risk Assessment</h2>
                </div>
                <div class="chart-container small">
                    <canvas id="unsafeRiskChart"></canvas>
                </div>
            </div>

            <div class="card">
                <div class="card-header">
                    <h2 class="card-title">🔄 Memory Lifecycle Flow</h2>
                    <span class="badge badge-info" id="lifecycleFlowCount">0 flows</span>
                </div>
                <div id="lifecycleFlowContainer" style="display: flex; flex-direction: column; gap: 12px;"></div>
            </div>

            <div class="card">
                <div class="card-header">
                    <h2 class="card-title">🚨 High Risk Operations</h2>
                    <button class="btn-sm" onclick="toggleSection('unsafeReportsContainer', this)">▼ Expand</button>
                </div>
                <div id="unsafeReportsContainer" style="max-height: 400px; overflow-y: auto;"></div>
            </div>

            <div class="card">
                <div class="card-header">
                    <h2 class="card-title">📋 FFI Boundary Events</h2>
                    <button class="btn-sm" onclick="toggleTable('ffiTableWrapper', this)">▼ Expand</button>
                </div>
                <div class="table-wrapper" id="ffiTableWrapper" style="max-height: 300px; overflow-y: auto;">
                    <table>
                        <thead>
                            <tr>
                                <th>Type</th>
                                <th>From</th>
                                <th>To</th>
                                <th>Timestamp</th>
                                <th>Direction</th>
                            </tr>
                        </thead>
                        <tbody id="ffiTableBody"></tbody>
                    </table>
                </div>
            </div>
        </div>
    </div>

    <div class="tooltip" id="tooltip" style="display: none;"></div>

    <script>
        const data = {{{json_data}}};

        const DataIndex = (function() {
            const idx = data.data_index || {};
            const allocs = data.allocations || [];
            const eventSummary = data.event_summary || {};
            const sampling = data.sampling || {};
            return {
                allocationByPtr(ptr) {
                    const indices = idx.ptr_to_allocation && idx.ptr_to_allocation[ptr];
                    return indices && indices.length > 0 ? allocs[indices[0]] : null;
                },
                allocationsByThread(threadId) {
                    const indices = idx.thread_id_to_allocations && idx.thread_id_to_allocations[String(threadId)];
                    return indices ? indices.map(i => allocs[i]) : [];
                },
                allocationsByType(typeName) {
                    const indices = idx.type_name_to_allocations && idx.type_name_to_allocations[typeName];
                    return indices ? indices.map(i => allocs[i]) : [];
                },
                allocationsByVarName(varName) {
                    const indices = idx.var_name_to_allocations && idx.var_name_to_allocations[varName];
                    return indices ? indices.map(i => allocs[i]) : [];
                },
                allocationsBySource(file, line) {
                    const key = file + ':' + line;
                    const indices = idx.source_location_to_allocations && idx.source_location_to_allocations[key];
                    return indices ? indices.map(i => allocs[i]) : [];
                },
                eventsByThread(threadId) {
                    const indices = idx.thread_id_to_events && idx.thread_id_to_events[String(threadId)];
                    return indices ? indices.map(i => (data.events || [])[i]) : [];
                },
                passportByPtr(ptr) {
                    const indices = idx.allocation_ptr_to_passport && idx.allocation_ptr_to_passport[ptr];
                    return indices && indices.length > 0 ? indices.map(i => (data.passport_details || [])[i]) : [];
                },
                unsafeReportsByPtr(ptr) {
                    const indices = idx.allocation_ptr_to_unsafe_reports && idx.allocation_ptr_to_unsafe_reports[ptr];
                    return indices && indices.length > 0 ? indices.map(i => (data.unsafe_reports || [])[i]) : [];
                }
            };
        })();

        const Utils = {
            formatBytes(bytes) {
                if (!bytes || bytes === 0) return '0 B';
                const k = 1024;
                const sizes = ['B', 'KB', 'MB', 'GB'];
                const i = Math.floor(Math.log(bytes) / Math.log(k));
                return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
            },
            formatTime(ns) {
                if (!ns || ns < 1000) return ns + ' ns';
                if (ns < 1000000) return (ns / 1000).toFixed(2) + ' μs';
                if (ns < 1000000000) return (ns / 1000000).toFixed(2) + ' ms';
                return (ns / 1000000000).toFixed(2) + ' s';
            },
            formatThreadId(tid) {
                if (!tid) return 'N/A';
                const str = String(tid);
                const match = str.match(/(\d+)/);
                return match ? 'T' + match[1].slice(-4) : str.slice(-6);
            },
            getChartColors() {
                const isDark = document.documentElement.getAttribute('data-theme') === 'dark';
                return {
                    primary: isDark ? '#60a5fa' : '#2563eb',
                    text: isDark ? '#f1f5f9' : '#1f2937',
                    text2: isDark ? '#94a3b8' : '#6b7280',
                    grid: isDark ? '#334155' : '#e5e7eb'
                };
            }
        };

        const Dashboard = {
            charts: {
                instances: {},
                init() {
                    Object.values(this.instances).forEach(chart => chart?.destroy?.());
                    this.instances = {};
                    const colors = Utils.getChartColors();
                    const allocations = data.allocations || [];
                    const threads = data.threads || [];
                    const passports = data.passport_details || [];
                    const unsafeReports = data.unsafe_reports || [];
                    const memoryCtx = document.getElementById('memoryChart');
                    if (memoryCtx) {
                        this.instances.memory = new Chart(memoryCtx, { type: 'bar', data: { labels: ['Active', 'Freed', 'Leaked'], datasets: [{ data: [data.active_allocations || 0, (data.total_allocations || 0) - (data.active_allocations || 0), data.leak_count || 0], backgroundColor: [colors.primary, '#10b981', '#ef4444'] }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } }, scales: { y: { grid: { color: colors.grid }, ticks: { color: colors.text2 } }, x: { grid: { display: false }, ticks: { color: colors.text2 } } } } });
                    }
                    const typeCtx = document.getElementById('typeChart');
                    if (typeCtx) {
                        const typeCount = {};
                        allocations.forEach(a => { const type = a.type_name || 'Unknown'; typeCount[type] = (typeCount[type] || 0) + 1; });
                        this.instances.type = new Chart(typeCtx, { type: 'doughnut', data: { labels: Object.keys(typeCount), datasets: [{ data: Object.values(typeCount), backgroundColor: Object.keys(typeCount).map((_, i) => `hsl(${i * 360 / Math.max(Object.keys(typeCount).length, 1)}, 70%, 50%)`) }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'right', labels: { color: colors.text } } } } });
                    }
                    const threadCtx = document.getElementById('threadChart');
                    if (threadCtx) {
                        const shortThreadIds = threads.map(t => {
                            const tid = t.thread_id || '?';
                            const match = String(tid).match(/(\d+)/);
                            return match ? 'T' + match[1].slice(-4) : String(tid).slice(-6);
                        });
                        this.instances.thread = new Chart(threadCtx, { type: 'bar', data: { labels: shortThreadIds, datasets: [{ label: 'Allocations', data: threads.map(t => parseInt(t.allocation_count) || 0), backgroundColor: colors.primary }] }, options: { responsive: true, maintainAspectRatio: false, indexAxis: 'y', scales: { y: { grid: { display: false }, ticks: { color: colors.text2 } }, x: { grid: { color: colors.grid }, ticks: { color: colors.text2 } } } } });
                    }
                    const asyncCtx = document.getElementById('asyncChart');
                    if (asyncCtx) {
                        const asyncTasks = data.async_tasks || [];
                        const asyncSummary = data.async_summary || {};
                        if (asyncTasks.length > 0) {
                            const sortedTasks = [...asyncTasks].sort((a, b) => a.task_id - b.task_id);
                            const timeLabels = sortedTasks.map((t, i) => `T+${i * 100}ms`);
                            const cpuUsage = sortedTasks.map(t => Math.min(100, (t.total_allocations || 0) * 5));
                            const memoryUsage = sortedTasks.map(t => Math.min(100, ((t.peak_memory || 0) / 1024 / 1024) * 2));
                            const ioUsage = sortedTasks.map(t => Math.min(100, ((t.total_bytes || 0) / 1024) * 0.5));
                            this.instances.async = new Chart(asyncCtx, {
                                type: 'line',
                                data: {
                                    labels: timeLabels,
                                    datasets: [
                                        { label: 'CPU Usage %', data: cpuUsage, borderColor: '#3b82f6', backgroundColor: '#3b82f633', fill: false, tension: 0.4 },
                                        { label: 'Memory %', data: memoryUsage, borderColor: '#10b981', backgroundColor: '#10b98133', fill: false, tension: 0.4 },
                                        { label: 'IO Usage %', data: ioUsage, borderColor: '#f59e0b', backgroundColor: '#f59e0b33', fill: false, tension: 0.4 }
                                    ]
                                },
                                options: {
                                    responsive: true,
                                    maintainAspectRatio: false,
                                    interaction: { mode: 'index', intersect: false },
                                    plugins: { legend: { display: true, position: 'top', labels: { color: colors.text } }, title: { display: true, text: `Resource Consumption Over Time | Tasks: ${asyncSummary.total_tasks || 0}`, color: colors.text2 } },
                                    scales: { y: { min: 0, max: 100, grid: { color: colors.grid }, ticks: { color: colors.text2, callback: (v) => v + '%' }, title: { display: true, text: 'Resource Usage %', color: colors.text2 } }, x: { grid: { display: false }, ticks: { color: colors.text2 }, title: { display: true, text: 'Time', color: colors.text2 } } }
                                }
                            });
                        } else {
                            const allocData = allocations.slice(0, 15).map((a, i) => ({ label: a.var_name || a.type_name || `Alloc ${i}`, value: parseInt(a.size) || 0 }));
                            this.instances.async = new Chart(asyncCtx, { type: 'line', data: { labels: allocData.map(d => d.label), datasets: [{ label: 'Memory', data: allocData.map(d => d.value), borderColor: colors.primary, backgroundColor: colors.primary + '33', fill: true, tension: 0.4 }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } }, scales: { y: { grid: { color: colors.grid }, ticks: { color: colors.text2 } }, x: { grid: { display: false }, ticks: { color: colors.text2, maxRotation: 45 } } } } });
                        }
                    }
                    const unsafeCtx = document.getElementById('unsafeChart');
                    if (unsafeCtx) {
                        const riskCount = { high: 0, medium: 0, low: 0 };
                        unsafeReports.forEach(r => { const level = r.risk_level || 'low'; riskCount[level] = (riskCount[level] || 0) + 1; });
                        this.instances.unsafe = new Chart(unsafeCtx, { type: 'bar', data: { labels: ['High Risk', 'Medium Risk', 'Low Risk'], datasets: [{ data: [riskCount.high, riskCount.medium, riskCount.low], backgroundColor: ['#ef4444', '#f59e0b', '#10b981'] }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } }, scales: { y: { grid: { color: colors.grid }, ticks: { color: colors.text2 } }, x: { grid: { display: false }, ticks: { color: colors.text2 } } } } });
                    }
                    const unsafeRiskCtx = document.getElementById('unsafeRiskChart');
                    if (unsafeRiskCtx) {
                        const ffiCount = unsafeReports.filter(r => r.ffi_tracked).length;
                        this.instances.unsafeRisk = new Chart(unsafeRiskCtx, { type: 'doughnut', data: { labels: ['FFI Tracked', 'Native'], datasets: [{ data: [ffiCount, unsafeReports.length - ffiCount], backgroundColor: [colors.primary, '#10b981'] }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'bottom', labels: { color: colors.text } } } } });
                    }
                    
                    // Time Travel Timeline Chart
                    const ttTimelineCtx = document.getElementById('ttTimelineChart');
                    if (ttTimelineCtx && allocations.length > 0) {
                        // Sort allocations by timestamp
                        const sortedAllocs = [...allocations].sort((a, b) => (a.timestamp_alloc || 0) - (b.timestamp_alloc || 0));
                        
                        // Group by time intervals (every 10% of time range)
                        const minTime = Math.min(...sortedAllocs.map(a => (a.timestamp_alloc || 0) / 1000000));
                        const maxTime = Math.max(...sortedAllocs.map(a => ((a.timestamp_alloc || 0) / 1000000) + (a.lifetime_ms || 0)));
                        const timeRange = maxTime - minTime || 1;
                        const interval = timeRange / 10;
                        
                        const timeLabels = [];
                        const allocCounts = [];
                        const memoryBytes = [];
                        const freedCounts = [];
                        
                        for (let i = 0; i <= 10; i++) {
                            const t = minTime + i * interval;
                            timeLabels.push(`T+${Math.round(t - minTime)}ms`);
                            
                            let count = 0;
                            let mem = 0;
                            let freed = 0;
                            
                            sortedAllocs.forEach(a => {
                                const allocMs = (a.timestamp_alloc || 0) / 1000000;
                                const deallocMs = (a.timestamp_dealloc || 0) / 1000000;
                                
                                if (allocMs <= t) {
                                    count++;
                                    mem += parseInt(a.size) || 0;
                                }
                                if (deallocMs > 0 && deallocMs <= t) {
                                    freed++;
                                }
                            });
                            
                            allocCounts.push(count);
                            memoryBytes.push(mem / 1024); // KB
                            freedCounts.push(freed);
                        }
                        
                        this.instances.ttTimeline = new Chart(ttTimelineCtx, {
                            type: 'line',
                            data: {
                                labels: timeLabels,
                                datasets: [
                                    {
                                        label: 'Active Allocations',
                                        data: allocCounts.map((a, i) => a - freedCounts[i]),
                                        borderColor: '#3b82f6',
                                        backgroundColor: '#3b82f633',
                                        fill: false,
                                        tension: 0.4,
                                        yAxisID: 'y'
                                    },
                                    {
                                        label: 'Memory (KB)',
                                        data: memoryBytes,
                                        borderColor: '#10b981',
                                        backgroundColor: '#10b98133',
                                        fill: false,
                                        tension: 0.4,
                                        yAxisID: 'y1'
                                    }
                                ]
                            },
                            options: {
                                responsive: true,
                                maintainAspectRatio: false,
                                interaction: { mode: 'index', intersect: false },
                                plugins: {
                                    legend: { display: true, position: 'top', labels: { color: colors.text } },
                                    title: { display: true, text: 'Memory Timeline', color: colors.text2 }
                                },
                                scales: {
                                    y: {
                                        type: 'linear',
                                        display: true,
                                        position: 'left',
                                        grid: { color: colors.grid },
                                        ticks: { color: colors.text2 },
                                        title: { display: true, text: 'Allocations', color: colors.text2 }
                                    },
                                    y1: {
                                        type: 'linear',
                                        display: true,
                                        position: 'right',
                                        grid: { drawOnChartArea: false },
                                        ticks: { color: colors.text2 },
                                        title: { display: true, text: 'Memory (KB)', color: colors.text2 }
                                    },
                                    x: {
                                        grid: { display: false },
                                        ticks: { color: colors.text2 }
                                    }
                                }
                            }
                        });
                    }
                }
            },

            diagnosis: {
                init() {
                    const container = document.getElementById('diagnosisContent');
                    if (!container) return;
                    
                    const diagnoses = [];
                    const allocations = data.allocations || [];
                    const passports = data.passport_details || [];
                    const unsafeReports = data.unsafe_reports || [];
                    const relationships = data.relationships || [];
                    
                    const leakCount = data.leak_count || 0;
                    const unsafeCount = unsafeReports.length;
                    const ffiCount = passports.filter(p => p.ffi_tracked).length;
                    const highRiskCount = unsafeReports.filter(r => r.risk_level === 'high').length;
                    
                    if (leakCount > 0) {
                        diagnoses.push({
                            level: 'critical',
                            icon: '🔴',
                            title: 'Memory Leak Detected',
                            desc: `${leakCount} allocation${leakCount > 1 ? 's' : ''} not freed - potential memory leak`
                        });
                    }
                    
                    const cycleCount = relationships.filter(r => r.is_part_of_cycle).length;
                    if (cycleCount > 0) {
                        diagnoses.push({
                            level: 'critical',
                            icon: '🔄',
                            title: 'Reference Cycle Detected',
                            desc: `${cycleCount} circular reference${cycleCount > 1 ? 's' : ''} found - may cause memory leak`
                        });
                    }
                    
                    if (highRiskCount > 0) {
                        diagnoses.push({
                            level: 'warning',
                            icon: '⚠️',
                            title: 'High Risk Unsafe Operations',
                            desc: `${highRiskCount} high-risk unsafe operation${highRiskCount > 1 ? 's' : ''} detected`
                        });
                    }
                    
                    const ffiPassports = passports.filter(p => p.ffi_tracked).length;
                    if (ffiPassports > 0) {
                        const leakedFfi = passports.filter(p => p.ffi_tracked && p.is_leaked).length;
                        if (leakedFfi > 0) {
                            diagnoses.push({
                                level: 'warning',
                                icon: '🔗',
                                title: 'FFI Memory Escape',
                                desc: `${leakedFfi} allocation${leakedFfi > 1 ? 's' : ''} leaked across FFI boundary`
                            });
                        } else {
                            diagnoses.push({
                                level: 'info',
                                icon: '🔗',
                                title: 'FFI Boundary Tracking Active',
                                desc: `${ffiPassports} allocation${ffiPassports > 1 ? 's' : ''} tracked across FFI boundaries`
                            });
                        }
                    }
                    
                    const largeAllocs = allocations.filter(a => (parseInt(a.size) || 0) > 1024 * 1024);
                    if (largeAllocs.length > 0) {
                        diagnoses.push({
                            level: 'warning',
                            icon: '📦',
                            title: 'Large Allocations Found',
                            desc: `${largeAllocs.length} allocation${largeAllocs.length > 1 ? 's' : ''} larger than 1MB detected`
                        });
                    }
                    
                    const healthScore = data.health_score || 0;
                    if (diagnoses.length === 0) {
                        if (healthScore >= 80) {
                            diagnoses.push({
                                level: 'success',
                                icon: '✅',
                                title: 'Memory Health Excellent',
                                desc: `Health score: ${healthScore} - No critical issues detected`
                            });
                        } else {
                            diagnoses.push({
                                level: 'info',
                                icon: '✅',
                                title: 'No Critical Issues',
                                desc: `Health score: ${healthScore} - Memory tracking looks healthy`
                            });
                        }
                    }
                    
                    const badge = document.getElementById('diagnosisBadge');
                    const criticalCount = diagnoses.filter(d => d.level === 'critical').length;
                    if (criticalCount > 0) {
                        badge.textContent = `${criticalCount} Critical Issue${criticalCount > 1 ? 's' : ''}`;
                        badge.style.background = 'var(--danger)';
                    } else {
                        const warningCount = diagnoses.filter(d => d.level === 'warning').length;
                        if (warningCount > 0) {
                            badge.textContent = `${warningCount} Warning${warningCount > 1 ? 's' : ''}`;
                            badge.style.background = 'var(--warning)';
                        } else {
                            badge.textContent = 'All Clear';
                            badge.style.background = 'var(--success)';
                        }
                    }
                    
                    container.innerHTML = diagnoses.map(d => `
                        <div class="diagnosis-item ${d.level}">
                            <span class="diagnosis-icon">${d.icon}</span>
                            <div class="diagnosis-content">
                                <div class="diagnosis-title">${d.title}</div>
                                <div class="diagnosis-desc">${d.desc}</div>
                            </div>
                        </div>
                    `).join('');
                }
            },

            variableGraph: {
                simulation: null,
                svg: null,
                zoom: null,
                g: null,
                highlightMode: false,
                init() {
                    const container = document.getElementById('relationshipGraph');
                    if (!container) return;
                    container.innerHTML = '';
                    const relationships = data.relationships || [];
                    if (relationships.length === 0) { container.innerHTML = '<div class="empty-state">No relationship data</div>'; return; }
                    document.getElementById('relationshipCount').textContent = relationships.length + ' relationships';
                    const width = container.clientWidth;
                    const height = container.clientHeight || 400;
                    this.svg = d3.select(container).append('svg').attr('width', '100%').attr('height', height).attr('viewBox', [0, 0, width, height]);
                    this.zoom = d3.zoom().on('zoom', (event) => { this.g.attr('transform', event.transform); });
                    this.svg.call(this.zoom);
                    this.g = this.svg.append('g');
                    this.svg.append('defs').append('marker').attr('id', 'arrowhead').attr('viewBox', '-0 -5 10 10').attr('refX', 20).attr('refY', 0).attr('orient', 'auto').attr('markerWidth', 6).attr('markerHeight', 6).append('path').attr('d', 'M 0,-5 L 10,0 L 0,5').attr('fill', '#64748b');
                    function inferOwnershipModel(node) {
                        const tn = (node.typeName || '').toLowerCase();
                        if (node.isContainer) return 'Container';
                        if (tn.includes('arc<')) return 'Arc';
                        if (tn.includes('rc<')) return 'Rc';
                        if (tn.includes('box<')) return 'Box';
                        if (tn.includes('*const ') || tn.includes('*mut ') || tn.includes('raw') || tn.includes('ptr')) return 'RawPointer';
                        const alloc = DataIndex.allocationByPtr(node.ptr);
                        if (alloc && alloc.is_leaked) return 'Leaked';
                        return 'HeapOwner';
                    }
                    const ownerColorMap = { Container: '#f59e0b', Box: '#3b82f6', Rc: '#8b5cf6', Arc: '#8b5cf6', RawPointer: '#ef4444', Leaked: '#dc2626', HeapOwner: '#10b981' };
                    const ownerLabels = { Container: 'Container', Box: 'Box Owner', Rc: 'Rc Shared', Arc: 'Arc Shared', RawPointer: 'Raw Ptr', Leaked: '🚨 LEAKED', HeapOwner: 'Heap Owner' };
                    const nodeMap = new Map();
                    relationships.forEach((r) => {
                        const sourceId = r.source_ptr;
                        const targetId = r.target_ptr;
                        const isSourceContainer = r.is_container_source || false;
                        const isTargetContainer = r.is_container_target || false;
                        if (!nodeMap.has(sourceId)) nodeMap.set(sourceId, { id: sourceId, ptr: sourceId, name: r.source_var_name || ('Ptr ' + sourceId.slice(-6)), type: r.relationship_type, typeName: r.type_name || 'unknown', isSource: true, hasCycle: false, inDegree: 0, outDegree: 0, isContainer: isSourceContainer });
                        if (!nodeMap.has(targetId)) nodeMap.set(targetId, { id: targetId, ptr: targetId, name: r.target_var_name || ('Ptr ' + targetId.slice(-6)), type: r.relationship_type, typeName: r.type_name || 'unknown', isSource: false, hasCycle: false, inDegree: 0, outDegree: 0, isContainer: isTargetContainer });
                    });
                    const nodes = Array.from(nodeMap.values());
                    nodes.forEach(n => { n.ownerModel = inferOwnershipModel(n); n.ownerColor = ownerColorMap[n.ownerModel] || '#64748b'; });
                    const nodeIds = new Set(nodes.map(n => n.id));
                    const links = relationships.map(r => ({ source: r.source_ptr, target: r.target_ptr, type: r.relationship_type || 'reference', strength: r.strength || 0.5, typeName: r.type_name || 'unknown', isPartOfCycle: r.is_part_of_cycle || false })).filter(l => nodeIds.has(l.source) && nodeIds.has(l.target));
                    links.forEach(l => { 
                        if (l.isPartOfCycle) { 
                            const srcNode = nodeMap.get(l.source.id || l.source); 
                            const tgtNode = nodeMap.get(l.target.id || l.target); 
                            if (srcNode) srcNode.hasCycle = true; 
                            if (tgtNode) tgtNode.hasCycle = true; 
                        }
                        const srcId = l.source.id || l.source;
                        const tgtId = l.target.id || l.target;
                        const srcNode = nodeMap.get(srcId);
                        const tgtNode = nodeMap.get(tgtId);
                        if (srcNode) srcNode.outDegree++;
                        if (tgtNode) tgtNode.inDegree++;
                    });
                    const colorMap = { clone: '#10b981', ownership_transfer: '#dc2626', immutable_borrow: '#3b82f6', mutable_borrow: '#f59e0b', Arc: '#8b5cf6', Rc: '#8b5cf6', reference: '#64748b', evolution: '#06b6d4', contains: '#f59e0b' };
                    const typeLabels = { clone: 'Clone', ownership_transfer: 'Ownership Transfer', immutable_borrow: 'Immutable Borrow (&)', mutable_borrow: 'Mutable Borrow (&mut)', Arc: 'Arc Smart Pointer', Rc: 'Rc Smart Pointer', reference: 'Reference', evolution: 'Variable Evolution', contains: 'Contains' };
                    const getColor = (type) => colorMap[type] || '#64748b';
                    const getTypeLabel = (type) => typeLabels[type] || type;
                    const getLinkColor = (d) => d.isPartOfCycle ? '#ef4444' : getColor(d.type);
                    this.simulation = d3.forceSimulation(nodes).force('link', d3.forceLink(links).id(d => d.id).distance(100)).force('charge', d3.forceManyBody().strength(-300)).force('center', d3.forceCenter(width / 2, height / 2)).force('collision', d3.forceCollide().radius(35));
                    const link = this.g.append('g').selectAll('line').data(links).join('line').attr('class', 'link').attr('stroke', getLinkColor).attr('stroke-width', d => Math.max(1.5, d.strength * 3)).attr('stroke-dasharray', d => d.isPartOfCycle ? '6,3' : null).attr('marker-end', 'url(#arrowhead)');
                    const node = this.g.append('g').selectAll('g').data(nodes).join('g').attr('class', 'node').call(d3.drag().on('start', (event) => { if (!event.active) this.simulation.alphaTarget(0.3).restart(); event.subject.fx = event.subject.x || 0; event.subject.fy = event.subject.y || 0; }).on('drag', (event) => { event.subject.fx = event.x || 0; event.subject.fy = event.y || 0; }).on('end', (event) => { if (!event.active) this.simulation.alphaTarget(0); event.subject.fx = null; event.subject.fy = null; }));
                    node.append('circle').attr('r', d => d.hasCycle ? 18 : 14).attr('fill', d => d.ownerColor).attr('stroke', d => d.hasCycle ? '#ef4444' : 'none').attr('stroke-width', 3);
                    node.append('text').attr('dx', 20).attr('dy', 4).text(d => { const name = d.name || 'unknown'; return name.length > 14 ? name.substring(0, 12) + '..' : name; }).style('font-size', '12px').style('fill', 'var(--text)').style('font-weight', '500');
                    const tooltip = document.getElementById('tooltip');
                    node.on('mouseover', (event, d) => { 
                        tooltip.style.display = 'block'; 
                        const totalConnections = d.inDegree + d.outDegree;
                        tooltip.innerHTML = `<div style="min-width: 180px;">
                            <div style="font-weight: 600; font-size: 14px; margin-bottom: 8px; color: var(--primary);">${d.name || 'Unknown'}</div>
                            <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 4px; font-size: 12px;">
                                <div style="color: var(--text2);">Type:</div><div>${d.typeName || 'unknown'}</div>
                                <div style="color: var(--text2);">Ownership:</div><div style="color: ${d.ownerColor};">${ownerLabels[d.ownerModel] || d.ownerModel}</div>
                                <div style="color: var(--text2);">Relation:</div><div style="color: ${getColor(d.type)};">${getTypeLabel(d.type)}</div>
                                <div style="color: var(--text2);">Incoming:</div><div>${d.inDegree} links</div>
                                <div style="color: var(--text2);">Outgoing:</div><div>${d.outDegree} links</div>
                                <div style="color: var(--text2);">Total:</div><div>${totalConnections} connections</div>
                            </div>
                            ${d.hasCycle ? '<div style="margin-top: 8px; color: #ef4444; font-size: 11px;">⚠️ Part of reference cycle</div>' : ''}
                        </div>`; 
                        tooltip.style.left = (event.pageX + 15) + 'px'; 
                        tooltip.style.top = (event.pageY - 10) + 'px'; 
                    }).on('mouseout', () => { tooltip.style.display = 'none'; });
                    this.simulation.on('tick', () => { link.attr('x1', d => d.source?.x || 0).attr('y1', d => d.source?.y || 0).attr('x2', d => d.target?.x || 0).attr('y2', d => d.target?.y || 0); node.attr('transform', d => `translate(${d.x || 0},${d.y || 0})`); });
                },
                resetZoom() { if (this.svg && this.zoom) this.svg.transition().duration(500).call(this.zoom.transform, d3.zoomIdentity); },
                highlightCycles() { this.highlightMode = !this.highlightMode; if (!this.g) return; this.g.selectAll('.node circle').attr('stroke-width', d => this.highlightMode && d.hasCycle ? 5 : d.hasCycle ? 3 : 0); this.g.selectAll('.link').attr('stroke-width', d => this.highlightMode && d.isPartOfCycle ? 4 : Math.max(1.5, d.strength * 3)); },
                selectNode(ptr) {
                    document.querySelectorAll('.mode-section').forEach(el => el.classList.remove('active'));
                    document.querySelectorAll('.mode-tab').forEach(el => el.classList.remove('active'));
                    document.getElementById('mode-variable').classList.add('active');
                    const tab = document.querySelector('.mode-tab[onclick*="\'variable\'"]');
                    if (tab) tab.classList.add('active');
                    setTimeout(() => Dashboard.variableGraph.init(), 100);
                    setTimeout(() => {
                        if (this.g) this.g.selectAll('.node circle').attr('stroke', d => d.id === ptr ? '#f59e0b' : d.hasCycle ? '#ef4444' : 'none').attr('stroke-width', d => d.id === ptr ? 6 : d.hasCycle ? 3 : 0);
                    }, 400);
                }
            },

            allocations: {
                showAll: false,
                currentPage: 1,
                pageSize: 100,
                filteredData: [],
                toggle() {
                    const wrapper = document.getElementById('allocationsWrapper');
                    const btn = document.getElementById('toggleAllocBtn');
                    const pagination = document.getElementById('allocationsPagination');
                    this.showAll = !this.showAll;
                    if (this.showAll) {
                        wrapper.style.maxHeight = 'none';
                        wrapper.style.overflow = 'visible';
                        btn.textContent = '▲ Collapse';
                        if (this.filteredData.length > this.pageSize) pagination.style.display = 'flex';
                    } else {
                        wrapper.style.maxHeight = '400px';
                        wrapper.style.overflow = 'auto';
                        btn.textContent = '▼ Expand';
                        pagination.style.display = 'none';
                        this.currentPage = 1;
                    }
                    this.render();
                },
                sort() {
                    const sortEl = document.getElementById('allocSortOrder');
                    if (!sortEl) return;
                    const sortBy = sortEl.value;
                    this.filteredData.sort((a, b) => {
                        switch (sortBy) {
                            case 'size-desc':
                                return (b.size || 0) - (a.size || 0);
                            case 'size-asc':
                                return (a.size || 0) - (b.size || 0);
                            case 'time-desc':
                                return (b.timestamp || 0) - (a.timestamp || 0);
                            case 'time-asc':
                                return (a.timestamp || 0) - (b.timestamp || 0);
                            case 'type':
                                return (a.type_name || '').localeCompare(b.type_name || '');
                            case 'var':
                                return (a.var_name || '').localeCompare(b.var_name || '');
                            default:
                                return 0;
                        }
                    });
                    this.renderPage(this.currentPage);
                },
                renderPage(page) {
                    const start = (page - 1) * this.pageSize;
                    const end = start + this.pageSize;
                    const pageData = this.filteredData.slice(start, end);
                    const tbody = document.getElementById('allocationsBody');
                    tbody.innerHTML = pageData.map(a =>
                        `<tr><td><a href="#" onclick="Dashboard.timetravel.showDetail('${a.address}'); return false;">${a.address}</a></td><td>${Utils.formatBytes(parseInt(a.size) || 0)}</td><td>${a.var_name || 'unknown'}</td><td>${a.type_name || 'Unknown'}</td><td><span class="badge ${a.is_leaked ? 'badge-danger' : 'badge-success'}">${a.is_leaked ? 'Leaked' : 'Normal'}</span></td><td>${Utils.formatTime(parseInt(a.timestamp) || 0)}</td></tr>`
                    ).join('');
                    const totalPages = Math.ceil(this.filteredData.length / this.pageSize);
                    document.getElementById('allocPageInfo').textContent = `Page ${page} of ${totalPages} (${this.filteredData.length} total)`;
                    document.getElementById('allocPrevBtn').disabled = page <= 1;
                    document.getElementById('allocNextBtn').disabled = page >= totalPages;
                },
                prevPage() {
                    if (this.currentPage > 1) this.renderPage(this.currentPage - 1);
                },
                nextPage() {
                    const totalPages = Math.ceil(this.filteredData.length / this.pageSize);
                    if (this.currentPage < totalPages) this.renderPage(this.currentPage + 1);
                },
                render() {
                    this.renderPage(this.currentPage);
                },
                init() {
                    const allocations = data.allocations || [];
                    this.filteredData = [...allocations];
                    if (allocations.length === 0) {
                        document.getElementById('allocationsBody').innerHTML = '<tr><td colspan="6" class="empty-state">No allocation data</td></tr>';
                        return;
                    }
                    this.sort();
                }
            },

            threads: {
                filteredData: [],
                heatmapType: 'memory',
                sort() {
                    const sortEl = document.getElementById('threadSortOrder');
                    if (!sortEl) return;
                    const sortBy = sortEl.value;
                    this.filteredData.sort((a, b) => {
                        switch (sortBy) {
                            case 'alloc-desc':
                                return (b.allocation_count || 0) - (a.allocation_count || 0);
                            case 'alloc-asc':
                                return (a.allocation_count || 0) - (b.allocation_count || 0);
                            case 'memory-desc':
                                return (b.current_memory_bytes || 0) - (a.current_memory_bytes || 0);
                            case 'memory-asc':
                                return (a.current_memory_bytes || 0) - (b.current_memory_bytes || 0);
                            case 'thread-id':
                                return (a.thread_id || '0').localeCompare(b.thread_id || '0');
                            default:
                                return 0;
                        }
                    });
                    this.render();
                },
                render() { 
                    const tbody = document.getElementById('threadTableBody'); 
                    tbody.innerHTML = this.filteredData.map(t => `<tr><td><code title="${t.thread_id || ''}">${Utils.formatThreadId(t.thread_id)}</code></td><td><span class="text-secondary">${t.thread_summary || ''}</span></td><td>${t.allocation_count || 0}</td><td>${t.current_memory || '0 B'}</td><td>${t.peak_memory || '0 B'}</td><td><span class="badge badge-success">active</span></td></tr>`).join(''); 
                    this.updateStats();
                    this.renderHeatmap();
                    this.renderTimeline();
                },
                updateStats() {
                    const threads = this.filteredData;
                    const activeCount = threads.length;
                    const totalMemory = threads.reduce((sum, t) => sum + (t.current_memory_bytes || 0), 0);
                    const peakMemory = threads.reduce((sum, t) => sum + (t.peak_memory_bytes || 0), 0);
                    const totalAllocs = threads.reduce((sum, t) => sum + (t.allocation_count || 0), 0);
                    
                    const activeEl = document.getElementById('activeThreadCount');
                    const totalMemEl = document.getElementById('totalThreadMemory');
                    const peakMemEl = document.getElementById('peakThreadMemory');
                    const totalAllocsEl = document.getElementById('totalThreadAllocs');
                    
                    if (activeEl) activeEl.textContent = activeCount;
                    if (totalMemEl) totalMemEl.textContent = Utils.formatBytes(totalMemory);
                    if (peakMemEl) peakMemEl.textContent = Utils.formatBytes(peakMemory);
                    if (totalAllocsEl) totalAllocsEl.textContent = totalAllocs.toLocaleString();
                },
                toggleHeatmap(type) {
                    this.heatmapType = type;
                    this.renderHeatmap();
                },
                renderHeatmap() {
                    const container = document.getElementById('threadHeatmapContainer');
                    if (!container || this.filteredData.length === 0) {
                        if (container) container.innerHTML = '<div class="empty-state">No thread data for heatmap</div>';
                        return;
                    }
                    
                    const maxVal = Math.max(...this.filteredData.map(t => 
                        this.heatmapType === 'memory' ? (t.current_memory_bytes || 0) : (t.allocation_count || 0)
                    ));
                    
                    container.innerHTML = `
                        <div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(80px, 1fr)); gap: 8px;">
                            ${this.filteredData.map(t => {
                                const val = this.heatmapType === 'memory' ? (t.current_memory_bytes || 0) : (t.allocation_count || 0);
                                const intensity = maxVal > 0 ? val / maxVal : 0;
                                const hue = 120 - (intensity * 120);
                                const color = `hsl(${hue}, 70%, ${50 + intensity * 20}%)`;
                                const tid = Utils.formatThreadId(t.thread_id);
                                return `
                                    <div style="background: ${color}; border-radius: 8px; padding: 12px; text-align: center; cursor: pointer;" title="${tid}: ${this.heatmapType === 'memory' ? Utils.formatBytes(val) : val + ' allocs'}">
                                        <div style="font-weight: 600; font-size: 0.85rem;">${tid}</div>
                                        <div style="font-size: 0.75rem; opacity: 0.9;">${this.heatmapType === 'memory' ? Utils.formatBytes(val) : val}</div>
                                    </div>
                                `;
                            }).join('')}
                        </div>
                    `;
                },
                renderTimeline() {
                    const container = document.getElementById('threadTimelineContainer');
                    if (!container || this.filteredData.length === 0) {
                        if (container) container.innerHTML = '<div class="empty-state">No thread timeline data</div>';
                        return;
                    }
                    
                    const maxAllocs = Math.max(...this.filteredData.map(t => t.allocation_count || 0));
                    
                    container.innerHTML = `
                        <div style="display: flex; flex-direction: column; gap: 8px;">
                            ${this.filteredData.slice(0, 10).map(t => {
                                const width = maxAllocs > 0 ? ((t.allocation_count || 0) / maxAllocs * 100) : 0;
                                const tid = Utils.formatThreadId(t.thread_id);
                                return `
                                    <div style="display: flex; align-items: center; gap: 12px;">
                                        <div style="width: 60px; font-size: 0.85rem; font-weight: 500;">${tid}</div>
                                        <div style="flex: 1; height: 24px; background: var(--bg3); border-radius: 4px; overflow: hidden;">
                                            <div style="width: ${width}%; height: 100%; background: linear-gradient(90deg, #10b981, #3b82f6); border-radius: 4px;"></div>
                                        </div>
                                        <div style="width: 80px; font-size: 0.8rem; color: var(--text2);">${t.allocation_count || 0} allocs</div>
                                    </div>
                                `;
                            }).join('')}
                        </div>
                    `;
                },
                graph: {
                    simulation: null,
                    svg: null,
                    zoom: null,
                    g: null,
                    render() {
                        const container = document.getElementById('threadGraphContainer');
                        if (!container) return;
                        const threads = data.threads || [];
                        if (threads.length === 0) { container.innerHTML = '<div class="empty-state">No thread data for graph</div>'; return; }
                        container.innerHTML = '';
                        const allocs = data.allocations || [];
                        const unsafeReports = data.unsafe_reports || [];
                        const threadMap = {};
                        threads.forEach(t => { threadMap[String(t.thread_id)] = t; });
                        const leakByThread = {};
                        allocs.filter(a => a.is_leaked).forEach(a => { const tid = String(a.thread_id); leakByThread[tid] = (leakByThread[tid] || 0) + 1; });
                        const unsafeByThread = {};
                        unsafeReports.forEach(r => {
                            const linked = DataIndex.allocationByPtr(r.allocation_ptr);
                            if (linked) { const tid = String(linked.thread_id); unsafeByThread[tid] = (unsafeByThread[tid] || 0) + 1; }
                        });
                        const nodes = threads.map(t => {
                            const tid = String(t.thread_id);
                            const mem = t.current_memory_bytes || 0;
                            const leaks = leakByThread[tid] || 0;
                            const unsafes = unsafeByThread[tid] || 0;
                            let risk = 'normal';
                            if (leaks > 0 && unsafes > 0) risk = 'mixed';
                            else if (leaks > 0) risk = 'leak';
                            else if (unsafes > 0) risk = 'unsafe';
                            const colorMap = { normal: '#10b981', leak: '#f59e0b', unsafe: '#ef4444', mixed: '#8b5cf6' };
                            return { id: tid, label: 'T' + tid.slice(-4), memory: mem, allocCount: t.allocation_count || 0, leaks, unsafes, risk, color: colorMap[risk] || '#10b981' };
                        });
                        const memValues = nodes.map(n => n.memory);
                        const minMem = Math.min(...memValues) || 1;
                        const maxMem = Math.max(...memValues) || 1;
                        const edges = [];
                        const varByThread = {};
                        allocs.forEach(a => {
                            const tid = String(a.thread_id);
                            if (!varByThread[tid]) varByThread[tid] = new Set();
                            if (a.var_name) varByThread[tid].add(a.var_name);
                        });
                        const typeByThread = {};
                        allocs.forEach(a => {
                            const tid = String(a.thread_id);
                            if (!typeByThread[tid]) typeByThread[tid] = new Set();
                            if (a.type_name) typeByThread[tid].add(a.type_name);
                        });
                        for (let i = 0; i < nodes.length; i++) {
                            for (let j = i + 1; j < nodes.length; j++) {
                                const tida = nodes[i].id, tidb = nodes[j].id;
                                const sharedVars = [...(varByThread[tida] || [])].filter(v => (varByThread[tidb] || []).has(v));
                                const sharedTypes = [...(typeByThread[tida] || [])].filter(v => (typeByThread[tidb] || []).has(v));
                                if (sharedVars.length > 0) edges.push({ source: tida, target: tidb, type: 'variable', label: 'Shared Var: ' + sharedVars.slice(0, 2).join(', '), strength: Math.min(1, sharedVars.length / 3), color: '#10b981', dash: '4,2' });
                                if (sharedTypes.length > 0) edges.push({ source: tida, target: tidb, type: 'type', label: 'Shared Type: ' + sharedTypes.slice(0, 2).join(', '), strength: Math.min(1, sharedTypes.length / 5), color: '#3b82f6', dash: null });
                            }
                        }
                        (data.relationships || []).forEach(r => {
                            const srcAlloc = DataIndex.allocationByPtr(r.source_ptr);
                            const tgtAlloc = DataIndex.allocationByPtr(r.target_ptr);
                            if (srcAlloc && tgtAlloc && String(srcAlloc.thread_id) !== String(tgtAlloc.thread_id)) {
                                edges.push({ source: String(srcAlloc.thread_id), target: String(tgtAlloc.thread_id), type: 'transfer', label: r.relationship_type || 'transfer', strength: (r.strength || 0.5), color: '#ef4444', dash: null });
                            }
                        });
                        document.getElementById('threadGraphEdgeCount').textContent = edges.length + ' edges';
                        const width = container.clientWidth || 700;
                        const height = 300;
                        Dashboard.threads.graph.svg = d3.select(container).append('svg').attr('width', '100%').attr('height', height).attr('viewBox', [0, 0, width, height]);
                        Dashboard.threads.graph.zoom = d3.zoom().on('zoom', (event) => { if (Dashboard.threads.graph.g) Dashboard.threads.graph.g.attr('transform', event.transform); });
                        Dashboard.threads.graph.svg.call(Dashboard.threads.graph.zoom);
                        Dashboard.threads.graph.g = Dashboard.threads.graph.svg.append('g');
                        Dashboard.threads.graph.svg.append('defs').append('marker').attr('id', 'threadArrow').attr('viewBox', '-0 -5 10 10').attr('refX', 25).attr('refY', 0).attr('orient', 'auto').attr('markerWidth', 6).attr('markerHeight', 6).append('path').attr('d', 'M 0,-5 L 10,0 L 0,5').attr('fill', '#64748b');
                        const link = Dashboard.threads.graph.g.append('g').selectAll('line').data(edges).join('line').attr('stroke', d => d.color).attr('stroke-width', d => 1 + d.strength * 2).attr('stroke-dasharray', d => d.dash || 'none').attr('stroke-opacity', 0.6).attr('marker-end', 'url(#threadArrow)');
                        link.append('title').text(d => d.label);
                        const node = Dashboard.threads.graph.g.append('g').selectAll('g').data(nodes).join('g').attr('class', 'node').style('cursor', 'pointer').on('click', (event, d) => Dashboard.threads.showDetail(d.id));
                        node.append('circle').attr('r', d => 10 + (d.memory / (maxMem || 1)) * 20).attr('fill', d => d.color).attr('stroke', '#fff').attr('stroke-width', 2).attr('opacity', 0.85);
                        node.append('text').attr('dx', d => 12 + (d.memory / (maxMem || 1)) * 20).attr('dy', 4).text(d => d.label).style('font-size', '11px').style('fill', 'var(--text)').style('font-weight', '600');
                        const tooltip = d3.select('#tooltip');
                        node.on('mouseover', (event, d) => {
                            tooltip.style('display', 'block').html(`<div style="min-width:150px;"><div style="font-weight:600;margin-bottom:4px;">Thread ${d.id}</div><table style="font-size:12px;border-collapse:collapse;"><tr><td style="color:var(--text2);padding:1px 4px;">Risk</td><td style="padding:1px 4px;">${d.risk}</td></tr><tr><td style="color:var(--text2);padding:1px 4px;">Memory</td><td style="padding:1px 4px;">${Utils.formatBytes(d.memory)}</td></tr><tr><td style="color:var(--text2);padding:1px 4px;">Allocs</td><td style="padding:1px 4px;">${d.allocCount}</td></tr><tr><td style="color:var(--text2);padding:1px 4px;">Leaks</td><td style="padding:1px 4px;">${d.leaks}</td></tr><tr><td style="color:var(--text2);padding:1px 4px;">Unsafe</td><td style="padding:1px 4px;">${d.unsafes}</td></tr></table></div>`).style('left', (event.pageX + 15) + 'px').style('top', (event.pageY - 10) + 'px');
                        }).on('mouseout', () => tooltip.style('display', 'none'));
                        Dashboard.threads.graph.simulation = d3.forceSimulation(nodes).force('link', d3.forceLink(edges).id(d => d.id).distance(120)).force('charge', d3.forceManyBody().strength(-300)).force('center', d3.forceCenter(width / 2, height / 2)).force('collision', d3.forceCollide().radius(40));
                        Dashboard.threads.graph.simulation.on('tick', () => { link.attr('x1', d => d.source?.x || 0).attr('y1', d => d.source?.y || 0).attr('x2', d => d.target?.x || 0).attr('y2', d => d.target?.y || 0); node.attr('transform', d => `translate(${d.x || 0},${d.y || 0})`); });
                    },
                    resetZoom() { if (Dashboard.threads.graph.svg && Dashboard.threads.graph.zoom) Dashboard.threads.graph.svg.transition().duration(500).call(Dashboard.threads.graph.zoom.transform, d3.zoomIdentity); }
                },
                showDetail(threadId) {
                    const panel = document.getElementById('threadDetailPanel');
                    if (!panel) return;
                    panel.style.display = 'block';
                    document.getElementById('tdTitle').textContent = '🧵 Thread ' + threadId + ' Details';
                    const allocs = DataIndex.allocationsByThread(threadId);
                    const typeMap = {};
                    allocs.forEach(a => { const cat = Dashboard.timetravel.classifyType(a.type_name); typeMap[cat] = (typeMap[cat] || 0) + (parseInt(a.size) || 0); });
                    const typeHtml = '<div class="detail-label">💾 Memory by Type</div>' + Object.entries(typeMap).sort((a, b) => b[1] - a[1]).slice(0, 8).map(([cat, bytes]) => `<div class="flex-between" style="padding:2px 0;"><span style="color:${Dashboard.timetravel.getCategoryColor(cat)};">${cat}</span><span>${Utils.formatBytes(bytes)}</span></div>`).join('') || '<div style="color:var(--text2);">None</div>';
                    document.getElementById('tdMemByType').innerHTML = typeHtml;
                    const varMap = {};
                    allocs.forEach(a => { if (a.var_name) varMap[a.var_name] = (varMap[a.var_name] || 0) + (parseInt(a.size) || 0); });
                    const varHtml = '<div class="detail-label">📦 Top Variables</div>' + Object.entries(varMap).sort((a, b) => b[1] - a[1]).slice(0, 8).map(([name, bytes]) => `<div class="flex-between" style="padding:2px 0;"><span>${name}</span><span>${Utils.formatBytes(bytes)}</span></div>`).join('') || '<div style="color:var(--text2);">None</div>';
                    document.getElementById('tdTopVariables').innerHTML = varHtml;
                    const srcMap = {};
                    allocs.forEach(a => { if (a.source_file) { const key = a.source_file + (a.source_line ? ':' + a.source_line : ''); srcMap[key] = (srcMap[key] || 0) + 1; } });
                    const srcHtml = '<div class="detail-label">📍 Top Source Locations</div>' + Object.entries(srcMap).sort((a, b) => b[1] - a[1]).slice(0, 5).map(([loc, count]) => `<div class="flex-between" style="padding:2px 0;"><span style="font-size:0.75rem;">${loc}</span><span>${count}x</span></div>`).join('') || '<div style="color:var(--text2);">None</div>';
                    document.getElementById('tdTopSources').innerHTML = srcHtml;
                    const unsafeList = [];
                    (data.unsafe_reports || []).forEach(r => {
                        const linked = r.allocation_ptr ? DataIndex.allocationByPtr(r.allocation_ptr) : null;
                        if (linked && String(linked.thread_id) === threadId) unsafeList.push(r);
                    });
                    const unsafeHtml = '<div class="detail-label">⚠️ Unsafe Reports</div>' + (unsafeList.length > 0 ? unsafeList.slice(0, 5).map(r => `<div class="flex-between" style="padding:2px 0;"><span style="color:${r.risk_level === 'high' ? '#ef4444' : '#f59e0b'};">${r.var_name || 'unknown'}</span><span class="badge ${r.risk_level === 'high' ? 'badge-danger' : 'badge-warning'}">${r.risk_level}</span></div>`).join('') : '<div style="color:var(--text2);">None</div>');
                    document.getElementById('tdUnsafeReports').innerHTML = unsafeHtml;
                },
                hideDetail() { document.getElementById('threadDetailPanel').style.display = 'none'; },
                init() {
                    const threads = data.threads || [];
                    this.filteredData = [...threads];
                    document.getElementById('threadCount').textContent = threads.length + ' threads';
                    const tbody = document.getElementById('threadTableBody');
                    if (threads.length === 0) {
                        tbody.innerHTML = '<tr><td colspan="6" class="empty-state">No thread data</td></tr>';
                        return;
                    }
                    this.sort();
                    this.graph.render();
                }
            },

            taskGraph: {
                init() {
                    const container = document.getElementById('taskGraphContainer');
                    const emptyState = document.getElementById('taskGraphEmptyState');
                    const svgContainer = document.getElementById('taskGraphSvg');
                    const countBadge = document.getElementById('taskGraphCount');

                    if (!container) return;

                    let taskGraphData = null;
                    try {
                        if (typeof data.task_graph_json !== 'undefined' && data.task_graph_json) {
                            taskGraphData = JSON.parse(data.task_graph_json);
                        }
                    } catch (e) {
                        console.error('Failed to parse task_graph_json:', e);
                    }

                    if (taskGraphData && taskGraphData.nodes && taskGraphData.nodes.length > 0) {
                        emptyState.style.display = 'none';
                        svgContainer.style.display = 'block';
                        countBadge.textContent = taskGraphData.nodes.length + ' tasks (graph)';
                        this.renderTree(taskGraphData);
                        return;
                    }

                    const asyncTasks = data.async_tasks || [];
                    if (asyncTasks.length > 0) {
                        emptyState.style.display = 'none';
                        svgContainer.style.display = 'block';
                        countBadge.textContent = asyncTasks.length + ' tasks (async)';
                        svgContainer.innerHTML = '<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:12px;padding:12px;">' +
                            asyncTasks.slice(0, 50).map(t => {
                                const statusColor = t.is_completed ? '#10b981' : '#3b82f6';
                                const eff = (t.efficiency_score || 0) * 100;
                                return '<div style="background:var(--bg2);border:1px solid var(--border);border-radius:8px;padding:12px;border-left:4px solid ' + statusColor + ';">' +
                                    '<div class="flex-between" style="margin-bottom:8px;"><span style="font-weight:600;color:var(--primary);">#' + t.task_id + '</span>' +
                                    '<span class="badge" style="background:' + statusColor + '30;color:' + statusColor + ';">' + (t.is_completed ? 'Completed' : 'Active') + '</span></div>' +
                                    '<div style="font-size:0.85rem;margin-bottom:8px;">' + (t.task_name || 'Unknown') + '</div>' +
                                    '<div style="display:grid;grid-template-columns:1fr 1fr;gap:4px;font-size:0.8rem;">' +
                                    '<div><span class="detail-label">Memory</span><div style="font-weight:600;">' + Utils.formatBytes(t.current_memory || 0) + '</div></div>' +
                                    '<div><span class="detail-label">Peak</span><div style="font-weight:600;">' + Utils.formatBytes(t.peak_memory || 0) + '</div></div>' +
                                    '<div><span class="detail-label">Allocs</span><div style="font-weight:600;">' + (t.total_allocations || 0) + '</div></div>' +
                                    '<div><span class="detail-label">Dur.</span><div style="font-weight:600;">' + (t.duration_ms || 0).toFixed(1) + 'ms</div></div>' +
                                    '<div><span class="detail-label">Eff.</span><div style="font-weight:600;">' + eff.toFixed(0) + '%</div></div>' +
                                    '<div><span class="detail-label">Type</span><div style="font-weight:600;font-size:0.75rem;">' + (t.task_type || '—') + '</div></div>' +
                                    '</div>' + (t.has_potential_leak ? '<div style="margin-top:8px;color:#f59e0b;font-size:0.8rem;">⚠️ Potential Leak</div>' : '') +
                                    '</div>';
                            }).join('') + '</div>';
                        return;
                    }

                    emptyState.style.display = 'flex';
                    svgContainer.style.display = 'none';
                    countBadge.textContent = '0 tasks';
                },

                renderTree(graphData) {
                    const container = document.getElementById('taskGraphSvg');
                    if (!container) return;

                    const width = container.clientWidth || 800;
                    const height = Math.max(400, container.clientHeight || 600);

                    container.innerHTML = '';

                    const svg = d3.select(container)
                        .append('svg')
                        .attr('width', '100%')
                        .attr('height', height)
                        .attr('viewBox', [0, 0, width, height]);

                    const nodes = graphData.nodes || [];
                    const edges = graphData.edges || [];

                    const childIds = new Set(edges.map(e => e.to));
                    const rootNodes = nodes.filter(n => !childIds.has(n.id));

                    const buildTree = (nodeId) => {
                        const node = nodes.find(n => n.id === nodeId);
                        if (!node) return null;

                        const children = edges
                            .filter(e => e.from === nodeId)
                            .map(e => buildTree(e.to))
                            .filter(n => n !== null);

                        return {
                            id: node.id,
                            name: node.name,
                            memory_usage: node.memory_usage || 0,
                            allocation_count: node.allocation_count || 0,
                            status: node.status,
                            children: children
                        };
                    };

                    const trees = rootNodes.map(n => buildTree(n.id)).filter(t => t !== null);

                    if (trees.length === 0) {
                        container.innerHTML = '<div class="empty-state">No valid task hierarchy</div>';
                        return;
                    }

                    const zoomGroup = svg.append('g').attr('class', 'zoom-group');

                    const zoom = d3.zoom()
                        .scaleExtent([0.1, 5])
                        .on('zoom', (event) => {
                            zoomGroup.attr('transform', event.transform);
                        });

                    svg.call(zoom);

                    svg.on('dblclick.zoom', (event) => {
                        event.preventDefault();
                        svg.transition().duration(500).call(
                            zoom.transform,
                            d3.zoomIdentity,
                            d3.pointer(event, svg.node())
                        );
                    });

                    this.renderSimpleTree(zoomGroup, trees, width, height, 0, 0);

                    setTimeout(() => {
                        const bbox = zoomGroup.node()?.getBBox();
                        if (bbox && bbox.width > 0 && bbox.height > 0) {
                            const pad = 40;
                            const scale = Math.min(
                                (width - pad * 2) / bbox.width,
                                (height - pad * 2) / bbox.height,
                                1.5
                            );
                            const tx = (width - bbox.width * scale) / 2 - bbox.x * scale;
                            const ty = (height - bbox.height * scale) / 2 - bbox.y * scale;
                            svg.transition().duration(500).call(
                                zoom.transform,
                                d3.zoomIdentity.translate(tx, ty).scale(scale)
                            );
                        }
                    }, 50);
                },

                renderSimpleTree(svg, trees, width, height, xOffset, yOffset) {
                    const nodeWidth = 200;
                    const nodeHeight = 60;
                    const horizontalGap = 40;
                    const verticalGap = 80;

                    let currentX = xOffset;
                    let currentY = yOffset;

                    let clickStartPos = null;

                    const renderNode = (node, x, y) => {
                        const g = svg.append('g')
                            .attr('transform', `translate(${x}, ${y})`)
                            .style('cursor', 'pointer');

                        g.append('rect')
                            .attr('width', nodeWidth)
                            .attr('height', nodeHeight)
                            .attr('rx', 8)
                            .attr('fill', node.status === 'Running' ? '#10b981' : '#64748b')
                            .attr('opacity', 0.2);

                        g.append('rect')
                            .attr('width', nodeWidth)
                            .attr('height', nodeHeight)
                            .attr('rx', 8)
                            .attr('fill', 'none')
                            .attr('stroke', node.status === 'Running' ? '#10b981' : '#64748b')
                            .attr('stroke-width', 2);

                        g.append('text')
                            .attr('x', nodeWidth / 2)
                            .attr('y', 25)
                            .attr('text-anchor', 'middle')
                            .attr('fill', 'currentColor')
                            .style('font-size', '14px')
                            .style('font-weight', '600')
                            .text(node.name || `Task ${node.id}`);

                        g.append('text')
                            .attr('x', nodeWidth / 2)
                            .attr('y', 45)
                            .attr('text-anchor', 'middle')
                            .attr('fill', 'var(--text2)')
                            .style('font-size', '12px')
                            .text(`${Utils.formatBytes(node.memory_usage)} • ${node.allocation_count} allocs`);

                        const drag = d3.drag()
                            .on('start', (event) => {
                                clickStartPos = { x: event.x, y: event.y };
                            })
                            .on('drag', (event) => {
                                const transform = g.attr('transform');
                                const match = transform.match(/translate\(([-\d.]+),\s*([-\d.]+)\)/);
                                let cx = x, cy = y;
                                if (match) { cx = parseFloat(match[1]); cy = parseFloat(match[2]); }
                                g.attr('transform', `translate(${cx + event.dx}, ${cy + event.dy})`);
                            })
                            .on('end', (event) => {
                                if (clickStartPos) {
                                    const dist = Math.hypot(event.x - clickStartPos.x, event.y - clickStartPos.y);
                                    clickStartPos = null;
                                    if (dist < 5) {
                                        this.showTaskDetails(node);
                                    }
                                }
                            });

                        g.call(drag);

                        return nodeWidth + horizontalGap;
                    };

                    const renderTreeRecursive = (node, x, y) => {
                        const nextX = renderNode(node, x, y);

                        if (node.children && node.children.length > 0) {
                            let childX = x;
                            let childY = y + nodeHeight + verticalGap;

                            node.children.forEach(child => {
                                svg.append('line')
                                    .attr('x1', x + nodeWidth / 2)
                                    .attr('y1', y + nodeHeight)
                                    .attr('x2', childX + nodeWidth / 2)
                                    .attr('y2', childY)
                                    .attr('stroke', '#10b981')
                                    .attr('stroke-width', 2)
                                    .attr('stroke-dasharray', '5,5');

                                childX = renderTreeRecursive(child, childX, childY);
                            });
                        }

                        return nextX;
                    };

                    trees.forEach(tree => {
                        currentX = renderTreeRecursive(tree, currentX, currentY);
                        currentY += nodeHeight + verticalGap + 50;
                    });
                },

                showTaskDetails(node) {
                    const detailsPanel = document.getElementById('taskGraphDetails');
                    const detailsContent = document.getElementById('taskGraphDetailsContent');

                    if (!detailsPanel || !detailsContent) return;

                    detailsPanel.style.display = 'block';
                    detailsContent.innerHTML = `
                        <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px;">
                            <div class="detail-panel" style="border-left: 4px solid #10b981;">
                                <div class="detail-label">Task ID</div>
                                <div class="stat-value">${node.id}</div>
                            </div>
                            <div class="detail-panel" style="border-left: 4px solid #3b82f6;">
                                <div class="detail-label">Task Name</div>
                                <div class="stat-value" style="font-size: 1.2rem;">${node.name || 'N/A'}</div>
                            </div>
                            <div class="detail-panel" style="border-left: 4px solid #f59e0b;">
                                <div class="detail-label">Memory Usage</div>
                                <div class="stat-value">${Utils.formatBytes(node.memory_usage)}</div>
                            </div>
                            <div class="detail-panel" style="border-left: 4px solid #8b5cf6;">
                                <div class="detail-label">Allocations</div>
                                <div class="stat-value">${node.allocation_count}</div>
                            </div>
                            <div class="detail-panel" style="border-left: 4px solid #10b981;">
                                <div class="detail-label">Status</div>
                                <div class="stat-value" style="color: ${node.status === 'Running' ? '#10b981' : '#64748b'};">${node.status}</div>
                            </div>
                        </div>
                    `;
                }
            },

            relationships: {
                showAll: false,
                filteredData: [],
                toggle() {
                    const wrapper = document.getElementById('relTableWrapper');
                    const btn = document.getElementById('toggleRelBtn');
                    this.showAll = !this.showAll;
                    if (this.showAll) {
                        wrapper.style.maxHeight = 'none';
                        wrapper.style.overflow = 'visible';
                        btn.textContent = '▲ Collapse';
                    } else {
                        wrapper.style.maxHeight = '300px';
                        wrapper.style.overflow = 'auto';
                        btn.textContent = '▼ Expand';
                    }
                },
                sort() {
                    const sortEl = document.getElementById('relSortOrder');
                    if (!sortEl) return;
                    const sortBy = sortEl.value;
                    this.filteredData.sort((a, b) => {
                        switch (sortBy) {
                            case 'strength-desc':
                                return (b.strength || 0.5) - (a.strength || 0.5);
                            case 'strength-asc':
                                return (a.strength || 0.5) - (b.strength || 0.5);
                            case 'type':
                                return (a.relationship_type || '').localeCompare(b.relationship_type || '');
                            case 'source':
                                return (a.source_var_name || '').localeCompare(b.source_var_name || '');
                            default:
                                return 0;
                        }
                    });
                    this.render();
                },
                render() {
                    const tbody = document.getElementById('relationshipsBody');
                    tbody.innerHTML = this.filteredData.map(r =>
                        `<tr><td>${r.source_var_name || ('Ptr ' + r.source_ptr) || 'N/A'}</td><td>${r.target_var_name || ('Ptr ' + r.target_ptr) || 'N/A'}</td><td><span class="badge badge-info">${r.relationship_type || 'reference'}</span></td><td>${r.type_name || 'Unknown'}</td><td>${(((r.strength || 0.5) * 100)).toFixed(0)}%</td></tr>`
                    ).join('');
                },
                init() {
                    const relationships = data.relationships || [];
                    this.filteredData = [...relationships];
                    document.getElementById('relationshipCount2').textContent = relationships.length + ' relationships';
                    if (relationships.length === 0) {
                        document.getElementById('relationshipsBody').innerHTML = '<tr><td colspan="5" class="empty-state">No relationship data</td></tr>';
                        return;
                    }
                    this.sort();
                }
            },

            passports: {
                filteredData: [],
                sort() {
                    const sortEl = document.getElementById('passportSortOrder');
                    if (!sortEl) return;
                    const sortBy = sortEl.value;
                    this.filteredData.sort((a, b) => {
                        switch (sortBy) {
                            case 'size-desc':
                                return (b.size_bytes || 0) - (a.size_bytes || 0);
                            case 'size-asc':
                                return (a.size_bytes || 0) - (b.size_bytes || 0);
                            case 'lifetime-desc':
                                return (b.lifetime_ms || 0) - (a.lifetime_ms || 0);
                            case 'lifetime-asc':
                                return (a.lifetime_ms || 0) - (b.lifetime_ms || 0);
                            default:
                                return 0;
                        }
                    });
                    this.render();
                },
                render() {
                    const container = document.getElementById('passportCardContainer');
                    const timeline = document.getElementById('passportTimeline');
                    const riskCount = { high: 0, medium: 0, low: 0 };
                    const passportCtx = document.getElementById('passportChart');
                    const chartColors = Utils.getChartColors();

                    container.innerHTML = this.filteredData.slice(0, 50).map(p => {
                        const risk = p.risk_level || 'low';
                        riskCount[risk] = (riskCount[risk] || 0) + 1;
                        const visaFlow = (p.cross_boundary_events && p.cross_boundary_events.length > 0) ? `<div style="margin-top: 12px; padding: 12px; background: linear-gradient(135deg, #1e3a5f10 0%, #1e3a5f20 100%); border-radius: 8px; border: 1px solid #1e3a5f40;"><div style="font-size: 0.75rem; color: #1e3a5f; margin-bottom: 8px; font-weight: 600; display: flex; align-items: center; gap: 6px;"><span style="font-size: 1rem;">🛂</span> FFI VISA STAMPS - Border Crossings</div><div style="display: flex; flex-wrap: wrap; gap: 8px;">${p.cross_boundary_events.map((e, idx) => `<div style="display: flex; flex-direction: column; align-items: center; padding: 8px 12px; border-radius: 8px; background: white; border: 2px dashed ${e.color || '#10b981'}; min-width: 80px;"><div style="font-size: 1.25rem;">${e.icon || '📍'}</div><div style="font-size: 0.7rem; font-weight: 600; color: #1e293b;">${e.event_type || 'Cross'}</div><div style="font-size: 0.65rem; color: #64748b;">${e.from_context || 'Rust'} → ${e.to_context || 'FFI'}</div></div>`).join('')}</div></div>` : '<div style="margin-top: 12px; padding: 8px 12px; background: var(--bg3); border-radius: 8px; border: 1px dashed var(--border); font-size: 0.8rem; color: var(--text2); display: flex; align-items: center; gap: 8px;"><span>🛂</span> No FFI boundary crossings - stayed in Rust territory</div>';
                        return `<div class="detail-panel" style="border-left: 4px solid ${p.is_leaked ? '#ef4444' : 'var(--primary)'}; position: relative; overflow: hidden;"><div style="position: absolute; top: 8px; right: 8px; font-size: 0.65rem; padding: 2px 8px; border-radius: 4px; background: ${p.is_leaked ? '#ef444420' : '#10b98120'}; color: ${p.is_leaked ? '#ef4444' : '#10b981'}; font-weight: 600;">${p.is_leaked ? '🔴 LEAKED' : '🟢 VALID'}</div><div style="display: flex; align-items: center; gap: 8px; margin-bottom: 8px;"><span style="font-size: 1.25rem;">🛂</span><h4 style="color: var(--primary); margin: 0;">${p.var_name || 'unknown'}</h4></div><div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(100px, 1fr)); gap: 8px;"><div><span class="detail-label">Type</span><div class="detail-value" style="font-family: monospace; font-size: 0.8rem;">${(p.type_name || 'Unknown').substring(0, 25)}${(p.type_name || '').length > 25 ? '...' : ''}</div></div><div><span class="detail-label">Size</span><div class="detail-value">${Utils.formatBytes(parseInt(p.size_bytes) || 0)}</div></div><div><span class="detail-label">Status</span><div class="detail-value">${p.status || 'N/A'}</div></div><div><span class="detail-label">Risk</span><div class="detail-value ${p.risk_level === 'high' ? 'risk-high' : p.risk_level === 'medium' ? 'risk-medium' : ''}">${p.risk_level || 'low'}</div></div></div>${visaFlow}</div>`;
                    }).join('');

                    if (timeline) {
                        timeline.innerHTML = this.filteredData.slice(0, 10).map(p => `
                            <div class="timeline-item ${p.is_leaked ? 'leaked' : 'active'}">
                                <strong>${p.var_name || 'unknown'}</strong>
                                <span class="badge ${p.is_leaked ? 'badge-danger' : 'badge-success'}">${p.status || 'active'}</span>
                                <div style="color: var(--text2); font-size: 0.85rem;">
                                    ${Utils.formatBytes(parseInt(p.size_bytes) || 0)} | ${p.type_name || 'Unknown'}
                                </div>
                            </div>
                        `).join('');
                    }

                    document.getElementById('highRiskCount').textContent = riskCount.high;
                    document.getElementById('mediumRiskCount').textContent = riskCount.medium;
                    document.getElementById('lowRiskCount').textContent = riskCount.low;

                    if (passportCtx && !Dashboard.charts.instances.passport) {
                        Dashboard.charts.instances.passport = new Chart(passportCtx, {
                            type: 'radar',
                            data: {
                                labels: ['Size', 'Lifetime', 'Refs', 'Clones', 'Borrows'],
                                datasets: [{
                                    label: 'Risk Metrics',
                                    data: [
                                        Math.min(100, this.filteredData.length * 5),
                                        Math.min(100, riskCount.high * 20),
                                        Math.min(100, riskCount.medium * 15),
                                        Math.min(100, riskCount.low * 10),
                                        50
                                    ],
                                    borderColor: chartColors.primary,
                                    backgroundColor: chartColors.primary + '20'
                                }]
                            },
                            options: {
                                responsive: true,
                                maintainAspectRatio: false,
                                scales: {
                                    r: {
                                        grid: { color: chartColors.grid },
                                        angleLines: { color: chartColors.grid },
                                        ticks: { display: false }
                                    }
                                },
                                plugins: { legend: { labels: { color: chartColors.text } } }
                            }
                        });
                    }
                },
                init() {
                    const passports = data.passport_details || [];
                    this.filteredData = [...passports];
                    document.getElementById('passportBadge').textContent = passports.length + ' passports';
                    if (passports.length === 0) {
                        document.getElementById('passportCardContainer').innerHTML = '<div class="empty-state">No passport data</div>';
                        return;
                    }
                    this.render();
                }
            },

            unsafe: {
                heatmapType: 'risk',
                init() {
                    const container = document.getElementById('unsafeReportsContainer');
                    const unsafeReports = data.unsafe_reports || [];
                    document.getElementById('unsafeBadge').textContent = unsafeReports.length + ' reports';
                    if (unsafeReports.length === 0) {
                        container.innerHTML = '<div class="empty-state">✅ No high risk operations detected</div>';
                        document.getElementById('lifecycleFlowContainer').innerHTML = '<div class="empty-state">No lifecycle data available</div>';
                        document.getElementById('lifecycleFlowCount').textContent = '0 flows';
                        return;
                    }
                    const highRisk = unsafeReports.filter(r => r.risk_level === 'high').slice(0, 10);
                    if (highRisk.length === 0) container.innerHTML = '<div class="empty-state">✅ No high risk operations</div>';
                    else container.innerHTML = highRisk.map(r => `<div class="detail-panel" style="border-left: 4px solid var(--danger); margin-bottom: 12px;"><div style="display: flex; justify-content: space-between; align-items: center;"><h4 style="color: var(--danger); margin: 0;">⚠️ ${r.var_name || 'unknown'}</h4><span class="badge badge-danger">High Risk</span></div><div style="margin-top: 12px; display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 8px;"><div><span class="detail-label">Address</span><div class="detail-value">${r.allocation_ptr || 'N/A'}</div></div><div><span class="detail-label">Type</span><div class="detail-value">${r.type_name || 'Unknown'}</div></div><div><span class="detail-label">Size</span><div class="detail-value">${Utils.formatBytes(parseInt(r.size_bytes) || 0)}</div></div><div><span class="detail-label">Status</span><div class="detail-value">${r.status || 'unknown'}</div></div></div>${r.risk_factors && r.risk_factors.length > 0 ? `<div style="margin-top: 12px;"><span class="detail-label">Risk Factors:</span><ul style="margin: 8px 0 0 20px; color: var(--danger);">${r.risk_factors.map(f => `<li>${f}</li>`).join('')}</ul></div>` : ''}</div>`).join('');
                    const ffiBody = document.getElementById('ffiTableBody');
                    const ffiEvents = [];
                    const lifecycleFlows = [];
                    unsafeReports.forEach(r => { if (r.cross_boundary_events) r.cross_boundary_events.forEach(e => ffiEvents.push({...e, var_name: r.var_name, size_bytes: r.size_bytes})); if (r.lifecycle_events && r.lifecycle_events.length > 0) lifecycleFlows.push({var_name: r.var_name, allocation_ptr: r.allocation_ptr, size_bytes: r.size_bytes, events: r.lifecycle_events}); });
                    document.getElementById('lifecycleFlowCount').textContent = lifecycleFlows.length + ' flows';
                    const flowContainer = document.getElementById('lifecycleFlowContainer');
                    if (lifecycleFlows.length === 0) flowContainer.innerHTML = '<div class="empty-state">No lifecycle flow data available</div>';
                    else flowContainer.innerHTML = lifecycleFlows.slice(0, 10).map(flow => { const flowSteps = flow.events.map((e, idx) => { const isLast = idx === flow.events.length - 1; const stepColor = e.color || '#64748b'; return `<div style="display: flex; align-items: center; gap: 8px;"><div style="width: 32px; height: 32px; border-radius: 50%; background: ${stepColor}; display: flex; align-items: center; justify-content: center; font-size: 14px;">${e.icon || '•'}</div><div style="flex: 1;"><div style="font-weight: 600; color: var(--text);">${e.context || e.event_type}</div><div style="font-size: 0.8rem; color: var(--text2);">${flow.var_name} | ${Utils.formatBytes(flow.size_bytes || 0)}</div></div>${!isLast ? '<div style="width: 2px; height: 24px; background: var(--border);"></div>' : ''}</div>`; }).join(''); return `<div class="detail-panel" style="border: 1px solid var(--border); border-radius: 8px; padding: 16px;"><div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px;"><h4 style="color: var(--primary); margin: 0;">${flow.var_name}</h4><code style="font-size: 0.8rem; color: var(--text2);">${flow.allocation_ptr || 'N/A'}</code></div><div style="display: flex; align-items: flex-start; gap: 12px; flex-wrap: wrap;">${flowSteps}</div></div>`; }).join('');
                    if (ffiEvents.length === 0) ffiBody.innerHTML = '<tr><td colspan="5" class="empty-state">No FFI events</td></tr>';
                    else ffiBody.innerHTML = ffiEvents.slice(0, 50).map(e => `<tr><td><span class="badge badge-warning">${e.event_type || 'N/A'}</span></td><td>${e.from_context || 'N/A'}</td><td>${e.to_context || 'N/A'}</td><td>${e.timestamp || 0}</td><td>${e.icon || '→'}</td></tr>`).join('');
                    
                    this.updateSafetyGauge();
                    this.renderHeatmap();
                    this.renderCrossBoundaryTrace(ffiEvents);
                },
                updateSafetyGauge() {
                    const unsafeReports = data.unsafe_reports || [];
                    const highRisk = unsafeReports.filter(r => r.risk_level === 'high').length;
                    const total = unsafeReports.length || 1;
                    const safetyScore = Math.max(0, 100 - (highRisk / total * 100));
                    
                    const arc = document.getElementById('safetyArc');
                    const valueEl = document.getElementById('safetyScoreValue');
                    
                    if (arc) {
                        const dashOffset = 157 - (safetyScore / 100 * 157);
                        arc.style.strokeDashoffset = dashOffset;
                        arc.style.stroke = safetyScore >= 80 ? '#10b981' : safetyScore >= 50 ? '#f59e0b' : '#ef4444';
                    }
                    if (valueEl) {
                        valueEl.textContent = Math.round(safetyScore) + '%';
                        valueEl.style.color = safetyScore >= 80 ? '#10b981' : safetyScore >= 50 ? '#f59e0b' : '#ef4444';
                    }
                },
                toggleHeatmap(type) {
                    this.heatmapType = type;
                    this.renderHeatmap();
                },
                renderHeatmap() {
                    const container = document.getElementById('unsafeHeatmapContainer');
                    const unsafeReports = data.unsafe_reports || [];
                    
                    if (!container || unsafeReports.length === 0) {
                        if (container) container.innerHTML = '<div class="empty-state">No unsafe operations data</div>';
                        return;
                    }
                    
                    if (this.heatmapType === 'risk') {
                        const riskGroups = { high: [], medium: [], low: [] };
                        unsafeReports.forEach(r => {
                            const level = r.risk_level || 'low';
                            riskGroups[level].push(r);
                        });
                        
                        container.innerHTML = `
                            <div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px;">
                                <div style="background: #ef444420; border: 2px solid #ef4444; border-radius: 8px; padding: 16px;">
                                    <div style="font-weight: 600; color: #ef4444; margin-bottom: 12px;">🚨 High Risk (${riskGroups.high.length})</div>
                                    <div style="display: flex; flex-direction: column; gap: 8px;">
                                        ${riskGroups.high.slice(0, 5).map(r => `
                                            <div style="background: #ef444410; border-radius: 4px; padding: 8px; font-size: 0.85rem;">
                                                <div style="font-weight: 500;">${r.var_name || 'unknown'}</div>
                                                <div style="color: var(--text2); font-size: 0.75rem;">${r.type_name || 'Unknown'}</div>
                                            </div>
                                        `).join('')}
                                    </div>
                                </div>
                                <div style="background: #f59e0b20; border: 2px solid #f59e0b; border-radius: 8px; padding: 16px;">
                                    <div style="font-weight: 600; color: #f59e0b; margin-bottom: 12px;">⚠️ Medium Risk (${riskGroups.medium.length})</div>
                                    <div style="display: flex; flex-direction: column; gap: 8px;">
                                        ${riskGroups.medium.slice(0, 5).map(r => `
                                            <div style="background: #f59e0b10; border-radius: 4px; padding: 8px; font-size: 0.85rem;">
                                                <div style="font-weight: 500;">${r.var_name || 'unknown'}</div>
                                                <div style="color: var(--text2); font-size: 0.75rem;">${r.type_name || 'Unknown'}</div>
                                            </div>
                                        `).join('')}
                                    </div>
                                </div>
                                <div style="background: #10b98120; border: 2px solid #10b981; border-radius: 8px; padding: 16px;">
                                    <div style="font-weight: 600; color: #10b981; margin-bottom: 12px;">✅ Low Risk (${riskGroups.low.length})</div>
                                    <div style="display: flex; flex-direction: column; gap: 8px;">
                                        ${riskGroups.low.slice(0, 5).map(r => `
                                            <div style="background: #10b98110; border-radius: 4px; padding: 8px; font-size: 0.85rem;">
                                                <div style="font-weight: 500;">${r.var_name || 'unknown'}</div>
                                                <div style="color: var(--text2); font-size: 0.75rem;">${r.type_name || 'Unknown'}</div>
                                            </div>
                                        `).join('')}
                                    </div>
                                </div>
                            </div>
                        `;
                    } else {
                        const typeCount = {};
                        unsafeReports.forEach(r => {
                            const type = r.type_name || 'Unknown';
                            typeCount[type] = (typeCount[type] || 0) + 1;
                        });
                        
                        const sortedTypes = Object.entries(typeCount).sort((a, b) => b[1] - a[1]);
                        const maxCount = sortedTypes[0]?.[1] || 1;
                        
                        container.innerHTML = `
                            <div style="display: flex; flex-direction: column; gap: 8px;">
                                ${sortedTypes.slice(0, 10).map(([type, count]) => {
                                    const width = (count / maxCount) * 100;
                                    return `
                                        <div style="display: flex; align-items: center; gap: 12px;">
                                            <div style="width: 150px; font-size: 0.85rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;" title="${type}">${type}</div>
                                            <div style="flex: 1; height: 24px; background: var(--bg3); border-radius: 4px; overflow: hidden;">
                                                <div style="width: ${width}%; height: 100%; background: linear-gradient(90deg, #f59e0b, #ef4444); border-radius: 4px;"></div>
                                            </div>
                                            <div style="width: 50px; font-size: 0.8rem; color: var(--text2);">${count}</div>
                                        </div>
                                    `;
                                }).join('')}
                            </div>
                        `;
                    }
                },
                renderCrossBoundaryTrace(ffiEvents) {
                    const container = document.getElementById('boundaryEvents');
                    const countEl = document.getElementById('boundaryEventCount');
                    
                    if (!container) return;
                    
                    if (countEl) countEl.textContent = ffiEvents.length + ' events';
                    
                    if (ffiEvents.length === 0) {
                        container.innerHTML = '<div style="text-align: center; color: var(--text2); font-size: 0.85rem;">No boundary events</div>';
                        return;
                    }
                    
                    container.innerHTML = ffiEvents.slice(0, 8).map(e => `
                        <div style="display: flex; align-items: center; gap: 8px; padding: 8px 12px; background: var(--bg3); border-radius: 8px; font-size: 0.8rem;">
                            <span style="font-size: 1rem;">${e.icon || '→'}</span>
                            <div>
                                <div style="font-weight: 500;">${e.event_type || 'Cross'}</div>
                                <div style="color: var(--text2); font-size: 0.7rem;">${e.var_name || 'unknown'}</div>
                            </div>
                        </div>
                    `).join('');
                    this.renderCallStackView();
                    this.renderFfiBoundaryFlow();
                },
                renderCallStackView() {
                    const container = document.getElementById('callStackContainer');
                    const countEl = document.getElementById('callStackGroupCount');
                    if (!container) return;
                    const reports = data.unsafe_reports || [];
                    const allocs = data.allocations || [];
                    const sourceMap = {};
                    reports.forEach(r => {
                        const alloc = DataIndex.allocationByPtr(r.allocation_ptr) || allocs.find(a => a.address === r.allocation_ptr);
                        const key = (alloc && alloc.source_file) ? `${alloc.source_file}:${alloc.source_line || 0}` : 'unknown:0';
                        if (!sourceMap[key]) sourceMap[key] = { file: alloc ? alloc.source_file : 'unknown', line: alloc ? alloc.source_line : 0, reports: [] };
                        sourceMap[key].reports.push(r);
                    });
                    const groups = Object.values(sourceMap).sort((a, b) => b.reports.length - a.reports.length);
                    if (countEl) countEl.textContent = groups.length + ' groups';
                    if (groups.length === 0) { container.innerHTML = '<div class="empty-state">No unsafe reports</div>'; return; }
                    const flowLegend = { 'RustToFfi': { icon: '→', color: '#f59e0b', label: 'Rust→FFI' }, 'FfiToRust': { icon: '←', color: '#10b981', label: 'FFI→Rust' }, 'OwnershipTransfer': { icon: '⇄', color: '#8b5cf6', label: 'Ownership Transfer' }, 'SharedAccess': { icon: '⇌', color: '#3b82f6', label: 'Shared Access' }, 'Unknown': { icon: '?', color: '#64748b', label: 'Unknown' } };
                    container.innerHTML = groups.map((g, gi) => `<div style="margin-bottom: 12px; border: 1px solid var(--border); border-radius: 8px; overflow: hidden;">
                        <div onclick="toggleTable('callStackGroup_${gi}', this)" style="cursor: pointer; display: flex; align-items: center; gap: 12px; padding: 10px 14px; background: var(--bg3); border-bottom: 1px solid var(--border);">
                            <span style="font-size: 1.2rem;">📂</span>
                            <div style="flex: 1;">
                                <div style="font-weight: 600; font-size: 0.9rem;">${g.file}:${g.line} <a href="#" onclick="jumpToSource('${g.file}', ${g.line}); return false;" style="color: var(--primary); font-size: 0.75rem; text-decoration: underline;">Jump</a></div>
                                <div style="color: var(--text2); font-size: 0.75rem;">${g.reports.length} report(s) | ${g.reports.filter(r => r.risk_level === 'high').length} high risk</div>
                            </div>
                            <span style="font-size: 0.8rem; color: var(--text2);">▼</span>
                        </div>
                        <div id="callStackGroup_${gi}" style="display: none; padding: 12px;">
                            ${g.reports.map(r => `<div class="detail-panel" style="border-left: 4px solid ${r.risk_level === 'high' ? '#ef4444' : r.risk_level === 'medium' ? '#f59e0b' : '#10b981'}; margin-bottom: 8px;">
                                <div style="display: flex; justify-content: space-between; align-items: flex-start;">
                                    <div>
                                        <div style="font-weight: 600; color: var(--text);">${r.var_name || 'unknown'}</div>
                                        <div style="color: var(--text2); font-size: 0.8rem;">${r.type_name || 'Unknown'} | ${Utils.formatBytes(r.size_bytes || 0)} | <code style="font-size: 0.75rem;">${r.allocation_ptr || 'N/A'}</code></div>
                                    </div>
                                    <span class="badge ${r.risk_level === 'high' ? 'badge-danger' : r.risk_level === 'medium' ? 'badge-warning' : 'badge-success'}">${r.risk_level || 'unknown'}</span>
                                </div>
                                ${r.risk_factors && r.risk_factors.length > 0 ? `<div style="margin-top: 6px;"><div style="font-size: 0.8rem; color: var(--text2);">Risk Factors:</div><ul style="margin: 4px 0 0 16px; font-size: 0.8rem; color: var(--text2);">${r.risk_factors.map(f => `<li>${f}</li>`).join('')}</ul></div>` : ''}
                                ${r.cross_boundary_events && r.cross_boundary_events.length > 0 ? `<div style="margin-top: 6px;"><div style="font-size: 0.8rem; color: var(--text2);">Call Stack:</div><div style="display: flex; flex-direction: column; gap: 4px; margin-top: 4px;">${r.cross_boundary_events.map((e, ei) => `<div style="display: flex; align-items: center; gap: 6px; font-size: 0.75rem; padding: 4px 8px; background: var(--bg3); border-radius: 4px;"><span>${flowLegend[e.event_type]?.icon || '→'}</span><span style="color: ${flowLegend[e.event_type]?.color || '#64748b'};">${e.event_type || 'Unknown'}</span><span style="color: var(--text2);">${e.from_context || ''} → ${e.to_context || ''}</span></div>`).join('')}</div></div>` : ''}
                                <div style="margin-top: 6px; display: flex; gap: 8px; flex-wrap: wrap;">
                                    <button class="btn-sm" onclick="Dashboard.timetravel.showDetail('${r.allocation_ptr}')">⏱ Timeline</button>
                                    <button class="btn-sm" onclick="Dashboard.variableGraph.selectNode('${r.allocation_ptr}')">🔗 Variable Graph</button>
                                </div>
                            </div>`).join('')}
                        </div>
                    </div>`).join('');
                },
                renderFfiBoundaryFlow() {
                    const container = document.getElementById('ffiBoundaryFlowContainer');
                    const countEl = document.getElementById('ffiFlowCount');
                    const mismatchEl = document.getElementById('ffiMismatchCount');
                    const legendEl = document.getElementById('ffiFlowLegend');
                    if (!container) return;
                    const reports = data.unsafe_reports || [];
                    const flowEvents = [];
                    reports.forEach(r => { if (r.cross_boundary_events) r.cross_boundary_events.forEach(e => flowEvents.push({...e, var_name: r.var_name, allocation_ptr: r.allocation_ptr, is_leaked: r.is_leaked, risk_level: r.risk_level })); });
                    const distinctTypes = [...new Set(flowEvents.map(e => e.event_type))];
                    const flowTypeColors = { 'RustToFfi': '#f59e0b', 'FfiToRust': '#10b981', 'OwnershipTransfer': '#8b5cf6', 'SharedAccess': '#3b82f6', 'Unknown': '#64748b' };
                    const mismatches = flowEvents.filter(e => e.event_type === 'OwnershipTransfer' || (e.from_context && e.to_context && e.from_context.includes('alloc') !== e.to_context.includes('free')));
                    if (countEl) countEl.textContent = flowEvents.length + ' flows';
                    if (mismatchEl) mismatchEl.textContent = mismatches.length + ' mismatches';
                    if (legendEl) legendEl.innerHTML = Object.entries(flowTypeColors).map(([type, color]) => `<span style="display: inline-flex; align-items: center; gap: 4px; padding: 4px 8px; background: var(--bg3); border-radius: 4px; font-size: 0.75rem;"><span style="display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: ${color};"></span>${type}</span>`).join('');
                    if (flowEvents.length === 0) { container.innerHTML = '<div class="empty-state">No FFI boundary flows</div>'; return; }
                    const sortedEvents = flowEvents.sort((a, b) => (a.timestamp || 0) - (b.timestamp || 0));
                    container.innerHTML = '<div style="display: flex; flex-direction: column; gap: 8px;">' + sortedEvents.slice(0, 30).map(e => {
                        const color = flowTypeColors[e.event_type] || '#64748b';
                        const isMismatch = e.event_type === 'OwnershipTransfer' || (e.from_context && e.to_context && e.from_context.includes('alloc') !== e.to_context.includes('free'));
                        return `<div style="display: flex; align-items: center; gap: 10px; padding: 8px 12px; background: var(--bg3); border-radius: 8px; border-left: 4px solid ${color}; ${isMismatch ? 'border: 2px solid #ef4444;' : ''}">
                            <span style="font-size: 1.2rem; color: ${color};">${e.event_type === 'RustToFfi' ? '🦀→🌐' : e.event_type === 'FfiToRust' ? '🌐→🦀' : e.event_type === 'OwnershipTransfer' ? '⇄' : e.event_type === 'SharedAccess' ? '⇌' : '→'}</span>
                            <div style="flex: 1;">
                                <div style="display: flex; justify-content: space-between; align-items: center;">
                                    <span style="font-weight: 500; font-size: 0.85rem; color: ${color};">${e.event_type || 'Unknown'}</span>
                                    ${isMismatch ? '<span class="badge badge-danger">⚠ Mismatch</span>' : ''}
                                </div>
                                <div style="color: var(--text2); font-size: 0.75rem;">
                                    ${e.from_context || '?'} → ${e.to_context || '?'} | ${e.var_name || 'unknown'} | <code style="font-size: 0.7rem;">${e.allocation_ptr ? e.allocation_ptr.slice(-8) : 'N/A'}</code>
                                </div>
                            </div>
                            <span style="font-size: 0.7rem; color: var(--text2);">${Utils.formatTime(parseInt(e.timestamp || 0))}</span>
                        </div>`;
                    }).join('') + '</div>';
                }
            },

            timetravel: {
                filteredData: [],
                filter() { const query = document.getElementById('timetravelSearch').value.toLowerCase(); this.filteredData = (data.allocations || []).filter(a => (a.address || '').toLowerCase().includes(query) || (a.var_name || '').toLowerCase().includes(query)); this.sort(); },
                sort() { const sortBy = document.getElementById('ttSortOrder').value; this.filteredData.sort((a, b) => { switch (sortBy) { case 'time-asc': return (a.timestamp_alloc || 0) - (b.timestamp_alloc || 0); case 'time-desc': return (b.timestamp_alloc || 0) - (a.timestamp_alloc || 0); case 'size-desc': return (b.size || 0) - (a.size || 0); case 'size-asc': return (a.size || 0) - (b.size || 0); case 'lifetime-desc': return (b.lifetime_ms || 0) - (a.lifetime_ms || 0); default: return 0; } }); this.render(); },
                render() { const tbody = document.getElementById('timetravelBody'); tbody.innerHTML = this.filteredData.slice(0, 100).map(a => { const sourceLoc = (a.source_file && a.source_line) ? `<a href="#" onclick="jumpToSource('${a.source_file}', ${a.source_line}); return false;" title="Jump to source" style="color: var(--primary); text-decoration: underline;">${a.source_file}:${a.source_line}</a>` : '<span style="color: var(--text2);">—</span>'; const genBadge = a.generation_id > 0 ? `<span class="badge" style="background:${a.generation_id > 1 ? '#ef444430' : '#3b82f630'};color:${a.generation_id > 1 ? '#ef4444' : '#3b82f6'};">G${a.generation_id}</span>` : ''; const provIcon = { allocator: '💠', clone: '📋', smart_pointer: '🔗', reallocation: '🔄', reallocated_then_leaked: '🚨' }[a.provenance] || '💠'; const confMap = { Confirmed: '🔴', Likely: '🟡', Possible: '🟢', Unknown: '⚪' }; const confEl = a.confidence ? `<span style="font-size:0.75rem;" title="Confidence: ${a.confidence}">${confMap[a.confidence] || '⚪'}</span>` : ''; return `<tr onclick="Dashboard.timetravel.showDetail('${a.address}')" style="cursor: pointer;"><td style="font-size:0.75rem;color:var(--text2);">${genBadge}</td><td><code>${a.address || '0x0'}</code></td><td>${a.var_name || 'unknown'}</td><td>${Utils.formatBytes(parseInt(a.size) || 0)}</td><td style="font-size:0.8rem;"><span title="Provenance: ${a.provenance || 'allocator'}">${provIcon} ${a.provenance || 'allocator'}</span></td><td style="font-size:0.75rem;">${confEl} ${a.evidence || ''}</td><td style="font-size: 0.85rem;">${sourceLoc}</td><td>${Utils.formatTime(parseInt(a.lifetime_ms || 0) * 1000000)}</td><td>${a.is_leaked ? '<span class="badge badge-danger">Leaked</span>' : a.timestamp_dealloc > 0 ? '<span class="badge badge-success">Freed</span>' : '<span class="badge badge-info">Active</span>'}</td><td><button class="btn-link">View History</button></td></tr>`; }).join(''); this.updateStats(); },
                updateStats() {
                    const allocations = this.filteredData;
                    const total = allocations.length;
                    const active = allocations.filter(a => !a.timestamp_dealloc).length;
                    const freed = allocations.filter(a => a.timestamp_dealloc > 0 && !a.is_leaked).length;
                    const leaked = allocations.filter(a => a.is_leaked).length;
                    
                    const totalEl = document.getElementById('ttTotalEvents');
                    const activeEl = document.getElementById('ttActiveCount');
                    const freedEl = document.getElementById('ttFreedCount');
                    const leakedEl = document.getElementById('ttLeakedCount');
                    
                    if (totalEl) totalEl.textContent = total;
                    if (activeEl) activeEl.textContent = active;
                    if (freedEl) freedEl.textContent = freed;
                    if (leakedEl) leakedEl.textContent = leaked;
                },
                showDetail(address) { const alloc = DataIndex.allocationByPtr(address) || data.allocations?.find(a => a.address === address); if (!alloc) { alert('Allocation not found: ' + address); return; } document.getElementById('timetravelDetail').style.display = 'block'; document.getElementById('ttAddress').textContent = address + (alloc.generation_id ? ` (G${alloc.generation_id})` : ''); document.getElementById('ttVarName').textContent = alloc.var_name || 'unknown'; document.getElementById('ttType').textContent = alloc.type_name || 'Unknown'; document.getElementById('ttSize').textContent = Utils.formatBytes(parseInt(alloc.size) || 0); document.getElementById('ttStatus').innerHTML = alloc.is_leaked ? '<span class="badge badge-danger">Leaked</span>' : '<span class="badge badge-success">Normal</span>'; document.getElementById('ttProvenance').textContent = (alloc.provenance || 'allocator') + ' | Evidence: ' + (alloc.evidence || 'Unknown') + ' | Confidence: ' + (alloc.confidence || 'Unknown'); const timeline = document.getElementById('ttTimeline'); const events = [{ type: 'alloc', time: alloc.timestamp_alloc || 0, desc: 'Allocation', icon: '➕' }]; if (alloc.timestamp_dealloc > 0) events.push({ type: 'dealloc', time: alloc.timestamp_dealloc, desc: 'Deallocation', icon: '➖' }); if (alloc.is_leaked) events.push({ type: 'leak', time: (alloc.timestamp_alloc || 0) + parseInt(alloc.lifetime_ms || 0) * 1000000, desc: 'Leak Detected', icon: '⚠️' }); timeline.innerHTML = events.map(e => `<div class="timeline-item ${e.type}"><strong>${e.icon} ${e.desc}</strong><div style="color: var(--text2); font-size: 0.85rem;">Timestamp: ${e.time} | Delta: +${Utils.formatTime(e.time - events[0].time)}</div></div>`).join(''); const layoutEl = document.getElementById('ttLayout'); if (layoutEl) { const ls = alloc.layout_snapshot; if (ls) { const layoutCards = []; if (ls.size_of_t) layoutCards.push(`<div class="detail-panel" style="border-left:4px solid var(--info);padding:8px;"><span class="detail-label">SizeOf(T)</span><div class="detail-value">${Utils.formatBytes(ls.size_of_t)}</div></div>`); if (ls.align_of_t) layoutCards.push(`<div class="detail-panel" style="border-left:4px solid var(--warning);padding:8px;"><span class="detail-label">AlignOf(T)</span><div class="detail-value">${ls.align_of_t}</div></div>`); if (ls.layout_kind) layoutCards.push(`<div class="detail-panel" style="border-left:4px solid var(--primary);padding:8px;"><span class="detail-label">Layout Kind</span><div class="detail-value">${ls.layout_kind}</div></div>`); if (ls.repr_hint && ls.repr_hint !== 'Unknown') layoutCards.push(`<div class="detail-panel" style="border-left:4px solid #8b5cf6;padding:8px;"><span class="detail-label">Repr Hint</span><div class="detail-value">${ls.repr_hint}</div></div>`); if (ls.pointer_width && ls.pointer_width !== 'Unknown') layoutCards.push(`<div class="detail-panel" style="border-left:4px solid #f59e0b;padding:8px;"><span class="detail-label">Ptr Width</span><div class="detail-value">${ls.pointer_width}</div></div>`); if (ls.element_size) layoutCards.push(`<div class="detail-panel" style="border-left:4px solid #10b981;padding:8px;"><span class="detail-label">Element Size</span><div class="detail-value">${ls.element_size} B</div></div>`); if (ls.container_len !== null && ls.container_len !== undefined) layoutCards.push(`<div class="detail-panel" style="border-left:4px solid #06b6d4;padding:8px;"><span class="detail-label">Length</span><div class="detail-value">${ls.container_len}</div></div>`); if (ls.container_capacity !== null && ls.container_capacity !== undefined) layoutCards.push(`<div class="detail-panel" style="border-left:4px solid #3b82f6;padding:8px;"><span class="detail-label">Capacity</span><div class="detail-value">${ls.container_capacity}</div></div>`); if (ls.strong_count !== null && ls.strong_count !== undefined) layoutCards.push(`<div class="detail-panel" style="border-left:4px solid #ef4444;padding:8px;"><span class="detail-label">Strong Ref</span><div class="detail-value">${ls.strong_count}</div></div>`); if (ls.weak_count !== null && ls.weak_count !== undefined) layoutCards.push(`<div class="detail-panel" style="border-left:4px solid #64748b;padding:8px;"><span class="detail-label">Weak Ref</span><div class="detail-value">${ls.weak_count}</div></div>`); if (ls.pointee_type) layoutCards.push(`<div class="detail-panel" style="border-left:4px solid #8b5cf6;padding:8px;"><span class="detail-label">Pointee</span><div class="detail-value" style="font-size:0.75rem;">${ls.pointee_type}</div></div>`); layoutEl.innerHTML = layoutCards.join('') || '<div style="color:var(--text2);font-size:0.85rem;">No type layout data</div>'; } else { layoutEl.innerHTML = '<div style="color:var(--text2);font-size:0.85rem;">No layout snapshot</div>'; } } },
                renderTimeSlice() {
                    const slider = document.getElementById('tsTimeSlider');
                    if (!slider) return;
                    const allocs = data.allocations || [];
                    if (allocs.length === 0) return;
                    const minTime = d3.min(allocs, a => a.timestamp_alloc || 0);
                    const maxTime = d3.max(allocs, a => Math.max(a.timestamp_dealloc || 0, (a.timestamp_alloc || 0) + (a.lifetime_ms || 0) * 1000000));
                    const timeRange = (maxTime - minTime) || 1;
                    const pct = parseInt(slider.value) / 100;
                    const currentTime = minTime + pct * timeRange;
                    document.getElementById('tsTimeCurrent').textContent = 'T+' + Utils.formatTime(currentTime - minTime);
                    const active = allocs.filter(a => (a.timestamp_alloc || 0) <= currentTime && (!a.timestamp_dealloc || a.timestamp_dealloc > currentTime));
                    const liveBytes = active.reduce((s, a) => s + (parseInt(a.size) || 0), 0);
                    let peakBytes = 0;
                    for (let i = 0; i <= parseInt(slider.value); i++) {
                        const t = minTime + (i / 100) * timeRange;
                        const bAtT = allocs.filter(a => (a.timestamp_alloc || 0) <= t && (!a.timestamp_dealloc || a.timestamp_dealloc > t)).reduce((s, a) => s + (parseInt(a.size) || 0), 0);
                        if (bAtT > peakBytes) peakBytes = bAtT;
                    }
                    const leakCandidates = active.filter(a => a.is_leaked).length;
                    const unsafeReports = data.unsafe_reports || [];
                    const unsafeCount = unsafeReports.filter(r => r.allocation_ptr && active.some(a => a.address === r.allocation_ptr)).length;
                    document.getElementById('tsAllocCount').textContent = active.length;
                    document.getElementById('tsLiveBytes').textContent = Utils.formatBytes(liveBytes);
                    document.getElementById('tsPeakBytes').textContent = Utils.formatBytes(peakBytes);
                    document.getElementById('tsLeakCandidates').textContent = leakCandidates;
                    document.getElementById('tsUnsafeCount').textContent = unsafeCount;
                    const threadMap = {};
                    active.forEach(a => { const tid = a.thread_id || '?'; threadMap[tid] = (threadMap[tid] || 0) + (parseInt(a.size) || 0); });
                    const threadHtml = Object.entries(threadMap).sort((a, b) => b[1] - a[1]).slice(0, 5).map(([tid, bytes]) => `<div class="flex-between" style="padding:2px 0;"><span>Thread ${tid}</span><span style="font-weight:600;">${Utils.formatBytes(bytes)}</span></div>`).join('') || '<div style="color:var(--text2);">None</div>';
                    document.getElementById('tsMemByThread').innerHTML = threadHtml;
                    const typeMap = {};
                    active.forEach(a => { const cat = this.classifyType(a.type_name); typeMap[cat] = (typeMap[cat] || 0) + (parseInt(a.size) || 0); });
                    const typeHtml = Object.entries(typeMap).sort((a, b) => b[1] - a[1]).slice(0, 5).map(([cat, bytes]) => `<div class="flex-between" style="padding:2px 0;"><span style="color:${this.getCategoryColor(cat)};">${cat}</span><span style="font-weight:600;">${Utils.formatBytes(bytes)}</span></div>`).join('') || '<div style="color:var(--text2);">None</div>';
                    document.getElementById('tsMemByType').innerHTML = typeHtml;
                },
                classifyType(typeName) {
                    if (!typeName) return 'Unknown';
                    const t = typeName.toLowerCase();
                    if (t.includes('vec') || t.includes('string') || t.includes('hashmap') || t.includes('hashset') || t.includes('btreemap') || t.includes('btreeset') || t.includes('linkedlist') || t.includes('vecdeque')) return 'Container';
                    if (t.includes('arc<') || t.includes('rc<') || t.includes('box<')) return 'SmartPointer';
                    if (t.includes('mutex') || t.includes('rwlock') || t.includes('refcell') || t.includes('cell<') || t.includes('atomic') || t.includes('unsafecell')) return 'SyncPrimitive';
                    if (t.includes('fn ') || t.includes('closure') || t.includes('fn(')) return 'Function';
                    if (t.includes('i8') || t.includes('i16') || t.includes('i32') || t.includes('i64') || t.includes('u8') || t.includes('u16') || t.includes('u32') || t.includes('u64') || t.includes('usize') || t.includes('isize') || t.includes('f32') || t.includes('f64') || t.includes('bool') || t.includes('char')) return 'Primitive';
                    if (t.includes('*const') || t.includes('*mut') || t.includes('ptr')) return 'RawPointer';
                    if (t.includes('&') || t.includes('ref')) return 'Reference';
                    return 'Other';
                },
                getCategoryColor(category) {
                    const map = { 'Container': '#3b82f6', 'SmartPointer': '#8b5cf6', 'SyncPrimitive': '#f59e0b', 'Function': '#06b6d4', 'Primitive': '#10b981', 'RawPointer': '#ef4444', 'Reference': '#64748b', 'Other': '#94a3b8', 'Unknown': '#cbd5e1' };
                    return map[category] || '#94a3b8';
                },
                renderLifecycleTimeline() {
                    const container = document.getElementById('lifecycleTimelineContainer');
                    if (!container) return;
                    const threadFilter = document.getElementById('lcThreadFilter').value;
                    const typeFilter = document.getElementById('lcTypeFilter').value;
                    const statusFilter = document.getElementById('lcStatusFilter').value;
                    let allocs = data.allocations || [];
                    if (threadFilter !== 'all') allocs = allocs.filter(a => String(a.thread_id) === threadFilter);
                    if (typeFilter !== 'all') allocs = allocs.filter(a => this.classifyType(a.type_name) === typeFilter);
                    if (statusFilter === 'active') allocs = allocs.filter(a => !a.timestamp_dealloc && !a.is_leaked);
                    else if (statusFilter === 'leaked') allocs = allocs.filter(a => a.is_leaked);
                    else if (statusFilter === 'freed') allocs = allocs.filter(a => a.timestamp_dealloc > 0 && !a.is_leaked);
                    const bars = allocs.slice(0, 200);
                    document.getElementById('lcBarCount').textContent = bars.length + ' bars' + (allocs.length > 200 ? ' (showing 200)' : '');
                    container.innerHTML = '';
                    if (bars.length === 0) { container.innerHTML = '<div class="empty-state">No allocations match filters</div>'; return; }
                    const width = container.clientWidth || 800;
                    const height = Math.max(100, bars.length * 20 + 40);
                    container.style.height = height + 'px';
                    const svg = d3.select(container).append('svg').attr('width', '100%').attr('height', height).attr('viewBox', [0, 0, width, height]);
                    const minTime = d3.min(bars, a => a.timestamp_alloc || 0);
                    const maxTime = d3.max(bars, a => Math.max(a.timestamp_dealloc || 0, (a.timestamp_alloc || 0) + (a.lifetime_ms || 0) * 1000000));
                    const timeRange = (maxTime - minTime) || 1;
                    const padding = { top: 10, right: 20, bottom: 30, left: 120 };
                    const innerW = width - padding.left - padding.right;
                    const innerH = height - padding.top - padding.bottom;
                    const barH = Math.min(16, innerH / bars.length - 1);
                    const xScale = d3.scaleLinear().domain([minTime, maxTime + timeRange * 0.05]).range([0, innerW]);
                    const g = svg.append('g').attr('transform', `translate(${padding.left},${padding.top})`);
                    const tooltip = d3.select('#tooltip');
                    bars.forEach((a, i) => {
                        const cat = this.classifyType(a.type_name);
                        const color = this.getCategoryColor(cat);
                        const x0 = xScale(a.timestamp_alloc || 0);
                        const x1 = a.timestamp_dealloc > 0 ? xScale(a.timestamp_dealloc) : xScale(maxTime + timeRange * 0.05);
                        const y = i * (barH + 1);
                        g.append('rect').attr('x', x0).attr('y', y).attr('width', Math.max(2, x1 - x0)).attr('height', barH).attr('fill', a.is_leaked ? '#ef4444' : color).attr('opacity', a.is_leaked ? 0.9 : 0.7).attr('stroke', a.is_leaked ? '#dc2626' : 'none').attr('stroke-width', a.is_leaked ? 1.5 : 0).attr('stroke-dasharray', a.is_leaked ? '4,2' : 'none').attr('rx', 2).attr('ry', 2).style('cursor', 'pointer').on('mouseover', (event) => {
                            const lifetimeNs = a.lifetime_ms ? a.lifetime_ms * 1000000 : 0;
                            tooltip.style('display', 'block').html(`<div style="min-width: 230px;"><div style="font-weight:600;color:var(--primary);margin-bottom:4px;">${a.var_name || 'unknown'}</div><table style="font-size:12px;width:100%;border-collapse:collapse;"><tr><td style="color:var(--text2);padding:1px 4px;">Address</td><td style="padding:1px 4px;font-family:monospace;">${a.address || '0x0'}${a.generation_id ? ` <span style="color:#64748b;font-size:10px;">G${a.generation_id}</span>` : ''}</td></tr><tr><td style="color:var(--text2);padding:1px 4px;">Type</td><td style="padding:1px 4px;">${a.type_name || 'Unknown'}</td></tr><tr><td style="color:var(--text2);padding:1px 4px;">Category</td><td style="padding:1px 4px;">${cat}</td></tr><tr><td style="color:var(--text2);padding:1px 4px;">Provenance</td><td style="padding:1px 4px;">${a.provenance || 'allocator'}</td></tr><tr><td style="color:var(--text2);padding:1px 4px;">Evidence</td><td style="padding:1px 4px;">${a.evidence || 'Unknown'} — ${a.confidence || 'Unknown'}</td></tr><tr><td style="color:var(--text2);padding:1px 4px;">Thread</td><td style="padding:1px 4px;">${a.thread_id || 'N/A'}</td></tr><tr><td style="color:var(--text2);padding:1px 4px;">Size</td><td style="padding:1px 4px;">${Utils.formatBytes(parseInt(a.size) || 0)}</td></tr><tr><td style="color:var(--text2);padding:1px 4px;">Lifetime</td><td style="padding:1px 4px;">${Utils.formatTime(lifetimeNs)}</td></tr><tr><td style="color:var(--text2);padding:1px 4px;">Source</td><td style="padding:1px 4px;">${a.source_file || ''}${a.source_line ? ':' + a.source_line : ''}</td></tr><tr><td style="color:var(--text2);padding:1px 4px;">Status</td><td style="padding:1px 4px;">${a.is_leaked ? '<span style="color:#ef4444;font-weight:600;">LEAKED</span>' : a.timestamp_dealloc > 0 ? '<span style="color:#10b981;">Freed</span>' : '<span style="color:#3b82f6;">Active</span>'}</td></tr></table></div>`).style('left', (event.pageX + 15) + 'px').style('top', (event.pageY - 10) + 'px');
                        }).on('mouseout', () => tooltip.style('display', 'none')).on('click', () => this.showDetail(a.address));
                        g.append('text').attr('x', -4).attr('y', y + barH / 2 + 4).attr('text-anchor', 'end').attr('fill', 'var(--text2)').style('font-size', '10px').style('font-family', 'monospace').text((a.var_name || a.address || '').substring(0, 18));
                    });
                    g.append('g').attr('transform', `translate(0,${bars.length * (barH + 1)})`).call(d3.axisBottom(xScale).ticks(6).tickFormat(d => Utils.formatTime(d - minTime)));
                },
                init() {
                    const allocations = data.allocations || [];
                    this.filteredData = [...allocations];
                    if (allocations.length === 0) {
                        document.getElementById('timetravelBody').innerHTML = '<tr><td colspan="8" class="empty-state">No allocation data</td></tr>';
                        return;
                    }
                    this.sort();
                    const threadSet = new Set((data.allocations || []).map(a => String(a.thread_id)));
                    const threadSel = document.getElementById('lcThreadFilter');
                    threadSet.forEach(tid => { const o = document.createElement('option'); o.value = tid; o.textContent = 'Thread ' + tid; threadSel.appendChild(o); });
                    const typeSet = new Set((data.allocations || []).map(a => this.classifyType(a.type_name)));
                    const typeSel = document.getElementById('lcTypeFilter');
                    typeSet.forEach(cat => { const o = document.createElement('option'); o.value = cat; o.textContent = cat; typeSel.appendChild(o); });
                    const allocs = data.allocations || [];
                    if (allocs.length > 0) {
                        const minT = d3.min(allocs, a => a.timestamp_alloc || 0);
                        const maxT = d3.max(allocs, a => Math.max(a.timestamp_dealloc || 0, (a.timestamp_alloc || 0) + (a.lifetime_ms || 0) * 1000000));
                        document.getElementById('tsTimeStart').textContent = 'T+0';
                        document.getElementById('tsTimeEnd').textContent = 'T+' + Utils.formatTime((maxT - minT));
                    }
                    this.renderTimeSlice();
                    this.renderLifecycleTimeline();
                }
            },

            tasks: {
                filteredData: [],
                currentView: 'timeline',
                
                toggleView(view) {
                    this.currentView = view;
                    this.renderTimeline();
                },
                
                filter() {
                    const filterBy = document.getElementById('taskFilterStatus').value;
                    const asyncTasks = data.async_tasks || [];
                    
                    switch(filterBy) {
                        case 'active':
                            this.filteredData = asyncTasks.filter(t => !t.is_completed);
                            break;
                        case 'completed':
                            this.filteredData = asyncTasks.filter(t => t.is_completed);
                            break;
                        case 'leak':
                            this.filteredData = asyncTasks.filter(t => t.has_potential_leak);
                            break;
                        default:
                            this.filteredData = [...asyncTasks];
                    }
                    
                    this.render();
                    this.renderTimeline();
                },
                
                sort() {
                    const sortBy = document.getElementById('taskSortOrder').value;
                    this.filteredData.sort((a, b) => {
                        switch (sortBy) {
                            case 'memory-desc': return (b.current_memory || 0) - (a.current_memory || 0);
                            case 'memory-asc': return (a.current_memory || 0) - (b.current_memory || 0);
                            case 'peak-desc': return (b.peak_memory || 0) - (a.peak_memory || 0);
                            case 'efficiency-desc': return (b.efficiency_score || 0) - (a.efficiency_score || 0);
                            case 'duration-desc': return (b.duration_ms || 0) - (a.duration_ms || 0);
                            case 'allocations-desc': return (b.total_allocations || 0) - (a.total_allocations || 0);
                            default: return 0;
                        }
                    });
                    this.render();
                },
                
                render() {
                    const container = document.getElementById('taskCardsContainer');
                    if (this.filteredData.length === 0) {
                        container.innerHTML = '<div class="empty-state">No async task data</div>';
                        return;
                    }
                    
                    container.innerHTML = this.filteredData.map(t => {
                        const statusClass = t.is_completed ? 'success' : 'info';
                        const statusText = t.is_completed ? 'Completed' : 'Running';
                        const leakBadge = t.has_potential_leak ? '<span class="badge badge-warning">⚠️ Leak?</span>' : '';
                        const efficiencyClass = (t.efficiency_score || 0) >= 0.8 ? 'success' : (t.efficiency_score || 0) >= 0.5 ? 'warning' : 'danger';
                        const taskType = t.task_type || 'Unknown';
                        
                        return `
                            <div class="card" style="padding: 12px; margin: 0; border-left: 4px solid ${t.is_completed ? '#10b981' : '#3b82f6'};">
                                <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;">
                                    <div>
                                        <span style="font-weight: 600; color: var(--primary);">#${t.task_id}</span>
                                        <span style="margin-left: 8px; font-size: 0.9rem;">${t.task_name || 'Unknown'}</span>
                                    </div>
                                    <div>
                                        <span class="badge badge-${statusClass}">${statusText}</span>
                                        ${leakBadge}
                                    </div>
                                </div>
                                
                                <div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin-bottom: 8px; font-size: 0.85rem;">
                                    <div>
                                        <div style="color: var(--text2); font-size: 0.75rem;">💾 Current Memory</div>
                                        <div style="font-weight: 600;">${Utils.formatBytes(t.current_memory || 0)}</div>
                                    </div>
                                    <div>
                                        <div style="color: var(--text2); font-size: 0.75rem;">🏔️ Peak Memory</div>
                                        <div style="font-weight: 600;">${Utils.formatBytes(t.peak_memory || 0)}</div>
                                    </div>
                                    <div>
                                        <div style="color: var(--text2); font-size: 0.75rem;">📊 Allocations</div>
                                        <div style="font-weight: 600;">${t.total_allocations || 0}</div>
                                    </div>
                                </div>
                                
                                <div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; font-size: 0.85rem;">
                                    <div>
                                        <div style="color: var(--text2); font-size: 0.75rem;">⏱️ Duration</div>
                                        <div style="font-weight: 600;">${(t.duration_ms || 0).toFixed(1)}ms</div>
                                    </div>
                                    <div>
                                        <div style="color: var(--text2); font-size: 0.75rem;">📈 Efficiency</div>
                                        <div class="badge badge-${efficiencyClass}">${((t.efficiency_score || 0) * 100).toFixed(0)}%</div>
                                    </div>
                                    <div>
                                        <div style="color: var(--text2); font-size: 0.75rem;">🏷️ Type</div>
                                        <div style="font-weight: 600; font-size: 0.75rem;">${taskType}</div>
                                    </div>
                                </div>
                            </div>
                        `;
                    }).join('');
                },
                
                renderTimeline() {
                    const container = document.getElementById('taskTimeline');
                    if (!container) return;
                    
                    const tasks = this.filteredData;
                    if (tasks.length === 0) {
                        container.innerHTML = '<div class="empty-state">No task data for timeline</div>';
                        return;
                    }
                    
                    if (this.currentView === 'gantt') {
                        this.renderGanttChart(container, tasks);
                    } else {
                        this.renderTimelineView(container, tasks);
                    }
                },
                
                renderTimelineView(container, tasks) {
                    const maxDuration = Math.max(...tasks.map(t => t.duration_ms || 0));
                    
                    container.innerHTML = `
                        <div style="display: flex; flex-direction: column; gap: 8px;">
                            ${tasks.slice(0, 15).map((t, i) => {
                                const width = maxDuration > 0 ? ((t.duration_ms || 0) / maxDuration * 100) : 0;
                                const statusColor = t.is_completed ? '#10b981' : '#3b82f6';
                                const leakIndicator = t.has_potential_leak ? '⚠️' : '';
                                
                                return `
                                    <div style="display: flex; align-items: center; gap: 12px;">
                                        <div style="width: 120px; font-size: 0.85rem; font-weight: 600; color: var(--primary);">
                                            #${t.task_id} ${leakIndicator}
                                        </div>
                                        <div style="flex: 1; height: 24px; background: var(--bg3); border-radius: 4px; overflow: hidden; position: relative;">
                                            <div style="height: 100%; width: ${width}%; background: ${statusColor}; border-radius: 4px; transition: width 0.3s;"></div>
                                            <div style="position: absolute; top: 50%; left: 8px; transform: translateY(-50%); font-size: 0.75rem; color: white; font-weight: 600; text-shadow: 0 1px 2px rgba(0,0,0,0.5);">
                                                ${t.task_name || 'Unknown'} (${(t.duration_ms || 0).toFixed(1)}ms)
                                            </div>
                                        </div>
                                        <div style="width: 80px; text-align: right; font-size: 0.75rem; color: var(--text2);">
                                            ${Utils.formatBytes(t.peak_memory || 0)}
                                        </div>
                                    </div>
                                `;
                            }).join('')}
                        </div>
                    `;
                },
                
                renderGanttChart(container, tasks) {
                    const sortedTasks = [...tasks].sort((a, b) => (a.created_at_ms || 0) - (b.created_at_ms || 0));
                    const minTime = Math.min(...sortedTasks.map(t => t.created_at_ms || 0));
                    const maxTime = Math.max(...sortedTasks.map(t => (t.created_at_ms || 0) + (t.duration_ms || 0)));
                    const timeRange = maxTime - minTime;
                    
                    container.innerHTML = `
                        <div style="display: flex; flex-direction: column; gap: 6px;">
                            ${sortedTasks.slice(0, 15).map((t, i) => {
                                const start = ((t.created_at_ms || 0) - minTime) / timeRange * 100;
                                const width = (t.duration_ms || 0) / timeRange * 100;
                                const statusColor = t.is_completed ? '#10b981' : '#3b82f6';
                                const leakIndicator = t.has_potential_leak ? 'border: 2px solid #f59e0b;' : '';
                                
                                return `
                                    <div style="display: flex; align-items: center; gap: 12px; height: 28px;">
                                        <div style="width: 100px; font-size: 0.8rem; font-weight: 600; color: var(--primary);">
                                            #${t.task_id}
                                        </div>
                                        <div style="flex: 1; height: 20px; background: var(--bg3); border-radius: 4px; position: relative;">
                                            <div style="position: absolute; left: ${start}%; width: ${Math.max(width, 2)}%; height: 100%; background: ${statusColor}; border-radius: 4px; ${leakIndicator} display: flex; align-items: center; padding: 0 8px;">
                                                <span style="font-size: 0.7rem; color: white; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
                                                    ${t.task_name || 'Task'}
                                                </span>
                                            </div>
                                        </div>
                                    </div>
                                `;
                            }).join('')}
                        </div>
                    `;
                },
                
                init() {
                    const asyncTasks = data.async_tasks || [];
                    const asyncSummary = data.async_summary || {};
                    this.filteredData = [...asyncTasks];
                    
                    // Update stats
                    const activeCount = asyncTasks.filter(t => !t.is_completed).length;
                    const completedCount = asyncTasks.filter(t => t.is_completed).length;
                    const leakCount = asyncTasks.filter(t => t.has_potential_leak).length;
                    const avgEfficiency = asyncTasks.length > 0 
                        ? (asyncTasks.reduce((sum, t) => sum + (t.efficiency_score || 0), 0) / asyncTasks.length * 100).toFixed(0)
                        : 0;
                    const totalMemory = asyncTasks.reduce((sum, t) => sum + (t.current_memory || 0), 0);
                    const avgDuration = asyncTasks.length > 0
                        ? (asyncTasks.reduce((sum, t) => sum + (t.duration_ms || 0), 0) / asyncTasks.length).toFixed(1)
                        : 0;
                    
                    document.getElementById('asyncTaskCount').textContent = (asyncSummary.total_tasks || asyncTasks.length) + ' tasks';
                    document.getElementById('activeTaskCount').textContent = activeCount;
                    document.getElementById('completedTaskCount').textContent = completedCount;
                    document.getElementById('leakTaskCount').textContent = leakCount;
                    document.getElementById('avgEfficiency').textContent = avgEfficiency + '%';
                    document.getElementById('totalAsyncMemory').textContent = Utils.formatBytes(totalMemory);
                    document.getElementById('avgDuration').textContent = avgDuration + 'ms';
                    
                    if (asyncTasks.length === 0) {
                        document.getElementById('taskCardsContainer').innerHTML = '<div class="empty-state">No async task data</div>';
                        document.getElementById('taskTimeline').innerHTML = '<div class="empty-state">No task data for timeline</div>';
                        return;
                    }
                    
                    this.sort();
                    this.renderTimeline();
                    this.initCharts();
                },
                
                initCharts() {
                    const colors = Utils.getChartColors();
                    const tasks = this.filteredData.slice(0, 10);
                    
                    // Duration Chart
                    const durationCtx = document.getElementById('asyncDurationChart');
                    if (durationCtx) {
                        new Chart(durationCtx, {
                            type: 'bar',
                            data: {
                                labels: tasks.map(t => t.task_name || `Task ${t.task_id}`),
                                datasets: [{
                                    label: 'Duration (ms)',
                                    data: tasks.map(t => t.duration_ms || 0),
                                    backgroundColor: colors.primary + '80',
                                    borderColor: colors.primary,
                                    borderWidth: 1
                                }]
                            },
                            options: {
                                responsive: true,
                                maintainAspectRatio: false,
                                scales: {
                                    y: { beginAtZero: true, grid: { color: colors.grid }, ticks: { color: colors.text2 } },
                                    x: { grid: { display: false }, ticks: { color: colors.text2, maxRotation: 45 } }
                                },
                                plugins: { legend: { labels: { color: colors.text } } }
                            }
                        });
                    }
                    
                    // Efficiency Chart
                    const efficiencyCtx = document.getElementById('asyncEfficiencyChart');
                    if (efficiencyCtx) {
                        new Chart(efficiencyCtx, {
                            type: 'doughnut',
                            data: {
                                labels: tasks.map(t => `#${t.task_id}`),
                                datasets: [{
                                    data: tasks.map(t => (t.efficiency_score || 0) * 100),
                                    backgroundColor: tasks.map(t => 
                                        (t.efficiency_score || 0) >= 0.8 ? '#10b981' : 
                                        (t.efficiency_score || 0) >= 0.5 ? '#f59e0b' : '#ef4444'
                                    )
                                }]
                            },
                            options: {
                                responsive: true,
                                maintainAspectRatio: false,
                                plugins: { 
                                    legend: { position: 'right', labels: { color: colors.text, font: { size: 10 } } }
                                }
                            }
                        });
                    }
                    
                    // Heatmap Chart
                    const heatmapCtx = document.getElementById('asyncHeatmapChart');
                    if (heatmapCtx) {
                        const taskLabels = tasks.map(t => t.task_name || `Task ${t.task_id}`);
                        new Chart(heatmapCtx, {
                            type: 'bar',
                            data: {
                                labels: taskLabels,
                                datasets: [
                                    {
                                        label: 'Memory %',
                                        data: tasks.map(t => Math.min(100, ((t.peak_memory || 0) / 1024 / 1024) * 10)),
                                        backgroundColor: '#3b82f680',
                                        borderColor: '#3b82f6',
                                        borderWidth: 1
                                    },
                                    {
                                        label: 'CPU %',
                                        data: tasks.map(t => Math.min(100, (t.total_allocations || 0) * 5)),
                                        backgroundColor: '#10b98180',
                                        borderColor: '#10b981',
                                        borderWidth: 1
                                    },
                                    {
                                        label: 'IO %',
                                        data: tasks.map(t => Math.min(100, ((t.total_bytes || 0) / 1024) * 0.5)),
                                        backgroundColor: '#f59e0b80',
                                        borderColor: '#f59e0b',
                                        borderWidth: 1
                                    }
                                ]
                            },
                            options: {
                                responsive: true,
                                maintainAspectRatio: false,
                                scales: {
                                    y: { 
                                        beginAtZero: true,
                                        max: 100,
                                        grid: { color: colors.grid }, 
                                        ticks: { color: colors.text2, callback: v => v + '%' }
                                    },
                                    x: { grid: { display: false }, ticks: { color: colors.text2, maxRotation: 45 } }
                                },
                                plugins: { legend: { labels: { color: colors.text } } }
                            }
                        });
                    }
                }
            },

            init() {
                this.charts.init();
                this.diagnosis.init();
                this.allocations.init();
                this.threads.init();
                this.relationships.init();
                this.passports.init();
                this.unsafe.init();
                this.timetravel.init();
                this.tasks.init();
            }
        };

        function jumpToSource(file, line) {
            const vscodeUri = `vscode://file/${file}:${line}`;
            const confirmed = confirm(`Open in VS Code?\n\n${file}:${line}\n\nClick OK to open in VS Code, or Cancel to copy to clipboard.`);
            if (confirmed) {
                window.open(vscodeUri, '_blank');
            } else {
                navigator.clipboard.writeText(`${file}:${line}`).then(() => {
                    alert('Source location copied to clipboard!');
                });
            }
        }

        function toggleTheme() {
            const html = document.documentElement;
            const currentTheme = html.getAttribute('data-theme');
            const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
            html.setAttribute('data-theme', newTheme);
            document.getElementById('theme-icon').textContent = newTheme === 'dark' ? '🌙' : '☀️';
            document.getElementById('theme-text').textContent = newTheme === 'dark' ? 'Dark' : 'Light';
            localStorage.setItem('theme', newTheme);
            Dashboard.charts.init();
        }

        (function() {
            const saved = localStorage.getItem('theme');
            if (saved) {
                document.documentElement.setAttribute('data-theme', saved);
                document.getElementById('theme-icon').textContent = saved === 'dark' ? '🌙' : '☀️';
                document.getElementById('theme-text').textContent = saved === 'dark' ? 'Dark' : 'Light';
            } else {
                document.documentElement.setAttribute('data-theme', 'dark');
            }
        })();

        function showMode(mode) {
            document.querySelectorAll('.mode-section').forEach(el => el.classList.remove('active'));
            document.querySelectorAll('.mode-tab').forEach(el => el.classList.remove('active'));
            document.getElementById('mode-' + mode).classList.add('active');
            event.target.classList.add('active');

            if (mode === 'variable') {
                setTimeout(() => Dashboard.variableGraph.init(), 100);
            }
            if (mode === 'taskgraph') {
                setTimeout(() => Dashboard.taskGraph.init(), 100);
            }

        }

        function showTab(tab) {
            document.querySelectorAll('.tab-content').forEach(el => el.classList.remove('active'));
            document.querySelectorAll('.tab-btn').forEach(el => el.classList.remove('active'));
            document.getElementById('tab-' + tab).classList.add('active');
            event.target.classList.add('active');
        }

        function filterTable(tableId, query) {
            const table = document.getElementById(tableId);
            if (!table) return;
            const rows = table.getElementsByTagName('tbody')[0]?.getElementsByTagName('tr') || [];
            query = query.toLowerCase();
            for (let row of rows) {
                const text = row.textContent.toLowerCase();
                row.style.display = text.includes(query) ? '' : 'none';
            }
        }

        function toggleSection(sectionId, btn) {
            const section = document.getElementById(sectionId);
            if (!section) return;
            const isCollapsed = section.style.maxHeight === '200px';
            if (isCollapsed) {
                section.style.maxHeight = 'none';
                btn.textContent = '▲ Collapse';
            } else {
                section.style.maxHeight = '200px';
                btn.textContent = '▼ Expand All';
            }
        }

        function toggleTable(wrapperId, btn) {
            const wrapper = document.getElementById(wrapperId);
            if (!wrapper) return;
            const isCollapsed = wrapper.style.maxHeight === '200px';
            if (isCollapsed) {
                wrapper.style.maxHeight = 'none';
                btn.textContent = '▲ Collapse';
            } else {
                wrapper.style.maxHeight = '200px';
                btn.textContent = '▼ Expand';
            }
        }

        let charts = {};

        function initCharts() {
            const colors = getChartColors();
            const allocations = data.allocations || [];
            const threads = data.threads || [];
            const passports = data.passport_details || [];
            const unsafeReports = data.unsafe_reports || [];

            Object.values(charts).forEach(chart => chart?.destroy?.());
            charts = {};

            const memoryCtx = document.getElementById('memoryChart');
            if (memoryCtx) {
                charts.memory = new Chart(memoryCtx, {
                    type: 'bar',
                    data: {
                        labels: ['Active', 'Freed', 'Leaked'],
                        datasets: [{
                            data: [
                                data.active_allocations || 0,
                                (data.total_allocations || 0) - (data.active_allocations || 0),
                                data.leak_count || 0
                            ],
                            backgroundColor: [colors.primary, '#10b981', '#ef4444']
                        }]
                    },
                    options: {
                        responsive: true,
                        maintainAspectRatio: false,
                        plugins: { legend: { display: false } },
                        scales: {
                            y: { grid: { color: colors.grid }, ticks: { color: colors.text2 } },
                            x: { grid: { display: false }, ticks: { color: colors.text2 } }
                        }
                    }
                });
            }

            const typeCtx = document.getElementById('typeChart');
            if (typeCtx) {
                const typeCount = {};
                allocations.forEach(a => {
                    const type = a.type_name || 'Unknown';
                    typeCount[type] = (typeCount[type] || 0) + 1;
                });
                charts.type = new Chart(typeCtx, {
                    type: 'doughnut',
                    data: {
                        labels: Object.keys(typeCount),
                        datasets: [{
                            data: Object.values(typeCount),
                            backgroundColor: Object.keys(typeCount).map((_, i) =>
                                `hsl(${i * 360 / Math.max(Object.keys(typeCount).length, 1)}, 70%, 50%)`)
                        }]
                    },
                    options: {
                        responsive: true,
                        maintainAspectRatio: false,
                        plugins: { legend: { position: 'right', labels: { color: colors.text } } }
                    }
                });
            }

            const threadCtx = document.getElementById('threadChart');
            if (threadCtx) {
                const shortThreadIds = threads.map(t => {
                    const tid = t.thread_id || '?';
                    const match = String(tid).match(/(\d+)/);
                    return match ? 'T' + match[1].slice(-4) : String(tid).slice(-6);
                });
                charts.thread = new Chart(threadCtx, {
                    type: 'bar',
                    data: {
                        labels: shortThreadIds,
                        datasets: [{
                            label: 'Allocations',
                            data: threads.map(t => parseInt(t.allocation_count) || 0),
                            backgroundColor: colors.primary
                        }]
                    },
                    options: {
                        responsive: true,
                        maintainAspectRatio: false,
                        indexAxis: 'y',
                        scales: {
                            y: { grid: { display: false }, ticks: { color: colors.text2 } },
                            x: { grid: { color: colors.grid }, ticks: { color: colors.text2 } }
                        }
                    }
                });
            }

            const asyncCtx = document.getElementById('asyncChart');
            if (asyncCtx) {
                const asyncTasks = data.async_tasks || [];
                const asyncSummary = data.async_summary || {};
                if (asyncTasks.length > 0) {
                    const sortedTasks = [...asyncTasks].sort((a, b) => a.task_id - b.task_id);
                    const timeLabels = sortedTasks.map((t, i) => `T+${i * 100}ms`);
                    const cpuUsage = sortedTasks.map(t => Math.min(100, (t.total_allocations || 0) * 5));
                    const memoryUsage = sortedTasks.map(t => Math.min(100, ((t.peak_memory || 0) / 1024 / 1024) * 2));
                    const ioUsage = sortedTasks.map(t => Math.min(100, ((t.total_bytes || 0) / 1024) * 0.5));
                    charts.async = new Chart(asyncCtx, {
                        type: 'line',
                        data: {
                            labels: timeLabels,
                            datasets: [
                                { label: 'CPU Usage %', data: cpuUsage, borderColor: '#3b82f6', backgroundColor: '#3b82f633', fill: false, tension: 0.4 },
                                { label: 'Memory %', data: memoryUsage, borderColor: '#10b981', backgroundColor: '#10b98133', fill: false, tension: 0.4 },
                                { label: 'IO Usage %', data: ioUsage, borderColor: '#f59e0b', backgroundColor: '#f59e0b33', fill: false, tension: 0.4 }
                            ]
                        },
                        options: {
                            responsive: true,
                            maintainAspectRatio: false,
                            interaction: { mode: 'index', intersect: false },
                            plugins: { legend: { display: true, position: 'top', labels: { color: colors.text } }, title: { display: true, text: `Resource Consumption Over Time | Tasks: ${asyncSummary.total_tasks || 0}`, color: colors.text2 } },
                            scales: { y: { min: 0, max: 100, grid: { color: colors.grid }, ticks: { color: colors.text2, callback: (v) => v + '%' }, title: { display: true, text: 'Resource Usage %', color: colors.text2 } }, x: { grid: { display: false }, ticks: { color: colors.text2 }, title: { display: true, text: 'Time', color: colors.text2 } } }
                        }
                    });
                } else {
                    const allocData = allocations.slice(0, 15).map((a, i) => ({
                        label: a.var_name || a.type_name || `Alloc ${i}`,
                        value: parseInt(a.size) || 0
                    }));
                    charts.async = new Chart(asyncCtx, {
                        type: 'line',
                        data: {
                            labels: allocData.map(t => t.label.substring(0, 20)),
                            datasets: [{
                                label: 'Memory Usage',
                                data: allocData.map(t => t.value),
                                borderColor: colors.primary,
                                fill: true,
                                backgroundColor: colors.primary + '20'
                            }]
                        },
                        options: {
                            responsive: true,
                            maintainAspectRatio: false,
                            scales: {
                                y: { grid: { color: colors.grid }, ticks: { color: colors.text2 } },
                                x: { grid: { display: false }, ticks: { color: colors.text2, maxRotation: 45 } }
                            }
                        }
                    });
                }
            }

            const passportCtx = document.getElementById('passportChart');
            if (passportCtx) {
                const riskCount = { high: 0, medium: 0, low: 0 };
                passports.forEach(p => {
                    const risk = p.risk_level || 'low';
                    riskCount[risk] = (riskCount[risk] || 0) + 1;
                });
                document.getElementById('highRiskCount').textContent = riskCount.high;
                document.getElementById('mediumRiskCount').textContent = riskCount.medium;
                document.getElementById('lowRiskCount').textContent = riskCount.low;

                charts.passport = new Chart(passportCtx, {
                    type: 'radar',
                    data: {
                        labels: ['Size', 'Lifetime', 'Refs', 'Clones', 'Borrows'],
                        datasets: [{
                            label: 'Risk Metrics',
                            data: [
                                Math.min(100, passports.length * 5),
                                Math.min(100, riskCount.high * 20),
                                Math.min(100, riskCount.medium * 15),
                                Math.min(100, riskCount.low * 10),
                                50
                            ],
                            borderColor: colors.primary,
                            backgroundColor: colors.primary + '20'
                        }]
                    },
                    options: {
                        responsive: true,
                        maintainAspectRatio: false,
                        scales: {
                            r: {
                                grid: { color: colors.grid },
                                angleLines: { color: colors.grid },
                                ticks: { display: false }
                            }
                        },
                        plugins: { legend: { labels: { color: colors.text } } }
                    }
                });
            }

            const unsafeCtx = document.getElementById('unsafeChart');
            if (unsafeCtx) {
                charts.unsafe = new Chart(unsafeCtx, {
                    type: 'bar',
                    data: {
                        labels: ['Unsafe Ops', 'FFI Calls', 'Cross-Boundary'],
                        datasets: [{
                            data: [
                                data.unsafe_count || 0,
                                data.ffi_count || 0,
                                unsafeReports.reduce((sum, r) => sum + (r.cross_boundary_events?.length || 0), 0)
                            ],
                            backgroundColor: ['#ef4444', '#f59e0b', '#3b82f6']
                        }]
                    },
                    options: {
                        responsive: true,
                        maintainAspectRatio: false,
                        scales: {
                            y: { grid: { color: colors.grid }, ticks: { color: colors.text2 } },
                            x: { grid: { display: false }, ticks: { color: colors.text2 } }
                        }
                    }
                });
            }

            const unsafeRiskCtx = document.getElementById('unsafeRiskChart');
            if (unsafeRiskCtx) {
                const riskGroups = { high: 0, medium: 0, low: 0 };
                unsafeReports.forEach(r => {
                    const risk = r.risk_level || 'low';
                    riskGroups[risk] = (riskGroups[risk] || 0) + 1;
                });
                charts.unsafeRisk = new Chart(unsafeRiskCtx, {
                    type: 'pie',
                    data: {
                        labels: ['High Risk', 'Medium Risk', 'Low Risk'],
                        datasets: [{
                            data: [riskGroups.high, riskGroups.medium, riskGroups.low],
                            backgroundColor: ['#ef4444', '#f59e0b', '#10b981']
                        }]
                    },
                    options: {
                        responsive: true,
                        maintainAspectRatio: false,
                        plugins: { legend: { position: 'bottom', labels: { color: colors.text } } }
                    }
                });
            }
        }

        function initRelationshipGraph() {
            const container = document.getElementById('relationshipGraph');
            if (!container) return;

            container.innerHTML = '';

            const relationships = data.relationships || [];

            if (relationships.length === 0) {
                container.innerHTML = '<div class="empty-state">No relationship data available. Try using clones, borrows, or smart pointers (Arc/Rc) in your code.</div>';
                return;
            }

            document.getElementById('relationshipCount').textContent = relationships.length + ' relationships';

            const width = container.clientWidth;
            const height = container.clientHeight || 400;

            const svg = d3.select(container)
                .append('svg')
                .attr('width', width)
                .attr('height', height);

            const nodeMap = new Map();
            relationships.forEach((r) => {
                const sourceId = r.source_ptr;
                const targetId = r.target_ptr;
                if (!nodeMap.has(sourceId)) {
                    nodeMap.set(sourceId, {
                        id: sourceId,
                        ptr: sourceId,
                        name: r.source_var_name || ('Ptr ' + sourceId),
                        type: r.relationship_type,
                        isSource: true
                    });
                }
                if (!nodeMap.has(targetId)) {
                    nodeMap.set(targetId, {
                        id: targetId,
                        ptr: targetId,
                        name: r.target_var_name || ('Ptr ' + targetId),
                        type: r.relationship_type,
                        isSource: false
                    });
                }
            });

            const nodes = Array.from(nodeMap.values());
            const nodeIds = new Set(nodes.map(n => n.id));
            const links = relationships
                .map(r => ({
                    source: r.source_ptr,
                    target: r.target_ptr,
                    type: r.relationship_type || 'reference',
                    strength: r.strength || 0.5,
                    typeName: r.type_name || 'unknown'
                }))
                .filter(l => nodeIds.has(l.source) && nodeIds.has(l.target));

            const colorMap = {
                'clone': '#10b981',
                'ownership_transfer': '#dc2626',
                'immutable_borrow': '#3b82f6',
                'mutable_borrow': '#f59e0b',
                'smart_pointer_arc': '#8b5cf6',
                'smart_pointer_rc': '#8b5cf6',
                'type_similarity': '#8b5cf6',
                'Arc': '#8b5cf6',
                'Rc': '#8b5cf6',
                'contains': '#ec4899',
                'Contains': '#ec4899',
                'reference': '#64748b'
            };

            const getColor = (type) => colorMap[type] || '#64748b';

            const simulation = d3.forceSimulation(nodes)
                .force('link', d3.forceLink(links).id(d => d.id).distance(120))
                .force('charge', d3.forceManyBody().strength(-400))
                .force('center', d3.forceCenter(width / 2, height / 2))
                .force('collision', d3.forceCollide().radius(40));

            const link = svg.append('g')
                .selectAll('line')
                .data(links)
                .join('line')
                .attr('class', 'link')
                .attr('stroke', d => getColor(d.type))
                .attr('stroke-width', d => Math.max(1, d.strength * 4));

            const node = svg.append('g')
                .selectAll('g')
                .data(nodes)
                .join('g')
                .attr('class', 'node')
                .call(d3.drag()
                    .on('start', dragstarted)
                    .on('drag', dragged)
                    .on('end', dragended));

            node.append('circle')
                .attr('r', 14)
                .attr('fill', d => getColor(d.type));

            node.append('text')
                .attr('dx', 18)
                .attr('dy', 4)
                .text(d => {
                    const name = d.name || 'unknown';
                    return name.length > 12 ? name.substring(0, 10) + '..' : name;
                })
                .style('font-size', '11px')
                .style('fill', 'var(--text)');

            const tooltip = document.getElementById('tooltip');

            node.on('mouseover', (event, d) => {
                tooltip.style.display = 'block';
                tooltip.innerHTML = `<strong>${d.name || 'Unknown'}</strong><br>Type: ${d.type || 'unknown'}<br>Ptr: ${d.ptr || 'N/A'}`;
                tooltip.style.left = (event.pageX + 10) + 'px';
                tooltip.style.top = (event.pageY - 10) + 'px';
            }).on('mouseout', () => {
                tooltip.style.display = 'none';
            });

            simulation.on('tick', () => {
                link
                    .attr('x1', d => d.source?.x || 0)
                    .attr('y1', d => d.source?.y || 0)
                    .attr('x2', d => d.target?.x || 0)
                    .attr('y2', d => d.target?.y || 0);

                node.attr('transform', d => `translate(${d.x || 0},${d.y || 0})`);
            });

            function dragstarted(event) {
                if (!event.active) simulation.alphaTarget(0.3).restart();
                event.subject.fx = event.subject.x || 0;
                event.subject.fy = event.subject.y || 0;
            }

            function dragged(event) {
                event.subject.fx = event.x || 0;
                event.subject.fy = event.y || 0;
            }

            function dragended(event) {
                if (!event.active) simulation.alphaTarget(0);
                event.subject.fx = null;
                event.subject.fy = null;
            }
        }

        const PAGE_SIZE = 100;
        let allocShowAll = false;
        let allocCurrentPage = 1;
        let allocFilteredData = [];

        function toggleAllocationsTable() {
            const wrapper = document.getElementById('allocationsWrapper');
            const btn = document.getElementById('toggleAllocBtn');
            const pagination = document.getElementById('allocationsPagination');
            allocShowAll = !allocShowAll;

            if (allocShowAll) {
                wrapper.style.maxHeight = 'none';
                wrapper.style.overflow = 'visible';
                btn.textContent = '▲ Collapse';
                if (allocFilteredData.length > PAGE_SIZE) {
                    pagination.style.display = 'flex';
                }
            } else {
                wrapper.style.maxHeight = '400px';
                wrapper.style.overflow = 'auto';
                btn.textContent = '▼ Show All';
                pagination.style.display = 'none';
                renderAllocPage(1);
            }
        }

        function renderAllocPage(page) {
            const tbody = document.getElementById('allocationsBody');
            const start = (page - 1) * PAGE_SIZE;
            const end = start + PAGE_SIZE;
            const pageData = allocFilteredData.slice(start, end);

            tbody.innerHTML = pageData.map(a => `
                <tr>
                    <td><code>${a.address || '0x0'}</code></td>
                    <td>${a.var_name || 'unknown'}</td>
                    <td>${a.type_name || 'Unknown'}</td>
                    <td>${Utils.formatBytes(parseInt(a.size) || 0)}</td>
                    <td>${a.thread_id || 'N/A'}</td>
                    <td>${Utils.formatTime(parseInt(a.lifetime_ms || 0) * 1000000)}</td>
                    <td>${a.is_leaked ? '<span class="badge badge-danger">Leaked</span>' :
                          a.timestamp_dealloc > 0 ? '<span class="badge badge-success">Freed</span>' :
                          '<span class="badge badge-info">Active</span>'}</td>
                </tr>
            `).join('');

            allocCurrentPage = page;
            const totalPages = Math.ceil(allocFilteredData.length / PAGE_SIZE);
            document.getElementById('allocPageInfo').textContent = `Page ${page} of ${totalPages}`;
            document.getElementById('prevAllocBtn').disabled = page <= 1;
            document.getElementById('nextAllocBtn').disabled = page >= totalPages;
        }

        function prevAllocPage() {
            if (allocCurrentPage > 1) {
                renderAllocPage(allocCurrentPage - 1);
            }
        }

        function nextAllocPage() {
            const totalPages = Math.ceil(allocFilteredData.length / PAGE_SIZE);
            if (allocCurrentPage < totalPages) {
                renderAllocPage(allocCurrentPage + 1);
            }
        }

        function populateAllocations() {
            const tbody = document.getElementById('allocationsBody');
            const data_list = data.allocations || [];
            allocFilteredData = data_list;
            document.getElementById('allocCount').textContent = data_list.length + ' items';

            if (data_list.length === 0) {
                tbody.innerHTML = '<tr><td colspan="7" class="empty-state">No allocation data</td></tr>';
                return;
            }

            if (data_list.length <= PAGE_SIZE) {
                tbody.innerHTML = data_list.map(a => `
                    <tr>
                        <td><code>${a.address || '0x0'}</code></td>
                        <td>${a.var_name || 'unknown'}</td>
                        <td>${a.type_name || 'Unknown'}</td>
                        <td>${Utils.formatBytes(parseInt(a.size) || 0)}</td>
                        <td>${a.thread_id || 'N/A'}</td>
                        <td>${Utils.formatTime(parseInt(a.lifetime_ms || 0) * 1000000)}</td>
                        <td>${a.is_leaked ? '<span class="badge badge-danger">Leaked</span>' :
                              a.timestamp_dealloc > 0 ? '<span class="badge badge-success">Freed</span>' :
                              '<span class="badge badge-info">Active</span>'}</td>
                    </tr>
                `).join('');
            } else {
                renderAllocPage(1);
            }
        }

        let threadFilteredData = [];

        function sortThreads() {
            const sortBy = document.getElementById('threadSortOrder').value;
            threadFilteredData.sort((a, b) => {
                switch (sortBy) {
                    case 'alloc-desc': return (b.allocation_count || 0) - (a.allocation_count || 0);
                    case 'alloc-asc': return (a.allocation_count || 0) - (b.allocation_count || 0);
                    case 'memory-desc': return (b.current_memory_bytes || 0) - (a.current_memory_bytes || 0);
                    case 'memory-asc': return (a.current_memory_bytes || 0) - (b.current_memory_bytes || 0);
                    case 'thread-id': return (a.thread_id || '0').localeCompare(b.thread_id || '0');
                    default: return 0;
                }
            });
            renderThreads();
        }

        function renderThreads() {
            const tbody = document.getElementById('threadTableBody');
            tbody.innerHTML = threadFilteredData.map(t => `
                <tr>
                    <td><code>${t.thread_id || '0'}</code></td>
                    <td>${t.allocation_count || 0}</td>
                    <td>${t.current_memory || '0 B'}</td>
                    <td>${t.peak_memory || '0 B'}</td>
                    <td>${t.total_allocated || '0 B'}</td>
                    <td><span class="badge badge-success">active</span></td>
                </tr>
            `).join('');
        }

        function populateThreads() {
            const threads = data.threads || [];
            threadFilteredData = [...threads];
            document.getElementById('threadCount').textContent = threads.length + ' threads';

            if (threads.length === 0) {
                document.getElementById('threadTableBody').innerHTML = '<tr><td colspan="6" class="empty-state">No thread data</td></tr>';
                return;
            }

            sortThreads();
        }

        let relShowAll = false;
        let relFilteredData = [];

        function toggleRelTable() {
            const wrapper = document.getElementById('relTableWrapper');
            const btn = document.getElementById('toggleRelBtn');
            relShowAll = !relShowAll;

            if (relShowAll) {
                wrapper.style.maxHeight = 'none';
                wrapper.style.overflow = 'visible';
                btn.textContent = '▲ Collapse';
            } else {
                wrapper.style.maxHeight = '300px';
                wrapper.style.overflow = 'auto';
                btn.textContent = '▼ Expand';
            }
        }

        function sortRelationships() {
            const sortBy = document.getElementById('relSortOrder').value;
            relFilteredData.sort((a, b) => {
                switch (sortBy) {
                    case 'strength-desc': return (b.strength || 0.5) - (a.strength || 0.5);
                    case 'strength-asc': return (a.strength || 0.5) - (b.strength || 0.5);
                    case 'type': return (a.relationship_type || '').localeCompare(b.relationship_type || '');
                    case 'source': return (a.source_var_name || '').localeCompare(b.source_var_name || '');
                    default: return 0;
                }
            });
            renderRelationships();
        }

        function renderRelationships() {
            const tbody = document.getElementById('relationshipsBody');
            tbody.innerHTML = relFilteredData.map(r => `
                <tr>
                    <td>${r.source_var_name || ('Ptr ' + r.source_ptr) || 'N/A'}</td>
                    <td>${r.target_var_name || ('Ptr ' + r.target_ptr) || 'N/A'}</td>
                    <td><span class="badge badge-info">${r.relationship_type || 'reference'}</span></td>
                    <td>${r.type_name || 'Unknown'}</td>
                    <td>${(((r.strength || 0.5) * 100)).toFixed(0)}%</td>
                </tr>
            `).join('');
        }

        function populateRelationships() {
            const relationships = data.relationships || [];
            relFilteredData = [...relationships];
            document.getElementById('relationshipCount2').textContent = relationships.length + ' relationships';

            if (relationships.length === 0) {
                document.getElementById('relationshipsBody').innerHTML = '<tr><td colspan="5" class="empty-state">No relationship data</td></tr>';
                return;
            }

            sortRelationships();
        }

        function populatePassports() {
            const passports = data.passport_details || [];
            document.getElementById('passportBadge').textContent = passports.length + ' passports';

            if (passports.length === 0) {
                const tbody = document.getElementById('passportTableBody');
                if (tbody) tbody.innerHTML = '<tr><td colspan="6" class="empty-state">No passport data</td></tr>';
                return;
            }

            const container = document.getElementById('passportCardContainer');
            if (container) {
                container.innerHTML = passports.slice(0, 20).map(p => {
                    const riskClass = p.risk_level === 'high' ? 'risk-high' :
                                      p.risk_level === 'medium' ? 'risk-medium' : 'risk-low';
                    return `<div class="detail-panel" style="border-left: 4px solid ${p.is_leaked ? '#ef4444' : 'var(--primary)'};">
                        <div style="display:flex;justify-content:space-between;align-items:center;">
                            <h4 style="color:var(--primary);margin:0;">${p.var_name || 'unknown'}</h4>
                            <span class="badge ${p.is_leaked ? 'badge-danger' : 'badge-success'}">${p.is_leaked ? '🔴 LEAKED' : '🟢 Active'}</span>
                        </div>
                        <div style="margin-top:8px;display:grid;grid-template-columns:repeat(auto-fit,minmax(100px,1fr));gap:8px;">
                            <div><span class="detail-label">Type</span><div class="detail-value">${p.type_name || 'Unknown'}</div></div>
                            <div><span class="detail-label">Size</span><div class="detail-value">${Utils.formatBytes(parseInt(p.size_bytes) || 0)}</div></div>
                            <div><span class="detail-label">Status</span><div class="detail-value">${p.status || 'N/A'}</div></div>
                            <div><span class="detail-label">Risk</span><div class="detail-value ${riskClass}">${p.risk_level || 'low'}</div></div>
                        </div>
                    </div>`;
                }).join('');
            }

            const tbody = document.getElementById('passportTableBody');
            if (tbody) {
                tbody.innerHTML = passports.map(p => {
                    const riskClass = p.risk_level === 'high' ? 'risk-high' :
                                      p.risk_level === 'medium' ? 'risk-medium' : 'risk-low';
                    return `<tr>
                        <td><code>${(p.passport_id || 'N/A').substring(0, 16)}...</code></td>
                        <td>${p.var_name || 'unknown'}</td>
                        <td>${p.type_name || 'Unknown'}</td>
                        <td>${Utils.formatBytes(parseInt(p.size_bytes) || 0)}</td>
                        <td>${p.is_leaked ? '<span class="badge badge-danger">Leaked</span>' : '<span class="badge badge-success">Normal</span>'}</td>
                        <td class="${riskClass}">${p.risk_level || 'low'}</td>
                    </tr>`;
                }).join('');
            }

            const timeline = document.getElementById('passportTimeline');
            timeline.innerHTML = passports.slice(0, 10).map(p => `
                <div class="timeline-item ${p.is_leaked ? 'leaked' : 'active'}">
                    <strong>${p.var_name || 'unknown'}</strong>
                    <span class="badge ${p.is_leaked ? 'badge-danger' : 'badge-success'}">${p.status || 'active'}</span>
                    <div style="color: var(--text2); font-size: 0.85rem;">
                        ${Utils.formatBytes(parseInt(p.size_bytes) || 0)} | ${p.type_name || 'Unknown'}
                    </div>
                </div>
            `).join('');
        }

        function populateUnsafeReports() {
            const container = document.getElementById('unsafeReportsContainer');
            const unsafeReports = data.unsafe_reports || [];
            document.getElementById('unsafeBadge').textContent = unsafeReports.length + ' reports';

            if (unsafeReports.length === 0) {
                container.innerHTML = '<div class="empty-state">✅ No high risk operations detected</div>';
                document.getElementById('lifecycleFlowContainer').innerHTML = '<div class="empty-state">No lifecycle data available</div>';
                document.getElementById('lifecycleFlowCount').textContent = '0 flows';
                return;
            }

            const highRisk = unsafeReports.filter(r => r.risk_level === 'high').slice(0, 10);

            if (highRisk.length === 0) {
                container.innerHTML = '<div class="empty-state">✅ No high risk operations</div>';
            } else {
                container.innerHTML = highRisk.map(r => `
                    <div class="detail-panel" style="border-left: 4px solid var(--danger); margin-bottom: 12px;">
                        <div style="display: flex; justify-content: space-between; align-items: center;">
                            <h4 style="color: var(--danger); margin: 0;">⚠️ ${r.var_name || 'unknown'}</h4>
                            <span class="badge badge-danger">High Risk</span>
                        </div>
                        <div style="margin-top: 12px; display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 8px;">
                            <div><span class="detail-label">Address</span><div class="detail-value">${r.allocation_ptr || 'N/A'}</div></div>
                            <div><span class="detail-label">Type</span><div class="detail-value">${r.type_name || 'Unknown'}</div></div>
                            <div><span class="detail-label">Size</span><div class="detail-value">${Utils.formatBytes(parseInt(r.size_bytes) || 0)}</div></div>
                            <div><span class="detail-label">Status</span><div class="detail-value">${r.status || 'unknown'}</div></div>
                        </div>
                        ${r.risk_factors && r.risk_factors.length > 0 ? `
                        <div style="margin-top: 12px;">
                            <span class="detail-label">Risk Factors:</span>
                            <ul style="margin: 8px 0 0 20px; color: var(--danger);">
                                ${r.risk_factors.map(f => `<li>${f}</li>`).join('')}
                            </ul>
                        </div>
                        ` : ''}
                    </div>
                `).join('');
            }

            const ffiBody = document.getElementById('ffiTableBody');
            const ffiEvents = [];
            const lifecycleFlows = [];

            unsafeReports.forEach(r => {
                if (r.cross_boundary_events) {
                    r.cross_boundary_events.forEach(e => ffiEvents.push({...e, var_name: r.var_name, size_bytes: r.size_bytes}));
                }
                if (r.lifecycle_events && r.lifecycle_events.length > 0) {
                    lifecycleFlows.push({
                        var_name: r.var_name,
                        allocation_ptr: r.allocation_ptr,
                        size_bytes: r.size_bytes,
                        events: r.lifecycle_events
                    });
                }
            });

            document.getElementById('lifecycleFlowCount').textContent = lifecycleFlows.length + ' flows';
            const flowContainer = document.getElementById('lifecycleFlowContainer');
            if (lifecycleFlows.length === 0) {
                flowContainer.innerHTML = '<div class="empty-state">No lifecycle flow data available</div>';
            } else {
                flowContainer.innerHTML = lifecycleFlows.slice(0, 10).map(flow => {
                    const flowSteps = flow.events.map((e, idx) => {
                        const isLast = idx === flow.events.length - 1;
                        const stepColor = e.color || '#64748b';
                        return `
                            <div style="display: flex; align-items: center; gap: 8px;">
                                <div style="width: 32px; height: 32px; border-radius: 50%; background: ${stepColor}; display: flex; align-items: center; justify-content: center; font-size: 14px;">${e.icon || '•'}</div>
                                <div style="flex: 1;">
                                    <div style="font-weight: 600; color: var(--text);">${e.context || e.event_type}</div>
                                    <div style="font-size: 0.8rem; color: var(--text2);">${flow.var_name} | ${Utils.formatBytes(flow.size_bytes || 0)}</div>
                                </div>
                                ${!isLast ? '<div style="width: 2px; height: 24px; background: var(--border);"></div>' : ''}
                            </div>
                        `;
                    }).join('');

                    return `
                        <div class="detail-panel" style="border: 1px solid var(--border); border-radius: 8px; padding: 16px;">
                            <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px;">
                                <h4 style="color: var(--primary); margin: 0;">${flow.var_name}</h4>
                                <code style="font-size: 0.8rem; color: var(--text2);">${flow.allocation_ptr || 'N/A'}</code>
                            </div>
                            <div style="display: flex; align-items: flex-start; gap: 12px; flex-wrap: wrap;">
                                ${flowSteps}
                            </div>
                        </div>
                    `;
                }).join('');
            }

            if (ffiEvents.length === 0) {
                ffiBody.innerHTML = '<tr><td colspan="5" class="empty-state">No FFI events</td></tr>';
            } else {
                ffiBody.innerHTML = ffiEvents.slice(0, 50).map(e => `
                    <tr>
                        <td><span class="badge badge-warning">${e.event_type || 'N/A'}</span></td>
                        <td>${e.from_context || 'N/A'}</td>
                        <td>${e.to_context || 'N/A'}</td>
                        <td>${e.timestamp || 0}</td>
                        <td>${e.icon || '→'}</td>
                    </tr>
                `).join('');
            }
        }

        let ttFilteredData = [];

        function filterTimetravel() {
            const query = document.getElementById('timetravelSearch').value.toLowerCase();
            ttFilteredData = data.allocations?.filter(a =>
                (a.address || '').toLowerCase().includes(query) ||
                (a.var_name || '').toLowerCase().includes(query)
            ) || [];
            sortTimetravel();
        }

        function sortTimetravel() {
            const sortBy = document.getElementById('ttSortOrder').value;
            ttFilteredData.sort((a, b) => {
                switch (sortBy) {
                    case 'time-asc': return (a.timestamp_alloc || 0) - (b.timestamp_alloc || 0);
                    case 'time-desc': return (b.timestamp_alloc || 0) - (a.timestamp_alloc || 0);
                    case 'size-desc': return (b.size || 0) - (a.size || 0);
                    case 'size-asc': return (a.size || 0) - (b.size || 0);
                    case 'lifetime-desc': return (b.lifetime_ms || 0) - (a.lifetime_ms || 0);
                    default: return 0;
                }
            });
            renderTimetravelTable();
        }

        function renderTimetravelTable() {
            const tbody = document.getElementById('timetravelBody');
            tbody.innerHTML = ttFilteredData.slice(0, 100).map(a => {
                const sourceLoc = (a.source_file && a.source_line)
                    ? `<a href="#" onclick="jumpToSource('${a.source_file}', ${a.source_line}); return false;" title="Jump to source" style="color: var(--primary); text-decoration: underline;">${a.source_file}:${a.source_line}</a>`
                    : '<span style="color: var(--text2);">—</span>';
                return `
                <tr onclick="showTimetravelDetail('${a.address}')" style="cursor: pointer;">
                    <td><code>${a.address || '0x0'}</code></td>
                    <td>${a.var_name || 'unknown'}</td>
                    <td>${Utils.formatBytes(parseInt(a.size) || 0)}</td>
                    <td style="font-size: 0.85rem;">${sourceLoc}</td>
                    <td>${Utils.formatTime(parseInt(a.timestamp_alloc) || 0)}</td>
                    <td>${Utils.formatTime(parseInt(a.lifetime_ms || 0) * 1000000)}</td>
                    <td>${a.is_leaked ? '<span class="badge badge-danger">Leaked</span>' :
                          a.timestamp_dealloc > 0 ? '<span class="badge badge-success">Freed</span>' :
                          '<span class="badge badge-info">Active</span>'}</td>
                    <td><button class="btn-link">View History</button></td>
                </tr>
            `}).join('');
        }

        function populateTimetravel() {
            const allocations = data.allocations || [];
            ttFilteredData = [...allocations];

            if (allocations.length === 0) {
                document.getElementById('timetravelBody').innerHTML = '<tr><td colspan="7" class="empty-state">No allocation data</td></tr>';
                return;
            }

            sortTimetravel();
        }

        function showTimetravelDetail(address) {
            const alloc = DataIndex.allocationByPtr(address) || data.allocations?.find(a => a.address === address);
            if (!alloc) {
                alert('Allocation not found: ' + address);
                return;
            }

            document.getElementById('timetravelDetail').style.display = 'block';
            document.getElementById('ttAddress').textContent = address;
            document.getElementById('ttVarName').textContent = alloc.var_name || 'unknown';
            document.getElementById('ttType').textContent = alloc.type_name || 'Unknown';
            document.getElementById('ttSize').textContent = Utils.formatBytes(parseInt(alloc.size) || 0);
            document.getElementById('ttStatus').innerHTML = alloc.is_leaked ?
                '<span class="badge badge-danger">Leaked</span>' :
                '<span class="badge badge-success">Normal</span>';
            const provEl = document.getElementById('ttProvenance');
            if (provEl) provEl.textContent = (alloc.provenance || 'allocator') + ' | Evidence: ' + (alloc.evidence || 'Unknown') + ' | Confidence: ' + (alloc.confidence || 'Unknown');

            const timeline = document.getElementById('ttTimeline');
            const events = [
                { type: 'alloc', time: alloc.timestamp_alloc || 0, desc: 'Allocation', icon: '➕' },
            ];
            if (alloc.timestamp_dealloc > 0) {
                events.push({ type: 'dealloc', time: alloc.timestamp_dealloc, desc: 'Deallocation', icon: '➖' });
            }
            if (alloc.is_leaked) {
                events.push({ type: 'leak', time: (alloc.timestamp_alloc || 0) + parseInt(alloc.lifetime_ms || 0) * 1000000, desc: 'Leak Detected', icon: '⚠️' });
            }

            timeline.innerHTML = events.map(e => `
                <div class="timeline-item ${e.type}">
                    <strong>${e.icon} ${e.desc}</strong>
                    <div style="color: var(--text2); font-size: 0.85rem;">
                        Timestamp: ${e.time} | Delta: +${Utils.formatTime(e.time - events[0].time)}
                    </div>
                </div>
            `).join('');

            const layoutEl = document.getElementById('ttLayout');
            if (layoutEl) {
                const ls = alloc.layout_snapshot;
                if (ls) {
                    const cards = [];
                    if (ls.size_of_t) cards.push(`<div class="detail-panel" style="border-left:4px solid var(--info);padding:8px;"><span class="detail-label">SizeOf(T)</span><div class="detail-value">${Utils.formatBytes(ls.size_of_t)}</div></div>`);
                    if (ls.align_of_t) cards.push(`<div class="detail-panel" style="border-left:4px solid var(--warning);padding:8px;"><span class="detail-label">AlignOf(T)</span><div class="detail-value">${ls.align_of_t}</div></div>`);
                    if (ls.layout_kind) cards.push(`<div class="detail-panel" style="border-left:4px solid var(--primary);padding:8px;"><span class="detail-label">Layout Kind</span><div class="detail-value">${ls.layout_kind}</div></div>`);
                    if (ls.repr_hint && ls.repr_hint !== 'Unknown') cards.push(`<div class="detail-panel" style="border-left:4px solid #8b5cf6;padding:8px;"><span class="detail-label">Repr Hint</span><div class="detail-value">${ls.repr_hint}</div></div>`);
                    if (ls.pointer_width && ls.pointer_width !== 'Unknown') cards.push(`<div class="detail-panel" style="border-left:4px solid #f59e0b;padding:8px;"><span class="detail-label">Ptr Width</span><div class="detail-value">${ls.pointer_width}</div></div>`);
                    if (ls.element_size) cards.push(`<div class="detail-panel" style="border-left:4px solid #10b981;padding:8px;"><span class="detail-label">Element Size</span><div class="detail-value">${ls.element_size} B</div></div>`);
                    if (ls.container_len !== null && ls.container_len !== undefined) cards.push(`<div class="detail-panel" style="border-left:4px solid #06b6d4;padding:8px;"><span class="detail-label">Length</span><div class="detail-value">${ls.container_len}</div></div>`);
                    if (ls.container_capacity !== null && ls.container_capacity !== undefined) cards.push(`<div class="detail-panel" style="border-left:4px solid #3b82f6;padding:8px;"><span class="detail-label">Capacity</span><div class="detail-value">${ls.container_capacity}</div></div>`);
                    if (ls.strong_count !== null && ls.strong_count !== undefined) cards.push(`<div class="detail-panel" style="border-left:4px solid #ef4444;padding:8px;"><span class="detail-label">Strong Ref</span><div class="detail-value">${ls.strong_count}</div></div>`);
                    if (ls.weak_count !== null && ls.weak_count !== undefined) cards.push(`<div class="detail-panel" style="border-left:4px solid #64748b;padding:8px;"><span class="detail-label">Weak Ref</span><div class="detail-value">${ls.weak_count}</div></div>`);
                    if (ls.pointee_type) cards.push(`<div class="detail-panel" style="border-left:4px solid #8b5cf6;padding:8px;"><span class="detail-label">Pointee</span><div class="detail-value" style="font-size:0.75rem;">${ls.pointee_type}</div></div>`);
                    layoutEl.innerHTML = cards.join('') || '<div style="color:var(--text2);font-size:0.85rem;">No type layout data</div>';
                } else {
                    layoutEl.innerHTML = '<div style="color:var(--text2);font-size:0.85rem;">No layout snapshot</div>';
                }
            }
        }

        let taskFilteredData = [];

        function sortTasks() {
            const sortBy = document.getElementById('taskSortOrder').value;
            taskFilteredData.sort((a, b) => {
                switch (sortBy) {
                    case 'memory-desc': return (b.current_memory || 0) - (a.current_memory || 0);
                    case 'memory-asc': return (a.current_memory || 0) - (b.current_memory || 0);
                    case 'peak-desc': return (b.peak_memory || 0) - (a.peak_memory || 0);
                    case 'peak-asc': return (a.peak_memory || 0) - (b.peak_memory || 0);
                    case 'allocations-desc': return (b.total_allocations || 0) - (a.total_allocations || 0);
                    case 'allocations-asc': return (a.total_allocations || 0) - (b.total_allocations || 0);
                    default: return 0;
                }
            });
            renderTasks();
        }

        function renderTasks() {
            const tbody = document.getElementById('taskTableBody');
            if (taskFilteredData.length === 0) {
                tbody.innerHTML = '<tr><td colspan="6" class="empty-state">No async task data</td></tr>';
                return;
            }
            tbody.innerHTML = taskFilteredData.map(t => `
                <tr>
                    <td><code>#${t.task_id}</code></td>
                    <td>${t.task_name || 'Unknown'}</td>
                    <td>${Utils.formatBytes(t.current_memory || 0)}</td>
                    <td>${Utils.formatBytes(t.peak_memory || 0)}</td>
                    <td>${t.total_allocations || 0}</td>
                    <td>${t.is_completed ? '<span class="badge badge-success">Completed</span>' : '<span class="badge badge-info">Running</span>'}${t.has_potential_leak ? ' <span class="badge badge-warning">Leak?</span>' : ''}</td>
                </tr>
            `).join('');
        }

        function populateTasks() {
            const asyncTasks = data.async_tasks || [];
            taskFilteredData = [...asyncTasks];

            if (asyncTasks.length === 0) {
                document.getElementById('taskTableBody').innerHTML = '<tr><td colspan="6" class="empty-state">No async task data</td></tr>';
                return;
            }

            sortTasks();
        }

        function updateEventSummaryCard() {
            const card = document.getElementById('eventSummaryCard');
            if (!card) return;
            const eventSummary = data.event_summary || {};
            const sampling = data.sampling || {};
            const events = data.events || [];
            if (events.length === 0 && !sampling.is_sampled) { card.style.display = 'none'; return; }
            card.style.display = 'block';
            const totalEvents = eventSummary.total_event_count || events.length;
            const exportedEvents = eventSummary.exported_event_count || events.length;
            const isSampled = eventSummary.is_sampled || sampling.is_sampled;
            const strategy = eventSummary.sampling_strategy || sampling.sampling_strategy || 'none';
            card.innerHTML = '<div style="display: flex; align-items: center; gap: 12px; flex-wrap: wrap; padding: 8px 12px; background: var(--bg3); border-radius: 8px; border: 1px solid var(--border);">' +
                '<span style="font-size: 1.2rem;">📋</span>' +
                '<span style="font-weight: 600; font-size: 0.85rem;">Events:</span>' +
                '<span style="font-size: 0.85rem;">' + totalEvents + ' total</span>' +
                (isSampled ? '<span class="badge badge-warning" title="Sampling strategy: ' + strategy + '">⚠️ Sampled (' + exportedEvents + ' displayed)</span>' : '<span class="badge badge-success">✅ Complete (' + exportedEvents + ' exported)</span>') +
                '</div>';
        }

        document.addEventListener('DOMContentLoaded', function() {
            Dashboard.diagnosis.init();
            Dashboard.charts.init();
            Dashboard.allocations.init();
            Dashboard.threads.init();
            Dashboard.relationships.init();
            Dashboard.passports.init();
            Dashboard.unsafe.init();
            Dashboard.timetravel.init();
            Dashboard.tasks.init();
            populatePassports();
            
            // Initialize Quick Health Dashboard
            updateQuickHealthDashboard();
            updateCriticalAlerts();
            updateEventSummaryCard();
        });
        
        function updateQuickHealthDashboard() {
            const asyncTasks = data.async_tasks || [];
            const activeCount = asyncTasks.filter(t => !t.is_completed).length;
            const leakCount = data.leak_count || 0;
            const unsafeCount = data.unsafe_count || 0;
            
            // Update active tasks
            const activeTaskEl = document.getElementById('quickActiveTasks');
            if (activeTaskEl) {
                activeTaskEl.textContent = activeCount;
            }
            
            // Update status badge
            const statusBadge = document.getElementById('quickStatusBadge');
            if (statusBadge) {
                if (leakCount > 0) {
                    statusBadge.className = 'badge badge-danger';
                    statusBadge.textContent = `⚠️ ${leakCount} Leak${leakCount > 1 ? 's' : ''} Detected`;
                } else if (unsafeCount > 0) {
                    statusBadge.className = 'badge badge-warning';
                    statusBadge.textContent = `⚡ ${unsafeCount} Unsafe Operation${unsafeCount > 1 ? 's' : ''}`;
                } else {
                    statusBadge.className = 'badge badge-success';
                    statusBadge.textContent = '✅ Healthy';
                }
            }
            
            // Update health trend
            const healthScore = parseInt(data.health_score) || 0;
            const healthTrend = document.getElementById('healthTrend');
            if (healthTrend) {
                if (healthScore >= 80) {
                    healthTrend.textContent = '✅';
                } else if (healthScore >= 60) {
                    healthTrend.textContent = '⚠️';
                } else {
                    healthTrend.textContent = '🚨';
                }
            }
        }
        
        function updateCriticalAlerts() {
            const alertsContainer = document.getElementById('criticalAlerts');
            if (!alertsContainer) return;
            
            const alerts = [];
            const leakCount = data.leak_count || 0;
            const unsafeCount = data.unsafe_count || 0;
            const ffiCount = data.ffi_count || 0;
            const asyncTasks = data.async_tasks || [];
            const leakyTasks = asyncTasks.filter(t => t.has_potential_leak).length;
            
            if (leakCount > 0) {
                alerts.push({
                    type: 'danger',
                    icon: '🔴',
                    message: `<strong>${leakCount} Memory Leak${leakCount > 1 ? 's' : ''}</strong> detected - Immediate attention required`,
                    action: `onclick="showMode('passport')" style="cursor: pointer;"`
                });
            }
            
            if (leakyTasks > 0) {
                alerts.push({
                    type: 'warning',
                    icon: '⚠️',
                    message: `<strong>${leakyTasks} Async Task${leakyTasks > 1 ? 's' : ''}</strong> with potential memory leaks`,
                    action: `onclick="showMode('task')" style="cursor: pointer;"`
                });
            }
            
            if (unsafeCount > 10) {
                alerts.push({
                    type: 'warning',
                    icon: '⚡',
                    message: `<strong>${unsafeCount} Unsafe Operations</strong> - Consider reviewing for safety`,
                    action: `onclick="showMode('unsafe')" style="cursor: pointer;"`
                });
            }
            
            if (ffiCount > 20) {
                alerts.push({
                    type: 'info',
                    icon: '🔗',
                    message: `<strong>${ffiCount} FFI Calls</strong> - High cross-boundary activity`,
                    action: `onclick="showMode('unsafe')" style="cursor: pointer;"`
                });
            }
            
            if (alerts.length === 0) {
                alertsContainer.innerHTML = `
                    <div style="background: rgba(16, 185, 129, 0.1); border-left: 4px solid #10b981; padding: 12px; border-radius: 4px;">
                        <span style="font-size: 1.2rem;">✅</span>
                        <span style="margin-left: 8px; font-weight: 600; color: #10b981;">All systems healthy - No critical issues detected</span>
                    </div>
                `;
            } else {
                alertsContainer.innerHTML = alerts.map(alert => `
                    <div class="alert alert-${alert.type}" style="background: rgba(${alert.type === 'danger' ? '239, 68, 68' : alert.type === 'warning' ? '245, 158, 11' : '59, 130, 246'}, 0.1); border-left: 4px solid ${alert.type === 'danger' ? '#ef4444' : alert.type === 'warning' ? '#f59e0b' : '#3b82f6'}; padding: 12px; border-radius: 4px; margin-bottom: 8px;" ${alert.action}>
                        <span style="font-size: 1.2rem;">${alert.icon}</span>
                        <span style="margin-left: 8px;">${alert.message}</span>
                    </div>
                `).join('');
            }
        }
    </script>
</body>
</html>
