<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>HEDGEHOG Pipeline Report</title>
    <script src="https://cdn.plot.ly/plotly-2.27.0.min.js"></script>
    <style>
        :root {
            --primary: #8b5cf6;
            --primary-light: #a78bfa;
            --primary-pale: #ede9fe;
            --primary-subtle: #f5f3ff;
            --success: #7c3aed;
            --danger: #a855f7;
            --text: #1f2937;
            --text-secondary: #6b7280;
            --border: #e5e7eb;
            --bg: #fafafa;
        }

        * {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }

        body {
            font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Text', 'Inter', system-ui, sans-serif;
            line-height: 1.5;
            color: var(--text);
            background: var(--bg);
            -webkit-font-smoothing: antialiased;
        }

        .container {
            max-width: 1200px;
            margin: 0 auto;
            padding: 48px 24px;
        }

        header {
            text-align: center;
            padding: 64px 24px;
            margin-bottom: 48px;
        }

        header h1 {
            font-size: 2rem;
            font-weight: 600;
            color: var(--text);
            margin-bottom: 16px;
            letter-spacing: -0.02em;
        }

        header .meta {
            color: var(--text-secondary);
            font-size: 0.875rem;
        }

        header .meta p {
            margin: 4px 0;
            overflow-wrap: anywhere;
            word-break: break-word;
        }

        .summary-grid {
            display: grid;
            grid-template-columns: repeat(4, 1fr);
            gap: 1px;
            background: var(--border);
            border-radius: 12px;
            overflow: hidden;
            margin-bottom: 48px;
        }

        .summary-card {
            background: white;
            padding: 32px 24px;
            text-align: center;
        }

        .summary-card .value {
            font-size: 2rem;
            font-weight: 600;
            color: var(--primary);
            margin-bottom: 4px;
            letter-spacing: -0.02em;
        }

        .summary-card .label {
            color: var(--text-secondary);
            font-size: 0.75rem;
            text-transform: uppercase;
            letter-spacing: 0.05em;
            font-weight: 500;
        }

        section {
            background: white;
            padding: 32px;
            border-radius: 12px;
            margin-bottom: 24px;
            border: 1px solid var(--border);
        }

        section h2 {
            color: var(--text);
            margin-bottom: 24px;
            font-weight: 500;
            font-size: 1.125rem;
            letter-spacing: -0.01em;
        }

        table {
            width: 100%;
            border-collapse: collapse;
        }

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

        th {
            font-weight: 500;
            font-size: 0.75rem;
            text-transform: uppercase;
            letter-spacing: 0.05em;
            color: var(--text-secondary);
        }

        td {
            font-size: 0.875rem;
        }

        tr:last-child td {
            border-bottom: none;
        }

        .plot-container {
            margin: 16px 0;
        }

        .two-col {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 24px;
        }

        .three-col {
            display: grid;
            grid-template-columns: 1fr 1fr 1fr;
            gap: 20px;
        }

        .status-completed {
            color: var(--primary);
            font-weight: 500;
        }

        .status-failed {
            color: var(--danger);
            font-weight: 500;
        }

        .status-disabled {
            color: var(--text-secondary);
        }

        .badge {
            display: inline-block;
            padding: 4px 10px;
            border-radius: 100px;
            font-size: 0.75rem;
            font-weight: 500;
        }

        .badge-success {
            background: var(--primary-pale);
            color: var(--primary);
        }

        .badge-warning {
            background: #fef3c7;
            color: #d97706;
        }

        .badge-danger {
            background: #fce7f3;
            color: #be185d;
        }

        footer {
            text-align: center;
            padding: 48px 24px;
            color: var(--text-secondary);
            font-size: 0.875rem;
        }

        @media (max-width: 768px) {
            .two-col, .three-col {
                grid-template-columns: 1fr;
            }
            .summary-grid {
                grid-template-columns: repeat(2, 1fr);
            }
            .container {
                padding: 24px 16px;
            }
            .js-plotly-plot .modebar {
                display: none !important;
            }
        }

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

        .view-toggle {
            display: flex;
            flex-wrap: nowrap;
            align-items: center;
            width: 100%;
            max-width: 100%;
            gap: 4px;
            padding: 4px;
            background: var(--primary-subtle);
            border-radius: 8px;
            margin-bottom: 16px;
            overflow-x: auto;
            overflow-y: hidden;
            -webkit-overflow-scrolling: touch;
        }

        .view-toggle button {
            padding: 8px 16px;
            border: none;
            border-radius: 6px;
            background: transparent;
            cursor: pointer;
            font-size: 0.875rem;
            font-weight: 500;
            color: var(--text-secondary);
            transition: all 0.15s;
            max-width: 220px;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
            flex: 0 0 auto;
        }

        .view-toggle button.active {
            background: white;
            color: var(--primary);
            box-shadow: 0 1px 2px rgba(0,0,0,0.05);
        }

        .view-toggle button:hover:not(.active) {
            color: var(--primary);
        }

        .descriptor-table {
            font-size: 0.95em;
        }

        .descriptor-table td:not(:first-child) {
            text-align: right;
            font-family: 'SF Mono', 'Monaco', 'Inconsolata', monospace;
        }

        /* Metrics grid for stage-specific cards */
        .metrics-grid {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
            gap: 12px;
            margin-bottom: 24px;
        }

        .metric-card {
            background: var(--primary-subtle);
            padding: 20px 16px;
            border-radius: 8px;
            text-align: center;
            border: 1px solid var(--primary-pale);
        }

        .metric-card .metric-value {
            font-size: 1.5rem;
            font-weight: 600;
            color: var(--primary);
            margin-bottom: 4px;
            letter-spacing: -0.02em;
        }

        .metric-card .metric-label {
            color: var(--text-secondary);
            font-size: 0.7rem;
            text-transform: uppercase;
            letter-spacing: 0.05em;
            font-weight: 500;
        }

        /* Section headers */
        section h3 {
            font-size: 0.95rem;
            font-weight: 500;
            color: var(--text);
        }

        section h4 {
            font-size: 0.875rem;
            font-weight: 500;
            color: var(--text-secondary);
        }
    </style>
</head>
<body>
    <div class="container">
        <header>
            <h1>HEDGEHOG Pipeline Report</h1>
            <div class="meta">
                <p><strong>Generated:</strong> {{ generated_at }}</p>
                <p><strong>Run path:</strong> {{ data.metadata.run_path }}</p>
                <p><strong>Version:</strong> {{ data.metadata.hedgehog_version }}</p>
            </div>
        </header>

        <!-- Summary Cards -->
        <div class="summary-grid">
            <div class="summary-card">
                <div class="value">{{ data.summary.initial_molecules }}</div>
                <div class="label">Initial Molecules</div>
            </div>
            <div class="summary-card">
                <div class="value">{{ data.summary.final_molecules }}</div>
                <div class="label">Final Molecules</div>
            </div>
            <div class="summary-card">
                <div class="value">{{ data.summary.retention_percent }}</div>
                <div class="label">Retention Rate</div>
            </div>
            <div class="summary-card">
                <div class="value">{{ data.summary.stages_completed }}/{{ data.summary.stages_enabled }}</div>
                <div class="label">Stages Completed</div>
            </div>
        </div>

        <!-- Stage Execution Summary -->
        {% if data.summary.stage_statuses %}
        <section>
            <h2>Stage Execution Summary</h2>
            <table>
                <thead>
                    <tr>
                        <th>Stage</th>
                        <th>Status</th>
                    </tr>
                </thead>
                <tbody>
                    {% for stage in data.summary.stage_statuses %}
                    <tr>
                        <td>{{ stage.name }}</td>
                        <td class="status-{{ stage.status }}">
                            {% if stage.completed %}
                            &#10003; COMPLETED
                            {% elif stage.enabled %}
                            &#10007; FAILED
                            {% else %}
                            &#8722; DISABLED
                            {% endif %}
                        </td>
                    </tr>
                    {% endfor %}
                </tbody>
            </table>
        </section>
        {% endif %}

        <!-- Pipeline Flow -->
        <section>
            <h2>Pipeline Flow</h2>

            {% if data.available_models %}
            <div class="view-toggle">
                <button class="{% if not data.available_models %}active{% endif %}" onclick="selectModel('all', this)">All Models</button>
                <button onclick="selectModel('__compare__', this)">Compare</button>
                {% for model in data.available_models %}
                <button title="{{ model }}" onclick="selectModel('{{ model }}', this)">{{ model }}</button>
                {% endfor %}
            </div>
            {% endif %}

            {% if plots.sankey %}
            <div id="sankey-container" class="plot-container">{{ plots.sankey | safe }}</div>
            {% else %}
            <div id="sankey-container" class="plot-container empty-state"><p>No data available</p></div>
            {% endif %}

            <script>
                var sankeyDataByModel = {{ (plots.sankey_data or {}) | tojson }};
                var currentModel = 'all';

                function selectModel(model, btn) {
                    currentModel = model;
                    // Update active button
                    var buttons = btn.parentElement.querySelectorAll('button');
                    buttons.forEach(function(b) { b.className = ''; });
                    btn.className = 'active';
                    updateSankeyDiagram();
                }

                // Set initial active state
                document.addEventListener('DOMContentLoaded', function() {
                    var firstBtn = document.querySelector('.view-toggle button');
                    if (firstBtn) firstBtn.className = 'active';
                });

                function updateSankeyDiagram() {
                    var model = currentModel;
                    var data = sankeyDataByModel[model];
                    var container = document.getElementById('sankey-container');

                    if (!data || !data.labels || data.labels.length < 2) {
                        container.textContent = 'No data available';
                        container.style.textAlign = 'center';
                        container.style.color = '#6b7280';
                        container.style.padding = '48px';
                        return;
                    }

                    var trace = {
                        type: 'sankey',
                        arrangement: 'snap',
                        node: {
                            pad: 40,
                            thickness: 20,
                            line: { color: 'rgba(0,0,0,0.15)', width: 0.5 },
                            label: data.labels,
                            color: data.node_colors,
                            x: data.x_positions,
                            y: data.y_positions
                        },
                        link: {
                            source: data.sources,
                            target: data.targets,
                            value: data.values,
                            color: data.link_colors,
                            customdata: data.link_labels || [],
                            hovertemplate: data.is_comparison
                                ? '%{customdata}: %{value} molecules<extra></extra>'
                                : '%{value} molecules<extra></extra>'
                        }
                    };

                    var layout = {
                        font: { family: '-apple-system, BlinkMacSystemFont, sans-serif', size: 10 },
                        height: 420,
                        margin: { l: 5, r: 5, t: data.is_comparison ? 50 : 30, b: 20 },
                        paper_bgcolor: 'white',
                        annotations: []
                    };

                    // Add legend for comparison mode
                    if (data.is_comparison && data.models && data.model_colors) {
                        for (var i = 0; i < data.models.length; i++) {
                            layout.annotations.push({
                                x: i * 0.15,
                                y: 1.08,
                                xref: 'paper',
                                yref: 'paper',
                                text: '<b>●</b> ' + data.models[i],
                                showarrow: false,
                                font: { size: 11, color: data.model_colors[i] },
                                xanchor: 'left'
                            });
                        }
                    }

                    Plotly.react('sankey-container', [trace], layout, {displayModeBar: true});
                }

                // =====================================================
                // Synthesis Analysis - Model Filtering
                // =====================================================
                var synthesisDataByModel = {{ (plots.synthesis_data or {}) | tojson }};
                var currentSynthesisModel = 'all';

                function selectSynthesisModel(model, btn) {
                    currentSynthesisModel = model;
                    var toggle = document.getElementById('synthesis-toggle');
                    if (toggle) {
                        var buttons = toggle.querySelectorAll('button');
                        buttons.forEach(function(b) { b.className = ''; });
                        btn.className = 'active';
                    }
                    updateSynthesisPlots();
                }

                function updateSynthesisPlots() {
                    var data = synthesisDataByModel[currentSynthesisModel];
                    if (!data) return;

                    // Update metrics
                    updateSynthesisMetrics(data);

                    // Update plots
                    drawHistogram('synthesis-sa-hist', data.sa_scores || [], 'SA Score (lower = easier)', '', 'sa_scores');
                    drawHistogram('synthesis-syba-hist', data.syba_scores || [], 'SYBA Score (higher = easier)', '', 'syba_scores');
                    drawHistogram('synthesis-ra-hist', data.ra_scores || [], 'RA Score (higher = easier)', '', 'ra_scores');
                    drawPie('synthesis-solved-pie', data.solved_count || 0, data.unsolved_count || 0);
                }

                function updateSynthesisMetrics(data) {
                    var summary = data.summary || {};
                    var pctSolvedEl = document.getElementById('synthesis-pct-solved');
                    var avgTimeEl = document.getElementById('synthesis-avg-time');

                    if (pctSolvedEl) {
                        pctSolvedEl.textContent = summary.pct_solved !== undefined ? summary.pct_solved.toFixed(1) + '%' : '-';
                    }
                    if (avgTimeEl) {
                        avgTimeEl.textContent = summary.avg_search_time !== undefined ? summary.avg_search_time.toFixed(1) + 's' : '-';
                    }
                }

                // Synthesis score thresholds for gray zones
                {% if plots.synthesis_thresholds %}
                var synthesisThresholds = {{ (plots.synthesis_thresholds or {}) | tojson }};
                {% else %}
                var synthesisThresholds = {
                    'sa_scores': { max: 6 },
                    'syba_scores': { min: 0 },
                    'ra_scores': { min: 0.5 }
                };
                {% endif %}

                // Common font configuration for all plots
                var plotFont = { family: '-apple-system, BlinkMacSystemFont, sans-serif', size: 11 };

                // Show empty state message in container
                function showEmptyState(container) {
                    if (container) {
                        container.innerHTML = '<p style="text-align:center;color:#6b7280;padding:48px;">No data available</p>';
                    }
                }

                // Build threshold shapes for synthesis score histograms
                function buildThresholdShapes(threshold, xMin, xMax, xPad) {
                    var shapes = [];
                    if (!threshold) return shapes;

                    if (threshold.min !== undefined && threshold.min > xMin) {
                        shapes.push({
                            type: 'rect', xref: 'x', yref: 'paper',
                            x0: xMin - xPad, x1: threshold.min, y0: 0, y1: 1,
                            fillcolor: 'rgba(156, 163, 175, 0.3)', line: { width: 0 }, layer: 'below'
                        });
                        shapes.push({
                            type: 'line', xref: 'x', yref: 'paper',
                            x0: threshold.min, x1: threshold.min, y0: 0, y1: 1,
                            line: { color: 'rgba(239, 68, 68, 0.9)', width: 2 }
                        });
                    }
                    if (threshold.max !== undefined && threshold.max < xMax) {
                        shapes.push({
                            type: 'rect', xref: 'x', yref: 'paper',
                            x0: threshold.max, x1: xMax + xPad, y0: 0, y1: 1,
                            fillcolor: 'rgba(156, 163, 175, 0.3)', line: { width: 0 }, layer: 'below'
                        });
                        shapes.push({
                            type: 'line', xref: 'x', yref: 'paper',
                            x0: threshold.max, x1: threshold.max, y0: 0, y1: 1,
                            line: { color: 'rgba(239, 68, 68, 0.9)', width: 2 }
                        });
                    }
                    return shapes;
                }

                function drawHistogram(containerId, values, xLabel, title, scoreKey) {
                    var container = document.getElementById(containerId);
                    if (!container || !values || values.length === 0) {
                        showEmptyState(container);
                        return;
                    }

                    var mean = values.reduce(function(a,b) { return a + b; }, 0) / values.length;
                    var xMin = Math.min.apply(null, values);
                    var xMax = Math.max.apply(null, values);
                    var xPad = (xMax - xMin) * 0.1;

                    var trace = {
                        x: values,
                        type: 'histogram',
                        nbinsx: 40,
                        marker: {
                            color: 'rgba(139, 92, 246, 0.75)',
                            line: { color: 'rgba(109, 40, 217, 1)', width: 1 }
                        }
                    };

                    var threshold = scoreKey ? synthesisThresholds[scoreKey] : null;
                    var shapes = buildThresholdShapes(threshold, xMin, xMax, xPad);

                    // Add mean line
                    shapes.push({
                        type: 'line', xref: 'x', yref: 'paper',
                        x0: mean, x1: mean, y0: 0, y1: 1,
                        line: { color: 'rgba(109, 40, 217, 0.9)', width: 2 }
                    });

                    var layout = {
                        title: title ? { text: title, font: { size: 14 } } : undefined,
                        xaxis: {},
                        yaxis: { title: 'Count' },
                        height: 380,
                        margin: { l: 50, r: 25, t: title ? 40 : 25, b: 30 },
                        paper_bgcolor: 'white',
                        plot_bgcolor: 'white',
                        font: plotFont,
                        shapes: shapes,
                        annotations: [{
                            x: mean, y: 1, yref: 'paper',
                            text: '\u03bc=' + mean.toFixed(2),
                            showarrow: false, yanchor: 'bottom', font: { size: 10 }
                        }]
                    };

                    Plotly.react(containerId, [trace], layout, {displayModeBar: false});
                }

                function drawCompareHistogram(containerId, data, scoreKey, xLabel, title) {
                    var container = document.getElementById(containerId);
                    if (!container || !data.data) {
                        showEmptyState(container);
                        return;
                    }

                    var traces = [];
                    var models = data.models || [];
                    var colors = data.model_colors || [];
                    var allValues = [];

                    for (var i = 0; i < models.length; i++) {
                        var modelData = data.data[models[i]];
                        if (modelData && modelData[scoreKey] && modelData[scoreKey].length > 0) {
                            allValues = allValues.concat(modelData[scoreKey]);
                            traces.push({
                                x: modelData[scoreKey],
                                type: 'histogram',
                                name: models[i],
                                opacity: 0.6,
                                nbinsx: 40,
                                marker: { color: colors[i] || 'rgba(139, 92, 246, 0.75)' }
                            });
                        }
                    }

                    if (traces.length === 0) {
                        showEmptyState(container);
                        return;
                    }

                    var xMin = Math.min.apply(null, allValues);
                    var xMax = Math.max.apply(null, allValues);
                    var xPad = (xMax - xMin) * 0.1;
                    var shapes = buildThresholdShapes(synthesisThresholds[scoreKey], xMin, xMax, xPad);

                    var layout = {
                        title: title ? { text: title, font: { size: 14 } } : undefined,
                        xaxis: { title: xLabel },
                        yaxis: { title: 'Count' },
                        barmode: 'overlay',
                        height: 300,
                        margin: { l: 45, r: 20, t: title ? 40 : 25, b: 45 },
                        paper_bgcolor: 'white',
                        plot_bgcolor: 'white',
                        font: plotFont,
                        legend: { orientation: 'h', y: 1.15, x: 0.5, xanchor: 'center' },
                        shapes: shapes
                    };

                    Plotly.react(containerId, traces, layout, {displayModeBar: false});
                }

                function drawScatter(containerId, saScores, sybaScores) {
                    var container = document.getElementById(containerId);
                    if (!container || !saScores || saScores.length === 0) {
                        showEmptyState(container);
                        return;
                    }

                    var trace = {
                        x: saScores,
                        y: sybaScores,
                        mode: 'markers',
                        type: 'scatter',
                        marker: {
                            color: 'rgba(139, 92, 246, 0.6)',
                            size: 6
                        }
                    };

                    var layout = {
                        xaxis: { title: 'SA Score' },
                        yaxis: { title: 'SYBA Score' },
                        height: 350,
                        margin: { l: 50, r: 30, t: 30, b: 50 },
                        paper_bgcolor: 'white',
                        plot_bgcolor: 'white',
                        font: plotFont
                    };

                    Plotly.react(containerId, [trace], layout, {displayModeBar: true});
                }

                function drawCompareScatter(containerId, data) {
                    var container = document.getElementById(containerId);
                    if (!container || !data.data) {
                        showEmptyState(container);
                        return;
                    }

                    var traces = [];
                    var models = data.models || [];
                    var colors = data.model_colors || [];

                    for (var i = 0; i < models.length; i++) {
                        var modelData = data.data[models[i]];
                        if (modelData && modelData.sa_scores && modelData.syba_scores) {
                            traces.push({
                                x: modelData.sa_scores,
                                y: modelData.syba_scores,
                                mode: 'markers',
                                type: 'scatter',
                                name: models[i],
                                marker: {
                                    color: colors[i] || 'rgba(139, 92, 246, 0.6)',
                                    size: 6
                                }
                            });
                        }
                    }

                    if (traces.length === 0) {
                        showEmptyState(container);
                        return;
                    }

                    var layout = {
                        xaxis: { title: 'SA Score' },
                        yaxis: { title: 'SYBA Score' },
                        height: 350,
                        margin: { l: 50, r: 30, t: 30, b: 50 },
                        paper_bgcolor: 'white',
                        plot_bgcolor: 'white',
                        font: plotFont,
                        legend: { orientation: 'h', y: 1.1, x: 0 }
                    };

                    Plotly.react(containerId, traces, layout, {displayModeBar: true});
                }

                function drawPie(containerId, solved, unsolved) {
                    var container = document.getElementById(containerId);
                    if (!container || (solved === 0 && unsolved === 0)) {
                        showEmptyState(container);
                        return;
                    }

                    var total = solved + unsolved;
                    var pct = total > 0 ? (100 * solved / total).toFixed(1) : 0;

                    var trace = {
                        labels: ['Route Found', 'No Route'],
                        values: [solved, unsolved],
                        type: 'pie',
                        hole: 0.5,
                        marker: {
                            colors: ['rgba(139, 92, 246, 0.85)', 'rgba(229, 231, 235, 0.85)']
                        },
                        textinfo: 'percent+label',
                        textfont: { size: 12 }
                    };

                    var layout = {
                        height: 350,
                        margin: { l: 30, r: 30, t: 30, b: 30 },
                        paper_bgcolor: 'white',
                        font: plotFont,
                        showlegend: false,
                        annotations: [{
                            text: pct + '%<br>Solved',
                            x: 0.5,
                            y: 0.5,
                            font: { size: 16 },
                            showarrow: false
                        }]
                    };

                    Plotly.react(containerId, [trace], layout, {displayModeBar: true});
                }

                function drawComparePie(containerId, data) {
                    var container = document.getElementById(containerId);
                    if (!container || !data.data) {
                        showEmptyState(container);
                        return;
                    }

                    var models = data.models || [];
                    var colors = data.model_colors || [];
                    var labels = [];
                    var values = [];
                    var barColors = [];

                    for (var i = 0; i < models.length; i++) {
                        var modelData = data.data[models[i]];
                        if (modelData) {
                            labels.push(models[i]);
                            var pct = 0;
                            var total = (modelData.solved_count || 0) + (modelData.unsolved_count || 0);
                            if (total > 0) {
                                pct = 100 * (modelData.solved_count || 0) / total;
                            }
                            values.push(pct);
                            barColors.push(colors[i] || 'rgba(139, 92, 246, 0.85)');
                        }
                    }

                    if (labels.length === 0) {
                        showEmptyState(container);
                        return;
                    }

                    var trace = {
                        x: labels,
                        y: values,
                        type: 'bar',
                        marker: { color: barColors },
                        text: values.map(function(v) { return v.toFixed(1) + '%'; }),
                        textposition: 'outside'
                    };

                    var layout = {
                        title: { text: 'Routes Found by Model', font: { size: 14 } },
                        yaxis: { title: '% Routes Found', range: [0, 100] },
                        height: 350,
                        margin: { l: 50, r: 30, t: 50, b: 50 },
                        paper_bgcolor: 'white',
                        plot_bgcolor: 'white',
                        font: plotFont
                    };

                    Plotly.react(containerId, [trace], layout, {displayModeBar: true});
                }

                // =====================================================
                // Docking Results - Model Filtering
                // =====================================================
                var dockingGninaData = {{ (plots.docking_gnina_data or {}) | tojson }};
                var dockingSminaData = {{ (plots.docking_smina_data or {}) | tojson }};
                var currentDockingModel = 'all';

                function selectDockingModel(model, btn) {
                    currentDockingModel = model;
                    var toggle = document.getElementById('docking-toggle');
                    if (toggle) {
                        var buttons = toggle.querySelectorAll('button');
                        buttons.forEach(function(b) { b.className = ''; });
                        btn.className = 'active';
                    }
                    updateDockingPlots();
                }

                function updateDockingPlots() {
                    // Update GNINA plots
                    var gninaData = dockingGninaData[currentDockingModel];
                    if (gninaData) {
                        updateDockingMetrics(gninaData, 'GNINA');
                        if (gninaData.is_comparison) {
                            drawDockingCompareHistogram('docking-gnina-hist', gninaData, 'GNINA');
                            drawDockingCompareTopMolecules('docking-gnina-top', gninaData);
                        } else {
                            drawDockingHistogram('docking-gnina-hist', gninaData.scores || [], 'GNINA');
                            drawDockingTopMolecules('docking-gnina-top', gninaData.top_molecules || []);
                        }
                    }

                    // Update SMINA plots
                    var sminaData = dockingSminaData[currentDockingModel];
                    if (sminaData) {
                        if (!gninaData) {
                            updateDockingMetrics(sminaData, 'SMINA');
                        }
                        if (sminaData.is_comparison) {
                            drawDockingCompareHistogram('docking-smina-hist', sminaData, 'SMINA');
                            drawDockingCompareTopMolecules('docking-smina-top', sminaData);
                        } else {
                            drawDockingHistogram('docking-smina-hist', sminaData.scores || [], 'SMINA');
                            drawDockingTopMolecules('docking-smina-top', sminaData.top_molecules || []);
                        }
                    }
                }

                function updateDockingMetrics(data, tool) {
                    var summary = data.summary || {};
                    var avgEl = document.getElementById('docking-avg-affinity');
                    var bestEl = document.getElementById('docking-best-affinity');
                    var countEl = document.getElementById('docking-count');
                    var avgLabel = document.getElementById('docking-avg-label');
                    var bestLabel = document.getElementById('docking-best-label');

                    if (avgEl && summary.avg_affinity !== undefined) {
                        avgEl.textContent = summary.avg_affinity.toFixed(2);
                    }
                    if (bestEl && summary.best_affinity !== undefined) {
                        bestEl.textContent = summary.best_affinity.toFixed(2);
                    }
                    if (countEl && summary.count !== undefined) {
                        countEl.textContent = summary.count;
                    }
                    if (avgLabel) avgLabel.textContent = 'Avg Affinity (' + tool + ')';
                    if (bestLabel) bestLabel.textContent = 'Best Affinity (' + tool + ')';
                }

                function drawDockingHistogram(containerId, scores, tool) {
                    var container = document.getElementById(containerId);
                    if (!container || !scores || scores.length === 0) {
                        if (container) container.innerHTML = '<p style="text-align:center;color:#6b7280;padding:48px;">No data available</p>';
                        return;
                    }

                    var mean = scores.reduce(function(a,b) { return a + b; }, 0) / scores.length;
                    var best = Math.min.apply(null, scores);

                    var trace = {
                        x: scores,
                        type: 'histogram',
                        nbinsx: 30,
                        marker: {
                            color: 'rgba(139, 92, 246, 0.75)',
                            line: { color: 'rgba(109, 40, 217, 1)', width: 1 }
                        }
                    };

                    var layout = {
                        title: { text: tool + ' Binding Affinity Distribution', font: { size: 14 } },
                        xaxis: { title: 'Binding Affinity (kcal/mol)' },
                        yaxis: { title: 'Count' },
                        height: 350,
                        margin: { l: 50, r: 30, t: 50, b: 50 },
                        paper_bgcolor: 'white',
                        plot_bgcolor: 'white',
                        font: { family: '-apple-system, BlinkMacSystemFont, sans-serif', size: 11 },
                        shapes: [
                            { type: 'line', x0: mean, x1: mean, y0: 0, y1: 1, yref: 'paper',
                              line: { color: 'rgba(109, 40, 217, 0.8)', width: 2, dash: 'dash' } },
                            { type: 'line', x0: best, x1: best, y0: 0, y1: 1, yref: 'paper',
                              line: { color: 'rgba(76, 29, 149, 1)', width: 2 } }
                        ],
                        annotations: [
                            { x: mean, y: 1, yref: 'paper', text: 'Mean: ' + mean.toFixed(2),
                              showarrow: false, yanchor: 'bottom', xanchor: 'left', font: { size: 10 } },
                            { x: best, y: 0.9, yref: 'paper', text: 'Best: ' + best.toFixed(2),
                              showarrow: false, yanchor: 'bottom', xanchor: 'right', font: { size: 10 } }
                        ]
                    };

                    Plotly.react(containerId, [trace], layout, {displayModeBar: true});
                }

                function drawDockingCompareHistogram(containerId, data, tool) {
                    var container = document.getElementById(containerId);
                    if (!container || !data.data) {
                        if (container) container.innerHTML = '<p style="text-align:center;color:#6b7280;padding:48px;">No data available</p>';
                        return;
                    }

                    var traces = [];
                    var models = data.models || [];
                    var colors = data.model_colors || [];

                    for (var i = 0; i < models.length; i++) {
                        var modelData = data.data[models[i]];
                        if (modelData && modelData.scores && modelData.scores.length > 0) {
                            traces.push({
                                x: modelData.scores,
                                type: 'histogram',
                                name: models[i],
                                opacity: 0.6,
                                marker: { color: colors[i] || 'rgba(139, 92, 246, 0.75)' }
                            });
                        }
                    }

                    if (traces.length === 0) {
                        container.innerHTML = '<p style="text-align:center;color:#6b7280;padding:48px;">No data available</p>';
                        return;
                    }

                    var layout = {
                        title: { text: tool + ' Binding Affinity Comparison', font: { size: 14 } },
                        xaxis: { title: 'Binding Affinity (kcal/mol)' },
                        yaxis: { title: 'Count' },
                        barmode: 'overlay',
                        height: 350,
                        margin: { l: 50, r: 30, t: 50, b: 50 },
                        paper_bgcolor: 'white',
                        plot_bgcolor: 'white',
                        font: { family: '-apple-system, BlinkMacSystemFont, sans-serif', size: 11 },
                        legend: { orientation: 'h', y: 1.12, x: 0 }
                    };

                    Plotly.react(containerId, traces, layout, {displayModeBar: true});
                }

                function drawDockingTopMolecules(containerId, topMolecules) {
                    var container = document.getElementById(containerId);
                    if (!container || !topMolecules || topMolecules.length === 0) {
                        if (container) container.innerHTML = '<p style="text-align:center;color:#6b7280;padding:48px;">No data available</p>';
                        return;
                    }

                    var labels = [];
                    var scores = [];
                    var n = Math.min(10, topMolecules.length);

                    for (var i = 0; i < n; i++) {
                        var mol = topMolecules[i];
                        var label = mol.molecule_id || 'Unknown';
                        if (label.length > 20) label = label.substring(0, 17) + '...';
                        labels.push(label);
                        scores.push(mol.affinity);
                    }

                    var colors = [];
                    for (var j = 0; j < n; j++) {
                        colors.push('rgba(139, 92, 246, ' + (0.95 - j * 0.05) + ')');
                    }

                    var trace = {
                        x: scores,
                        y: labels,
                        type: 'bar',
                        orientation: 'h',
                        marker: { color: colors },
                        text: scores.map(function(s) { return s.toFixed(2); }),
                        textposition: 'outside'
                    };

                    var layout = {
                        title: { text: 'Top 10 Molecules by Binding Affinity', font: { size: 14 } },
                        xaxis: { title: 'Binding Affinity (kcal/mol)' },
                        height: Math.max(300, 35 * n),
                        margin: { l: 150, r: 50, t: 50, b: 50 },
                        paper_bgcolor: 'white',
                        plot_bgcolor: 'white',
                        font: { family: '-apple-system, BlinkMacSystemFont, sans-serif', size: 11 },
                        yaxis: { categoryorder: 'total descending' }
                    };

                    Plotly.react(containerId, [trace], layout, {displayModeBar: true});
                }

                function drawDockingCompareTopMolecules(containerId, data) {
                    var container = document.getElementById(containerId);
                    if (!container || !data.data) {
                        if (container) container.innerHTML = '<p style="text-align:center;color:#6b7280;padding:48px;">No data available</p>';
                        return;
                    }

                    var models = data.models || [];
                    var colors = data.model_colors || [];
                    var labels = [];
                    var values = [];
                    var barColors = [];

                    // Show best affinity per model
                    for (var i = 0; i < models.length; i++) {
                        var modelData = data.data[models[i]];
                        if (modelData && modelData.summary && modelData.summary.best_affinity !== undefined) {
                            labels.push(models[i]);
                            values.push(modelData.summary.best_affinity);
                            barColors.push(colors[i] || 'rgba(139, 92, 246, 0.85)');
                        }
                    }

                    if (labels.length === 0) {
                        container.innerHTML = '<p style="text-align:center;color:#6b7280;padding:48px;">No data available</p>';
                        return;
                    }

                    var trace = {
                        x: values,
                        y: labels,
                        type: 'bar',
                        orientation: 'h',
                        marker: { color: barColors },
                        text: values.map(function(v) { return v.toFixed(2); }),
                        textposition: 'outside'
                    };

                    var layout = {
                        title: { text: 'Best Binding Affinity by Model', font: { size: 14 } },
                        xaxis: { title: 'Binding Affinity (kcal/mol)' },
                        height: Math.max(300, 40 * labels.length),
                        margin: { l: 100, r: 50, t: 50, b: 50 },
                        paper_bgcolor: 'white',
                        plot_bgcolor: 'white',
                        font: { family: '-apple-system, BlinkMacSystemFont, sans-serif', size: 11 },
                        yaxis: { categoryorder: 'total descending' }
                    };

                    Plotly.react(containerId, [trace], layout, {displayModeBar: true});
                }
            </script>
        </section>

        <!-- Model Comparison -->
        {% if data.models %}
        <section>
            <h2>Model Comparison</h2>
            {% if plots.model_comparison %}
            <div class="plot-container">{{ plots.model_comparison | safe }}</div>
            {% endif %}

            <table>
                <thead>
                    <tr>
                        <th>Model</th>
                        <th>Initial</th>
                        <th>Final</th>
                        <th>Retention</th>
                    </tr>
                </thead>
                <tbody>
                    {% for model in data.models %}
                    <tr>
                        <td>{{ model.model_name }}</td>
                        <td>{{ model.initial }}</td>
                        <td>{{ model.final }}</td>
                        <td>
                            {% set retention = (model.final / model.initial * 100) if model.initial > 0 else 0 %}
                            <span class="badge {% if retention >= 50 %}badge-success{% elif retention >= 25 %}badge-warning{% else %}badge-danger{% endif %}">
                                {{ "%.1f"|format(retention) }}%
                            </span>
                        </td>
                    </tr>
                    {% endfor %}
                </tbody>
            </table>

            {% if plots.model_losses %}
            <h3 style="margin-top: 30px; margin-bottom: 15px;">Molecule Losses by Stage</h3>
            <div class="plot-container">{{ plots.model_losses | safe }}</div>
            {% endif %}
        </section>
        {% endif %}

        <!-- Descriptor Analysis (Stage 01) -->
        {% if data.descriptors.distributions or data.descriptors_detailed.raw_data %}
        <section>
            <h2>Descriptor Analysis</h2>

            <!-- View Toggle for model comparison -->
            {% if data.available_models and plots.descriptors_data %}
            <div class="view-toggle" id="descriptor-model-toggle">
                <button class="active" onclick="selectDescriptorModel('all', this)">All Models</button>
                <button onclick="selectDescriptorModel('__compare__', this)">Compare</button>
                {% for model in data.available_models %}
                <button title="{{ model }}" onclick="selectDescriptorModel('{{ model }}', this)">{{ model }}</button>
                {% endfor %}
            </div>
            {% endif %}

            <!-- Interactive Plotly histograms -->
            {% if plots.descriptors_data %}
            <!-- Legend for descriptor plots (above, left-aligned) -->
            <div style="display: flex; justify-content: flex-start; gap: 24px; margin-bottom: 12px; font-size: 12px; color: #4b5563;">
                <span><span style="display: inline-block; width: 20px; height: 3px; background: rgba(109, 40, 217, 0.9); vertical-align: middle; margin-right: 6px;"></span>Mean</span>
                <span><span style="display: inline-block; width: 20px; height: 0; border-top: 3px dashed rgba(34, 197, 94, 0.9); vertical-align: middle; margin-right: 6px;"></span>Median</span>
                <span><span style="display: inline-block; width: 20px; height: 3px; background: rgba(239, 68, 68, 0.9); vertical-align: middle; margin-right: 6px;"></span>Filter Threshold</span>
                <span><span style="display: inline-block; width: 20px; height: 12px; background: rgba(156, 163, 175, 0.3); vertical-align: middle; margin-right: 6px; border: 1px solid #d1d5db;"></span>Out of Range</span>
            </div>
            <div id="descriptor-histograms-container" class="plot-container">
                <!-- Will be populated by JavaScript -->
            </div>

            <script>
                var descriptorsData = {{ (plots.descriptors_data or {}) | tojson }};
                var currentDescriptorModel = 'all';

                function selectDescriptorModel(model, btn) {
                    currentDescriptorModel = model;
                    var buttons = document.getElementById('descriptor-model-toggle').querySelectorAll('button');
                    buttons.forEach(function(b) { b.className = ''; });
                    btn.className = 'active';
                    updateDescriptorPlots();
                }

                function updateDescriptorPlots() {
                    var container = document.getElementById('descriptor-histograms-container');
                    if (!container || !descriptorsData || !descriptorsData.descriptors) {
                        if (container) container.innerHTML = '<p style="text-align:center;color:#6b7280;padding:48px;">No descriptor data available</p>';
                        return;
                    }

                    var model = currentDescriptorModel;
                    var descriptors = descriptorsData.descriptors || [];
                    var thresholds = descriptorsData.thresholds || {};

                    // Readable names for descriptors
                    var readableNames = {
                        'MolWt': 'Molecular Weight',
                        'LogP': 'LogP',
                        'cLogP': 'cLogP',
                        'TPSA': 'TPSA',
                        'NumHDonors': 'H-Bond Donors',
                        'NumHAcceptors': 'H-Bond Acceptors',
                        'QED': 'QED Score',
                        'Fsp3': 'Fsp³ Fraction',
                        'n_atoms': 'Total Atoms',
                        'n_heavy_atoms': 'Heavy Atoms',
                        'n_het_atoms': 'Heteroatoms',
                        'n_rings': 'Ring Count',
                        'n_aroma_rings': 'Aromatic Rings',
                        'n_rot_bonds': 'Rotatable Bonds',
                        'n_rigid_bonds': 'Rigid Bonds',
                        'SW': 'Solubility (SW)',
                        'n_N_atoms': 'Nitrogen Atoms',
                        'fN_atoms': 'Nitrogen Fraction',
                        'n_fused_aromatic_rings': 'Fused Arom. Rings',
                        'charged_mol': 'Charged Molecule',
                        'ring_size': 'Ring Size'
                    };

                    if (!descriptors.length) {
                        container.innerHTML = '<p style="text-align:center;color:#6b7280;padding:48px;">No descriptor data available</p>';
                        return;
                    }

                    var nCols = 3;
                    var nRows = Math.ceil(descriptors.length / nCols);
                    var traces = [];
                    var shapes = [];

                    // Brighter colors for Compare mode
                    var modelColors = [
                        'rgba(139, 92, 246, 0.8)', 'rgba(59, 130, 246, 0.8)', 'rgba(34, 197, 94, 0.8)',
                        'rgba(249, 115, 22, 0.8)', 'rgba(236, 72, 153, 0.8)', 'rgba(20, 184, 166, 0.8)',
                        'rgba(245, 158, 11, 0.8)', 'rgba(168, 85, 247, 0.8)'
                    ];
                    var modelLineColors = [
                        'rgba(109, 40, 217, 1)', 'rgba(37, 99, 235, 1)', 'rgba(22, 163, 74, 1)',
                        'rgba(234, 88, 12, 1)', 'rgba(219, 39, 119, 1)', 'rgba(13, 148, 136, 1)',
                        'rgba(217, 119, 6, 1)', 'rgba(147, 51, 234, 1)'
                    ];

                    for (var i = 0; i < descriptors.length; i++) {
                        var desc = descriptors[i];
                        var descThresholds = thresholds[desc] || {};

                        if (model === '__compare__') {
                            // Compare mode: smooth violin plots overlaid
                            var models = descriptorsData.models || [];

                            // Calculate global min/max for gray shading
                            var globalMin = Infinity, globalMax = -Infinity;
                            for (var j = 0; j < models.length; j++) {
                                var md = descriptorsData.by_model && descriptorsData.by_model[models[j]] && descriptorsData.by_model[models[j]][desc];
                                if (md && md.values) {
                                    globalMin = Math.min(globalMin, md.min || Math.min.apply(null, md.values));
                                    globalMax = Math.max(globalMax, md.max || Math.max.apply(null, md.values));
                                }
                            }

                            // Gray shading for out-of-range (left)
                            if (descThresholds.min !== undefined && descThresholds.min > globalMin) {
                                shapes.push({
                                    type: 'rect', xref: 'x' + (i + 1), yref: 'y' + (i + 1) + ' domain',
                                    x0: globalMin - (globalMax - globalMin) * 0.1, x1: descThresholds.min, y0: 0, y1: 1,
                                    fillcolor: 'rgba(156, 163, 175, 0.3)', line: { width: 0 }, layer: 'below'
                                });
                            }
                            // Gray shading for out-of-range (right)
                            if (descThresholds.max !== undefined && descThresholds.max < globalMax) {
                                shapes.push({
                                    type: 'rect', xref: 'x' + (i + 1), yref: 'y' + (i + 1) + ' domain',
                                    x0: descThresholds.max, x1: globalMax + (globalMax - globalMin) * 0.1, y0: 0, y1: 1,
                                    fillcolor: 'rgba(156, 163, 175, 0.3)', line: { width: 0 }, layer: 'below'
                                });
                            }

                            for (var j = 0; j < models.length; j++) {
                                var modelName = models[j];
                                var modelData = descriptorsData.by_model && descriptorsData.by_model[modelName] && descriptorsData.by_model[modelName][desc];
                                if (modelData && modelData.values && modelData.values.length > 0) {
                                    traces.push({
                                        type: 'violin',
                                        x: modelData.values,
                                        y0: 0,
                                        name: modelName,
                                        orientation: 'h',
                                        side: 'positive',
                                        width: 1.8,
                                        points: false,
                                        line: { color: modelLineColors[j % modelLineColors.length], width: 2 },
                                        fillcolor: modelColors[j % modelColors.length],
                                        xaxis: 'x' + (i + 1), yaxis: 'y' + (i + 1),
                                        showlegend: (i === 0), legendgroup: modelName,
                                        scalemode: 'width', spanmode: 'hard'
                                    });
                                }
                            }
                            // Threshold lines for compare mode
                            if (descThresholds.min !== undefined) {
                                shapes.push({
                                    type: 'line', xref: 'x' + (i + 1), yref: 'y' + (i + 1) + ' domain',
                                    x0: descThresholds.min, x1: descThresholds.min, y0: 0, y1: 1,
                                    line: { color: 'rgba(239, 68, 68, 0.9)', width: 2 }
                                });
                            }
                            if (descThresholds.max !== undefined) {
                                shapes.push({
                                    type: 'line', xref: 'x' + (i + 1), yref: 'y' + (i + 1) + ' domain',
                                    x0: descThresholds.max, x1: descThresholds.max, y0: 0, y1: 1,
                                    line: { color: 'rgba(239, 68, 68, 0.9)', width: 2 }
                                });
                            }
                        } else {
                            // All Models / Single model: histogram
                            var data = model === 'all'
                                ? (descriptorsData.all && descriptorsData.all[desc])
                                : (descriptorsData.by_model && descriptorsData.by_model[model] && descriptorsData.by_model[model][desc]);

                            if (data && data.values && data.values.length > 0) {
                                var xMin = data.min, xMax = data.max;

                                traces.push({
                                    type: 'histogram',
                                    x: data.values,
                                    name: desc,
                                    nbinsx: 50,
                                    marker: {
                                        color: 'rgba(139, 92, 246, 0.7)',
                                        line: { color: 'rgba(109, 40, 217, 0.9)', width: 1 }
                                    },
                                    xaxis: 'x' + (i + 1), yaxis: 'y' + (i + 1),
                                    showlegend: false
                                });

                                // Gray shading for out-of-range (left)
                                if (descThresholds.min !== undefined && descThresholds.min > xMin) {
                                    shapes.push({
                                        type: 'rect', xref: 'x' + (i + 1), yref: 'y' + (i + 1) + ' domain',
                                        x0: xMin - (xMax - xMin) * 0.1, x1: descThresholds.min, y0: 0, y1: 1,
                                        fillcolor: 'rgba(156, 163, 175, 0.3)', line: { width: 0 }, layer: 'below'
                                    });
                                }
                                // Gray shading for out-of-range (right)
                                if (descThresholds.max !== undefined && descThresholds.max < xMax) {
                                    shapes.push({
                                        type: 'rect', xref: 'x' + (i + 1), yref: 'y' + (i + 1) + ' domain',
                                        x0: descThresholds.max, x1: xMax + (xMax - xMin) * 0.1, y0: 0, y1: 1,
                                        fillcolor: 'rgba(156, 163, 175, 0.3)', line: { width: 0 }, layer: 'below'
                                    });
                                }

                                // Mean line (purple solid)
                                shapes.push({
                                    type: 'line', xref: 'x' + (i + 1), yref: 'y' + (i + 1) + ' domain',
                                    x0: data.mean, x1: data.mean, y0: 0, y1: 1,
                                    line: { color: 'rgba(109, 40, 217, 0.9)', width: 2 }
                                });

                                // Median line (green dashed)
                                if (data.median !== undefined) {
                                    shapes.push({
                                        type: 'line', xref: 'x' + (i + 1), yref: 'y' + (i + 1) + ' domain',
                                        x0: data.median, x1: data.median, y0: 0, y1: 1,
                                        line: { color: 'rgba(34, 197, 94, 0.9)', width: 2, dash: 'dash' }
                                    });
                                }

                                // Threshold min line (red)
                                if (descThresholds.min !== undefined) {
                                    shapes.push({
                                        type: 'line', xref: 'x' + (i + 1), yref: 'y' + (i + 1) + ' domain',
                                        x0: descThresholds.min, x1: descThresholds.min, y0: 0, y1: 1,
                                        line: { color: 'rgba(239, 68, 68, 0.9)', width: 2 }
                                    });
                                }
                                // Threshold max line (red)
                                if (descThresholds.max !== undefined) {
                                    shapes.push({
                                        type: 'line', xref: 'x' + (i + 1), yref: 'y' + (i + 1) + ' domain',
                                        x0: descThresholds.max, x1: descThresholds.max, y0: 0, y1: 1,
                                        line: { color: 'rgba(239, 68, 68, 0.9)', width: 2 }
                                    });
                                }
                            }
                        }
                    }

                    // Build layout with subplot titles anchored to axis domains
                    var annotations = [];
                    var totalHeight = model === '__compare__' ? 260 * nRows : 240 * nRows;
                    var topMargin = model === '__compare__' ? 60 : 40;
                    var layout = {
                        height: totalHeight,
                        margin: { l: 50, r: 30, t: topMargin, b: 50 },
                        paper_bgcolor: 'white', plot_bgcolor: 'white',
                        font: { family: '-apple-system, BlinkMacSystemFont, sans-serif', size: 11 },
                        showlegend: (model === '__compare__'),
                        legend: { orientation: 'h', y: 1.06, x: 0.5, xanchor: 'center', font: { size: 10 } },
                        shapes: shapes,
                        barmode: 'overlay',
                        grid: { rows: nRows, columns: nCols, pattern: 'independent', roworder: 'top to bottom', xgap: 0.1, ygap: 0.22 }
                    };

                    for (var i = 0; i < descriptors.length; i++) {
                        var axisNum = i + 1;
                        var displayName = readableNames[descriptors[i]] || descriptors[i];
                        var xAxisName = i === 0 ? 'x' : ('x' + axisNum);
                        var yAxisName = i === 0 ? 'y' : ('y' + axisNum);

                        // Title annotation anchored to subplot domain
                        annotations.push({
                            text: '<b>' + displayName + '</b>',
                            font: { size: 12, color: '#1f2937' },
                            showarrow: false,
                            xref: xAxisName + ' domain',
                            yref: yAxisName + ' domain',
                            x: 0.5,
                            y: 1.02,
                            xanchor: 'center',
                            yanchor: 'bottom'
                        });

                        layout['xaxis' + axisNum] = { showgrid: false, zeroline: false, tickfont: { size: 10 } };
                        layout['yaxis' + axisNum] = { showgrid: true, gridcolor: '#e5e7eb', zeroline: false, showticklabels: false };
                    }

                    layout.annotations = annotations;
                    Plotly.newPlot(container, traces, layout, { displayModeBar: true });
                }

                // Initialize on page load
                document.addEventListener('DOMContentLoaded', function() {
                    if (document.getElementById('descriptor-histograms-container')) {
                        updateDescriptorPlots();
                    }
                });
            </script>

            {% else %}
            <!-- Fallback: Violin plots by model -->
            {% if plots.descriptors_violin %}
            <h3 style="margin-top: 30px; margin-bottom: 15px;">Descriptor Distributions by Model</h3>
            <div class="plot-container">{{ plots.descriptors_violin | safe }}</div>
            {% endif %}
            {% endif %}

            <!-- Summary table by model -->
            {% if plots.descriptors_table %}
            <h3 style="margin-top: 30px; margin-bottom: 15px;">Mean Values by Model</h3>
            {{ plots.descriptors_table | safe }}
            {% endif %}
        </section>
        {% endif %}

        <!-- Structural Filters Analysis (Stage 02) -->
        {% if data.filters.by_filter or data.filters.totals or data.filters_detailed.by_filter or plots.filters_data %}
        <section>
            <h2>Structural Filters Analysis</h2>

            <!-- Summary cards for filters -->
            {% if data.filters.totals %}
            <div class="metrics-grid">
                <div class="metric-card">
                    <div class="metric-value">{{ data.filters.totals|length }}</div>
                    <div class="metric-label">Filters Applied</div>
                </div>
                <div class="metric-card">
                    <div class="metric-value">{{ data.filters.totals.values()|sum }}</div>
                    <div class="metric-label">Total Rejected</div>
                </div>
                {% if data.filters_detailed.filter_metrics %}
                {% for filter_name, metrics in data.filters_detailed.filter_metrics.items() %}
                {% if metrics.avg_demerit_score %}
                <div class="metric-card">
                    <div class="metric-value">{{ "%.1f"|format(metrics.avg_demerit_score) }}</div>
                    <div class="metric-label">Avg Demerit (Lilly)</div>
                </div>
                {% endif %}
                {% if metrics.avg_sas %}
                <div class="metric-card">
                    <div class="metric-value">{{ "%.2f"|format(metrics.avg_sas) }}</div>
                    <div class="metric-label">Avg SAS</div>
                </div>
                {% endif %}
                {% endfor %}
                {% endif %}
            </div>
            {% endif %}

            <!-- Interactive Filter Analysis Charts -->
            {% if plots.filters_data %}
            <div class="view-toggle" id="filter-chart-toggle">
                <button class="active" onclick="selectFilterChartType('overview', this)">Overview</button>
                <button onclick="selectFilterChartType('heatmap', this)">Sub-filters</button>
            </div>

            <div id="filter-overview-container" class="plot-container"></div>
            <div id="filter-heatmaps-container" class="plot-container" style="display: none;"></div>

            <!-- Filter descriptions (visible only for Overview, below chart) -->
            <div id="filter-legend" style="margin-top: 16px; font-size: 13px; color: #374151; line-height: 1.8;">
                <div style="font-weight: 600; margin-bottom: 8px; color: #1f2937;">Filter Descriptions:</div>
                <div style="margin-left: 8px;">
                    <div><strong>NIBR</strong> — Novartis structural alerts: flags reactive warheads, toxic motifs, and metabolically labile groups that cause safety failures in preclinical studies</div>
                    <div><strong>bredt</strong> — Bredt's rule: rejects molecules with impossible bridgehead double bonds — these cannot exist or be synthesized</div>
                    <div><strong>common_alerts</strong> — PAINS, toxicophores, skin sensitizers, frequent hitters — removes promiscuous binders and false positives from biochemical assays</div>
                    <div><strong>lilly</strong> — Eli Lilly MedChem rules: demerit scoring of 275+ structural features linked to poor ADMET, toxicity, or synthetic infeasibility</div>
                    <div><strong>molcomplexity</strong> — Synthetic accessibility (SAS), QED drug-likeness, Bertz complexity — ensures practical synthesizability and drug-like properties</div>
                    <div><strong>molgraph_stats</strong> — Topological constraints on ring count, heavy atoms, connectivity — excludes chemically unreasonable structures</div>
                    <div><strong>protecting_groups</strong> — Detects residual protecting groups (Boc, Fmoc, Cbz, etc.) that should be removed before biological testing</div>
                    <div><strong>ring_infraction</strong> — Flags strained ring systems and small heterocycles below minimum size — chemically unstable or synthetically challenging</div>
                    <div><strong>stereo_center</strong> — Limits number of stereo centers and undefined stereocenters — excessive chirality makes synthesis difficult and costly</div>
                    <div><strong>halogenicity</strong> — Limits halogen atom counts (F, Cl, Br) — over-halogenated molecules often have poor metabolic stability and toxicity</div>
                    <div><strong>symmetry</strong> — Rejects highly symmetric molecules (off by default) — many real drugs are symmetric, use cautiously</div>
                </div>
            </div>

            <script>
                var filtersData = {{ (plots.filters_data or {}) | tojson }};
                var currentFilterChartType = 'overview';

                // Filter descriptions for tooltips
                var filterDescriptions = {
                    'NIBR': 'Novartis structural alerts — flags reactive warheads, toxic motifs, and metabolically labile groups',
                    'bredt': "Bredt's rule — rejects molecules with impossible bridgehead double bonds",
                    'common_alerts': 'PAINS, toxicophores, skin sensitizers, frequent hitters — removes promiscuous binders',
                    'lilly': 'Eli Lilly MedChem rules — demerit scoring of 275+ structural features',
                    'molcomplexity': 'Synthetic accessibility (SAS), QED drug-likeness, Bertz complexity',
                    'molgraph_stats': 'Topological constraints on ring count, heavy atoms, connectivity',
                    'protecting_groups': 'Detects residual protecting groups (Boc, Fmoc, Cbz, etc.)',
                    'ring_infraction': 'Flags strained ring systems and small heterocycles below minimum size',
                    'stereo_center': 'Limits stereo centers and undefined stereocenters',
                    'halogenicity': 'Limits halogen atom counts (F, Cl, Br) — metabolic stability and toxicity',
                    'symmetry': 'Rejects highly symmetric molecules (off by default)'
                };

                function selectFilterChartType(chartType, btn) {
                    currentFilterChartType = chartType;
                    var buttons = document.getElementById('filter-chart-toggle').querySelectorAll('button');
                    buttons.forEach(function(b) { b.className = ''; });
                    btn.className = 'active';

                    document.getElementById('filter-overview-container').style.display = (chartType === 'overview') ? 'block' : 'none';
                    document.getElementById('filter-heatmaps-container').style.display = (chartType === 'heatmap') ? 'block' : 'none';
                    document.getElementById('filter-legend').style.display = (chartType === 'overview') ? 'block' : 'none';

                    updateFilterPlots();
                }

                function updateFilterPlots() {
                    if (currentFilterChartType === 'overview') renderFilterOverview();
                    else renderFilterHeatmaps();
                }

                function renderFilterOverview() {
                    var container = document.getElementById('filter-overview-container');
                    if (!container || !filtersData.filter_data) return;

                    var filterNames = filtersData.filters || [];
                    var models = filtersData.models || [];
                    var zVals = [], textVals = [], hoverVals = [];

                    for (var mi = 0; mi < models.length; mi++) {
                        var m = models[mi];
                        var rowVals = [], rowText = [], rowHover = [];
                        for (var fi = 0; fi < filterNames.length; fi++) {
                            var fn = filterNames[fi];
                            var filterInfo = filtersData.filter_data[fn];
                            if (filterInfo && filterInfo.pass_rate && filterInfo.pass_rate[m] !== undefined) {
                                var passedPct = filterInfo.pass_rate[m];
                                var numMol = filterInfo.num_mol[m] || 0;
                                var rejectedCount = Math.round(numMol * (100 - passedPct) / 100);
                                rowVals.push(passedPct);
                                rowText.push(passedPct.toFixed(0) + '%');
                                var desc = filterDescriptions[fn] || fn;
                                rowHover.push(
                                    '<b>' + fn + '</b><br>' + desc + '<br><br>' +
                                    '<b>Model:</b> ' + m + '<br>' +
                                    '<b>Pass rate:</b> ' + passedPct.toFixed(1) + '%<br>' +
                                    '<b>Total:</b> ' + numMol + '<br>' +
                                    '<b>Rejected:</b> ' + rejectedCount
                                );
                            } else {
                                rowVals.push(100);
                                rowText.push('100%');
                                rowHover.push(fn + '<br>Pass rate: 100%');
                            }
                        }
                        zVals.push(rowVals);
                        textVals.push(rowText);
                        hoverVals.push(rowHover);
                    }

                    var trace = {
                        type: 'heatmap', z: zVals, x: filterNames, y: models,
                        text: textVals, texttemplate: '%{text}', textfont: { size: 10 },
                        hovertext: hoverVals, hoverinfo: 'text',
                        colorscale: [[0, 'rgba(252, 165, 165, 0.9)'], [0.5, 'rgba(245, 243, 255, 0.9)'], [1, 'rgba(134, 239, 172, 0.9)']],
                        zmin: 0, zmax: 100, showscale: false
                    };

                    var layout = {
                        title: 'Pass Rate per Filter × Model',
                        height: Math.max(300, models.length * 50 + 100),
                        margin: { l: 100, r: 30, t: 50, b: 80 },
                        xaxis: { tickangle: -45 },
                        paper_bgcolor: 'white', plot_bgcolor: 'white',
                        font: { family: '-apple-system, BlinkMacSystemFont, sans-serif', size: 11 }
                    };

                    Plotly.react('filter-overview-container', [trace], layout, {displayModeBar: true});
                }

                function renderFilterHeatmaps() {
                    var container = document.getElementById('filter-heatmaps-container');
                    if (!container || !filtersData.filter_data) return;

                    var filterNames = filtersData.filters || [];
                    var models = filtersData.models || [];

                    // Find filters with detailed sub-metrics (more than just passed/failed)
                    var detailedFilters = [];
                    for (var fi = 0; fi < filterNames.length; fi++) {
                        var fn = filterNames[fi];
                        var filterInfo = filtersData.filter_data[fn];
                        if (filterInfo && filterInfo.sub_metric_names && filterInfo.sub_metric_names.length > 2) {
                            detailedFilters.push(fn);
                        }
                    }

                    if (detailedFilters.length === 0) {
                        container.innerHTML = '<p style="text-align:center;color:#6b7280;padding:48px;">No detailed sub-filter metrics available</p>';
                        return;
                    }

                    // Create a heatmap for each detailed filter
                    container.innerHTML = '';
                    for (var di = 0; di < detailedFilters.length; di++) {
                        var fn = detailedFilters[di];
                        var filterInfo = filtersData.filter_data[fn];

                        // Build sub-metrics list: start with 'all' (passed all), then individual ones
                        var individualMetrics = filterInfo.sub_metric_names.filter(function(m) {
                            return m !== 'all' && m !== 'any' && m !== 'passed' && m !== 'failed';
                        });
                        if (individualMetrics.length === 0) continue;

                        // Add 'all' at the beginning if it exists (shows "Passed All Sub-filters")
                        var hasAll = filterInfo.sub_metric_names.indexOf('all') !== -1;
                        var subMetrics = hasAll ? ['Passed All'].concat(individualMetrics) : individualMetrics;

                        // Calculate pass rate: (num_mol - banned_count) / num_mol * 100
                        var zVals = [], textVals = [], hoverVals = [];
                        for (var mi = 0; mi < models.length; mi++) {
                            var m = models[mi];
                            var rowVals = [], rowText = [], rowHover = [];
                            var metrics = filterInfo.sub_metrics[m] || {};
                            var numMol = filterInfo.num_mol[m] || 0;

                            for (var si = 0; si < subMetrics.length; si++) {
                                var sm = subMetrics[si];
                                var bannedCount, passedPct;

                                if (sm === 'Passed All') {
                                    // 'all' in data = banned by at least one, so passed all = 1 - all
                                    bannedCount = metrics['all'] || 0;
                                    passedPct = numMol > 0 ? ((numMol - bannedCount) / numMol * 100) : 100;
                                    rowHover.push('<b>Passed All Sub-filters</b><br>Model: ' + m + '<br>Pass rate: ' + passedPct.toFixed(1) + '%<br>Passed: ' + (numMol - bannedCount) + ' / ' + numMol);
                                } else {
                                    bannedCount = metrics[sm] || 0;
                                    passedPct = numMol > 0 ? ((numMol - bannedCount) / numMol * 100) : 100;
                                    rowHover.push('<b>' + sm + '</b><br>Model: ' + m + '<br>Pass rate: ' + passedPct.toFixed(1) + '%<br>Banned: ' + bannedCount + ' / ' + numMol);
                                }
                                rowVals.push(passedPct);
                                rowText.push(passedPct.toFixed(0) + '%');
                            }
                            zVals.push(rowVals);
                            textVals.push(rowText);
                            hoverVals.push(rowHover);
                        }

                        var divId = 'filter-heatmap-' + fn;
                        var div = document.createElement('div');
                        div.id = divId;
                        div.style.marginBottom = '30px';
                        container.appendChild(div);

                        var trace = {
                            type: 'heatmap', z: zVals, x: subMetrics, y: models,
                            text: textVals, texttemplate: '%{text}', textfont: { size: 9 },
                            hovertext: hoverVals, hoverinfo: 'text',
                            colorscale: [[0, 'rgba(239, 68, 68, 0.8)'], [0.5, 'rgba(250, 250, 250, 0.9)'], [1, 'rgba(34, 197, 94, 0.8)']],
                            zmin: 0, zmax: 100,
                            showscale: true,
                            colorbar: {
                                title: { text: 'Pass %', side: 'right' },
                                ticksuffix: '%',
                                len: 0.9,
                                thickness: 15
                            }
                        };

                        var layout = {
                            title: fn + ' — Pass Rate by Sub-filter',
                            height: Math.max(280, models.length * 45 + 120),
                            margin: { l: 100, r: 100, t: 50, b: Math.max(100, subMetrics.length > 8 ? 140 : 100) },
                            xaxis: { tickangle: -45 },
                            paper_bgcolor: 'white', plot_bgcolor: 'white',
                            font: { family: '-apple-system, BlinkMacSystemFont, sans-serif', size: 11 }
                        };

                        Plotly.react(divId, [trace], layout, {displayModeBar: true});
                    }
                }

                document.addEventListener('DOMContentLoaded', function() {
                    if (document.getElementById('filter-overview-container')) {
                        updateFilterPlots();
                    }
                });
            </script>
            {% endif %}
        </section>
        {% endif %}

        <!-- Synthesis Analysis (Stage 04) -->
        {% if data.synthesis.distributions or data.synthesis_detailed.sa_scores %}
        <section>
            <h2>Synthesis Analysis</h2>

            <!-- View Toggle for model selection -->
            {% if data.available_models and data.synthesis_detailed.by_model %}
            <div class="view-toggle" id="synthesis-toggle">
                <button class="active" onclick="selectSynthesisModel('all', this)">All Models</button>
                {% for model in data.available_models %}
                <button title="{{ model }}" onclick="selectSynthesisModel('{{ model }}', this)">{{ model }}</button>
                {% endfor %}
            </div>
            {% endif %}

            <!-- ==================== Synthesis Scores Section ==================== -->
            <h3 style="margin-top: 20px; margin-bottom: 15px;">Synthesis Scores</h3>

            <!-- Legend for score histograms -->
            <div style="display: flex; justify-content: flex-start; gap: 24px; margin: 15px 0 10px 0; font-size: 12px; color: #4b5563;">
                <span><span style="display: inline-block; width: 20px; height: 3px; background: rgba(109, 40, 217, 0.9); vertical-align: middle; margin-right: 6px;"></span>Mean</span>
                <span><span style="display: inline-block; width: 20px; height: 3px; background: rgba(239, 68, 68, 0.9); vertical-align: middle; margin-right: 6px;"></span>Threshold</span>
                <span><span style="display: inline-block; width: 20px; height: 12px; background: rgba(156, 163, 175, 0.3); vertical-align: middle; margin-right: 6px; border: 1px solid #d1d5db;"></span>Out of Range</span>
            </div>

            <!-- SA, SYBA, RA histograms (dynamic) -->
            <div class="three-col">
                <div>
                    <h4 style="margin-bottom: 8px;">SA Score Distribution</h4>
                    <div id="synthesis-sa-hist" class="plot-container">
                        {% if plots.synthesis_sa_hist %}{{ plots.synthesis_sa_hist | safe }}{% endif %}
                    </div>
                </div>
                <div>
                    <h4 style="margin-bottom: 8px;">SYBA Score Distribution</h4>
                    <div id="synthesis-syba-hist" class="plot-container">
                        {% if plots.synthesis_syba_hist %}{{ plots.synthesis_syba_hist | safe }}{% endif %}
                    </div>
                </div>
                <div>
                    <h4 style="margin-bottom: 8px;">RA Score Distribution</h4>
                    <div id="synthesis-ra-hist" class="plot-container">
                        {% if plots.synthesis_ra_hist %}{{ plots.synthesis_ra_hist | safe }}{% endif %}
                    </div>
                </div>
            </div>
            <div style="text-align: center; margin-top: 8px;">
                <p style="color: #6b7280; font-size: 12px; margin: 2px 0;">SA Score (lower = easier)</p>
                <p style="color: #6b7280; font-size: 12px; margin: 2px 0;">SYBA Score (higher = easier)</p>
                <p style="color: #6b7280; font-size: 12px; margin: 2px 0;">RA Score (higher = easier)</p>
            </div>

            <!-- ==================== AiZynthFinder Section ==================== -->
            {% if data.retrosynthesis and data.retrosynthesis.summary %}
            <h3 style="margin-top: 30px; margin-bottom: 15px; padding-top: 15px; border-top: 1px solid var(--border);">AiZynthFinder Retrosynthesis</h3>

            <!-- AiZynthFinder metrics -->
            <div class="metrics-grid">
                {% if data.synthesis_detailed.summary.pct_solved is defined %}
                <div class="metric-card">
                    <div class="metric-value" id="synthesis-pct-solved">{{ "%.1f"|format(data.synthesis_detailed.summary.pct_solved) }}%</div>
                    <div class="metric-label">Routes Found</div>
                </div>
                {% endif %}
                {% if data.synthesis_detailed.summary.avg_search_time %}
                <div class="metric-card">
                    <div class="metric-value" id="synthesis-avg-time">{{ "%.1f"|format(data.synthesis_detailed.summary.avg_search_time) }}s</div>
                    <div class="metric-label">Avg Search Time</div>
                </div>
                {% endif %}
                {% if data.retrosynthesis.summary.avg_route_score is defined %}
                <div class="metric-card">
                    <div class="metric-value">{{ "%.2f"|format(data.retrosynthesis.summary.avg_route_score) }}</div>
                    <div class="metric-label">Avg Route Score</div>
                </div>
                {% endif %}
                {% if data.retrosynthesis.summary.avg_steps is defined %}
                <div class="metric-card">
                    <div class="metric-value">{{ "%.1f"|format(data.retrosynthesis.summary.avg_steps) }}</div>
                    <div class="metric-label">Avg Steps</div>
                </div>
                {% endif %}
                {% if data.retrosynthesis.summary.avg_precursors is defined %}
                <div class="metric-card">
                    <div class="metric-value">{{ "%.1f"|format(data.retrosynthesis.summary.avg_precursors) }}</div>
                    <div class="metric-label">Avg Precursors</div>
                </div>
                {% endif %}
            </div>

            <!-- Route Status pie chart -->
            <div class="two-col" style="margin-top: 20px;">
                <div>
                    <h4 style="margin-bottom: 8px;">Route Status</h4>
                    <div id="synthesis-solved-pie" class="plot-container">
                        {% if plots.synthesis_solved_pie %}{{ plots.synthesis_solved_pie | safe }}{% endif %}
                    </div>
                </div>
                <div>
                    {% if plots.synthesis_time_box %}
                    <h4 style="margin-bottom: 8px;">Search Time by Model</h4>
                    <div class="plot-container">{{ plots.synthesis_time_box | safe }}</div>
                    {% endif %}
                </div>
            </div>

            <!-- AiZynthFinder plots - compact -->
            <div class="two-col" style="margin-top: 15px;">
                <div>
                    <h4 style="margin-bottom: 8px;">Route Score Distribution</h4>
                    <div class="plot-container">
                        {% if plots.retrosynthesis_route_score_hist %}
                        {{ plots.retrosynthesis_route_score_hist | safe }}
                        {% else %}
                        <div class="empty-state">No route score data available</div>
                        {% endif %}
                    </div>
                </div>
                <div>
                    <h4 style="margin-bottom: 8px;">Synthesis Steps</h4>
                    <div class="plot-container">
                        {% if plots.retrosynthesis_steps_hist %}
                        {{ plots.retrosynthesis_steps_hist | safe }}
                        {% else %}
                        <div class="empty-state">No steps data available</div>
                        {% endif %}
                    </div>
                </div>
            </div>
            {% endif %}
        </section>
        {% endif %}

        <!-- Docking Results (Stage 05) -->
        {% if data.docking.gnina or data.docking.smina or data.docking_detailed.gnina.raw_data or data.docking_detailed.smina.raw_data %}
        <section>
            <h2>Docking Results</h2>

            <!-- View Toggle for model comparison -->
            {% if data.available_models and (data.docking_detailed.gnina.by_model or data.docking_detailed.smina.by_model) %}
            <div class="view-toggle" id="docking-toggle">
                <button class="active" onclick="selectDockingModel('all', this)">All Models</button>
                <button onclick="selectDockingModel('__compare__', this)">Compare</button>
                {% for model in data.available_models %}
                <button title="{{ model }}" onclick="selectDockingModel('{{ model }}', this)">{{ model }}</button>
                {% endfor %}
            </div>
            {% endif %}

            <!-- Dynamic metrics cards -->
            <div id="docking-metrics" class="metrics-grid">
                {% if data.docking_detailed.gnina.summary %}
                <div class="metric-card">
                    <div class="metric-value" id="docking-avg-affinity">{{ "%.2f"|format(data.docking_detailed.gnina.summary.avg_affinity) }}</div>
                    <div class="metric-label" id="docking-avg-label">Avg Affinity (GNINA)</div>
                </div>
                <div class="metric-card">
                    <div class="metric-value" id="docking-best-affinity">{{ "%.2f"|format(data.docking_detailed.gnina.summary.best_affinity) }}</div>
                    <div class="metric-label" id="docking-best-label">Best Affinity (GNINA)</div>
                </div>
                <div class="metric-card">
                    <div class="metric-value" id="docking-count">{{ data.docking_detailed.gnina.summary.count }}</div>
                    <div class="metric-label">Molecules Docked</div>
                </div>
                {% elif data.docking_detailed.smina.summary %}
                <div class="metric-card">
                    <div class="metric-value" id="docking-avg-affinity">{{ "%.2f"|format(data.docking_detailed.smina.summary.avg_affinity) }}</div>
                    <div class="metric-label" id="docking-avg-label">Avg Affinity (SMINA)</div>
                </div>
                <div class="metric-card">
                    <div class="metric-value" id="docking-best-affinity">{{ "%.2f"|format(data.docking_detailed.smina.summary.best_affinity) }}</div>
                    <div class="metric-label" id="docking-best-label">Best Affinity (SMINA)</div>
                </div>
                <div class="metric-card">
                    <div class="metric-value" id="docking-count">{{ data.docking_detailed.smina.summary.count }}</div>
                    <div class="metric-label">Molecules Docked</div>
                </div>
                {% endif %}
            </div>

            <!-- GNINA Results -->
            {% if data.docking_detailed.gnina.raw_data or plots.docking_gnina %}
            <div id="docking-gnina-section">
                <h3 style="margin-top: 30px; margin-bottom: 15px;">GNINA Results</h3>

                <div class="two-col">
                    <div>
                        <div id="docking-gnina-hist" class="plot-container">
                            {% if plots.docking_gnina_affinity_hist %}{{ plots.docking_gnina_affinity_hist | safe }}
                            {% elif plots.docking_gnina %}{{ plots.docking_gnina | safe }}{% endif %}
                        </div>
                    </div>
                    <div>
                        <div id="docking-gnina-top" class="plot-container">
                            {% if plots.docking_gnina_top_molecules %}{{ plots.docking_gnina_top_molecules | safe }}{% endif %}
                        </div>
                    </div>
                </div>

                {% if plots.docking_gnina_affinity_box %}
                <h4 style="margin-top: 20px; margin-bottom: 15px;">Binding Affinity by Model</h4>
                <div class="plot-container">{{ plots.docking_gnina_affinity_box | safe }}</div>
                {% endif %}
            </div>
            {% endif %}

            <!-- SMINA Results -->
            {% if data.docking_detailed.smina.raw_data or plots.docking_smina %}
            <div id="docking-smina-section">
                <h3 style="margin-top: 30px; margin-bottom: 15px;">SMINA Results</h3>

                <div class="two-col">
                    <div>
                        <div id="docking-smina-hist" class="plot-container">
                            {% if plots.docking_smina_affinity_hist %}{{ plots.docking_smina_affinity_hist | safe }}
                            {% elif plots.docking_smina %}{{ plots.docking_smina | safe }}{% endif %}
                        </div>
                    </div>
                    <div>
                        <div id="docking-smina-top" class="plot-container">
                            {% if plots.docking_smina_top_molecules %}{{ plots.docking_smina_top_molecules | safe }}{% endif %}
                        </div>
                    </div>
                </div>

                {% if plots.docking_smina_affinity_box %}
                <h4 style="margin-top: 20px; margin-bottom: 15px;">Binding Affinity by Model</h4>
                <div class="plot-container">{{ plots.docking_smina_affinity_box | safe }}</div>
                {% endif %}
            </div>
            {% endif %}
        </section>
        {% endif %}

        <!-- Docking Filters Analysis (Stage 06) -->
        {% if data.docking_filters_detailed and data.docking_filters_detailed.per_filter %}
        <section>
            <h2>Docking Filters</h2>

            <!-- Model toggle -->
            {% if data.available_models and data.docking_filters_detailed.by_model %}
            <div class="view-toggle" id="docking-filters-toggle">
                <button class="active" onclick="selectDockingFiltersModel('all', this)">All Models</button>
                {% for model in data.available_models %}
                <button title="{{ model }}" onclick="selectDockingFiltersModel('{{ model }}', this)">{{ model }}</button>
                {% endfor %}
            </div>
            {% endif %}

            <!-- Metric cards -->
            <div class="metrics-grid">
                <div class="metric-card">
                    <div class="metric-value" id="df-total-poses">{{ data.docking_filters_detailed.total_poses }}</div>
                    <div class="metric-label">Total Poses</div>
                </div>
                <div class="metric-card">
                    <div class="metric-value" id="df-passed-poses">{{ data.docking_filters_detailed.passed_poses }}</div>
                    <div class="metric-label">Passed Poses</div>
                </div>
                <div class="metric-card">
                    <div class="metric-value" id="df-pass-rate">{{ data.docking_filters_detailed.pass_rate }}%</div>
                    <div class="metric-label">Pass Rate</div>
                </div>
                <div class="metric-card">
                    <div class="metric-value" id="df-unique-mols">{{ data.docking_filters_detailed.unique_molecules_passed }}</div>
                    <div class="metric-label">Unique Molecules</div>
                </div>
                <div class="metric-card">
                    <div class="metric-value" style="font-size: 1.1rem;">{{ data.docking_filters_detailed.aggregation_mode }}</div>
                    <div class="metric-label">Aggregation Mode</div>
                </div>
            </div>

            <!-- Per-filter pass/fail bar chart -->
            {% if plots.docking_filters_pass_fail %}
            <h3 style="margin-top: 24px; margin-bottom: 12px;">Per-Filter Results</h3>
            <div class="plot-container">{{ plots.docking_filters_pass_fail | safe }}</div>
            {% endif %}

            <!-- Numeric metric histograms -->
            {% if plots.docking_filters_metric_hists %}
            <h3 style="margin-top: 24px; margin-bottom: 12px;">Metric Distributions</h3>
            <div style="display: flex; justify-content: flex-start; gap: 24px; margin-bottom: 12px; font-size: 12px; color: #4b5563;">
                <span><span style="display: inline-block; width: 20px; height: 3px; background: rgba(239, 68, 68, 0.9); vertical-align: middle; margin-right: 6px;"></span>Filter Threshold</span>
            </div>
            <div class="plot-container">{{ plots.docking_filters_metric_hists | safe }}</div>
            {% endif %}

            <!-- By-model pass rate chart -->
            {% if plots.docking_filters_by_model %}
            <h3 style="margin-top: 24px; margin-bottom: 12px;">Pass Rate by Model</h3>
            <div class="plot-container">{{ plots.docking_filters_by_model | safe }}</div>
            {% endif %}

            <script>
                var dockingFiltersData = {{ (plots.docking_filters_data or {}) | tojson }};
                var currentDockingFiltersModel = 'all';

                function selectDockingFiltersModel(model, btn) {
                    currentDockingFiltersModel = model;
                    var toggle = document.getElementById('docking-filters-toggle');
                    if (toggle) {
                        var buttons = toggle.querySelectorAll('button');
                        buttons.forEach(function(b) { b.className = ''; });
                        btn.className = 'active';
                    }
                    updateDockingFiltersMetrics();
                }

                function updateDockingFiltersMetrics() {
                    var model = currentDockingFiltersModel;
                    var data = dockingFiltersData[model];
                    if (!data) return;

                    if (model === 'all') {
                        var el;
                        el = document.getElementById('df-total-poses');
                        if (el) el.textContent = data.total_poses || 0;
                        el = document.getElementById('df-passed-poses');
                        if (el) el.textContent = data.passed_poses || 0;
                        el = document.getElementById('df-pass-rate');
                        if (el) el.textContent = (data.pass_rate || 0) + '%';
                        el = document.getElementById('df-unique-mols');
                        if (el) el.textContent = data.unique_molecules_passed || 0;
                    } else {
                        var el;
                        el = document.getElementById('df-total-poses');
                        if (el) el.textContent = data.total || 0;
                        el = document.getElementById('df-passed-poses');
                        if (el) el.textContent = data.passed || 0;
                        el = document.getElementById('df-pass-rate');
                        if (el) el.textContent = (data.pass_rate || 0) + '%';
                        el = document.getElementById('df-unique-mols');
                        if (el) el.textContent = '-';
                    }
                }
            </script>
        </section>
        {% endif %}

        <!-- Final Descriptors Analysis (Stage 07) -->
        {% if data.descriptors_final_detailed and data.descriptors_final_detailed.raw_data %}
        <section>
            <h2>Final Descriptors</h2>

            <!-- Metric cards -->
            {% if data.descriptors_final.summary %}
            <div class="metrics-grid">
                {% for desc_name, desc_stats in data.descriptors_final.summary.items() %}
                <div class="metric-card">
                    <div class="metric-value">{{ "%.2f"|format(desc_stats.mean) }}</div>
                    <div class="metric-label">{{ desc_name }} (mean)</div>
                </div>
                {% endfor %}
            </div>
            {% endif %}

            <!-- Initial vs Final comparison histograms -->
            {% if plots.descriptors_comparison_data %}
            <h3 style="margin-top: 24px; margin-bottom: 12px;">Initial vs Final Descriptor Comparison</h3>
            <div style="display: flex; justify-content: flex-start; gap: 24px; margin-bottom: 12px; font-size: 12px; color: #4b5563;">
                <span><span style="display: inline-block; width: 20px; height: 12px; background: rgba(107, 114, 128, 0.65); vertical-align: middle; margin-right: 6px; border: 1px solid #6b7280;"></span>Initial</span>
                <span><span style="display: inline-block; width: 20px; height: 12px; background: rgba(139, 92, 246, 0.85); vertical-align: middle; margin-right: 6px; border: 1px solid #7c3aed;"></span>Final</span>
                <span><span style="display: inline-block; width: 20px; height: 3px; background: rgba(156, 163, 175, 0.9); vertical-align: middle; margin-right: 6px;"></span>Initial Mean</span>
                <span><span style="display: inline-block; width: 20px; height: 3px; background: rgba(109, 40, 217, 0.9); vertical-align: middle; margin-right: 6px;"></span>Final Mean</span>
            </div>
            <div id="descriptors-comparison-container" class="plot-container"></div>

            <script>
                var descriptorsComparisonData = {{ (plots.descriptors_comparison_data or {}) | tojson }};

                function drawDescriptorsComparison() {
                    var container = document.getElementById('descriptors-comparison-container');
                    if (!container || !descriptorsComparisonData || !descriptorsComparisonData.descriptors) return;

                    var descs = descriptorsComparisonData.descriptors;
                    var compData = descriptorsComparisonData.data;
                    if (!descs.length) return;

                    var readableNames = {
                        'MolWt': 'Molecular Weight', 'LogP': 'LogP', 'cLogP': 'cLogP',
                        'TPSA': 'TPSA', 'NumHDonors': 'H-Bond Donors', 'NumHAcceptors': 'H-Bond Acceptors',
                        'QED': 'QED Score', 'Fsp3': 'Fsp³ Fraction', 'n_atoms': 'Total Atoms',
                        'n_heavy_atoms': 'Heavy Atoms', 'n_rings': 'Ring Count', 'n_aroma_rings': 'Aromatic Rings',
                        'n_rot_bonds': 'Rotatable Bonds'
                    };

                    var nCols = 3;
                    var nRows = Math.ceil(descs.length / nCols);
                    var traces = [];
                    var shapes = [];
                    var annotations = [];

                    for (var i = 0; i < descs.length; i++) {
                        var desc = descs[i];
                        var d = compData[desc];
                        if (!d) continue;

                        var axisNum = i + 1;

                        // Initial distribution (gray)
                        traces.push({
                            type: 'histogram', x: d.initial_values,
                            histnorm: 'probability density',
                            name: 'Initial', legendgroup: 'initial', showlegend: (i === 0),
                            opacity: 0.65, nbinsx: 40,
                            marker: { color: 'rgba(107, 114, 128, 0.65)', line: { color: 'rgba(75, 85, 99, 0.8)', width: 1 } },
                            xaxis: 'x' + axisNum, yaxis: 'y' + axisNum
                        });

                        // Final distribution (purple)
                        traces.push({
                            type: 'histogram', x: d.final_values,
                            histnorm: 'probability density',
                            name: 'Final', legendgroup: 'final', showlegend: (i === 0),
                            opacity: 0.8, nbinsx: 40,
                            marker: { color: 'rgba(139, 92, 246, 0.85)', line: { color: 'rgba(109, 40, 217, 0.9)', width: 1 } },
                            xaxis: 'x' + axisNum, yaxis: 'y' + axisNum
                        });

                        // Initial mean line
                        shapes.push({
                            type: 'line', xref: 'x' + axisNum, yref: 'y' + axisNum + ' domain',
                            x0: d.mean_initial, x1: d.mean_initial, y0: 0, y1: 1,
                            line: { color: 'rgba(156, 163, 175, 0.9)', width: 2 }
                        });

                        // Final mean line
                        shapes.push({
                            type: 'line', xref: 'x' + axisNum, yref: 'y' + axisNum + ' domain',
                            x0: d.mean_final, x1: d.mean_final, y0: 0, y1: 1,
                            line: { color: 'rgba(109, 40, 217, 0.9)', width: 2 }
                        });

                        // Title annotation
                        var displayName = readableNames[desc] || desc;
                        var xAxisName = i === 0 ? 'x' : ('x' + axisNum);
                        var yAxisName = i === 0 ? 'y' : ('y' + axisNum);
                        annotations.push({
                            text: '<b>' + displayName + '</b>',
                            font: { size: 12, color: '#1f2937' },
                            showarrow: false,
                            xref: xAxisName + ' domain', yref: yAxisName + ' domain',
                            x: 0.5, y: 1.02, xanchor: 'center', yanchor: 'bottom'
                        });
                    }

                    var layout = {
                        height: 240 * nRows,
                        margin: { l: 50, r: 30, t: 60, b: 50 },
                        paper_bgcolor: 'white', plot_bgcolor: 'white',
                        font: { family: '-apple-system, BlinkMacSystemFont, sans-serif', size: 11 },
                        showlegend: true,
                        legend: { orientation: 'h', y: 1.06, x: 0.5, xanchor: 'center', font: { size: 10 } },
                        shapes: shapes,
                        barmode: 'overlay',
                        grid: { rows: nRows, columns: nCols, pattern: 'independent', roworder: 'top to bottom', xgap: 0.1, ygap: 0.22 },
                        annotations: annotations
                    };

                    for (var i = 0; i < descs.length; i++) {
                        var axisNum = i + 1;
                        layout['xaxis' + axisNum] = { showgrid: false, zeroline: false, tickfont: { size: 10 } };
                        layout['yaxis' + axisNum] = { showgrid: true, gridcolor: '#e5e7eb', zeroline: false, showticklabels: true, tickfont: { size: 9 }, title: i % 3 === 0 ? { text: 'Density', font: { size: 9 }, standoff: 4 } : undefined };
                    }

                    Plotly.newPlot(container, traces, layout, { displayModeBar: true });
                }

                document.addEventListener('DOMContentLoaded', function() {
                    if (document.getElementById('descriptors-comparison-container')) {
                        drawDescriptorsComparison();
                    }
                });
            </script>
            {% endif %}

        </section>
        {% endif %}

        <!-- MolEval Generative Metrics -->
        {% if data.moleval and data.moleval.by_stage %}
        <section>
            <h2>Generative Metrics (MolEval)</h2>
            <p style="color: var(--text-secondary); font-size: 0.875rem; margin-bottom: 20px;">
                Distribution quality metrics computed at key pipeline stages. Shows how molecular diversity,
                uniqueness, and filter passage rates evolve through the filtering funnel.
            </p>

            <!-- Detailed metrics table -->
            <h3>Detailed Values</h3>
            <div style="overflow-x: auto;">
                <table class="data-table" style="width: 100%; border-collapse: collapse; font-size: 0.85rem;">
                    <thead>
                        <tr style="border-bottom: 2px solid var(--border);">
                            <th style="text-align: left; padding: 8px 12px; font-weight: 600;">Metric</th>
                            {% for stage in data.moleval.stages %}
                            <th style="text-align: center; padding: 8px 12px; font-weight: 600;">{{ stage }}</th>
                            {% endfor %}
                        </tr>
                    </thead>
                    <tbody>
                        {% for metric in data.moleval.metrics %}
                        <tr style="border-bottom: 1px solid var(--border);">
                            <td style="padding: 6px 12px; font-weight: 500;">{{ metric }}</td>
                            {% for stage in data.moleval.stages %}
                            {% set val = data.moleval.by_stage[stage][metric] if metric in data.moleval.by_stage.get(stage, {}) else None %}
                            <td style="text-align: center; padding: 6px 12px;">
                                {% if val is not none %}{{ "%.4f"|format(val) }}{% else %}&mdash;{% endif %}
                            </td>
                            {% endfor %}
                        </tr>
                        {% endfor %}
                    </tbody>
                </table>
            </div>
        </section>
        {% endif %}

        <footer>
            <p><strong>HEDGEHOG</strong></p>
            <p>Hierarchical Evaluation of Drug GEnerators tHrOugh riGorous filtration</p>
            <p style="margin-top: 10px;">Report generated at {{ generated_at }}</p>
        </footer>
    </div>
</body>
</html>
