{% extends "base.html" %}
{% block title %}asiai — Benchmark{% endblock %}

{% block content %}
<div class="page-header">
    <h2>Benchmark</h2>
    <p>Run cross-engine benchmarks with live progress</p>
</div>

<!-- Quick Bench CTA -->
<div class="card card-accent fade-in" style="margin-bottom: 1.5rem;">
    <div style="display: flex; align-items: center; gap: 1.5rem; flex-wrap: wrap;">
        <button type="button" id="quick-bench-btn" class="btn btn-quick"
                {% if bench_running %}disabled{% endif %}>
            &#9654; Quick Bench
        </button>
        <div>
            <div class="text-sm" style="color: var(--text-secondary); font-weight: 500;">One-click inference benchmark</div>
            <span class="text-neutral-500 text-xs">
                1 prompt &middot; 1 run &middot; ~15s &middot; shareable card
            </span>
        </div>
    </div>
    <p class="help-hint" style="margin-top: 0.75rem;">
        <a href="https://asiai.dev/commands/bench/" target="_blank" rel="noopener">Documentation</a>
        &middot; Compare your Apple Silicon inference speed across engines
    </p>
</div>

<div class="fade-in" style="max-width: 720px;">
    <!-- Benchmark form -->
    <div class="card">
        <details id="advanced-options">
            <summary class="card-title advanced-toggle">
                <span class="chevron">&#9654;</span> Advanced options
            </summary>
            <form id="bench-form" style="margin-top: 1rem;">
                <!-- Model (dropdown of loaded models + free text) -->
                <div class="form-group">
                    <label class="form-label">Model</label>
                    <select name="model" class="form-select" id="model-select">
                        <option value="">(auto-detect)</option>
                        {% for engine in engines %}
                        {% if engine.reachable %}
                        {% for model in engine.models %}
                        <option value="{{ model }}">{{ model }} ({{ engine.name }})</option>
                        {% endfor %}
                        {% endif %}
                        {% endfor %}
                    </select>
                    <input type="text" name="model_custom" class="form-input" id="model-custom"
                           placeholder="or type a model name" style="margin-top: 0.5rem; display: none;">
                    <a href="#" id="model-custom-toggle" class="text-xs" style="margin-top: 0.25rem; display: inline-block;">
                        Type custom model name
                    </a>
                </div>

                <!-- Compare mode -->
                <div class="form-group" id="compare-group" style="display: none;">
                    <label class="form-label">Compare models (select 2-8)</label>
                    <select name="compare_models" class="form-select" id="compare-select" multiple
                            size="4" style="min-height: 100px;">
                        {% for engine in engines %}
                        {% if engine.reachable %}
                        {% for model in engine.models %}
                        <option value="{{ model }}@{{ engine.name }}">{{ model }} @ {{ engine.name }}</option>
                        {% endfor %}
                        {% endif %}
                        {% endfor %}
                    </select>
                    <p class="help-hint" style="margin-top: 0.25rem;">
                        <span class="text-neutral-500 text-xs">Hold Cmd/Ctrl to select multiple. Format: model@engine.</span>
                    </p>
                </div>
                <div class="form-group">
                    <label class="form-checkbox">
                        <input type="checkbox" id="compare-toggle" name="compare_mode">
                        Compare multiple models
                    </label>
                </div>

                <!-- Engines -->
                <div class="form-group" id="engines-group">
                    <label class="form-label">Engines</label>
                    {% for engine in engines %}
                    <label class="form-checkbox">
                        <input type="checkbox" name="engines" value="{{ engine.name }}"
                               {% if engine.reachable %}checked{% else %}disabled{% endif %}>
                        <span class="status-dot {% if engine.reachable %}green{% else %}gray{% endif %}"></span>
                        {{ engine.name }}
                        {% if not engine.reachable %}<span class="text-neutral-500">(offline)</span>{% endif %}
                    </label>
                    {% endfor %}
                    {% if not engines %}
                    <p class="text-neutral-400 text-sm">No engines detected</p>
                    {% endif %}
                </div>

                <!-- Prompts -->
                <div class="form-group">
                    <label class="form-label">Prompts</label>
                    {% for prompt in prompts %}
                    <label class="form-checkbox">
                        <input type="checkbox" name="prompts" value="{{ prompt.name }}" checked>
                        {{ prompt.label }} <span class="text-neutral-500 text-xs">({{ prompt.max_tokens }} tokens)</span>
                    </label>
                    {% endfor %}
                </div>

                <!-- Runs -->
                <div class="form-group">
                    <label class="form-label">Runs: <span id="runs-value">3</span></label>
                    <input type="range" name="runs" class="form-range" min="1" max="10" value="3"
                           oninput="document.getElementById('runs-value').textContent = this.value">
                </div>

                <!-- Context fill -->
                <div class="form-group">
                    <label class="form-label">Context fill</label>
                    <select name="context_size" class="form-select">
                        <option value="">None (default prompts)</option>
                        <option value="4k">4K tokens</option>
                        <option value="16k">16K tokens</option>
                        <option value="32k">32K tokens</option>
                        <option value="64k">64K tokens</option>
                    </select>
                </div>

                <!-- Power -->
                <div class="form-group">
                    {% if ioreport_power %}
                    <p class="help-hint" style="margin-left: 0;">
                        <span class="text-neutral-400 text-xs">Power monitoring: always-on via IOReport (no sudo needed)</span>
                    </p>
                    {% if power_available %}
                    <label class="form-checkbox">
                        <input type="checkbox" name="power">
                        Cross-validate with powermetrics (sudo)
                        <span class="text-neutral-500 text-xs">(stores both readings for comparison)</span>
                    </label>
                    {% endif %}
                    {% else %}
                    <label class="form-checkbox">
                        <input type="checkbox" name="power" {% if not power_available %}disabled{% endif %}>
                        Measure power (tok/s per watt)
                        {% if power_available %}
                        <span class="text-neutral-500 text-xs">(via powermetrics)</span>
                        {% else %}
                        <span class="text-neutral-500 text-xs">(unavailable)</span>
                        {% endif %}
                    </label>
                    {% if not power_available %}
                    <p class="help-hint" style="margin-top: 0.25rem; margin-left: 1.5rem;">
                        Enable with: <code style="font-size: 0.7rem; background: var(--bg-elevated); padding: 0.1rem 0.4rem; border-radius: 0.25rem;">echo "$USER ALL=(ALL) NOPASSWD: /usr/bin/powermetrics" | sudo tee /etc/sudoers.d/powermetrics</code>
                    </p>
                    {% endif %}
                    {% endif %}
                </div>

                <!-- Card & Share -->
                <div class="form-group">
                    <label class="form-checkbox">
                        <input type="checkbox" name="card" checked>
                        Generate shareable card
                    </label>
                    <label class="form-checkbox">
                        <input type="checkbox" name="share" checked>
                        Share to community
                        <span class="text-neutral-500 text-xs">(api.asiai.dev)</span>
                    </label>
                </div>

                <button type="submit" id="bench-submit" class="btn btn-primary"
                        {% if bench_running %}disabled{% endif %}>
                    {% if bench_running %}
                    <span class="spinner"></span> Running...
                    {% else %}
                    Run Benchmark
                    {% endif %}
                </button>
            </form>
        </details>
    </div>

    <!-- Progress & Status (below form, same column) -->
    <div class="card" id="status-card" style="margin-top: 1rem;">
        <h3 class="card-title">Status</h3>
        <div id="bench-status">
            {% if bench_running %}
            <div style="display: flex; align-items: center; gap: 0.75rem;">
                <span class="spinner"></span>
                <span class="text-sm animate-pulse">Benchmark in progress...</span>
            </div>
            {% else %}
            <p class="text-neutral-400 text-sm">Ready. Click Quick Bench or configure advanced options.</p>
            {% endif %}
        </div>
    </div>
</div>

<!-- System summary -->
<div class="card-grid mt-6 fade-in-delay-1" id="bench-system-info">
    {% if snapshot %}
    <div class="card">
        <h3 class="card-title">System</h3>
        <div class="metric-row">
            <div>
                <div class="metric-label">CPU</div>
                <div class="metric-value text-sm">{{ snapshot.cpu_cores }} cores &middot; load {{ snapshot.cpu_load_1 | format_number }}</div>
            </div>
            <div class="text-right">
                <div class="metric-label">Memory</div>
                <div class="metric-value text-sm">{{ snapshot.mem_used | format_bytes }} / {{ snapshot.mem_total | format_bytes }}</div>
            </div>
        </div>
        {% if snapshot.gpu_utilization_pct is defined and snapshot.gpu_utilization_pct >= 0 %}
        <div class="metric-row" style="margin-top: 0.5rem;">
            <div>
                <div class="metric-label">GPU</div>
                <div class="metric-value text-sm">{{ snapshot.gpu_utilization_pct | format_number }}%</div>
            </div>
            <div class="text-right">
                <div class="metric-label">GPU Memory</div>
                <div class="metric-value text-sm">{{ snapshot.gpu_mem_in_use | format_bytes }}</div>
            </div>
        </div>
        {% endif %}
    </div>
    {% endif %}
    <div class="card">
        <h3 class="card-title">Engines</h3>
        {% for engine in engines %}
        <div class="metric-row" {% if not loop.first %}style="margin-top: 0.5rem;"{% endif %}>
            <span>
                <span class="status-dot {% if engine.reachable %}green{% else %}gray{% endif %}"></span>
                {{ engine.name }}
                {% if engine.version %}<span class="text-neutral-500 text-xs">{{ engine.version }}</span>{% endif %}
            </span>
            <span class="text-neutral-500 text-xs">{{ engine.models | length }} model{{ 's' if engine.models | length != 1 }}</span>
        </div>
        {% endfor %}
        {% if not engines %}
        <p class="text-neutral-400 text-sm">No engines detected</p>
        {% endif %}
    </div>
</div>

<!-- Results area -->
<div id="bench-results" class="mt-6"></div>

<!-- Charts -->
<div class="card-grid-2 mt-6" id="bench-charts-row" style="display: none;">
    <div class="card">
        <h3 class="card-title">Throughput (tok/s)</h3>
        <div id="bench-tok-chart" class="chart-container"></div>
    </div>
    <div class="card">
        <h3 class="card-title">Time to First Token (ms)</h3>
        <div id="bench-ttft-chart" class="chart-container"></div>
    </div>
</div>

<!-- Share section (populated by JS after bench completes) -->
<div id="bench-share-section" class="mt-6" style="display: none;"></div>
{% endblock %}

{% block scripts %}
<script>
    const form = document.getElementById('bench-form');
    const submitBtn = document.getElementById('bench-submit');
    const quickBtn = document.getElementById('quick-bench-btn');
    const statusDiv = document.getElementById('bench-status');
    const resultsDiv = document.getElementById('bench-results');
    var benchTokChart = null;
    var benchTtftChart = null;

    /* Model: custom text toggle */
    var customToggle = document.getElementById('model-custom-toggle');
    var customInput = document.getElementById('model-custom');
    var modelSelect = document.getElementById('model-select');
    if (customToggle) {
        customToggle.addEventListener('click', function(e) {
            e.preventDefault();
            if (customInput.style.display === 'none') {
                customInput.style.display = '';
                modelSelect.style.display = 'none';
                customToggle.textContent = 'Use model dropdown';
            } else {
                customInput.style.display = 'none';
                modelSelect.style.display = '';
                customToggle.textContent = 'Type custom model name';
                customInput.value = '';
            }
        });
    }

    /* Compare mode toggle */
    var compareToggle = document.getElementById('compare-toggle');
    var compareGroup = document.getElementById('compare-group');
    var enginesGroup = document.getElementById('engines-group');
    var modelGroup = modelSelect ? modelSelect.closest('.form-group') : null;
    if (compareToggle) {
        compareToggle.addEventListener('change', function() {
            if (this.checked) {
                compareGroup.style.display = '';
                if (enginesGroup) enginesGroup.style.display = 'none';
                if (modelGroup) modelGroup.style.display = 'none';
            } else {
                compareGroup.style.display = 'none';
                if (enginesGroup) enginesGroup.style.display = '';
                if (modelGroup) modelGroup.style.display = '';
            }
        });
    }

    function showBenchProgress() {
        statusDiv.textContent = '';
        var progressRow = document.createElement('div');
        progressRow.style.display = 'flex';
        progressRow.style.alignItems = 'center';
        progressRow.style.gap = '0.75rem';
        var progressSpinner = document.createElement('span');
        progressSpinner.className = 'spinner';
        progressRow.appendChild(progressSpinner);
        var progressText = document.createElement('span');
        progressText.className = 'text-sm animate-pulse';
        progressText.id = 'progress-text';
        progressText.textContent = 'Starting benchmark...';
        progressRow.appendChild(progressText);
        statusDiv.appendChild(progressRow);
    }

    function disableAllBenchButtons() {
        submitBtn.disabled = true;
        submitBtn.textContent = '';
        var spinner = document.createElement('span');
        spinner.className = 'spinner';
        submitBtn.appendChild(spinner);
        submitBtn.appendChild(document.createTextNode(' Running...'));
        quickBtn.disabled = true;
    }

    function enableAllBenchButtons() {
        submitBtn.disabled = false;
        submitBtn.textContent = 'Run Benchmark';
        quickBtn.disabled = false;
    }

    // Quick Bench button
    quickBtn.addEventListener('click', function() {
        disableAllBenchButtons();
        showBenchProgress();

        var formData = new FormData();
        formData.append('quick', 'on');

        fetch('/bench/run', { method: 'POST', body: formData })
            .then(function(res) { return res.json(); })
            .then(function(data) {
                if (data.error) {
                    showError(data.error);
                    return;
                }
                startBenchSSE();
            })
            .catch(function(err) {
                showError(err.message);
            });
    });

    // Advanced form submit
    form.addEventListener('submit', function(e) {
        e.preventDefault();
        disableAllBenchButtons();
        showBenchProgress();

        var formData = new FormData(form);

        fetch('/bench/run', { method: 'POST', body: formData })
            .then(function(res) { return res.json(); })
            .then(function(data) {
                if (data.error) {
                    showError(data.error);
                    return;
                }
                startBenchSSE();
            })
            .catch(function(err) {
                showError(err.message);
            });
    });

    function startBenchSSE() {
        var evtSource = new EventSource('/bench/stream');

        evtSource.onmessage = function(event) {
            var data = JSON.parse(event.data);

            var progressText = document.getElementById('progress-text');
            if (progressText) {
                progressText.textContent = data.progress || 'Running...';
            }

            if (data.error) {
                evtSource.close();
                showError(data.error);
                return;
            }

            if (data.done) {
                evtSource.close();

                statusDiv.textContent = '';
                var doneP = document.createElement('p');
                doneP.className = 'text-green-400 text-sm';
                doneP.textContent = 'Benchmark complete!';
                statusDiv.appendChild(doneP);

                enableAllBenchButtons();

                // Render share section from SSE data
                renderShareSection(data);

                // Fetch results via JSON API and render with safe DOM methods
                fetchAndRenderResults();
            }
        };

        evtSource.onerror = function() {
            evtSource.close();
            showError('Lost connection to benchmark stream');
        };
    }

    function fetchAndRenderResults() {
        fetch('/api/benchmarks?hours=1')
            .then(function(res) { return res.json(); })
            .then(function(rows) {
                if (!rows || rows.length === 0) return;

                // Group by engine
                var engines = {};
                rows.forEach(function(r) {
                    if (!engines[r.engine]) {
                        engines[r.engine] = { tok_s_sum: 0, ttft_sum: 0, ttft_client_sum: 0, ttft_client_count: 0, count: 0, vram: r.vram_bytes };
                    }
                    engines[r.engine].tok_s_sum += r.tok_per_sec;
                    engines[r.engine].ttft_sum += r.ttft_ms;
                    if (r.ttft_client_ms && r.ttft_client_ms > 0) {
                        engines[r.engine].ttft_client_sum += r.ttft_client_ms;
                        engines[r.engine].ttft_client_count++;
                    }
                    engines[r.engine].count++;
                });

                renderResultsTable(engines);
                renderResultsCharts(engines);
            })
            .catch(function() { /* ignore render errors */ });
    }

    function renderResultsTable(engines) {
        resultsDiv.textContent = '';

        var card = document.createElement('div');
        card.className = 'card fade-in';

        var title = document.createElement('h3');
        title.className = 'card-title';
        title.textContent = 'Results';
        card.appendChild(title);

        var wrapper = document.createElement('div');
        wrapper.className = 'overflow-x-auto';

        var table = document.createElement('table');
        table.className = 'results-table';

        // Header
        var thead = document.createElement('thead');
        var headerRow = document.createElement('tr');
        ['Engine', 'Avg tok/s', 'Avg TTFT', 'Avg TTFT Client', 'VRAM'].forEach(function(text) {
            var th = document.createElement('th');
            th.textContent = text;
            th.className = text === 'Engine' ? 'text-left' : 'text-right';
            headerRow.appendChild(th);
        });
        thead.appendChild(headerRow);
        table.appendChild(thead);

        // Find winner
        var bestTokS = 0;
        var winnerName = '';
        Object.keys(engines).forEach(function(name) {
            var avg = engines[name].tok_s_sum / engines[name].count;
            if (avg > bestTokS) { bestTokS = avg; winnerName = name; }
        });

        // Body
        var tbody = document.createElement('tbody');
        Object.keys(engines).forEach(function(name) {
            var e = engines[name];
            var avgTok = e.tok_s_sum / e.count;
            var avgTtft = e.ttft_sum / e.count;

            var tr = document.createElement('tr');
            if (name === winnerName && Object.keys(engines).length > 1) {
                tr.className = 'winner-row';
            }

            var tdName = document.createElement('td');
            tdName.className = 'font-medium';
            tdName.style.fontFamily = 'var(--font-sans)';
            tdName.textContent = name;
            tr.appendChild(tdName);

            var tdTok = document.createElement('td');
            tdTok.className = 'text-right font-mono';
            tdTok.textContent = avgTok.toFixed(1);
            tr.appendChild(tdTok);

            var tdTtft = document.createElement('td');
            tdTtft.className = 'text-right font-mono';
            tdTtft.textContent = avgTtft.toFixed(0) + ' ms';
            tr.appendChild(tdTtft);

            var tdTtftClient = document.createElement('td');
            tdTtftClient.className = 'text-right font-mono';
            if (e.ttft_client_count > 0) {
                var avgTtftClient = e.ttft_client_sum / e.ttft_client_count;
                if (Math.abs(avgTtftClient - avgTtft) > 10) {
                    tdTtftClient.textContent = avgTtftClient.toFixed(0) + ' ms';
                } else {
                    tdTtftClient.textContent = '\u2014';
                }
            } else {
                tdTtftClient.textContent = '\u2014';
            }
            tr.appendChild(tdTtftClient);

            var tdVram = document.createElement('td');
            tdVram.className = 'text-right font-mono';
            var vramText = formatBytes(e.vram);
            var nativeVramEngines = ['ollama', 'lmstudio'];
            if (e.vram > 0 && nativeVramEngines.indexOf(name) === -1) {
                vramText += ' (est.)';
            }
            tdVram.textContent = vramText;
            tr.appendChild(tdVram);

            tbody.appendChild(tr);
        });
        table.appendChild(tbody);
        wrapper.appendChild(table);
        card.appendChild(wrapper);
        resultsDiv.appendChild(card);
    }

    function renderResultsCharts(engines) {
        var names = Object.keys(engines);
        if (names.length === 0) return;

        var tokValues = names.map(function(n) {
            return parseFloat((engines[n].tok_s_sum / engines[n].count).toFixed(1));
        });
        var ttftValues = names.map(function(n) {
            return parseFloat((engines[n].ttft_sum / engines[n].count).toFixed(0));
        });
        var ttftClientValues = names.map(function(n) {
            if (engines[n].ttft_client_count > 0) {
                return parseFloat((engines[n].ttft_client_sum / engines[n].ttft_client_count).toFixed(0));
            }
            return 0;
        });
        var hasClientTtft = ttftClientValues.some(function(v) { return v > 0; });

        document.getElementById('bench-charts-row').style.display = '';

        if (benchTokChart) { benchTokChart.destroy(); benchTokChart = null; }
        if (benchTtftChart) { benchTtftChart.destroy(); benchTtftChart = null; }

        benchTokChart = createBarChart('bench-tok-chart', names, tokValues, {
            label: 'Throughput', unit: 'tok/s', height: 200,
        });
        if (hasClientTtft) {
            benchTtftChart = createBarChart('bench-ttft-chart', names, ttftValues, {
                label: 'TTFT', unit: 'ms', height: 200,
                extraSeries: [{ name: 'TTFT Client', data: ttftClientValues }],
            });
        } else {
            benchTtftChart = createBarChart('bench-ttft-chart', names, ttftValues, {
                label: 'TTFT', unit: 'ms', height: 200,
            });
        }
    }

    function showError(msg) {
        statusDiv.textContent = '';
        var errP = document.createElement('p');
        errP.className = 'text-red-400 text-sm';
        errP.textContent = 'Error: ' + msg;
        statusDiv.appendChild(errP);
        enableAllBenchButtons();
    }

    function renderShareSection(data) {
        var section = document.getElementById('bench-share-section');
        section.textContent = '';

        var cardImgUrl = data.card_png_url || data.card_svg_url;
        if (!cardImgUrl && !data.share_url) {
            section.style.display = 'none';
            return;
        }

        section.style.display = '';
        var card = document.createElement('div');
        card.className = 'card fade-in';

        var title = document.createElement('h3');
        title.className = 'card-title';
        title.textContent = 'Share';
        card.appendChild(title);

        // Card image preview
        if (cardImgUrl) {
            var img = document.createElement('img');
            img.src = cardImgUrl;
            img.alt = 'Benchmark card';
            img.className = 'bench-card-preview';
            card.appendChild(img);
        }

        // Share URL pill (only if online share succeeded)
        if (data.share_url) {
            var urlRow = document.createElement('div');
            urlRow.className = 'share-url-row';

            var urlInput = document.createElement('input');
            urlInput.type = 'text';
            urlInput.readOnly = true;
            urlInput.value = data.share_url;
            urlInput.className = 'form-input share-url-input';
            urlRow.appendChild(urlInput);

            var copyBtn = document.createElement('button');
            copyBtn.type = 'button';
            copyBtn.className = 'btn btn-primary';
            copyBtn.textContent = 'Copy link';
            copyBtn.addEventListener('click', function() {
                // Clipboard API requires HTTPS; fallback for HTTP
                if (navigator.clipboard && window.isSecureContext) {
                    navigator.clipboard.writeText(data.share_url).then(function() {
                        copyBtn.textContent = 'Copied!';
                        setTimeout(function() { copyBtn.textContent = 'Copy link'; }, 2000);
                    });
                } else {
                    urlInput.select();
                    document.execCommand('copy');
                    copyBtn.textContent = 'Copied!';
                    setTimeout(function() { copyBtn.textContent = 'Copy link'; }, 2000);
                }
            });
            urlRow.appendChild(copyBtn);

            card.appendChild(urlRow);
        }

        // Action buttons
        var actions = document.createElement('div');
        actions.className = 'share-actions';

        // Download PNG (or SVG fallback)
        var downloadUrl = data.card_png_url || data.card_svg_url;
        if (downloadUrl) {
            var dlBtn = document.createElement('a');
            dlBtn.href = downloadUrl;
            dlBtn.download = '';
            dlBtn.className = 'btn btn-primary';
            dlBtn.textContent = data.card_png_url ? 'Download PNG' : 'Download SVG';
            actions.appendChild(dlBtn);
        }

        // Share on X (only if share_url exists)
        if (data.share_url) {
            var xBtn = document.createElement('a');
            xBtn.href = 'https://twitter.com/intent/tweet?url=' + encodeURIComponent(data.share_url) + '&text=' + encodeURIComponent('My local LLM benchmark results via asiai');
            xBtn.target = '_blank';
            xBtn.rel = 'noopener noreferrer';
            xBtn.className = 'btn';
            xBtn.textContent = 'Share on X';
            actions.appendChild(xBtn);

            var redditBtn = document.createElement('a');
            redditBtn.href = 'https://reddit.com/submit?url=' + encodeURIComponent(data.share_url) + '&title=' + encodeURIComponent('asiai benchmark results');
            redditBtn.target = '_blank';
            redditBtn.rel = 'noopener noreferrer';
            redditBtn.className = 'btn';
            redditBtn.textContent = 'Share on Reddit';
            actions.appendChild(redditBtn);
        }

        // Export JSON
        var exportBtn = document.createElement('a');
        exportBtn.href = '/bench/export';
        exportBtn.className = 'btn btn-muted';
        exportBtn.textContent = 'Export JSON';
        actions.appendChild(exportBtn);

        card.appendChild(actions);

        // Card error message (if any)
        if (data.card_error) {
            var errP = document.createElement('p');
            errP.className = 'text-neutral-500 text-xs';
            errP.textContent = 'Card generation note: ' + data.card_error;
            card.appendChild(errP);
        }

        section.appendChild(card);
    }

    // Cleanup: destroy charts when leaving page
    function cleanupBench() {
        if (benchTokChart) { benchTokChart.destroy(); benchTokChart = null; }
        if (benchTtftChart) { benchTtftChart.destroy(); benchTtftChart = null; }
    }
    window.addEventListener('pagehide', cleanupBench);
    window.addEventListener('beforeunload', cleanupBench);
</script>
{% endblock %}
