<?php
// Public Event Dashboard - No authentication required
include 'includes/db.php';

// Only keep server handlers for report proxies; moved data actions to api/public_dashboard_api.php
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['ajax'])) {
    header('Content-Type: application/json');

    if (isset($_POST['action']) && $_POST['action'] === 'load_summary_table') {
        $config_id = $_POST['config_id'] ?? null;
        
        if ($config_id) {
            try {
                // Get configuration details
                $config_stmt = $pdo->prepare("
                    SELECT * FROM result_configurations WHERE id = ?
                ");
                $config_stmt->execute([$config_id]);
                $config = $config_stmt->fetch(PDO::FETCH_ASSOC);
                
                if ($config) {
                    // Prefer .php4 if present, else .php
                    $summaryScript = file_exists(__DIR__ . '/api/summary_table_api.php')
                        ? 'api/summary_table_api.php'
                        : 'api/summary_table_api.php';

                    // Just pass config_id to API - let it resolve configuration settings
                    $params = [
                        'config_id' => (int)$config_id,
                        'format' => 'json',
                        't' => time() // cache buster
                    ];

                    // Make internal request to the API for JSON format
                    $query = http_build_query($params);
                    $full_url = $_SERVER['REQUEST_SCHEME'] . '://' . $_SERVER['HTTP_HOST'] . dirname($_SERVER['REQUEST_URI']) . '/' . $summaryScript . '?' . $query;

                    $ctx = stream_context_create(['http' => ['timeout' => 8]]);
                    $json_content = @file_get_contents($full_url, false, $ctx);
                    if ($json_content === false) {
                        $err = error_get_last();
                        throw new Exception('Failed to fetch summary table API: ' . ($err['message'] ?? 'unknown error'));
                    }

                    $data = json_decode($json_content, true);
                    if ($data === null && json_last_error() !== JSON_ERROR_NONE) {
                        throw new Exception('Invalid JSON from summary table API');
                    }
                    
                    echo json_encode([
                        'success' => true,
                        'data' => $data,
                        'config_name' => $config['name'],
                        'type' => 'summary_table'
                    ]);
                } else {
                    echo json_encode(['success' => false, 'message' => 'Configuration not found']);
                }
                
            } catch (Exception $e) {
                echo json_encode([
                    'success' => false,
                    'message' => 'Error loading summary table: ' . $e->getMessage()
                ]);
            }
        } else {
            echo json_encode(['success' => false, 'message' => 'Missing configuration ID']);
        }
        exit;
    }
    
    if (isset($_POST['action']) && $_POST['action'] === 'load_start_list') {
        $event_id = $_POST['event_id'] ?? null;
        $heat_number = $_POST['heat_number'] ?? null;
        $config_id = $_POST['config_id'] ?? null;
        
        if ($event_id) {
            try {
                // Use the same API endpoint as event_start_list.php
                $api_url = "api/start_list_api.php?event_id=" . $event_id;
                
                // Add heat filter if specified
                if ($heat_number) {
                    $api_url .= "&heat_number=" . $heat_number;
                }
                
                // Add config_id if specified
                if ($config_id) {
                    $api_url .= "&config_id=" . $config_id;
                }
                
                // Add format parameter for HTML (same as event_start_list.php)
                $api_url .= "&format=html";
                
                // Make internal request to the API
                $full_url = $_SERVER['REQUEST_SCHEME'] . '://' . $_SERVER['HTTP_HOST'] . dirname($_SERVER['REQUEST_URI']) . '/' . $api_url;
                
                $html_content = file_get_contents($full_url);
                
                // Also get JSON data for processing
                $json_url = str_replace('&format=html', '&format=json', $full_url);
                $json_content = file_get_contents($json_url);
                $json_data = json_decode($json_content, true);
                
                echo json_encode([
                    'success' => true,
                    'data' => $json_data,
                    'html_content' => $html_content,
                    'heat_number' => $heat_number,
                    'type' => 'start_list'
                ]);
                
            } catch (Exception $e) {
                echo json_encode([
                    'success' => false,
                    'message' => 'Error loading start list: ' . $e->getMessage()
                ]);
            }
        } else {
            echo json_encode(['success' => false, 'message' => 'Missing event ID']);
        }
        exit;
    }

    echo json_encode(['success' => false, 'message' => 'Invalid action']);
    exit;
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Live Event Dashboard</title>
    <!-- style skin holder -->
    <?php include_once 'includes/stylesheets.php'; ?>
    <style>
        /* Modern gradient background for event header */
        .bg-gradient-primary {
            background: linear-gradient(135deg, #007bff 0%, #0056b3 100%);
        }
        
        /* Enhanced shadow for cards */
        .shadow-sm {
            box-shadow: 0 0.125rem 0.5rem rgba(0, 0, 0, 0.1) !important;
        }
        
        /* Modern rounded elements */
        .rounded-3 {
            border-radius: 0.75rem !important;
        }
        
        .rounded-pill {
            border-radius: 50rem !important;
        }
        
        /* Apply consistent table styling */
        .table th {
            background-color: #343a40 !important;
            color: white !important;
            border-color: #454d55 !important;
            white-space: nowrap;
            position: sticky;
            top: 0;
            z-index: 10;
            font-size: 0.875rem;
            padding: 0.5rem;
        }
        
        .table td {
            border-color: #dee2e6;
            font-size: 0.875rem;
            padding: 0.5rem;
        }
        .data-table th, .data-table td {
            padding: 0.5em !important;
        }
        
        
        .summary-stats .badge {
            font-size: 0.7em;
        }
        
        /* Compact card styling */
        .card-body {
            padding: 1rem;
        }
        
        .card-body.p-2 {
            padding: 0.5rem !important;
        }
        
        .card-header {
            padding: 0.5rem 1rem;
        }
        
        .card-header.py-2 {
            padding-top: 0.5rem !important;
            padding-bottom: 0.5rem !important;
        }
        
        /* Table responsive with max height */
        .table-responsive {
            /* ...existing code... */
            overflow-y: auto;
            border: 1px solid #dee2e6;
            border-radius: 6px;
        }
        /* NEW: non-blocking overlay for report-card refresh */
        .report-updating-overlay {
            pointer-events: none;
            z-index: 5;
        }
        /* Summary table row movement indicators */
        .summary-table tbody tr.moved-up td {
            animation: flashUp 1.2s ease;
        }
        .summary-table tbody tr.moved-down td {
            animation: flashDown 1.2s ease;
        }
        .summary-table tbody tr.changed td {
            animation: flashChange 2.4s ease;
        }
        .summary-table tbody tr.latest td {
    position: relative;
    background-color: rgba(0, 123, 255, 0.1);
}
.summary-table tbody tr.latest::before {   
    
}
.summary-table tbody tr.latest.moved-up td,
.summary-table tbody tr.latest.moved-down td,
.summary-table tbody tr.latest.changed td {
    animation: flashLatest 2.4s ease;
}
@keyframes flashLatest {
    0% { background-color: rgba(0, 123, 255, 0.3); }
    50% { background-color: rgba(0, 123, 255, 0.1); }
    100% { background-color: rgba(0, 123, 255, 0.2); }
}
        @keyframes flashUp {
            0% { background-color: rgba(40, 167, 69, 0.25); }
            100% { background-color: transparent; }
        }
        @keyframes flashDown {
            0% { background-color: rgba(220, 53, 69, 0.25); }
            100% { background-color: transparent; }
        }
        @keyframes flashChange {
            0% { background-color: rgba(255, 193, 7, 0.25); }
            25% { background-color: transparent; }
            50% { background-color: rgba(255, 193, 7, 0.25); }
            100% { background-color: transparent; }
        }
        .move-indicator {
            position: absolute;
            display: inline-flex;
            align-items: center;
            gap: 4px;
            margin-left: .5rem;
            font-size: .8rem;
        }

        .move-indicator.up { color: #28a745; }   /* green */
        .move-indicator.down { color: #dc3545; } /* red */
.summary-table tbody tr {
    transition: transform 1200ms ease, opacity 1200ms ease;
    will-change: transform, opacity;
}
td.col-best {
    background-color: var(--bs-table-bg) !important;
}
@keyframes heatRowUpdated {
    0% { background-color: rgba(13, 110, 253, 0.1); }
    100% { background-color: transparent; }
}

tr.heat-row-updated {
    animation: heatRowUpdated 2s ease;
}
/* Animation for live heat details updates */
@keyframes highlightUpdate {
    0% { background-color: rgba(255, 193, 7, 0.4); }
    100% { background-color: transparent; }
}
.fullscreen{
    overflow:auto
}

.fullscreen .event-head-card, .fullscreen .card-general-reports  {
    display: none;
}
.col-figures {
    width: 5%;
}
td.col-figures {
    font-size: .6em;

}

    </style>
</head>

<body class="body-bg-aurora-bright">
    <?php include 'menu.php'; ?>
    <!-- Auto-refresh indicator -->
    <div class="auto-refresh-indicator position-fixed top-0 end-0 m-2" style="z-index: 1050;">
        <div class="badge bg-success" id="refreshIndicator">
            <i class="fas fa-sync-alt me-1"></i>Auto-refresh: ON
        </div>
    </div>

    <div class="container-fluid container-StyleScore p-4">
        <div class="row mb-3">
            <div class="col-12">
                <div class="border-0 shadow-none card text-primary">
                    <div class="border-0 shadow-none card-body p-3">
                        <div class="d-flex justify-content-between align-items-center">
                            <div>
                                <h4 class="card-title mb-1"><i class="fas fa-tv me-2"></i>Live Event Dashboard</h4>
                                <p class="card-text mb-0 small opacity-75">Real-time event monitoring and results</p>
                            </div>
                            <div class="d-flex align-items-center gap-2">
                                <div class="d-flex align-items-center gap-2">
                                    <label for="refreshIntervalSelect" class="form-label mb-0 small">Refresh every</label>
                                    <select id="refreshIntervalSelect" class="form-select form-select-sm" style="width: auto;">
                                        <option value="5000">5s</option>
                                        <option value="10000">10s</option>
                                        <option value="30000" selected>30s</option>
                                        <option value="60000">1 min</option>
                                        <option value="300000">5 min</option>
                                    </select>
                                </div>
                                <div class="btn-group" role="group">
                                    <button type="button" class="btn btn-outline-light btn-sm" id="toggleRefresh"><i class="fas fa-pause me-1"></i>Pause Updates</button>
                                    <button type="button" class="btn btn-outline-light btn-sm" onclick="runRefreshCycle()"><i class="fas fa-sync-alt me-1"></i>Refresh Now</button>
                                    <button type="button" class="btn btn-outline-light btn-sm" onclick="toggleDashboardFullscreen()"><i class="fas fa-expand me-1"></i>Fullscreen</button>
                                </div>
                                <small class="">Last updated: <span id="lastUpdate">Loading...</span></small>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>

        <div id="eventsContainer">
            <div class="col-12 text-center py-5">
                <div class="spinner-border text-primary" role="status"><span class="visually-hidden">Loading events...</span></div>
                <p class="mt-3">Loading events...</p>
            </div>
        </div>

        <div class="modal fade" id="summaryTableModal" tabindex="-1">
            <div class="modal-dialog modal-xl">
                <div class="modal-content">
                    <div class="modal-header py-2">
                        <h6 class="modal-title" id="modalSummaryTitle">Summary Table</h6>
                        <button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
                    </div>
                    <div class="modal-body p-2">
                        <div class="summary-display" id="summaryTableDisplay"></div>
                    </div>
                </div>
            </div>
        </div>
    </div>
<!-- Alpine Plugins -->
<script defer src="https://cdn.jsdelivr.net/npm/@alpinejs/morph@3.x.x/dist/cdn.min.js"></script>
 
<!-- Alpine Core -->
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
     <script>
        // Minimal helper to patch only changed DOM instead of full re-render
        function morphHtml(targetEl, html) {
            if (!targetEl) return;
            window.morphdom ? morphdom(targetEl, html, { childrenOnly: true }) : (targetEl.innerHTML = html);
            
            // Convert Font Awesome icons to Lucide first, then initialize
            console.log('morphHtml: Converting and initializing icons for element:', targetEl.id || 'unknown');
            if (window.convertFontAwesomeToLucide) {
                const converted = window.convertFontAwesomeToLucide(targetEl);
                console.log('morphHtml: Converted', converted, 'icons in', targetEl.id || 'unknown');
            }
            if (window.initializeLucideIcons) {
                window.initializeLucideIcons(targetEl);
            }
        }

        // Now calling the standalone API for data
        const DASH_API = 'api/public_dashboard_api.php';

        let autoRefresh = true;
        let refreshInterval;
        let refreshMs = Number(localStorage.getItem('dashboardRefreshMs')) || 30000; // NEW
        // Offset summary-card refresh to reduce layout jumps; default ~30% of main interval (clamped 300-2000ms)
        let reportRefreshOffsetMs = Math.max(300, Math.min(2000, Math.floor(refreshMs * 0.3)));
        // Small jitter so multiple report-cards don’t refresh at the exact same moment
        let reportRefreshJitterMs = 250;
        let currentEventId = null;
        let currentConfigId = null;
        let openHeatContainers = new Set(); // Track which heat containers are open
        let activeReports = new Map(); // Track active reports in heat cards
        
        document.addEventListener('DOMContentLoaded', function() {
            // Init interval select
            const sel = document.getElementById('refreshIntervalSelect');
            if (sel) {
                // set current value
                if ([5000,10000,30000,60000,300000].includes(refreshMs)) {
                    sel.value = String(refreshMs);
                } else {
                    refreshMs = 30000;
                    sel.value = '30000';
                }
                sel.addEventListener('change', function() {
                    refreshMs = Number(this.value) || 30000;
                    localStorage.setItem('dashboardRefreshMs', String(refreshMs));
                    // Recalculate offset when interval changes
                    reportRefreshOffsetMs = Math.max(300, Math.min(2000, Math.floor(refreshMs * 0.3)));
                    if (autoRefresh) {
                        startAutoRefresh(); // restarts with new interval/offset
                    } else {
                        updateRefreshIndicator();
                    }
                });
            }

            // Start auto-refresh
            startAutoRefresh();
            
            // Initialize Font Awesome to Lucide conversion and icons on page load
            if (window.convertFontAwesomeToLucide) {
                console.log('Initial page load: Converting Font Awesome icons to Lucide...');
                const converted = window.convertFontAwesomeToLucide();
                console.log('Initial page load: Converted', converted, 'Font Awesome icons');
            }
            if (window.initializeLucideIcons) {
                console.log('Initial page load: Initializing Lucide icons...');
                window.initializeLucideIcons();
            }
            
            // Initial load using staggered cycle
            runRefreshCycle();
            
            // Toggle refresh button
            document.getElementById('toggleRefresh').addEventListener('click', function() {
                if (autoRefresh) {
                    stopAutoRefresh();
                    this.innerHTML = '<i class="fas fa-play me-1"></i>Resume Updates';
                    autoRefresh = false;
                } else {
                    startAutoRefresh();
                    this.innerHTML = '<i class="fas fa-pause me-1"></i>Pause Updates';
                    autoRefresh = true;
                }
                updateRefreshIndicator();
                
                // Convert and initialize Lucide icons for the toggle button
                if (window.convertFontAwesomeToLucide) {
                    window.convertFontAwesomeToLucide();
                }
                if (window.initializeLucideIcons) {
                    window.initializeLucideIcons();
                }
            });
        });
        
        function getHumanIntervalLabel(ms) { // NEW
            return ms >= 60000 ? (ms/60000) + ' min' : (ms/1000) + 's';
        }

        function updateRefreshIndicator() { // NEW
            const el = document.getElementById('refreshIndicator');
            if (!el) return;
            if (autoRefresh) {
                el.className = 'badge bg-success';
                el.innerHTML = `<i class="fas fa-sync-alt me-1"></i>Auto-refresh: ON (${getHumanIntervalLabel(refreshMs)})`;
            } else {
                el.className = 'badge bg-warning';
                el.innerHTML = '<i class="fas fa-pause me-1"></i>Auto-refresh: OFF';
            }
            
            // Convert and initialize Lucide icons for the refresh indicator
            if (window.convertFontAwesomeToLucide) {
                window.convertFontAwesomeToLucide();
            }
            if (window.initializeLucideIcons) {
                window.initializeLucideIcons();
            }
        }

        function startAutoRefresh() {
            stopAutoRefresh(); // restart with current refreshMs
            refreshInterval = setInterval(runRefreshCycle, refreshMs);
            startHeatDetailsRefresh(); // Start the heat details refresh
            updateRefreshIndicator();
        }

        // Run one full refresh cycle: main first, then reports after an offset
        function runRefreshCycle() {
            refreshAllData();
            // Stagger summary-cards refresh to reduce LCP/CLS and visual jumps
            setTimeout(() => {
                refreshActiveReportsAuto();
            }, reportRefreshOffsetMs);
        }

        // NEW: auto-refresh any open report-cards unless paused per-card
        function refreshActiveReportsAuto() {
            if (!autoRefresh) return;
            // Spread refreshes with a small jitter per card
            activeReports.forEach((rep, key) => {
                if (rep && rep.auto !== false) {
                    const jitter = reportRefreshJitterMs ? Math.floor(Math.random() * reportRefreshJitterMs) : 0;
                    setTimeout(() => {
                        refreshReport(key);
                    }, jitter);
                }
            });
        }

        // NEW: toggle auto-refresh per report card
        function toggleReportAuto(reportKey) {
            const rep = activeReports.get(reportKey);
            if (!rep) return;
            rep.auto = rep.auto === false ? true : false;
            activeReports.set(reportKey, rep);
            // Re-render header controls to reflect new state without closing the card
            const [eventId, heatPart] = reportKey.split('-');
            const heatNumber = heatPart === 'all' ? null : Number(heatPart);
            if (rep.type === 'summary_table') {
                // re-render just the header by refreshing the report (keeps holder open)
                loadSummaryTableCard(Number(eventId), heatNumber, rep.configId, rep.configName, { refresh: true });
            } else if (rep.type === 'start_list') {
                loadStartListCard(Number(eventId), heatNumber, { refresh: true, config_id: rep.configId });
            }
        }
        
        function stopAutoRefresh() {
            if (refreshInterval) {
                clearInterval(refreshInterval);
                refreshInterval = null;
            }
            stopHeatDetailsRefresh(); // Stop the heat details refresh
        }
        
        function refreshAllData() {
            loadEventsOverview();
            updateLastUpdateTime();
        }
        
        function updateLastUpdateTime() {
            document.getElementById('lastUpdate').textContent = new Date().toLocaleTimeString();
        }
        
        function loadEventsOverview() {
            fetch(`${DASH_API}?action=get_events_overview`, { method: 'GET' })
                .then(r => r.json())
                .then(data => {
                    if (data.success) {
                        displayEvents(data.events);
                    } else {
                        console.error('Error loading events:', data.message);
                        morphHtml(document.getElementById('eventsContainer'),
                            '<div class="col-12 py-2"><div class="alert alert-danger">Error loading events: ' + (data.message||'Unknown') + '</div></div>');
                    }
                })
                .catch(error => {
                    console.error('Error:', error);
                    morphHtml(document.getElementById('eventsContainer'),
                        '<div class="col-12 py-2"><div class="alert alert-danger">Network error loading events</div></div>');
                });
        }
        
        function displayEvents(events) {
            const container = document.getElementById('eventsContainer');
            
            // Helper function to validate if participant data is complete and valid
            function isValidParticipantData(data) {
                return data && 
                       data.bib_number && 
                       data.first_name && 
                       data.last_name && 
                       data.score_value !== undefined && 
                       data.created_at;
            }
            
            // Helper function to validate if next participant data is complete
            function isValidNextParticipantData(data) {
                return data && 
                       data.bib_number && 
                       data.first_name && 
                       data.last_name;
            }
            
            // Preserve existing heat container contents for open events to avoid flicker & reload delay
            const existingHeats = {};
            openHeatContainers.forEach(id => {
                const el = document.getElementById('heats-' + id);
                if (el) existingHeats[id] = el.innerHTML;
            });

            if (events.length === 0) {
                morphHtml(container, '<div class="col-12 py-2"><div class="alert alert-info">No active events found</div></div>');
                return;
            }

            let html = '';
            events.forEach(event => {
                const isLive = ['in_progress','live'].includes(event.status);
                const latestScore = isValidParticipantData(event.latest_score) ? event.latest_score : null;
                const nextBip = isValidNextParticipantData(event.next_bib) ? event.next_bib : null;
                const isHeatContainerOpen = openHeatContainers.has(event.id);
                html += `
                <div class="event-head-card card mb-3 border-0 shadow-none" id="event-${event.id}">
                    <div class="border-0 shadow-none card-header bg-gradient-primary text-white border-0 position-relative overflow-hidden py-2">
                        <div class="position-absolute top-0 end-0 opacity-10" style="font-size: 4rem; line-height: 1; color: rgba(255,255,255,0.1);">
                            <i class="fas fa-trophy"></i>
                        </div>
                        <div class="row align-items-center position-relative">
                            <div class="col-md-8">
                                <h5 class="mb-1 fw-bold">
                                    <i class="fas fa-calendar-check me-2"></i>
                                    ${event.name}
                                </h5>
                                <p class="mb-0 text-white-50 small">
                                    <i class="fas fa-calendar me-1"></i>
                                    ${new Date(event.date).toLocaleDateString('en-US', { 
                                        weekday: 'long', 
                                        year: 'numeric', 
                                        month: 'long', 
                                        day: 'numeric' 
                                    })}
                                </p>
                            </div>
                            <div class="col-md-4 text-end">
                                <div class="d-flex flex-column align-items-end gap-1">
                                    ${isLive ? '<span class="badge bg-danger px-2 py-1 rounded-pill"><i class="fas fa-broadcast-tower me-1"></i>LIVE EVENT</span>' : ''}
                                    <span class="badge bg-light text-dark px-2 py-1 rounded-pill">
                                        <i class="fas fa-info-circle me-1"></i>
                                        ${event.status.replace('_',' ').toUpperCase()}
                                    </span>
                                </div>
                            </div>
                        </div>
                    </div>
                    <div class="border-0 shadow-none card-body p-3">
                        <!-- Event Statistics Row -->
                        <div class="row g-2 mb-3">
                            <div class="col-6 col-lg-3">
                                <div class="d-flex align-items-center p-2 bg-primary bg-opacity-10 rounded">
                                    <div class="bg-primary text-white rounded-circle p-2 me-2" style="width: 35px; height: 35px; display: flex; align-items: center; justify-content: center;">
                                        <i class="fas fa-fire"></i>
                                    </div>
                                    <div>
                                        <div class="fw-bold text-primary">${event.current_heat_name || event.current_heat || '—'}</div>
                                        <small class="text-muted">Active Heat</small>
                                    </div>
                                </div>
                            </div>
                            <div class="col-6 col-lg-3">
                                <div class="d-flex align-items-center p-2 bg-success bg-opacity-10 rounded">
                                    <div class="bg-success text-white rounded-circle p-2 me-2" style="width: 35px; height: 35px; display: flex; align-items: center; justify-content: center;">
                                        <i class="fas fa-play"></i>
                                    </div>
                                    <div>
                                        <div class="fw-bold text-success">${event.current_run || '—'}</div>
                                        <small class="text-muted">Current Run</small>
                                    </div>
                                </div>
                            </div>
                            <div class="col-6 col-lg-3">
                                <div class="d-flex align-items-center p-2 bg-info bg-opacity-10 rounded">
                                    <div class="bg-info text-white rounded-circle p-2 me-2" style="width: 35px; height: 35px; display: flex; align-items: center; justify-content: center;">
                                        <i class="fas fa-users"></i>
                                    </div>
                                    <div>
                                        <div class="fw-bold text-info">${event.total_participants || 0}</div>
                                        <small class="text-muted">Athletes</small>
                                    </div>
                                </div>
                            </div>
                            <div class="col-6 col-lg-3">
                                <div class="d-flex align-items-center p-2 bg-warning bg-opacity-10 rounded">
                                    <div class="bg-warning text-dark rounded-circle p-2 me-2" style="width: 35px; height: 35px; display: flex; align-items: center; justify-content: center;">
                                        <i class="fas fa-gavel"></i>
                                    </div>
                                    <div>
                                        <div class="fw-bold text-warning">${event.total_judges || 0}</div>
                                        <small class="text-muted">Judges</small>
                                    </div>
                                </div>
                            </div>
                        </div>
                        
                        <!-- Latest Updates Row -->
                        ${latestScore || nextBip ? `
                        <div class="row g-2 mb-3">
                            ${latestScore ? `
                            <div class="col-md-6">
                                <div class="border-0 shadow-none card border-0 bg-success bg-opacity-10 h-100">
                                    <div class="border-0 shadow-none card-body p-2">
                                        <div class="d-flex align-items-center mb-2">
                                            <div class="bg-success text-white rounded-circle p-1 me-2" style="width: 30px; height: 30px; display: flex; align-items: center; justify-content: center;">
                                                <i class="fas fa-trophy"></i>
                                            </div>
                                            <h6 class="mb-0 text-success fw-bold">Latest Score</h6>
                                        </div>
                                        <div class="row align-items-center">
                                            <div class="col-8">
                                                <div class="participant-info">
                                                    <div class="mb-1">
                                                        <span class="badge bg-success me-1 px-2 py-1">BIB ${latestScore.bib_number}</span>
                                                    </div>
                                                    <div class="fw-bold text-dark mb-1">${latestScore.first_name} ${latestScore.last_name}</div>
                                                    ${latestScore.club ? `<div class="text-muted small mb-1"><i class="fas fa-users me-1"></i>${latestScore.club}</div>` : ''}
                                                    ${latestScore.category ? `<div class="text-muted small mb-1"><i class="fas fa-tag me-1"></i>${latestScore.category}</div>` : ''}
                                                    ${latestScore.country ? `<div class="text-muted small mb-1"><i class="fas fa-flag me-1"></i>${latestScore.country}</div>` : ''}
                                                    ${latestScore.figure ? `<div class="text-muted small mb-1"><i class="fas fa-skating me-1"></i>${latestScore.figure}</div>` : ''}
                                                    <div class="text-muted small">
                                                        <i class="fas fa-clock me-1"></i>
                                                        ${new Date(latestScore.created_at).toLocaleTimeString()}
                                                        ${latestScore.heat_name ? ` • ${latestScore.heat_name}` : ''}
                                                    </div>
                                                </div>
                                            </div>
                                            <div class="col-4 text-end">
                                                <div class="score-rank-info">
                                                    <div class="fs-3 fw-bold text-success mb-1">${latestScore.score_value}</div>
                                                    ${latestScore.current_rank ? `<div class="badge bg-success px-2 py-1 rounded-pill">Rank #${latestScore.current_rank}</div>` : ''}
                                                </div>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                            </div>
                            ` : ''}
                            ${nextBip ? `
                            <div class="col-md-6">
                                <div class="border-0 shadow-none card border-0 bg-primary bg-opacity-10 h-100">
                                    <div class="border-0 shadow-none card-body p-2">
                                        <div class="d-flex align-items-center mb-2">
                                            <div class="bg-primary text-white rounded-circle p-1 me-2" style="width: 30px; height: 30px; display: flex; align-items: center; justify-content: center;">
                                                <i class="fas fa-play"></i>
                                            </div>
                                            <h6 class="mb-0 text-primary fw-bold">Next on Start</h6>
                                        </div>
                                        <div class="row align-items-center">
                                            <div class="col-8">
                                                <div class="participant-info">
                                                    <div class="mb-1">
                                                        <span class="badge bg-primary me-1 px-2 py-1">BIB ${nextBip.bib_number}</span>
                                                        ${nextBip.current_rank ? `<span class="badge bg-primary bg-opacity-50 px-2 py-1">Rank #${nextBip.current_rank}</span>` : ''}
                                                    </div>
                                                    <div class="fw-bold text-dark mb-1">${nextBip.first_name} ${nextBip.last_name}</div>
                                                    ${nextBip.club ? `<div class="text-muted small mb-1"><i class="fas fa-users me-1"></i>${nextBip.club}</div>` : ''}
                                                    ${nextBip.category ? `<div class="text-muted small mb-1"><i class="fas fa-tag me-1"></i>${nextBip.category}</div>` : ''}
                                                    ${nextBip.country ? `<div class="text-muted small mb-1"><i class="fas fa-flag me-1"></i>${nextBip.country}</div>` : ''}
                                                    ${nextBip.figure ? `<div class="text-muted small mb-1"><i class="fas fa-skating me-1"></i>${nextBip.figure}</div>` : ''}
                                                    <div class="text-muted small">
                                                        <i class="fas fa-stopwatch me-1"></i>
                                                        Ready to compete
                                                        ${nextBip.heat_name ? ` • ${nextBip.heat_name}` : ''}
                                                    </div>
                                                </div>
                                            </div>
                                            <div class="col-4 text-end">
                                                <div class="status-info text-center">
                                                    <div class="mb-1">
                                                        <i class="fas fa-hourglass-start text-primary fs-4"></i>
                                                    </div>
                                                    <div class="text-primary fw-bold">Ready</div>
                                                </div>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                            </div>
                            ` : ''}
                        </div>
                        ` : ''}
                        
                        <!-- Action Button -->
                        <div class="text-center">
                            <button class="btn btn-primary px-2 py-1 rounded-pill shadow-none" onclick="loadEventHeats(${event.id}, '${event.name.replace(/'/g,"&apos;")}')">
                                <i class="fas fa-fire me-1"></i>
                                ${isHeatContainerOpen ? 'Hide Heat Details' : 'Show Heat Details'}
                            </button>
                        </div>
                    </div>
                </div>
                <div id="heats-${event.id}" class="mb-3" style="display:${isHeatContainerOpen ? 'block' : 'none'};">
                    ${isHeatContainerOpen ? (existingHeats[event.id] || "<div class='text-center py-2'><div class='spinner-border spinner-border-sm text-primary' role='status'></div><p class='mt-1 text-muted small'>Loading heat details...</p></div>") : ''}
                </div>`;
            });

            morphHtml(container, html);

            // Reload heat details for open containers (if they were not preserved or need refresh)
            openHeatContainers.forEach(eventId => {
                const event = events.find(e => e.id == eventId);
                if (event) {
                    // Only trigger refresh if preserved content looks like loading or empty
                    const el = document.getElementById('heats-' + eventId);
                    if (el && /Loading heat details|Refreshing heat details|spinner/.test(el.innerHTML)) {
                        loadEventHeatsData(eventId, event.name);
                    }
                }
            });
        }
        
        function showEventDetails(eventId, eventName) {
            // This function is kept for backward compatibility but redirects to loadEventHeats
            loadEventHeats(eventId, eventName);
        }
        
        function loadEventHeats(eventId, eventName) {
            const container = document.getElementById(`heats-${eventId}`);
            if (container.style.display === 'none') {
                openHeatContainers.add(eventId);
                container.style.display = 'block';
                loadEventHeatsData(eventId, eventName);
            } else {
                openHeatContainers.delete(eventId);
                activeReports.delete(`${eventId}`);
                container.style.display = 'none';
            }
        }
        
        function loadEventHeatsData(eventId, eventName) {
            const container = document.getElementById(`heats-${eventId}`);
            morphHtml(container, '<div class="text-center py-2"><div class="spinner-border spinner-border-sm" role="status"></div><p class="mt-1 small">Loading heat details...</p></div>');
            
            fetch(`${DASH_API}?action=get_event_heats&event_id=${encodeURIComponent(eventId)}`, { method: 'GET' })
                .then(response => response.json())
                .then(data => {
                    if (data.success) {
                        displayHeats(eventId, eventName, data.heats, data.configurations); // pass eventName
                    } else {
                        morphHtml(container, '<div class="alert alert-danger">Error loading heats: ' + (data.message||'Unknown') + '</div>');
                    }
                })
                .catch(error => {
                    console.error('Error:', error);
                    morphHtml(container, '<div class="alert alert-danger">Network error loading heats</div>');
                });
        }
        
        function refreshGeneralReports(eventId) {
            fetch(`${DASH_API}?action=get_configurations&event_id=${encodeURIComponent(eventId)}`, { method: 'GET' })
                .then(response => response.json())
                .then(data => {
                    if (data.success) {
                        updateGeneralReports(eventId, data.configurations);
                    } else {
                        console.error('Error refreshing reports:', data.message);
                    }
                })
                .catch(error => {
                    console.error('Error refreshing reports:', error);
                });
        }
        // Renders heats table + per-heat report containers and event-level General Reports
        function displayHeats(eventId, eventName, heats, configurations) { // accept eventName
            const container = document.getElementById(`heats-${eventId}`);
            if (!container) return;

            // helper to build clean button text
            const cleanBtnText = (cfgName, evtName, heatName, viewType) => {
                const parts = String(cfgName || '')
                    .split(/\s*-\s*/).filter(Boolean)
                    .filter(p => {
                        const t = p.trim();
                        if (!t) return false;
                        if (evtName && t.localeCompare(evtName, undefined, { sensitivity: 'accent' }) === 0) return false;
                        if (/^heat\s*\d+$/i.test(t)) return false;            // remove "Heat 1"
                        if (/^summary\s*table$/i.test(t)) return false;       // remove "Summary Table"
                        if (/^start\s*list$/i.test(t)) return false;          // remove "Start List"
                        return true;
                    });
                const extra = parts.join(' - ');
                const base = (heatName && String(heatName).trim()) ? heatName : '';
                return extra ? (base ? `${base} - ${extra}` : extra) : (base || 'Report');
            };

            // Group configurations by heat_number (or 'all' for general configs)
            const configsByHeat = {};
            (configurations || []).forEach(cfg => {
                const key = cfg.heat_number ? String(cfg.heat_number) : 'all';
                (configsByHeat[key] ||= []).push(cfg);
            });

            if (!heats || heats.length === 0) {
                morphHtml(container, `
                    <div class="border-0 shadow-none card mb-2">
                        <div class="border-0 shadow-none card-header py-2"><strong>Heat Details</strong></div>
                        <div class="border-0 shadow-none card-body p-2"><div class="alert alert-info mb-0 py-2">No heats configured for this event</div></div>
                    </div>
                `);
                return;
            }

            let html = `
                <div class="border-0 shadow-none card-general-reports card mb-2">
                    <div class="border-0 shadow-none card-header d-flex justify-content-between align-items-center py-2">
                        <small class="text-uppercase fw-bold"><i class="fas fa-folder-open me-1"></i>General Reports</small>
                        <button class="btn btn-sm btn-outline-secondary" onclick="refreshGeneralReports(${eventId})">
                            <i class="fas fa-sync-alt"></i>
                        </button>
                    </div>
                    <div class="border-0 shadow-none card-body py-2" id="general-reports-${eventId}">
                        <div class="text-muted small">Loading general reports…</div>
                    </div>
                </div>

                <div class="border-0 shadow-none card">
                    <div class="border-0 shadow-none card-header py-2"><strong>Heat Details</strong></div>
                    <div class="border-0 shadow-none card-body p-2">
                        <div class="table-responsive">
                            <table class="table table-hover align-middle table-sm">
                                <thead>
                                    <tr>
                                        <th class="small">Heat</th>
                                        <th class="small">Time</th>
                                        <th class="text-center small">Participants</th>
                                        <th class="small">Progress</th>
                                        <th class="small">Reports</th>
                                        <th class="small">Actions</th>
                                    </tr>
                                </thead>
                                <tbody>`;

            heats.forEach(heat => {
                const completion = heat.expected_scores > 0
                    ? Math.round((Number(heat.total_scores || 0) / Number(heat.expected_scores)) * 100)
                    : 0;
                const progressClass = completion >= 100 ? 'bg-success' : (completion >= 50 ? 'bg-warning' : 'bg-primary');
                const heatStatusClass = Number(heat.is_active) === 1 ? 'bg-danger' : (completion >= 100 ? 'bg-success' : 'bg-secondary');
                const heatStatusText = Number(heat.is_active) === 1 ? 'Live' : (completion >= 100 ? 'Completed' : 'Upcoming');

                // Build a vertical list of report actions with icon + cleaned name
                const heatConfigs = (configsByHeat[String(heat.heat_number)] || []);
                const reportsList = heatConfigs.length ? `
                    <div class="d-grid gap-1">
                        ${heatConfigs.map(cfg => {
                            const isSummary = cfg.view_type === 'summary_table';
                            const isStart = cfg.view_type === 'start_list';
                            const icon = isSummary ? 'fa-table' : (isStart ? 'fa-users' : 'fa-file');
                            const color = isSummary ? 'info' : (isStart ? 'success' : 'secondary');
                            const handler = isSummary
                                ? `loadSummaryTableCard(${eventId}, ${heat.heat_number}, ${cfg.id}, '${String(cfg.name).replace(/'/g, "\\'")}')`
                                : (isStart ? `loadStartListCard(${eventId}, ${heat.heat_number})` : '');
                            if (!handler) return '';
                            const btnText = cleanBtnText(cfg.name, eventName, heat.heat_name, cfg.view_type);
                            const safeText = String(btnText).replace(/&/g,'&amp;').replace(/</g,'&lt;');
                            return `
                                <button class="btn btn-sm btn-${color} w-100 text-start d-flex align-items-center"
                                        onclick="${handler}">
                                    <i class="fas ${icon} me-2"></i>
                                    <span class="text-truncate" style="max-width: 220px;">${safeText}</span>
                                </button>`;
                        }).filter(Boolean).join('')}
                    </div>` : '<span class="text-muted small">No reports</span>';

                html += `
                    <tr id="event-${eventId}-heat-${heat.heat_number}" class="event-row">
                        <td>
                            <div class="d-flex align-items-center">
                                <span class="badge ${heatStatusClass} me-2">${heatStatusText}</span>
                                <div>
                                    <strong>Heat ${heat.heat_number}: ${heat.heat_name || ''}</strong>
                                    <div class="small text-muted">${heat.scoring_type || ''} • ${heat.runs_count || 0} runs</div>
                                </div>
                            </div>
                        </td>
                        <td>${heat.time_start ? new Date('1970-01-01T' + heat.time_start).toLocaleTimeString([], {hour:'2-digit',minute:'2-digit'}) : '—'}</td>
                        <td class="text-center">${heat.participants || 0}</td>
                        <td style="min-width:140px;">
                            <div class="progress" style="height:8px;">
                                <div class="progress-bar ${progressClass}" style="width:${completion}%;"></div>
                            </div>
                            <small class="text-muted">${completion}%</small>
                        </td>
                        <td class="reports-column">${reportsList}</td>
                        <td>
                            <button class="btn btn-sm btn-primary" onclick="toggleHeatDetails(${eventId}, ${heat.heat_number})">
                                <i class="fas fa-info-circle me-1"></i>Details
                            </button>
                        </td>
                    </tr>
                    <tr id="heat-details-${eventId}-${heat.heat_number}" style="display:none;">
                        <td colspan="6">
                            <div class="p-3 rounded">
                                <div class="row g-3">
                                    <div class="col-md-12">
                                        <h6 class="mb-2">Heat Details</h6>
                                        <div class="row g-2 small" id="heat-details-live-${eventId}-${heat.heat_number}">
                                            <div class="col-6"><strong>Flow Type:</strong> ${heat.flow_type || '—'}</div>
                                            <div class="col-6"><strong>Source Heat:</strong> ${heat.flow_source_heat || '—'}</div>
                                            <div class="col-6"><strong>Scoring Method:</strong> ${(heat.runs_scoring_method || '').replace('_',' ')}</div>
                                            <div class="col-6"><strong>Categories:</strong> ${(() => {
                                                // First try to use the category_names array if available
                                                if (heat.category_names && Array.isArray(heat.category_names) && heat.category_names.length > 0) {
                                                    return "<span class=\"badge bg-info\">" + heat.category_names.join('</span> <span class=\"badge bg-info\">') + "</span>";
                                                }
                                                
                                                // Fall back to looking up IDs if needed
                                                try { 
                                                    const cats = heat.categories ? JSON.parse(heat.categories) : null;
                                                    if (cats && Array.isArray(cats)) {
                                                        // If IDs are shown, prefix with "Categories: " for clarity
                                                        return "<span class=\"badge bg-info\">" + (cats.length > 0 ? cats.join(', ') : 'All') + "</span>";
                                                    } else {
                                                        return "<span class=\"badge bg-info\">" + (heat.categories || 'All') + "</span>";
                                                    }
                                                } catch(_) { 
                                                    return "<span class=\"badge bg-info\">" + (heat.categories || 'All') + "</span>";
                                                }
                                            })()}</div>
                                            ${Number(heat.is_active) === 1 ? `
                                                <div class="col-6" id="active-run-${eventId}-${heat.heat_number}"><strong>Active Run:</strong> ${heat.active_run || 1}</div>
                                                <div class="col-6" id="bib-start-${eventId}-${heat.heat_number}"><strong>BIB Start:</strong> ${heat.bib_on_start ? `<span class="badge bg-secondary">${heat.bib_on_start}</span> ${heat.bib_start_name || ''}` : '—'}</div>
                                                <div class="col-6" id="latest-bib-${eventId}-${heat.heat_number}"><strong>Latest BIB:</strong> ${heat.bib_latest_on_run ? `<span class="badge bg-secondary">${heat.bib_latest_on_run}</span> ${heat.bib_latest_name || ''}` : '—'}</div>
                                            ` : ''}
                                        </div>
                                    </div>
                                    <div class="col-md-12">
                                        <div class="report-cards" id="report-cards-${eventId}-${heat.heat_number}"></div>
                                    </div>
                                </div>
                            </div>
                        </td>
                    </tr>`;
            });

            html += `
                                </tbody>
                            </table>
                        </div>
                    </div>
                </div>`;

            morphHtml(container, html);

            // Populate General Reports area for event-level configs (heat_number null)
            updateGeneralReports(eventId, configurations);

            // Restore any open report cards
            activeReports.forEach((rep, key) => {
            if (!key.startsWith(`${eventId}-`)) return;
            const parts = key.split('-'); // NEW parsing
            const heatPart = parts[1];
            const heatNum = heatPart === 'all' ? 'all' : String(heatPart);
            const holder = (heatNum === 'all')
                ? (document.getElementById(`general-report-cards-${eventId}`) || document.getElementById(`report-cards-${eventId}-all`))
                : document.getElementById(`report-cards-${eventId}-${heatNum}`);
            if (holder && rep.html) {
                morphHtml(holder, rep.html);
                if (heatNum !== 'all') {
                    const row = document.getElementById(`heat-details-${eventId}-${heatNum}`);
                    if (row) row.style.display = 'table-row';
                }
            }
        });
        }

        function toggleHeatDetails(eventId, heatNumber) {
            const row = document.getElementById(`heat-details-${eventId}-${heatNumber}`);
            if (!row) return;
            row.style.display = (row.style.display === 'none' || !row.style.display) ? 'table-row' : 'none';
        }

        // Event-level General Reports (heat_number null)
        function updateGeneralReports(eventId, configurations = []) {
            const container = document.getElementById(`general-reports-${eventId}`);
            if (!container) return;

            const oldHolder = document.getElementById(`general-report-cards-${eventId}`);
            const oldHtml = oldHolder ? oldHolder.innerHTML : '';

            const general = configurations.filter(c => !c.heat_number);
            if (general.length === 0) {
                morphHtml(container, `<div class="text-muted small">No general reports available</div>`);
                return;
            }

            const summaryTables = general.filter(c => c.view_type === 'summary_table');
            const startLists = general.filter(c => c.view_type === 'start_list');

            let html = '<div class="row g-2">';
            if (summaryTables.length) {
                html += `
                    <div class="col-12">
                        <small class="text-muted">Summary Tables:</small>
                        <div class="d-flex flex-wrap gap-1 mt-1">
                            ${summaryTables.map(cfg => `
                                <button class="btn btn-sm btn-info"
                                    onclick="loadSummaryTableCard(${eventId}, null, ${cfg.id}, '${String(cfg.name).replace(/'/g, "\\'")}')">
                                    <i class="fas fa-table me-1"></i>${cfg.name}
                                </button>`).join('')}
                        </div>
                    </div>`;
            }
            if (startLists.length) {
                html += `
                    <div class="col-12">
                        <small class="text-muted">Start Lists:</small>
                        <div class="d-flex flex-wrap gap-1 mt-1">
                            ${startLists.map(cfg => `
                                <button class="btn btn-sm btn-success"
                                    onclick="loadStartListCard(${eventId}, null, { config_id: ${cfg.id} })">
                                    <i class="fas fa-users me-1"></i>${cfg.name}
                                </button>`).join('')}
                        </div>
                    </div>`;
            }
            // NEW: event-level holder for general report cards
            html += `
                <div class="col-12 mt-2">
                    <div class="general-report-cards" id="general-report-cards-${eventId}">${oldHtml}</div>
                </div>`;
            html += '</div>';
            morphHtml(container, html);
        }

        // Report handling
        function loadSummaryTableCard(eventId, heatNumber, configId, configName, options = {}) {
            const refresh = options.refresh === true;
            const reportKey = `${eventId}-${heatNumber ?? 'all'}-summary`;
            const holderId = `report-cards-${eventId}-${heatNumber ?? 'all'}`;
            let holder = document.getElementById(holderId);
            if (!holder && heatNumber == null) {
                holder = document.getElementById(`general-report-cards-${eventId}`) 
                      || document.querySelector(`[id^="report-cards-${eventId}-"]`);
            }
            if (!holder) return;

            // Ensure heat details row is open if heat-specific
            if (heatNumber != null) {
                const row = document.getElementById(`heat-details-${eventId}-${heatNumber}`);
                if (row) row.style.display = 'table-row';
            }

            // Toggle only on direct button click, not on refresh
            if (!refresh && activeReports.has(reportKey) && activeReports.get(reportKey).configId === configId) {
                activeReports.delete(reportKey);
                morphHtml(holder, '');
                return;
            }

            const hadContent = holder.innerHTML.trim().length > 0;
            if (hadContent) showReportUpdating(reportKey);
            else {
                morphHtml(holder, `
                    <div class="border-0 shadow-none card mb-3">
                        <div class="border-0 shadow-none card-header d-flex justify-content-between align-items-center py-2">
                            <small class="fw-bold"><i class="fas fa-table me-1"></i>${configName}</small>
                            <div class="d-flex align-items-center gap-1 table-buttons">
                                <span class="badge bg-info"><i class="fas fa-sync-alt fa-spin me-1"></i>Loading…</span>
                                <button class="btn btn-sm btn-close btn-close-white" onclick="closeReportCard('${reportKey}')"></button>
                            </div>
                        </div>
                        <div class="border-0 shadow-none card-body p-2 text-center">
                            <div class="spinner-border spinner-border-sm text-primary"></div>
                            <small class="ms-2">Loading summary table…</small>
                        </div>
                    </div>`);
            }

            fetch(window.location.href, {
                method: 'POST',
                headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
                body: `ajax=1&action=load_summary_table&config_id=${encodeURIComponent(configId)}`
            })
            .then(r => r.json())
            .then(res => {
                if (!res.success) throw new Error(res.message || 'Failed to load summary table');

                // FLIP: capture "First" positions BEFORE DOM update
                const prevRects = getRowRects(reportKey);

                const prev = activeReports.get(reportKey);
                const auto = prev ? (prev.auto !== false) : true;
                const html = convertSummaryTableToHTML(res.data, configName, reportKey, { auto });

                // Morph in new HTML (the order may change)
                morphHtml(holder, html);

                // FLIP: get "Last" and animate to new positions
                playFlipAnimation(reportKey, prevRects);

                // Previous movement/changed/latest marking still applies
                let maps;
                if (refresh && prev && prev.type === 'summary_table') {
                    maps = trackAndAnimateSummaryTable(reportKey, prev);
                } else {
                    maps = buildSummaryRowMaps(reportKey);
                }

                activeReports.set(reportKey, {
                    html,
                    type: 'summary_table',
                    configId,
                    configName,
                    auto,
                    rowIndexMap: maps.indexMap,
                    rowContentMap: maps.contentMap
                });
            })
            .catch(err => {
                console.error(err);
                if (!hadContent) {
                    morphHtml(holder, `
                        <div class="border-0 shadow-none card mb-3">
                            <div class="border-0 shadow-none card-header d-flex justify-content-between align-items-center py-2">
                                <small class="fw-bold"><i class="fas fa-table me-1"></i>${configName}</small>
                                <button class="btn btn-sm btn-close btn-close-white" onclick="closeReportCard('${reportKey}')"></button>
                            </div>
                            <div class="border-0 shadow-none card-body p-2"><div class="alert alert-danger py-2 mb-0">${err.message}</div></div>
                        </div>`);
                }
            })
            .finally(() => hideReportUpdating(reportKey));
        }

        function loadStartListCard(eventId, heatNumber, options = {}) {
            const refresh = options.refresh === true;
            const configId = options.config_id || null;
            const reportKey = `${eventId}-${heatNumber ?? 'all'}-start`; // NEW: include type to avoid collision

            // NEW: choose holder for event-level start list under general-reports
            let holder;
            if (heatNumber == null) {
                holder = document.getElementById(`general-report-cards-${eventId}`);
                if (!holder) {
                    const general = document.getElementById(`general-reports-${eventId}`);
                    if (general) {
                        const wrap = document.createElement('div');
                        wrap.className = 'general-report-cards';
                        wrap.id = `general-report-cards-${eventId}`;
                        general.appendChild(wrap);
                        holder = wrap;
                    }
                }
            } else {
                holder = document.getElementById(`report-cards-${eventId}-${heatNumber}`);
            }
            if (!holder) return;

            // Ensure heat details row is open if heat-specific
            if (heatNumber != null) {
                const row = document.getElementById(`heat-details-${eventId}-${heatNumber}`);
                if (row) row.style.display = 'table-row';
            }

            // Toggle only on direct button click, not on refresh
            if (!refresh && activeReports.has(reportKey) && activeReports.get(reportKey).type === 'start_list') {
                activeReports.delete(reportKey);
                morphHtml(holder, '');
                return;
            }

            const hadContent = holder.innerHTML.trim().length > 0;
            if (hadContent) {
                showReportUpdating(reportKey);
            } else {
                morphHtml(holder, `
                    <div class="border-0 shadow-none card mb-3">
                        <div class="border-0 shadow-none card-header d-flex justify-content-between align-items-center py-2">
                            <small class="fw-bold"><i class="fas fa-users me-1"></i>Start List - ${heatNumber ? `Heat ${heatNumber}` : 'All Heats'}</small>
                            <div class="d-flex align-items-center gap-1  table-buttons">
                                <span class="badge bg-info"><i class="fas fa-sync-alt fa-spin me-1"></i>Loading…</span>
                                <button class="btn btn-sm btn-close btn-close-white" onclick="closeReportCard('${reportKey}')"></button>
                            </div>
                        </div>
                        <div class="border-0 shadow-none card-body p-2 text-center">
                            <div class="spinner-border spinner-border-sm text-primary"></div>
                            <small class="ms-2">Loading start list…</small>
                        </div>
                    </div>`);
            }

            fetch(window.location.href, {
                method: 'POST',
                headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
                body: `ajax=1&action=load_start_list&event_id=${encodeURIComponent(eventId)}${heatNumber ? `&heat_number=${encodeURIComponent(heatNumber)}` : ''}${configId ? `&config_id=${encodeURIComponent(configId)}` : ''}`
            })
            .then(r => r.json())
            .then(res => {
                if (!res.success) throw new Error(res.message || 'Failed to load start list');
                const prev = activeReports.get(reportKey);
                const auto = prev ? (prev.auto !== false) : true;
                const html = convertStartListToHTML(res, (heatNumber ? `Heat ${heatNumber}` : 'All Heats'), reportKey, { auto });
                morphHtml(holder, html);
                activeReports.set(reportKey, { html, type: 'start_list', auto, configId });
            })
            .catch(err => {
                console.error(err);
                if (!hadContent) {
                    morphHtml(holder, `
                        <div class="border-0 shadow-none card mb-3">
                            <div class="border-0 shadow-none card-header d-flex justify-content-between align-items-center py-2">
                                <small class="fw-bold"><i class="fas fa-users me-1"></i>Start List - ${heatNumber ? `Heat ${heatNumber}` : 'All Heats'}</small>
                                <button class="btn btn-sm btn-close btn-close-white" onclick="closeReportCard('${reportKey}')"></button>
                            </div>
                            <div class="border-0 shadow-none card-body p-2"><div class="alert alert-danger py-2 mb-0">${err.message}</div></div>
                        </div>`);
                }
            })
            .finally(() => {
                hideReportUpdating(reportKey);
            });
        }

        function convertSummaryTableToHTML(apiData, configName, reportKey, opts = {}) {
            const auto = opts.auto !== false; // default true
            const table_badge_colors = apiData?.table_badge_colors || '';
            const tableHead = apiData?.table_head || '';
            const tableBody = apiData?.table_body || '';
            const participantCount = apiData?.participant_count ?? 0;
            const filterSummary = apiData?.filter_summary || '';

            const tableHTML = (tableHead || tableBody) ? `
                <div class="d-flex justify-content-between align-items-center mb-2">
                    <div class="d-flex align-items-center gap-2">
                        <span class="badge bg-primary">${participantCount} Participants</span>
                        ${auto ? `<span class="badge bg-success"><i class="fas fa-sync-alt me-1"></i>Auto ${getHumanIntervalLabel(refreshMs)}</span>` 
                               : `<span class="badge bg-secondary"><i class="fas fa-pause me-1"></i>Manual</span>`}
                    </div>
                    <div class="d-flex align-items-center gap-1 table-buttons">
                        <button class="btn btn-sm btn-outline-secondary" onclick="refreshReport('${reportKey}')" title="Refresh now">
                            <i class="fas fa-sync-alt"></i>
                        </button>
                        <button class="btn btn-sm ${auto ? 'btn-warning' : 'btn-success'}" onclick="toggleReportAuto('${reportKey}')" title="${auto ? 'Pause auto-refresh' : 'Resume auto-refresh'}">
                            <i class="fas ${auto ? 'fa-pause' : 'fa-play'}"></i>
                        </button>
                        <button class="btn btn-sm btn-outline-secondary" onclick="toggleDashboardFullscreen('${reportKey}')" title="Fullscreen">
                            <i class="fas fa-expand"></i>
                        </button>
                        <button class="btn btn-sm btn-close btn-close-white" onclick="closeReportCard('${reportKey}')"></button>
                    </div>
                </div>
                ${filterSummary ? `<div class="text-muted small mb-2">${filterSummary}</div>` : ''}
                <style>${table_badge_colors}</style>
                <div class="table-responsive summary-table" id="summary-table-${reportKey}">
                    <table class="table table-striped table-hover data-table mb-0">
                        ${tableHead}
                        ${tableBody}
                    </table>
                </div>` : `<div class="alert alert-info py-2 mb-0">No summary data available</div>`;

            return `
                <div class="border-0 shadow-none card mb-3">
                    <div class="border-0 shadow-none card-header d-flex justify-content-between align-items-center py-2">
                        <small class="fw-bold"><i class="fas fa-table me-1"></i>${configName}</small>
                        <button class="btn btn-sm btn-close btn-close-white" onclick="closeReportCard('${reportKey}')"></button>
                    </div>
                    <div class="border-0 shadow-none card-body p-2">${tableHTML}</div>
                </div>`;
        }
function toggleDashboardFullscreen() {
    const container = document.getElementById('eventsContainer');
    if (!container) return;
    if (document.fullscreenElement) {
        document.exitFullscreen();
    } else {
        if (container.requestFullscreen) {
            container.requestFullscreen();
            container.classList.add('fullscreen');
        } else if (container.webkitRequestFullscreen) {
            container.webkitRequestFullscreen();
            container.classList.add('fullscreen');
        } else if (container.mozRequestFullScreen) {
            container.mozRequestFullScreen();
            container.classList.add('fullscreen');
        } else if (container.msRequestFullscreen) {
            container.msRequestFullscreen();
            container.classList.add('fullscreen');
        }
    }
}

// Optionally, listen for fullscreenchange to remove the class
document.addEventListener('fullscreenchange', function() {
    const container = document.getElementById('eventsContainer');
    if (container && !document.fullscreenElement) {
        container.classList.remove('fullscreen');
    }
});
        function convertStartListToHTML(res, heatText, reportKey, opts = {}) {
            const auto = opts.auto !== false; // default true

            const htmlContent = res.html_content;
            const headerRight = `
                <div class="d-flex align-items-center gap-1  table-buttons">
                    ${auto ? `<span class="badge bg-success"><i class="fas fa-sync-alt me-1"></i>Auto ${getHumanIntervalLabel(refreshMs)}</span>` 
                           : `<span class="badge bg-secondary"><i class="fas fa-pause me-1"></i>Manual</span>`}
                    <button class="btn btn-sm btn-outline-secondary" onclick="refreshReport('${reportKey}')" title="Refresh now"><i class="fas fa-sync-alt"></i></button>
                    <button class="btn btn-sm ${auto ? 'btn-warning' : 'btn-success'}" onclick="toggleReportAuto('${reportKey}')" title="${auto ? 'Pause auto-refresh' : 'Resume auto-refresh'}">
                        <i class="fas ${auto ? 'fa-pause' : 'fa-play'}"></i>
                    </button>
                    <button class="btn btn-sm btn-close btn-close-white" onclick="closeReportCard('${reportKey}')"></button>
                </div>`;

            if (htmlContent) {
                return `
                    <div class="border-0 shadow-none card mb-3">
                        <div class="border-0 shadow-none card-header d-flex justify-content-between align-items-center py-2">
                            <small class="fw-bold"><i class="fas fa-users me-1"></i>Start List - ${heatText}</small>
                            ${headerRight}
                        </div>
                        <div class="border-0 shadow-none card-body p-2 start-list" id="start-list-${reportKey}" style="overflow:auto;">
                            ${htmlContent}
                        </div>
                    </div>`;
            }

            const participants = res?.data?.participants || [];
            const listHTML = participants.length ? `
                <div class="list-group list-group-flush">
                    ${participants.map(p => `
                        <div class="list-group-item p-2 d-flex justify-content-between align-items-center">
                            <div>
                                <span class="badge bg-dark me-2">BIB ${p.bib_number}</span>
                                <strong>${p.first_name} ${p.last_name}</strong>
                                <div class="text-muted small">${p.category || ''}</div>
                            </div>
                            <div class="text-end small">
                                ${p.club ? `<div>${p.club}</div>` : ''}
                                <div class="text-muted">Heat ${p.heat_number || '—'}</div>
                            </div>
                        </div>`).join('')}
                </div>` : `<div class="alert alert-info py-2 mb-0">No participants found</div>`;

            return `
                <div class="border-0 shadow-none card mb-3">
                    <div class="border-0 shadow-none card-header d-flex justify-content-between align-items-center py-2">
                        <small class="fw-bold"><i class="fas fa-users me-1"></i>Start List - ${heatText}</small>
                        ${headerRight}
                    </div>
                    <div class="border-0 shadow-none card-body p-2 start-list" id="start-list-${reportKey}" style="overflow:auto;">
                        ${listHTML}
                    </div>
                </div>`;
        }

        // Keep refreshReport logic; it now respects per-card auto flag via load*Card
        function refreshReport(reportKey) {
            const rep = activeReports.get(reportKey);
            if (!rep) return;
            const parts = reportKey.split('-'); // NEW: eventId-heat|all-type
            const eventId = Number(parts[0]);
            const heatPart = parts[1];
            const heatNumber = heatPart === 'all' ? null : Number(heatPart);
            if (rep.type === 'summary_table') {
                loadSummaryTableCard(eventId, heatNumber, rep.configId, rep.configName, { refresh: true });
            } else if (rep.type === 'start_list') {
                loadStartListCard(eventId, heatNumber, { refresh: true, config_id: rep.configId });
            }
        }

        // Helpers to manage non-intrusive updating overlay on report-cards
        function getReportHolder(reportKey) {
            const parts = (reportKey || '').split('-'); // eventId-heat|all-type
            const eventId = parts[0];
            const heatKey = parts[1] || 'all';
            if (heatKey === 'all') {
                const general = document.getElementById(`general-report-cards-${eventId}`);
                if (general) return general;
            }
            let holder = document.getElementById(`report-cards-${eventId}-${heatKey}`);
            if (!holder && heatKey === 'all') {
                holder = document.querySelector(`[id^="report-cards-${eventId}-"]`);
            }
            return holder;
        }

        // NEW: overlay show/hide to avoid clearing content during refresh
        function showReportUpdating(reportKey) {
            const holder = getReportHolder(reportKey);
            if (!holder) return false;
            if (holder.querySelector('.report-updating-overlay')) return true;
            if (!holder.style.position || holder.style.position === 'static') {
                holder.style.position = 'relative';
            }
            const overlay = document.createElement('div');
            overlay.className = 'report-updating-overlay position-absolute top-0 end-0 m-2';
            overlay.innerHTML = '<span class="badge bg-info"><i class="fas fa-sync-alt fa-spin me-1"></i>Updating…</span>';
            holder.appendChild(overlay);
            
            // Convert and initialize Lucide icons for the updating overlay
            if (window.convertFontAwesomeToLucide) {
                window.convertFontAwesomeToLucide();
            }
            if (window.initializeLucideIcons) {
                window.initializeLucideIcons();
            }
            
            return true;
        }

        function hideReportUpdating(reportKey) {
            const holder = getReportHolder(reportKey);
            if (!holder) return;
            const ov = holder.querySelector('.report-updating-overlay');
            if (ov) ov.remove();
        }

        function closeReportCard(reportKey) {
            activeReports.delete(reportKey);
            const holder = getReportHolder(reportKey);
            if (holder) morphHtml(holder, '');
        }

        // Build key from column 2 (second column) text
        function getRowKeyBySecondColumn(row) {
            const cells = row.querySelectorAll('td, th');
            if (cells.length >= 2) {
                return (cells[1].textContent || '').trim();
            }
            return (row.textContent || '').trim();
        }

        // Collect current row rects keyed by a stable key (2nd column text)
function getRowRects(reportKey) {
    const container = document.getElementById(`summary-table-${reportKey}`);
    const rows = container ? container.querySelectorAll('tbody tr') : null;
    const rects = {};
    if (!rows) return rects;
    rows.forEach(tr => {
        const key = getRowKeyBySecondColumn(tr);
        if (key) rects[key] = tr.getBoundingClientRect();
    });
    return rects;
}

// Play FLIP animation: invert from old rects to new, then animate to identity
function playFlipAnimation(reportKey, prevRects) {
    const container = document.getElementById(`summary-table-${reportKey}`);
    if (!container) return;
    const rows = container.querySelectorAll('tbody tr');
    rows.forEach(tr => {
        const key = getRowKeyBySecondColumn(tr);
        if (!key) return;

        const oldRect = prevRects[key];
        const newRect = tr.getBoundingClientRect();

        if (oldRect) {
            const dy = oldRect.top - newRect.top;
            if (Math.abs(dy) > 0.5) {
                tr.style.transition = 'none';
                tr.style.transform = `translateY(${dy}px)`;
                // force reflow
                void tr.offsetHeight;
                tr.style.transition = 'transform 1200ms ease, opacity 1200ms ease';
                tr.style.transform = '';
            }
        } else {
            // New row: fade+slide in
            tr.style.transition = 'none';
            tr.style.opacity = '0';
            tr.style.transform = 'translateY(8px)';
            void tr.offsetHeight;
            tr.style.transition = 'transform 1200ms ease, opacity 1200ms ease';
            tr.style.opacity = '1';
            tr.style.transform = '';
        }

        const cleanup = () => {
            tr.style.transition = '';
            tr.style.transform = '';
            tr.style.opacity = '';
            tr.removeEventListener('transitionend', cleanup);
        };
        tr.addEventListener('transitionend', cleanup);
    });
}

// Build current maps for a summary table
        function buildSummaryRowMaps(reportKey) {
            const container = document.getElementById(`summary-table-${reportKey}`);
            const rows = container ? container.querySelectorAll('tbody tr') : null;
            const indexMap = {};
            const contentMap = {};
            if (!rows) return { indexMap, contentMap };
            let idx = 0;
            rows.forEach(tr => {
                const key = getRowKeyBySecondColumn(tr);
                if (!key) return;
                indexMap[key] = idx++;
                contentMap[key] = (tr.textContent || '').trim();
            });
            return { indexMap, contentMap };
        }

        function addMoveIndicator(row, direction) {
            // put indicator into first cell
            const firstCell = row.querySelector('td, th');
            if (!firstCell) return;
            const existing = firstCell.querySelector('.move-indicator');
            if (existing) existing.remove();
            const span = document.createElement('span');
            span.className = `move-indicator ${direction}`;
            span.innerHTML = direction === 'up'
                ? '<span class="badge bg-success"><i class="fas fa-arrow-up"></i> Up</span>'
                : '<span class="badge bg-danger"><i class="fas fa-arrow-down"></i> Down</span>';
            firstCell.appendChild(span);
            
            // Convert and initialize Lucide icons for the movement indicators
            if (window.convertFontAwesomeToLucide) {
                window.convertFontAwesomeToLucide();
            }
            if (window.initializeLucideIcons) {
                window.initializeLucideIcons();
            }
            
            setTimeout(() => span.remove(), 5000);
        }

        // Compare previous vs current and animate row moves/changes
        function animateSummaryRowChanges(reportKey, prevIndexMap, prevContentMap, newIndexMap, newContentMap) {
            const container = document.getElementById(`summary-table-${reportKey}`);
            if (!container) return;

            const rows = container.querySelectorAll('tbody tr');
            
            // Find the most recent approval timestamp
            let latestTimestamp = null;
            rows.forEach(tr => {
                const approvedAt = tr.getAttribute('data-approved_at');
                if (approvedAt && (!latestTimestamp || approvedAt > latestTimestamp)) {
                    latestTimestamp = approvedAt;
                }
            });

        // Calculate the cutoff time (60 seconds ago)
            const sixtySecondsAgo = new Date();
            sixtySecondsAgo.setSeconds(sixtySecondsAgo.getSeconds() - 60);
            const cutoffTime = sixtySecondsAgo.toISOString();

            rows.forEach(tr => {
                const key = getRowKeyBySecondColumn(tr);
                if (!key) return;
                const prevIdx = prevIndexMap ? prevIndexMap[key] : undefined;
                const newIdx = newIndexMap[key];
                const prevContent = prevContentMap ? prevContentMap[key] : undefined;
                const newContent = newContentMap ? newContentMap[key] : undefined;

                // Clear any previous classes
                tr.classList.remove('moved-up', 'moved-down', 'changed', 'latest');

                // Mark rows with the most recent approval timestamp ONLY if it's within the last 60 seconds
                const approvedAt = tr.getAttribute('data-approved_at');
                if (approvedAt && approvedAt === latestTimestamp && approvedAt > cutoffTime) {
                    tr.classList.add('latest');
                }

                if (prevIdx !== undefined && newIdx !== undefined) {
                    const delta = prevIdx - newIdx;
                    if (delta > 0) {
                        tr.classList.add('moved-up');
                        addMoveIndicator(tr, 'up');
                    } else if (delta < 0) {
                        tr.classList.add('moved-down');
                        addMoveIndicator(tr, 'down');
                    } else if (prevContent !== undefined && prevContent !== newContent) {
                        tr.classList.add('changed');
                    }
                } else if (prevIdx === undefined && newIdx !== undefined) {
                    // New row: highlight as changed
                    tr.classList.add('changed');
                }
            });
        }

        // Orchestrate building maps + animating; return new maps for storage
        function trackAndAnimateSummaryTable(reportKey, prevMeta) {
            const prevIndexMap = prevMeta && prevMeta.rowIndexMap ? prevMeta.rowIndexMap : {};
            const prevContentMap = prevMeta && prevMeta.rowContentMap ? prevMeta.rowContentMap : {};
            const maps = buildSummaryRowMaps(reportKey);
            animateSummaryRowChanges(reportKey, prevIndexMap, prevContentMap, maps.indexMap, maps.contentMap);
            return maps;
        }

        // Get heat live details independently (smaller, more frequent update)
function refreshHeatLiveDetails() {
    if (!autoRefresh) return;
    
    // Collect all visible active heat detail sections
    const activeHeatDetails = document.querySelectorAll('[id^="heat-details-live-"]');
    if (!activeHeatDetails.length) return;
    
    // Build array of event_id and heat_number pairs to refresh
    const heatsToRefresh = [];
    activeHeatDetails.forEach(el => {
        const idParts = el.id.split('-');
        if (idParts.length >= 5) {
            const eventId = idParts[3];
            const heatNumber = idParts[4];
            
            // Only if this heat is marked as active
            if (document.getElementById(`active-run-${eventId}-${heatNumber}`)) {
                heatsToRefresh.push({eventId, heatNumber});
            }
        }
    });
    
    if (!heatsToRefresh.length) return;
    
    // Fetch updated details for all active heats
    const eventIds = [...new Set(heatsToRefresh.map(h => h.eventId))];
    
    // Fetch data for each event (better than fetching individual heats)
    eventIds.forEach(eventId => {
        fetch(`${DASH_API}?action=get_active_heat_details&event_id=${eventId}`)
            .then(r => r.json())
            .then(data => {
                if (!data.success) return;
                
                // Process each heat's data
                Object.entries(data.heats || {}).forEach(([heatNumber, heatData]) => {
                    const activeRunEl = document.getElementById(`active-run-${eventId}-${heatNumber}`);
                    const bibStartEl = document.getElementById(`bib-start-${eventId}-${heatNumber}`);
                    const latestBipEl = document.getElementById(`latest-bib-${eventId}-${heatNumber}`);
                    
                    if (activeRunEl) {
                        activeRunEl.innerHTML = `<strong>Active Run:</strong> ${heatData.active_run || 1}`;
                    }
                    if (bibStartEl) {
                        bibStartEl.innerHTML = `<strong>BIB Start:</strong> ${heatData.bib_on_start ? 
                            `<span class="badge bg-secondary">${heatData.bib_on_start}</span> ${heatData.bib_start_name || ''}` : '—'}`;
                    }
                    if (latestBipEl) {
                        latestBipEl.innerHTML = `<strong>Latest BIB:</strong> ${heatData.bib_latest_on_run ? 
                            `<span class="badge bg-secondary">${heatData.bib_latest_on_run}</span> ${heatData.bib_latest_name || ''}` : '—'}`;
                        
                        // Highlight the latest BIB with a flash animation when it changes
                        if (latestBipEl.dataset.lastValue !== String(heatData.bib_latest_on_run)) {
                            latestBipEl.classList.add('highlight-update');
                            setTimeout(() => {
                                latestBipEl.classList.remove('highlight-update');
                            }, 2000);
                        }
                        latestBipEl.dataset.lastValue = String(heatData.bib_latest_on_run);
                    }
                });
                
                // Convert and initialize Lucide icons after heat details update
                if (window.convertFontAwesomeToLucide) {
                    window.convertFontAwesomeToLucide();
                }
                if (window.initializeLucideIcons) {
                    window.initializeLucideIcons();
                }
            })
            .catch(err => console.error('Error refreshing heat details:', err));
    });
}

// Refresh just the heat rows in the event table without reloading full heat details
function refreshHeatRows() {
    if (!autoRefresh) return;
    
    // Find all visible event heat rows
    const eventRows = document.querySelectorAll('tr.event-row');
    if (!eventRows.length) return;
    
    // Group by event ID to minimize API calls
    const eventMap = {};
    eventRows.forEach(row => {
        const idParts = row.id.split('-');
        if (idParts.length >= 4) {
            const eventId = idParts[1];
            const heatNumber = idParts[3];
            
            if (!eventMap[eventId]) {
                eventMap[eventId] = [];
            }
            eventMap[eventId].push(heatNumber);
        }
    });
    
    // Fetch updated heat data for each event
    Object.entries(eventMap).forEach(([eventId, heats]) => {
        fetch(`${DASH_API}?action=get_heat_rows&event_id=${eventId}`)
            .then(r => r.json())
            .then(data => {
                if (!data.success) return;
                
                // Update each heat row with new data
                data.heats.forEach(heat => {
                    const rowId = `event-${eventId}-heat-${heat.heat_number}`;
                    const row = document.getElementById(rowId);
                    if (!row) return;
                    
                    // Calculate completion percentage
                    const completion = heat.expected_scores > 0
                        ? Math.round((Number(heat.total_scores || 0) / Number(heat.expected_scores)) * 100)
                        : 0;
                    const progressClass = completion >= 100 ? 'bg-success' : (completion >= 50 ? 'bg-warning' : 'bg-info');
                    const heatStatusClass = Number(heat.is_active) === 1 ? 'bg-danger' : (completion >= 100 ? 'bg-success' : 'bg-secondary');
                    const heatStatusText = Number(heat.is_active) === 1 ? 'Live' : (completion >= 100 ? 'Completed' : 'Upcoming');
                    
                    // Update first cell (heat status + name)
                    const statusCell = row.cells[0];
                    if (statusCell) {
                        const statusBadge = statusCell.querySelector('.badge');
                        if (statusBadge) {
                            statusBadge.className = `badge ${heatStatusClass} me-2`;
                            statusBadge.textContent = heatStatusText;
                        }
                        
                        const heatInfoDiv = statusCell.querySelector('div > div');
                        if (heatInfoDiv) {
                            const nameElement = heatInfoDiv.querySelector('strong');
                            if (nameElement) {
                                nameElement.textContent = `Heat ${heat.heat_number}: ${heat.heat_name || ''}`;
                            }
                            
                            const infoElement = heatInfoDiv.querySelector('.small');
                            if (infoElement) {
                                infoElement.textContent = `${heat.scoring_type || ''} • ${heat.runs_count || 0} runs`;
                            }
                        }
                    }
                    
                    // Update participant count (3rd cell)
                    if (row.cells[2]) {
                        row.cells[2].textContent = heat.participants || 0;
                    }
                    
                    // Update progress bar (4th cell)
                    const progressCell = row.cells[3];
                    if (progressCell) {
                        const progressBar = progressCell.querySelector('.progress-bar');
                        if (progressBar) {
                            progressBar.className = `progress-bar ${progressClass}`;
                            progressBar.style.width = `${completion}%`;
                        }
                        
                        const progressText = progressCell.querySelector('small');
                        if (progressText) {
                            progressText.textContent = `${completion}%`;
                        }
                    }
                    
                    // Highlight the row briefly to show it was updated
                    row.classList.add('heat-row-updated');
                    setTimeout(() => {
                        row.classList.remove('heat-row-updated');
                    }, 2000);
                });
            })
            .catch(err => console.error('Error refreshing heat rows:', err));
    });
}

// Add to refresh cycle with faster updates for active heat details
let heatDetailsInterval;
function startHeatDetailsRefresh() {
    stopHeatDetailsRefresh();
    // Update active heat details more frequently (every 5 seconds)
    heatDetailsInterval = setInterval(() => {
        refreshHeatLiveDetails();
        refreshHeatRows(); // Also refresh heat rows
    }, 5000);
}

function stopHeatDetailsRefresh() {
    if (heatDetailsInterval) {
        clearInterval(heatDetailsInterval);
        heatDetailsInterval = null;
    }
}
    </script>

        <?php include 'admin/footer.php'; ?>
</body>
</html>