<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Geofeed Tuner Report</title>
    <link href="https://fonts.googleapis.com/css2?family=Raleway:wght@300;400;600;700&display=swap" rel="stylesheet">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css">
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
    <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
    <script src="https://unpkg.com/h3-js@4.1.0/dist/h3-js.umd.js"></script>
    <style>
        body {
            background-color: #adadad;
            padding: 20px;
            font-family: 'Raleway', sans-serif;
        }
        i[class*="bi"] {
            font-weight: 700;
            text-shadow: 0 0 0.3px currentColor;
        }
        .report-container {
            background-color: white;
            border-radius: 15px;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
            padding: 30px;
            margin: 0 auto 20px auto;
            max-width: 1400px;
        }
        .metadata {
            background-color: #f0f0f0;
            padding: 20px;
            border-radius: 5px;
            margin-bottom: 30px;
        }
        .metadata h3 {
            margin-bottom: 15px;
            color: #333333;
        }
        .metadata-item {
            display: inline-block;
            margin-right: 40px;
            margin-bottom: 10px;
        }
        .metadata-label {
            font-weight: bold;
            color: #696969;
        }
        .metadata-value {
            color: #333333;
        }
        .status-badge {
            padding: 3px 10px;
            border-radius: 4px;
            font-weight: bold;
            font-size: 0.75em;
            display: inline-flex;
            align-items: center;
            justify-content: center;
            gap: 4px;
            white-space: nowrap;
            width: 150px;
        }
        .status-error {
            background-color: #ff707f;
            color: #ffffff;
            border: 1px solid #ff707f;
        }
        .status-warning {
            background-color: #ffdc73;
            color: #000000;
            border: 1px solid #ffdc73;
        }
        .status-suggestion {
            background-color: #1877ec;
            color: #ffffff;
            border: 1px solid #1877ec;
        }
        .status-ok {
            background-color: #28a745;
            color: #ffffff;
            border: 1px solid #28a745;
        }
        /* Checkbox styling */
        input[type="checkbox"] {
            width: 16px;
            height: 16px;
            cursor: pointer;
            accent-color: #4a4a4a;
            display: block;
            margin: 0 auto;
        }
        input[type="checkbox"]:hover {
            transform: scale(1.1);
        }
        /* Disabled checkbox with X effect */
        input[type="checkbox"]:disabled {
            cursor: not-allowed;
            opacity: 0.6;
            position: relative;
        }
        input[type="checkbox"]:disabled:hover {
            transform: none;
        }
        /* Create X overlay on disabled checkbox */
        .message-line input[type="checkbox"]:disabled {
            appearance: none;
            -webkit-appearance: none;
            -moz-appearance: none;
            width: 16px;
            height: 16px;
            border: 2px solid #dc3545;
            border-radius: 3px;
            background-color: #dc3545;
            position: relative;
            cursor: not-allowed;
        }
        .message-line input[type="checkbox"]:disabled::before,
        .message-line input[type="checkbox"]:disabled::after {
            content: '';
            position: absolute;
            top: 50%;
            left: 50%;
            width: 10px;
            height: 2px;
            background-color: #ffffff;
        }
        .message-line input[type="checkbox"]:disabled::before {
            transform: translate(-50%, -50%) rotate(45deg);
        }
        .message-line input[type="checkbox"]:disabled::after {
            transform: translate(-50%, -50%) rotate(-45deg);
        }
        #selectAll {
            width: 17px;
            height: 17px;
        }
        .entries-table td:first-child,
        .entries-table th:first-child {
            text-align: center;
            vertical-align: middle;
        }
        .entries-table {
            width: 100%;
            border-collapse: collapse;
            border-radius: 8px;
            overflow: hidden;
            table-layout: fixed;
        }
        .entries-table thead {
            background-color: #4a4a4a;
            color: white;
        }
        .entries-table th {
            padding: 12px;
            text-align: left;
            border: 1px solid #d3d3d3;
            font-weight: 600;
            background-color: #6b6b6b;
            color: #ffffff;
        }
        .entries-table td {
            padding: 12px;
            border: 1px solid #d3d3d3;
            vertical-align: top;
            word-wrap: break-word;
            overflow-wrap: break-word;
        }
        .entries-table tbody tr:not(.expand-details-row) td:nth-child(3) {
            text-align: center;
            vertical-align: top;
        }
        .entries-table tbody tr:nth-child(odd) {
            background-color: #f9f9f9;
        }
        .entries-table tbody tr:hover {
            background-color: #f0f0f0;
        }
        .messages-list {
            list-style: none;
            padding: 0;
            margin: 0;
        }
        .messages-list li {
            padding: 1px 0;
            border-left: 3px solid #b0b0b0;
            padding-left: 10px;
            color: #696969;
            font-size: 0.95em;
        }
        h1 {
            color: #ffffff;
            margin-bottom: 10px;
            margin-top: -30px;
            margin-left: -30px;
            margin-right: -30px;
            border-bottom: none;
            padding-bottom: 10px;
            font-size: 3em;
            font-weight: 700;
            font-family: 'Raleway', sans-serif;
            background: linear-gradient(135deg, #2a2a2a 0%, #4a4a4a 100%);
            padding: 30px;
            border-radius: 15px 15px 0 0;
            text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
        }
        .metrics-table {
            width: 100%;
            border-collapse: collapse;
            margin: 0 auto 20px auto;
            border-radius: 8px;
            overflow: hidden;
        }
        .metrics-table td {
            padding: 10px 15px;
            border: 1px solid #d3d3d3;
        }
        .metrics-table tr:nth-child(even) {
            background-color: #f9f9f9;
        }
        .metrics-label {
            font-weight: bold;
            width: 30%;
            background-color: #f0f0f0;
        }
        .metrics-value {
            width: 70%;
            color: #333333;
        }
        .metrics-table.feed-metadata .metrics-label {
            width: 20%;
        }
        .metrics-table.feed-metadata .metrics-value {
            width: 80%;
        }
        .accuracy-summary-table td {
            width: auto !important;
        }
        .accuracy-summary-table .metrics-label {
            width: 15% !important;
        }
        .accuracy-summary-table .metrics-value {
            width: 35% !important;
        }
        #filterCountry, #filterRegion, #filterCity, #filterStatus, #filterIpPrefix {
            box-sizing: border-box;
            font-family: 'Raleway', sans-serif;
        }
        #filterCountry::placeholder, #filterRegion::placeholder, #filterCity::placeholder, #filterIpPrefix::placeholder {
            color: #aaa;
        }
        #resetFilters:hover, #toggleFilters:hover, #toggleExpandAll:hover, #downloadCSV:hover {
            background-color: #6a6a6a;
            border-color: #aaa;
        }
        .expand-details-row {
            display: none;
        }
        .expand-details-row.show {
            display: table-row;
        }
        .expand-details-row td {
            border-left: none;
            border-right: none;
        }
        .expand-details-row td:first-child {
            border-left: 1px solid #d3d3d3;
            padding: 0;
        }
        .expand-details-row td:nth-child(2) {
            padding: 0;
        }
        .expand-details-row td:last-child {
            border-right: 1px solid #d3d3d3;
        }
        .previous-values-row td {
            background-color: #f0f0f0;
            font-style: italic;
            color: #555;
            padding: 8px 12px;
            border-top: 1px solid #d3d3d3;
            border-bottom: 1px solid #d3d3d3;
        }
        .previous-values-row td:first-child {
            border-left: 1px solid #d3d3d3;
        }
        .previous-values-row td:last-child {
            border-right: 1px solid #d3d3d3;
        }
        .issues-row td {
            border-top: 1px solid #d3d3d3;
            border-bottom: 1px solid #d3d3d3;
        }
        .expandable-row {
            cursor: pointer;
        }
        .expandable-row:focus {
            outline: 2px solid #4a9eff;
            outline-offset: -2px;
        }
        .tune-all-btn {
            display: none;
            padding: 3px 8px;
            border: 1px solid #888;
            border-radius: 3px;
            background-color: #5a5a5a;
            color: #ffffff;
            cursor: pointer;
            font-weight: 600;
            font-size: 0.8em;
        }
        body.tuning-mode .tune-all-btn {
            display: inline;
        }
        .issues-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
        }
        .message-line {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin: 5px 0;
        }
        .message-line input[type="checkbox"] {
            display: none;
        }
        body.tuning-mode .message-line input[type="checkbox"] {
            display: inline-block;
        }
        .message-line span {
            flex: 1;
            font-size: 0.98em;
        }
        .modal {
            display: none;
            position: fixed;
            z-index: 1000;
            left: 0;
            top: 0;
            width: 100%;
            height: 100%;
            background-color: rgba(0, 0, 0, 0.6);
        }
        .modal-content {
            background-color: #ffffff;
            margin: 50px auto;
            padding: 0;
            border: none;
            width: 90%;
            max-width: 1200px;
            border-radius: 15px;
            max-height: 90vh;
            overflow: hidden;
            box-shadow: 0 4px 20px rgba(0,0,0,0.3);
            display: flex;
            flex-direction: column;
        }
        .modal-content h2 {
            background-color: #4a4a4a;
            color: #ffffff;
            padding: 20px 25px;
            margin: 0;
            font-size: 1.3em;
            font-weight: bold;
            font-family: 'Raleway', sans-serif;
            border-radius: 15px 15px 0 0;
            position: relative;
        }
        .close {
            color: #ffffff;
            position: absolute;
            right: 20px;
            top: 50%;
            transform: translateY(-50%);
            font-size: 32px;
            font-weight: bold;
            cursor: pointer;
            line-height: 1;
            transition: color 0.2s;
        }
        .close:hover,
        .close:focus {
            color: #ddd;
        }
        .matches-container {
            margin-top: 0;
            padding: 25px;
            display: flex;
            flex-wrap: nowrap;
            gap: 15px;
            justify-content: center;
            overflow-x: auto;
            background-color: #f5f5f5;
            flex: 1;
            overflow-y: auto;
        }
        .match-item {
            flex: 1 1 0;
            max-width: 350px;
            min-width: 280px;
            border: 2px solid #c0c0c0;
            border-radius: 8px;
            padding: 0;
            background-color: white;
            box-shadow: 0 2px 8px rgba(0,0,0,0.15);
            cursor: pointer;
            transition: all 0.2s;
            overflow: hidden;
        }
        .match-item:hover {
            box-shadow: 0 4px 12px rgba(0,0,0,0.25);
            border-color: #4a9eff;
            transform: translateY(-2px);
        }
        .match-item:active {
            transform: translateY(0);
        }
        .match-item.selected {
            border-color: #4a9eff;
            border-width: 3px;
            box-shadow: 0 0 15px rgba(74, 158, 255, 0.6);
        }
        .match-map {
            height: 180px;
            width: 100%;
            margin: 0;
            border: none;
            border-radius: 0;
        }
        .match-info-container {
            display: flex;
            flex-direction: column;
            gap: 6px;
            padding: 12px;
            background-color: #f0f0f0;
        }
        .match-header {
            font-size: 22px;
            font-weight: bold;
            color: #333;
            text-align: left;
            font-family: 'Raleway', sans-serif;
            line-height: 1.2;
        }
        .location-details {
            font-size: 15px;
            color: #666;
            display: inline;
        }
        .location-details span {
            margin-right: 15px;
        }
        .default-values {
            display: none;
            padding: 5px 0;
            font-size: 13px;
            margin-bottom: 10px;
        }
        .default-values.show {
            display: block;
        }
        .mode-toggle-container {
            display: flex;
            align-items: center;
            gap: 10px;
            font-size: 0.72em;
        }
        .mode-toggle {
            position: relative;
            width: 240px;
            height: 32px;
            background-color: #888;
            border-radius: 16px;
            cursor: pointer;
            transition: background-color 0.3s;
            display: flex;
            align-items: center;
            justify-content: space-between;
            padding: 0 8px;
        }
        .mode-toggle:focus {
            outline: 2px solid #4a9eff;
            outline-offset: 2px;
        }
        .mode-toggle-text {
            font-size: 0.85em;
            font-weight: 600;
            color: #cccccc;
            z-index: 1;
            transition: color 0.3s;
            flex: 1;
            text-align: center;
        }
        .mode-toggle-text.active {
            color: #333333;
        }
        .mode-toggle-slider {
            position: absolute;
            top: 3px;
            left: 3px;
            width: 116px;
            height: 26px;
            background-color: #ffffff;
            border-radius: 13px;
            transition: transform 0.3s;
            box-shadow: 0 2px 4px rgba(0,0,0,0.2);
        }
        .mode-toggle.tuning .mode-toggle-slider {
            transform: translateX(118px);
        }
        .mode-toggle.h3mode .mode-toggle-slider {
            transform: translateX(96px);
        }
        .mode-toggle-label {
            font-weight: 600;
            color: #ffffff;
            font-size: 0.9em;
        }
        
        /* Keyboard accessibility focus styles */
        button:focus {
            outline: 2px solid #4a9eff;
            outline-offset: 2px;
        }
        input[type="text"]:focus,
        select:focus {
            outline: 2px solid #4a9eff;
            outline-offset: 1px;
        }
        input[type="checkbox"]:focus {
            outline: 2px solid #4a9eff;
            outline-offset: 2px;
        }
        /* Pagination styles */
        .pagination-container {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-top: 20px;
            padding: 15px 20px;
            background-color: #f5f5f5;
            border-radius: 8px;
            flex-wrap: wrap;
            gap: 15px;
        }
        .pagination-info {
            color: #333333;
            font-size: 0.95em;
        }
        .pagination-controls {
            display: flex;
            gap: 5px;
            align-items: center;
        }
        .pagination-btn {
            padding: 6px 12px;
            border: 1px solid #888;
            border-radius: 4px;
            background-color: #5a5a5a;
            color: #ffffff;
            cursor: pointer;
            font-weight: 600;
            font-size: 0.9em;
            transition: background-color 0.2s;
        }
        .pagination-btn:hover:not(:disabled) {
            background-color: #4a4a4a;
        }
        .pagination-btn:disabled {
            background-color: #888;
            cursor: not-allowed;
            opacity: 0.5;
        }
        .pagination-btn.active {
            background-color: #1877ec;
            border-color: #1877ec;
        }
        .page-size-selector {
            display: flex;
            align-items: center;
            gap: 8px;
        }
        .page-size-selector select {
            padding: 5px 10px;
            border: 1px solid #888;
            border-radius: 4px;
            background-color: #5a5a5a;
            color: #ffffff;
            font-size: 0.9em;
            cursor: pointer;
        }
        /* Helper classes for common inline styles */
        .table-header {
            background-color: #6b6b6b;
            padding: 12px 15px;
            font-weight: bold;
            color: #ffffff;
            font-size: 1.1em;
            font-family: 'Raleway', sans-serif;
        }
        .icon-spacer {
            margin-right: 8px;
        }
        .flex-container {
            display: flex;
            justify-content: space-between;
            align-items: center;
        }
        .action-button {
            padding: 5px 11px;
            border: 1px solid #888;
            border-radius: 3px;
            background-color: #5a5a5a;
            color: #ffffff;
            cursor: pointer;
            font-weight: 600;
            font-size: 0.9em;
        }
        .action-button:hover {
            background-color: #6a6a6a;
        }
        .subtitle-span {
            font-size: 0.4em;
            font-weight: 700;
            display: block;
            margin-top: 10px;
        }
    </style>
</head>
<body>
    <div class="report-container">
        <h1>Geofeed Tuner Report<br><span style="font-size: 0.4em; font-weight: 700; display: block; margin-top: 10px;">RFC 8805 Validation & Recommendations</span></h1>
        
        <table class="metrics-table feed-metadata" style="margin-top: 30px; margin-bottom: 30px;">
            <tr style="background-color: #6b6b6b;">
                <td colspan="2" style="padding: 12px 15px; font-weight: bold; color: #ffffff; font-size: 1.1em; font-family: 'Raleway', sans-serif;">Feed Metadata</td>
            </tr>
            <tr>
                <td class="metrics-label"><i class="bi bi-filetype-csv" style="margin-right: 8px;"></i>Input file</td>
                <td class="metrics-value"><span id="inputFileMetrics">{{.Metadata.InputFile}}</span></td>
            </tr>
            <tr>
                <td class="metrics-label"><i class="bi bi-calendar-event-fill" style="margin-right: 8px;"></i>Tuning timestamp</td>
                <td class="metrics-value"><script>document.write(new Intl.DateTimeFormat("en", {dateStyle: "full", timeStyle: "long",}).format(new Date({{.Metadata.Timestamp}})))</script></td>
            </tr>
        </table>
        
        <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-bottom: 20px;">
            <table class="metrics-table">
                <tr style="background-color: #6b6b6b;">
                    <td colspan="2" style="padding: 12px 15px; font-weight: bold; color: #ffffff; font-size: 1.1em; font-family: 'Raleway', sans-serif;">Entries</td>
                </tr>
                <tr>
                    <td class="metrics-label"><i class="bi bi-list-ul" style="margin-right: 8px;"></i>Total entries</td>
                    <td class="metrics-value"><span id="totalEntriesMetrics">{{.Metadata.TotalEntries}}</span></td>
                </tr>
                <tr>
                    <td class="metrics-label"><i class="bi bi-diagram-3-fill" style="margin-right: 8px;"></i>IPv4 entries</td>
                    <td class="metrics-value"><span id="ipv4EntriesMetrics">{{.Metadata.IpV4Entries}}</span></td>
                </tr>
                <tr>
                    <td class="metrics-label"><i class="bi bi-diagram-3-fill" style="margin-right: 8px;"></i>IPv6 entries</td>
                    <td class="metrics-value"><span id="ipv6EntriesMetrics">{{.Metadata.IpV6Entries}}</span></td>
                </tr>
                <tr>
                    <td class="metrics-label"><i class="bi bi-exclamation-circle-fill" style="margin-right: 8px;"></i>Invalid entries</td>
                    <td class="metrics-value"><span id="invalidEntriesMetrics">{{.Metadata.InvalidEntries}}</span></td>
                </tr>
            </table>
            
            <table class="metrics-table">
                <tr style="background-color: #6b6b6b;">
                    <td colspan="2" style="padding: 12px 15px; font-weight: bold; color: #ffffff; font-size: 1.1em; font-family: 'Raleway', sans-serif;">Analysis Summary</td>
                </tr>
                <tr>
                    <td class="metrics-label"><i class="bi bi-x-circle-fill" style="margin-right: 8px;"></i>Errors</td>
                    <td class="metrics-value"><span id="errorCountMetrics">{{.Metadata.Errors}}</span></td>
                </tr>
                <tr>
                    <td class="metrics-label"><i class="bi bi-exclamation-triangle-fill" style="margin-right: 8px;"></i>Warnings</td>
                    <td class="metrics-value"><span id="warningCountMetrics">{{.Metadata.Warnings}}</span></td>
                </tr>
                <tr>
                    <td class="metrics-label"><i class="bi bi-lightbulb-fill" style="margin-right: 8px;"></i>Suggestions</td>
                    <td class="metrics-value"><span id="suggestionsMetrics">{{.Metadata.Suggestions}}</span></td>
                </tr>
                <tr>
                    <td class="metrics-label"><i class="bi bi-check-circle-fill" style="margin-right: 8px;"></i>OK </td>
                    <td class="metrics-value"><span id="okCountMetrics">{{.Metadata.OK}}</span></td>
                </tr>
            </table>
        </div>
        
        <div style="margin-bottom: 40px;">
            <table class="metrics-table accuracy-summary-table" style="margin-top: 0; margin-bottom: 0; table-layout: fixed; width: 100%;">
                <colgroup>
                    <col style="width: 20%;">
                    <col style="width: 30%;">
                    <col style="width: 20%;">
                    <col style="width: 30%;">
                </colgroup>
                <tr style="background-color: #6b6b6b;">
                    <td colspan="4" style="padding: 12px 15px; font-weight: bold; color: #ffffff; font-size: 1.1em; font-family: 'Raleway', sans-serif;">Accuracy Summary</td>
                </tr>
                <tr>
                    <td class="metrics-label"><i class="bi bi-building-fill" style="margin-right: 8px;"></i>City-level accuracy</td>
                    <td class="metrics-value"><span id="cityAccuracy">{{.Metadata.CityLevelAccuracy}}</span></td>
                    <td class="metrics-label"><i class="bi bi-map-fill" style="margin-right: 8px;"></i>Region-level accuracy</td>
                    <td class="metrics-value"><span id="regionAccuracy">{{.Metadata.RegionLevelAccuracy}}</span></td>
                </tr>
                <tr>
                    <td class="metrics-label"><i class="bi bi-globe-americas" style="margin-right: 8px;"></i>Country-level accuracy</td>
                    <td class="metrics-value"><span id="countryAccuracy">{{.Metadata.CountryLevelAccuracy}}</span></td>
                    <td class="metrics-label"><i class="bi bi-ban-fill" style="margin-right: 8px;"></i>Do-not-geolocate entries</td>
                    <td class="metrics-value"><span id="doNotGeolocate">{{.Metadata.DoNotGeolocate}}</span></td>
                </tr>
            </table>
        </div>

        <div style="overflow-x: auto;">
            <table class="entries-table">
                <colgroup>
                    <col style="width: 3%;">
                    <col style="width: 5%;">
                    <col style="width: 20%;">
                    <col style="width: 24%;">
                    <col style="width: 10%;">
                    <col style="width: 19%;">
                    <col style="width: 19%;">
                </colgroup>
                <thead>
                    <tr style="background-color: #4a4a4a;">
                        <th colspan="7" style="padding: 12px; font-weight: bold; color: #ffffff; font-size: 1.3em; font-family: 'Raleway', sans-serif;">
                            <div style="display: flex; justify-content: space-between; align-items: center;">
                                <span><i class="bi bi-list-ul" style="margin-right: 10px;"></i>Entries</span>
                                <div class="mode-toggle-container">
                                    <div class="mode-toggle" id="modeToggle" onclick="toggleMode()" tabindex="0" onkeydown="if(event.key === 'Enter' || event.key === ' ') { event.preventDefault(); toggleMode(); }">
                                        <span class="mode-toggle-text active" id="viewingText">Viewing Mode</span>
                                        <div class="mode-toggle-slider"></div>
                                        <span class="mode-toggle-text" id="tuningText">Tuning Mode</span>
                                    </div>
                                </div>
                            </div>
                        </th>
                    </tr>
                    <tr>
                        <th colspan="7" style="background-color: #6b6b6b; padding: 8px;">
                            <div style="display: flex; justify-content: space-between; gap: 5px;">
                                <div style="display: flex; gap: 5px;">
                                    <button id="toggleFilters" style="padding: 5px 11px; border: 1px solid #888; border-radius: 3px; background-color: #5a5a5a; color: #ffffff; cursor: pointer; font-weight: 600; font-size: 0.9em; width: 130px;">
                                        <i class="bi bi-funnel-fill" style="margin-right: 5px;"></i>Show Filters
                                    </button>
                                    <button id="toggleExpandAll" style="padding: 5px 11px; border: 1px solid #888; border-radius: 3px; background-color: #5a5a5a; color: #ffffff; cursor: pointer; font-weight: 600; font-size: 0.9em; width: 145px;">
                                        <i class="bi bi-arrows-expand" style="margin-right: 5px;"></i>Expand Rows
                                    </button>
                                    <button id="tuneAll" class="tune-all-btn" style="padding: 5px 11px; border: 1px solid #888; border-radius: 3px; background-color: #5a5a5a; color: #ffffff; cursor: pointer; font-weight: 600; font-size: 0.9em;">
                                        <i class="bi bi-wrench" style="margin-right: 5px;"></i>Tune All
                                    </button>
                                </div>
                                <button id="downloadCSV" style="padding: 5px 11px; border: 1px solid #888; border-radius: 3px; background-color: #5a5a5a; color: #ffffff; cursor: pointer; font-weight: 600; font-size: 0.9em;">
                                    <i class="bi bi-filetype-csv" style="margin-right: 5px;"></i>Download
                                </button>
                            </div>
                        </th>
                    </tr>
                    <tr>
                        <th><input type="checkbox" id="selectAll" checked></th>
                        <th>Line</th>
                        <th>Status</th>
                        <th>IP Prefix</th>
                        <th>Country</th>
                        <th>Region</th>
                        <th>City</th>
                    </tr>
                    <tr id="filterRow" style="background-color: #6b6b6b; display: none;">
                        <th colspan="2" style="padding: 8px;">
                            <button id="resetFilters" style="padding: 5px 11px; border: 1px solid #888; border-radius: 3px; background-color: #5a5a5a; color: #ffffff; cursor: pointer; font-weight: 600; font-size: 0.9em; width: 100%;">Reset</button>
                        </th>
                        <th style="padding: 8px;">
                            <select id="filterStatus" style="padding: 5px 9px; border: 1px solid #888; border-radius: 3px; background-color: #5a5a5a; color: #ffffff; font-size: 0.9em; width: 100%;">
                                <option value="">All</option>
                                <option value="ERROR">ERROR</option>
                                <option value="WARNING">WARNING</option>
                                <option value="SUGGESTION">SUGGESTION</option>
                                <option value="OK">OK</option>
                            </select>
                        </th>
                        <th style="padding: 8px;">
                            <input type="text" id="filterIpPrefix" placeholder="IP Prefix" style="padding: 5px 9px; border: 1px solid #888; border-radius: 3px; background-color: #5a5a5a; color: #ffffff; font-size: 0.9em; width: 100%; box-sizing: border-box;">
                        </th>
                        <th style="padding: 8px;">
                            <input type="text" id="filterCountry" placeholder="Country" style="padding: 5px 9px; border: 1px solid #888; border-radius: 3px; background-color: #5a5a5a; color: #ffffff; font-size: 0.9em; width: 100%; box-sizing: border-box;">
                        </th>
                        <th style="padding: 8px;">
                            <input type="text" id="filterRegion" placeholder="Region" style="padding: 5px 9px; border: 1px solid #888; border-radius: 3px; background-color: #5a5a5a; color: #ffffff; font-size: 0.9em; width: 100%; box-sizing: border-box;">
                        </th>
                        <th style="padding: 8px;">
                            <input type="text" id="filterCity" placeholder="City" style="padding: 5px 9px; border: 1px solid #888; border-radius: 3px; background-color: #5a5a5a; color: #ffffff; font-size: 0.9em; width: 100%; box-sizing: border-box;">
                        </th>
                    </tr>
                </thead>
                <tbody id="entriesTableBody">
            {{range .Entries}}
            <tr
             id="csv-r-{{.Line}}" 
             class="expandable-row" 
             data-geocoding-hint="{{.GeocodingHint}}" 
             data-do-not-geolocate="{{.DoNotGeolocate}}" 
             data-has-warning="{{.HasWarning}}" 
             data-has-error="{{.HasError}}" 
             data-has-suggestion="{{.HasSuggestion}}" 
             data-tunable="{{.Tunable}}" 
             data-tuned-country="{{.TunedEntry.CountryCode}}" 
             data-tuned-region="{{.TunedEntry.RegionCode}}" 
             data-tuned-city="{{.TunedEntry.Name}}"
             data-h3-cells="{{.TunedEntry.H3Cells}}"
             data-bounding-box="{{.TunedEntry.BoundingBox}}">
                <td><input type="checkbox" class="row-checkbox" checked></td>
                <td>{{.Line}}</td>
                <td><span class="status-badge status-{{if eq .Status "ERROR"}}error{{else if eq .Status "WARNING"}}warning{{else if eq .Status "SUGGESTION"}}suggestion{{else}}ok{{end}}"><i class="bi {{if eq .Status "ERROR"}}bi-x-circle-fill{{else if eq .Status "WARNING"}}bi-exclamation-triangle-fill{{else if eq .Status "SUGGESTION"}}bi-lightbulb-fill{{else}}bi-check-circle-fill{{end}}"></i>{{.Status}}</span></td>
                <td><strong>{{.IPPrefix}}</strong></td>
                <td>{{.CountryCode}}</td>
                <td>{{.RegionCode}}</td>
                <td>{{.City}}</td>
            </tr>
            <tr class="expand-details-row previous-values-row">
                <td></td>
                <td></td>
                <td></td>
                <td><strong>Previous values:</strong></td>
                <td class="previous-value"><span class="default-country">{{.CountryCode}}</span></td>
                <td class="previous-value"><span class="default-region">{{.RegionCode}}</span></td>
                <td class="previous-value"><span class="default-city">{{.City}}</span></td>
            </tr>
            <tr class="expand-details-row issues-row">
                <td></td>
                <td></td>
                <td colspan="5">
                    <div class="issues-header">
                        <strong>Issues:</strong>
                        {{if .Tunable}}
                        <button class="tune-all-btn" onclick="handleTuneButtonClick(this)">Tune</button>
                        {{end}}
                    </div>
                    {{range .Messages}}
                    <div class="message-line" data-id="{{.ID}}">
                        <span>{{.Text}}</span>
                        <input type="checkbox"{{if .Checked}} checked{{else}} disabled{{end}}>
                    </div>
                    {{end}}
                </td>
            </tr>
            {{end}}
        </tbody>
            </table>
 
            <!-- Pagination Controls -->
            <div class="pagination-container">
                <div class="pagination-info">
                    <span id="paginationInfo">Showing 0-0 of 0 rows</span>
                </div>
                <div class="pagination-controls">
                    <button class="pagination-btn" id="firstPage" title="First Page">
                        <i class="bi bi-chevron-bar-left"></i>
                    </button>
                    <button class="pagination-btn" id="prevPage" title="Previous Page">
                        <i class="bi bi-chevron-left"></i>
                    </button>
                    <div id="pageNumbers" style="display: flex; gap: 5px;"></div>
                    <button class="pagination-btn" id="nextPage" title="Next Page">
                        <i class="bi bi-chevron-right"></i>
                    </button>
                    <button class="pagination-btn" id="lastPage" title="Last Page">
                        <i class="bi bi-chevron-bar-right"></i>
                    </button>
                </div>
                <div class="page-size-selector">
                    <label for="pageSize" style="color: #333333; font-size: 0.9em;">Rows per page:</label>
                    <select id="pageSize">
                        <option value="100" selected>100</option>
                        <option value="all">All</option>
                    </select>
                </div>
            </div>
        </div>
	
        <div style="margin-top: 30px;">
            <table class="metrics-table" style="margin-bottom: 0; width: 100%; border-bottom-left-radius: 0; border-bottom-right-radius: 0;">
                <tr style="background-color: #6b6b6b;">
                    <td style="padding: 12px 15px; font-weight: bold; color: #ffffff; font-size: 1.1em; font-family: 'Raleway', sans-serif;">
                        <div style="display: flex; justify-content: space-between; align-items: center;">
                            <span><i class="bi bi-map-fill" style="margin-right: 10px;"></i>Geographic Coverage Map</span>
                            <div class="mode-toggle-container" style="font-size: 1em;">
                                <div class="mode-toggle" id="mapModeToggle" onclick="switchMapMode()" tabindex="0" onkeydown="if(event.key==='Enter'||event.key===' '){event.preventDefault();switchMapMode();}" style="width: 170px;">
                                    <span class="mode-toggle-text active" id="mapModeBboxText">BBox</span>
                                    <div class="mode-toggle-slider" style="width: 81px;"></div>
                                    <span class="mode-toggle-text" id="mapModeH3Text">H3 Cells</span>
                                </div>
                            </div>
                        </div>
                    </td>
                </tr>
            </table>
            <div id="summaryMap" style="height: 480px; border: 1px solid #bbb; border-top: none; border-radius: 0 0 4px 4px;"></div>
        </div>
    </div>

    
    <div id="locationModal" class="modal" tabindex="-1">
        <div class="modal-content" tabindex="0">
            <h2>
                <i class="bi bi-geo-alt" style="margin-right: 10px;"></i>Location Matches
                <span class="close" tabindex="0">&times;</span>
            </h2>
            <div id="matchesList" class="matches-container"></div>
        </div>
    </div>

    <script>
        /**
         * Table cell indices for accessing column data
         * @type {Object<string, number>}
         */
        // Constants
        const CELL_INDEX = {
            CHECKBOX: 0,
            LINE: 1,
            STATUS: 2,
            IP_PREFIX: 3,
            COUNTRY: 4,
            REGION: 5,
            CITY: 6,
            MESSAGES: 7
        };
        
        /**
         * Timing delays for UI interactions (in milliseconds)
         * @type {Object<string, number>}
         */
        const TIMING = {
            MAP_INIT_DELAY: 100,
            MODAL_FOCUS_DELAY: 150,
            MAP_RESIZE_DELAY: 200
        };

        /**
         * Global state variables for modal and map management
         * @type {Object}
         */
        // Global state
        /** @type {Object<string, L.Map>} Maps indexed by mapId */
        let maps = {};
        
        /** @type {HTMLButtonElement|null} Currently active tune button reference */
        let currentTuningButton = null;
        
        /** @type {number} Currently selected match index in modal */
        let selectedMatchIndex = 0;
        
        /** @type {Array<HTMLElement>} Array of match item elements in modal */
        let matchItems = [];
        
        /** @type {boolean} Flag indicating if modal is currently open */
        let isModalOpen = false;

        
        let summaryMapInstance = null;
        let summaryLayerGroup = null;
        let currentMapMode = 'bbox';
        let summaryRowData = [];

        /**
         * Stores CSV comment lines by line number for download functionality
         * @type {Object<number, string>}
         */
        // Comment map: stores CSV comments by line number 
        const commentMap = {{.Comments}};

        // Modal elements
        const modal = document.getElementById('locationModal');
        const modalContent = document.querySelector('.modal-content');
        const closeBtn = document.querySelector('.close');

        // Modal functions
        closeBtn.onclick = function() {
            closeModal();
        };
        
        closeBtn.addEventListener('keydown', function(e) {
            if (e.key === 'Enter' || e.key === ' ') {
                e.preventDefault();
                closeModal();
            }
        });

        window.onclick = function(event) {
            if (event.target == modal) {
                closeModal();
            }
        };

        /**
         * Closes the location matches modal and cleans up resources
         * @returns {void}
         */
        function closeModal() {
            if (!isModalOpen) return;
            
            modal.style.display = 'none';
            isModalOpen = false;
            currentTuningButton = null;
            document.body.style.overflow = '';
            
            Object.values(maps).forEach(map => {
                if (map && map.remove) {
                    map.remove();
                }
            });
            maps = {};
            matchItems = [];
        }

        // Modal keyboard navigation
        modal.addEventListener('keydown', function(event) {
            if (!isModalOpen) return;
            
            if (event.key === 'Escape' || event.key === 'Esc') {
                event.preventDefault();
                closeModal();
                return;
            }
            
            if (event.key === 'Enter') {
                event.preventDefault();
                if (matchItems[selectedMatchIndex]) {
                    matchItems[selectedMatchIndex].click();
                }
                return;
            }
            
            if (event.key === 'ArrowLeft' || event.key === 'ArrowUp') {
                event.preventDefault();
                navigateMatches(-1);
                return;
            }
            
            if (event.key === 'ArrowRight' || event.key === 'ArrowDown') {
                event.preventDefault();
                navigateMatches(1);
                return;
            }
            
            if (event.key === '+' || event.key === '=') {
                event.preventDefault();
                zoomCurrentMap(1);
                return;
            }
            
            if (event.key === '-' || event.key === '_') {
                event.preventDefault();
                zoomCurrentMap(-1);
                return;
            }
        });

        document.addEventListener('keydown', function(event) {
            if ((event.key === 'Escape' || event.key === 'Esc') && isModalOpen) {
                event.preventDefault();
                closeModal();
            }
        });

        /**
         * Navigates between location match items with arrow keys
         * @param {number} direction - Navigate direction: positive (right/down) or negative (left/up)
         * @returns {void}
         */
        function navigateMatches(direction) {
            if (matchItems.length === 0) return;
            
            if (matchItems[selectedMatchIndex]) {
                matchItems[selectedMatchIndex].classList.remove('selected');
            }
            
            selectedMatchIndex = (selectedMatchIndex + direction + matchItems.length) % matchItems.length;
            
            if (matchItems[selectedMatchIndex]) {
                matchItems[selectedMatchIndex].classList.add('selected');
                matchItems[selectedMatchIndex].scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'center' });
            }
        }

        /**
         * Zooms the currently selected map in or out
         * @param {number} direction - Zoom direction: positive to zoom in, negative to zoom out
         * @returns {void}
         */
        function zoomCurrentMap(direction) {
            const mapId = `map-${selectedMatchIndex}`;
            const map = maps[mapId];
            if (map) {
                const currentZoom = map.getZoom();
                map.setZoom(currentZoom + direction);
            }
        }

        /**
         * API endpoint for place search functionality
         * @type {string}
         */
        const API_ENDPOINT = 'https://mcp.fastah.ai/rest/geofeeds/place-search';

        /**
         * Fetches geolocation matches from the API for given location data
         * @async
         * @param {Array<Object>} rows - Array of row objects with location data (countryCode, regionCode, cityName)
         * @returns {Promise<{results: Array<Object>}|null>} API response with matches or null on error
         */
        async function fetchPlaceMatches(rows) {
            const MAX_BATCH_SIZE = 1000;
            let allResults = [];
            let batchCount = Math.ceil(rows.length / MAX_BATCH_SIZE);
            let lastError = null;
            
            for (let i = 0; i < batchCount; i++) {
                const batchRows = rows.slice(i * MAX_BATCH_SIZE, (i + 1) * MAX_BATCH_SIZE);
                try {
                    const response = await fetch(API_ENDPOINT, {
                        method: 'POST',
                        headers: {
                            'Accept': 'application/json',
                            'Content-Type': 'application/json'
                        },
                        body: JSON.stringify({ rows: batchRows })
                    });
                    
                    if (!response.ok) {
                        throw new Error(`API request failed: ${response.status} ${response.statusText}`);
                    }
                    
                    const data = await response.json();
                    if (data && data.results) {
                        allResults = allResults.concat(data.results);
                    } else {
                        lastError = 'No results in API response';
                    }
                } catch (error) {
                    console.error('Error calling place search API:', error);
                    lastError = error.message;
                }
            }
            
            if (allResults.length === 0 && lastError) {
                alert(`Failed to fetch location data: ${lastError}`);
                return null;
            }
            
            return { results: allResults };
        }

        /**
         * Prepares geofeed row data for API request
         * @param {HTMLTableRowElement} dataRow - The table row element to extract data from
         * @returns {Object|null} Object with countryCode, regionCode, cityName, maxResults and searchMode, or null if required data missing
         */
        function prepareRowForApi(dataRow) {
            const cells = dataRow.querySelectorAll('td');
            if (cells.length <= CELL_INDEX.STATUS) return null;

            const countryCode = cells[CELL_INDEX.COUNTRY].textContent.trim();
            const regionCode = cells[CELL_INDEX.REGION].textContent.trim();
            const cityName = cells[CELL_INDEX.CITY].textContent.trim();

            if (!countryCode) {
                return null;
            }

            return {
                rowKey: crypto.randomUUID(),
                countryCode: countryCode,
                regionCode: regionCode || '',
                cityName: cityName || '',
                searchMode: 'auto'
            };
        }

        /**
         * Handles tune button click - fetches location matches for selected issues
         * @async
         * @param {HTMLButtonElement} button - The tune button that was clicked
         * @returns {Promise<void>}
         */
        async function handleTuneButtonClick(button) {
            currentTuningButton = button;
            
            const issuesContainer = button.closest('td');
            const checkedBoxes = issuesContainer.querySelectorAll('.message-line input[type="checkbox"]:checked');
            
            const selectedIssues = Array.from(checkedBoxes).map(cb => {
                const messageLine = cb.closest('.message-line');
                return {
                    id: messageLine.getAttribute('data-id'),
                    message: messageLine.querySelector('span').textContent
                };
            });
            
            if (selectedIssues.length === 0) {
                alert('Please select at least one issue to tune.');
                return;
            }
            
            const detailsRow = button.closest('tr');
            
            // Find the data row - it's before the expand-details-rows
            let dataRow = detailsRow.previousElementSibling;
            while (dataRow && dataRow.classList.contains('expand-details-row')) {
                dataRow = dataRow.previousElementSibling;
            }
            
            if (!dataRow || !dataRow.classList.contains('expandable-row')) {
                console.error('Could not find data row');
                return;
            }
            
            const rowData = prepareRowForApi(dataRow);
            if (!rowData) {
                alert('Missing required location data for API call');
                return;
            }
            
            const apiResponse = await fetchPlaceMatches([rowData]);
            
            if (!apiResponse || !apiResponse.results || apiResponse.results.length === 0) {
                alert('No results returned from API');
                return;
            }
            
            const result = apiResponse.results[0];
            
            if (result.matches && result.matches.length > 0) {
                showLocationPopup(result.matches, selectedIssues);
            } else {
                alert('No location matches found for this entry');
            }
        }

        /**
         * Displays the location matches popup modal with maps and selectable options
         * @param {Array<Object>} matches - Array of location match objects from API
         * @param {Array<Object>} [selectedIssues=[]] - Array of selected issues for tracking
         * @returns {void}
         */
        function showLocationPopup(matches, selectedIssues = []) {
            if (!matches || matches.length === 0) {
                return;
            }
            
            const matchesList = document.getElementById('matchesList');
            matchesList.innerHTML = '';
            
            maps = {};
            matchItems = [];
            selectedMatchIndex = 0;

            matches.forEach((match, index) => {
                const matchItem = document.createElement('div');
                matchItem.className = 'match-item';
                if (index === 0) {
                    matchItem.classList.add('selected');
                }
                const mapId = `map-${index}`;
                
                // Determine primary location text: city > region > country
                let primaryLocation = match.placeName;
                let locationType = 'city';
                
                if (!primaryLocation || primaryLocation.trim() === '') {
                    primaryLocation = match.stateCode;
                    locationType = 'region';
                }
                
                if (!primaryLocation || primaryLocation.trim() === '') {
                    primaryLocation = match.countryCode;
                    locationType = 'country';
                }
                
                // Build secondary info (show what's available)
                let secondaryInfo = [];
                if (match.placeName && locationType !== 'city') {
                    secondaryInfo.push(`City: ${escapeHtml(match.placeName)}`);
                }
                if (match.stateCode && locationType !== 'region') {
                    secondaryInfo.push(`Region: ${escapeHtml(match.stateCode)}`);
                }
                if (match.countryCode && locationType !== 'country') {
                    secondaryInfo.push(`Country: ${escapeHtml(match.countryCode)}`);
                }
                    
                matchItem.innerHTML = `
                    <div id="${mapId}" class="match-map"></div>
                    <div class="match-info-container">
                        <div class="match-header">${escapeHtml(primaryLocation)}</div>
                        ${secondaryInfo.length > 0 ? `<div class="location-details">${secondaryInfo.join(' \u2022 ')}</div>` : ''}
                    </div>
                `;
                
                matchItem.addEventListener('click', function() {
                    selectLocationMatch(match);
                });
                
                matchesList.appendChild(matchItem);
                matchItems.push(matchItem);
                
                setTimeout(() => {
                    initializeMapForMatch(mapId, match);
                }, TIMING.MAP_INIT_DELAY);
            });

            modal.style.display = 'block';
            isModalOpen = true;
            document.body.style.overflow = 'hidden';
            
            setTimeout(() => {
                if (modalContent) {
                    modalContent.focus();
                }
            }, TIMING.MODAL_FOCUS_DELAY);
        }

        /**
         * Selects a location match and updates the corresponding table row
         * @param {Object} match - Match object with countryCode, regionCode, name, and boundingBox
         * @returns {void}
         */
        function selectLocationMatch(match) {
            if (!currentTuningButton) {
                console.error('No tuning button reference found');
                return;
            }
            
            const detailsRow = currentTuningButton.closest('tr');
            
            // Find the data row - it's before the expand-details-rows
            let dataRow = detailsRow.previousElementSibling;
            while (dataRow && dataRow.classList.contains('expand-details-row')) {
                dataRow = dataRow.previousElementSibling;
            }
            
            if (!dataRow || !dataRow.classList.contains('expandable-row')) {
                console.error('Could not find data row');
                return;
            }
            
            updateRowWithMatchData(dataRow, match);
            closeModal();
        }
        
        /**
         * Updates a table row with selected match data and stores original values
         * @param {HTMLTableRowElement} dataRow - The table row to update
         * @param {Object} match - Match object with countryCode, regionCode, and name
         * @returns {void}
         */
        function updateRowWithMatchData(dataRow, match) {
            const cells = dataRow.querySelectorAll('td');
            const currentCountry = cells[CELL_INDEX.COUNTRY].textContent;
            const currentRegion = cells[CELL_INDEX.REGION].textContent;
            const currentCity = cells[CELL_INDEX.CITY].textContent;
            
            if (!dataRow.hasAttribute('data-original-country')) {
                dataRow.setAttribute('data-original-country', currentCountry);
                dataRow.setAttribute('data-original-region', currentRegion);
                dataRow.setAttribute('data-original-city', currentCity);
            }
            
            cells[CELL_INDEX.COUNTRY].textContent = match.countryCode;
            cells[CELL_INDEX.REGION].textContent = match.stateCode;
            cells[CELL_INDEX.CITY].textContent = match.placeName;
            
            // Find the previous values row (it's the first expand-details-row after the data row)
            const previousValuesRow = dataRow.nextElementSibling;
            if (previousValuesRow && previousValuesRow.classList.contains('previous-values-row')) {
                previousValuesRow.classList.add('show');
                
                const defaultCountry = previousValuesRow.querySelector('.default-country');
                const defaultRegion = previousValuesRow.querySelector('.default-region');
                const defaultCity = previousValuesRow.querySelector('.default-city');
                
                if (defaultCountry) defaultCountry.textContent = dataRow.getAttribute('data-original-country');
                if (defaultRegion) defaultRegion.textContent = dataRow.getAttribute('data-original-region');
                if (defaultCity) defaultCity.textContent = dataRow.getAttribute('data-original-city');
            }
        }

        /**
         * Initializes a Leaflet map with location boundary and marker for a match
         * @param {string} mapId - The DOM element ID where map will render
         * @param {Object} match - Match object with name, boundingBox, regionCode, countryCode
         * @returns {void}
         */
        function initializeMapForMatch(mapId, match) {
            try {
                const mapInstance = L.map(mapId, {
                    scrollWheelZoom: false,
                    dragging: true,
                    zoomControl: true
                });
                
                L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
                    attribution: '© OpenStreetMap',
                    maxZoom: 19
                }).addTo(mapInstance);

                const bbox = match.boundingBox;
                
                if (bbox && bbox.length === 4) {
                    const [west, north, east, south] = bbox;
                    const centerLat = (south + north) / 2;
                    const centerLng = (west + east) / 2;
                    
                    const isPoint = (west === east && north === south);
                    
                    if (isPoint) {
                        const offset = 0.05;
                        const bounds = [
                            [south - offset, west - offset], 
                            [north + offset, east + offset]  
                        ];
                        
                        L.rectangle(bounds, {
                            color: '#dc3545',
                            weight: 3,
                            opacity: 1,
                            fillColor: '#dc3545',
                            fillOpacity: 0.2
                        }).addTo(mapInstance);
                        
                        mapInstance.fitBounds(bounds, { padding: [30, 30] });
                    } else {
                        const bounds = [
                            [south, west], 
                            [north, east]  
                        ];

                        L.rectangle(bounds, {
                            color: '#dc3545',
                            weight: 3,
                            opacity: 1,
                            fillColor: '#dc3545',
                            fillOpacity: 0.2
                        }).addTo(mapInstance);
                        
                        mapInstance.fitBounds(bounds, { padding: [20, 20] });
                    }

                    L.marker([centerLat, centerLng]).addTo(mapInstance)
                        .bindPopup(`<b>${escapeHtml(match.placeName)}</b><br>${escapeHtml(match.stateCode)}`);
                    
                    maps[mapId] = mapInstance;
                    
                    setTimeout(() => {
                        mapInstance.invalidateSize();
                    }, TIMING.MAP_RESIZE_DELAY);
                }
            } catch (error) {
                console.error('Error initializing map:', error);
            }
        }

        /**
         * Sets the report mode between 'viewing' and 'tuning'
         * Tuning mode shows checkboxes and expands rows with issues
         * @param {string} mode - 'viewing' or 'tuning'
         * @returns {void}
         */
        function setMode(mode) {
            const toggle = document.getElementById('modeToggle');
            const viewingText = document.getElementById('viewingText');
            const tuningText = document.getElementById('tuningText');
            
            if (mode === 'tuning') {
                document.body.classList.add('tuning-mode');
                toggle.classList.add('tuning');
                viewingText.classList.remove('active');
                tuningText.classList.add('active');
                
                // Only expand rows with issues
                const expandableRows = document.querySelectorAll('.expandable-row');
                expandableRows.forEach(row => {
                    const hasError = row.getAttribute('data-has-error') === 'true';
                    const hasWarning = row.getAttribute('data-has-warning') === 'true';
                    const hasSuggestion = row.getAttribute('data-has-suggestion') === 'true';
                    
                    if (hasError || hasWarning || hasSuggestion) {
                        let detailRow = row.nextElementSibling;
                        while (detailRow && detailRow.classList.contains('expand-details-row')) {
                            // Only show issues-row, not previous-values-row
                            if (detailRow.classList.contains('issues-row')) {
                                detailRow.classList.add('show');
                            }
                            detailRow = detailRow.nextElementSibling;
                        }
                    }
                });
            } else {
                document.body.classList.remove('tuning-mode');
                toggle.classList.remove('tuning');
                viewingText.classList.add('active');
                tuningText.classList.remove('active');
                document.querySelectorAll('.expand-details-row').forEach(row => {
                    row.classList.remove('show');
                });
            }
            
            // Refresh pagination after mode change
            updatePagination();
        }

        /**
         * Toggles between viewing and tuning modes
         * @returns {void}
         */
        function toggleMode() {
            const isTuning = document.body.classList.contains('tuning-mode');
            setMode(isTuning ? 'viewing' : 'tuning');
        }

        /**
         * Escapes HTML special characters to prevent XSS attacks
         * @param {string} text - Text to escape
         * @returns {string} Escaped HTML text
         */
        function escapeHtml(text) {
            const map = {
                '&': '&amp;',
                '<': '&lt;',
                '>': '&gt;',
                '"': '&quot;',
                "'": '&#039;'
            };
            return text.replace(/[&<>"']/g, m => map[m]);
        }
        
        /**
         * Filters the entries table based on current filter input values
         * Resets to page 1 and updates pagination
         * @returns {void}
         */
        function filterTable() {
            const countryFilter = document.getElementById('filterCountry').value.toLowerCase();
            const regionFilter = document.getElementById('filterRegion').value.toLowerCase();
            const cityFilter = document.getElementById('filterCity').value.toLowerCase();
            const statusFilter = document.getElementById('filterStatus').value;
            const ipPrefixFilter = document.getElementById('filterIpPrefix').value.toLowerCase();
            
            const allExpandableRows = document.querySelectorAll('.expandable-row');
            
            // Filter rows and update allRows array
            allRows = Array.from(allExpandableRows).filter(row => {
                const cells = row.querySelectorAll('td');
                if (cells.length <= CELL_INDEX.STATUS) return false;
                
                const rowData = {
                    country: cells[CELL_INDEX.COUNTRY].textContent.toLowerCase(),
                    region: cells[CELL_INDEX.REGION].textContent.toLowerCase(),
                    city: cells[CELL_INDEX.CITY].textContent.toLowerCase(),
                    status: cells[CELL_INDEX.STATUS].textContent,
                    ipPrefix: cells[CELL_INDEX.IP_PREFIX].textContent.toLowerCase()
                };

                return (
                    (!countryFilter || rowData.country.includes(countryFilter)) &&
                    (!regionFilter || rowData.region.includes(regionFilter)) &&
                    (!cityFilter || rowData.city.includes(cityFilter)) &&
                    (!statusFilter || rowData.status === statusFilter) &&
                    (!ipPrefixFilter || rowData.ipPrefix.includes(ipPrefixFilter))
                );
            });
            
            // Reset to page 1 and update pagination
            currentPage = 1;
            updatePagination();
        }
        
        /**
         * Pagination state and configuration
         * @type {Object}
         */
        // Pagination state
        /** @type {number} Current page number (1-indexed) */
        let currentPage = 1;
        
        /** @type {number} Number of rows to display per page */
        let rowsPerPage = 100;
        
        /** @type {Array<HTMLTableRowElement>} Array of expandable rows after filtering */
        let allRows = [];
        
        // Initialize on page load
        window.addEventListener('DOMContentLoaded', function() {
            // Initialize pagination
            allRows = Array.from(document.querySelectorAll('.expandable-row'));
            initializePagination();
            
            // Setup select all checkbox
            const selectAllCheckbox = document.getElementById('selectAll');
            const rowCheckboxes = document.querySelectorAll('.row-checkbox');
            
            selectAllCheckbox.addEventListener('change', function() {
                rowCheckboxes.forEach(checkbox => {
                    checkbox.checked = selectAllCheckbox.checked;
                });
            });
            
            // Update select all checkbox when individual checkboxes change
            rowCheckboxes.forEach(checkbox => {
                checkbox.addEventListener('click', function(e) {
                    e.stopPropagation(); // Prevent row expansion when clicking checkbox
                });
                checkbox.addEventListener('keydown', function(e) {
                    if (e.key === ' ' || e.key === 'Enter') {
                        e.stopPropagation(); // Prevent row expansion when using keyboard on checkbox
                    }
                });
                checkbox.addEventListener('change', function(e) {
                    e.stopPropagation(); // Prevent row expansion when clicking checkbox
                    const allChecked = Array.from(rowCheckboxes).every(cb => cb.checked);
                    const noneChecked = Array.from(rowCheckboxes).every(cb => !cb.checked);
                    selectAllCheckbox.checked = allChecked;
                    selectAllCheckbox.indeterminate = !allChecked && !noneChecked;
                });
            });
            
            // Setup expandable rows
            const expandableRows = document.querySelectorAll('.expandable-row');
            expandableRows.forEach(row => {
                const hasError = row.getAttribute('data-has-error') === 'true';
                const hasWarning = row.getAttribute('data-has-warning') === 'true';
                const hasSuggestion = row.getAttribute('data-has-suggestion') === 'true';
                
                // Only make rows with issues focusable
                if (hasError || hasWarning || hasSuggestion) {
                    row.setAttribute('tabindex', '0');
                    
                    // Click handler
                    row.addEventListener('click', function(e) {
                        // Don't expand if clicking on checkbox
                        if (e.target.classList.contains('row-checkbox')) {
                            return;
                        }
                        let detailRow = this.nextElementSibling;
                        let expanding = false;
                        // First pass: check if we are expanding (issues-row will be shown)
                        while (detailRow && detailRow.classList.contains('expand-details-row')) {
                            if (detailRow.classList.contains('issues-row')) {
                                expanding = !detailRow.classList.contains('show');
                                break;
                            }
                            detailRow = detailRow.nextElementSibling;
                        }
                        detailRow = this.nextElementSibling;
                        while (detailRow && detailRow.classList.contains('expand-details-row')) {
                            if (detailRow.classList.contains('issues-row')) {
                                // Always toggle issues-row
                                detailRow.classList.toggle('show');
                            } else if (detailRow.classList.contains('previous-values-row')) {
                                if (expanding) {
                                    // Show previous-values-row if this row has original data (was tuned)
                                    if (row.hasAttribute('data-original-country') || 
                                        row.hasAttribute('data-original-region') || 
                                        row.hasAttribute('data-original-city')) {
                                        detailRow.classList.add('show');
                                    }
                                } else {
                                    // If collapsing, hide previous-values-row
                                    detailRow.classList.remove('show');
                                }
                            }
                            detailRow = detailRow.nextElementSibling;
                        }
                        // Refresh pagination to show/hide the toggled detail rows
                        updatePagination();
                    });
                    
                    // Keyboard handler
                    row.addEventListener('keydown', function(e) {
                        // Don't handle if focus is on checkbox
                        if (document.activeElement.classList.contains('row-checkbox')) {
                            return;
                        }
                        if (e.key === 'Enter' || e.key === ' ') {
                            e.preventDefault();
                            let detailRow = this.nextElementSibling;
                            let expanding = false;
                            // First pass: check if we are expanding (issues-row will be shown)
                            while (detailRow && detailRow.classList.contains('expand-details-row')) {
                                if (detailRow.classList.contains('issues-row')) {
                                    expanding = !detailRow.classList.contains('show');
                                    break;
                                }
                                detailRow = detailRow.nextElementSibling;
                            }
                            detailRow = this.nextElementSibling;
                            while (detailRow && detailRow.classList.contains('expand-details-row')) {
                                if (detailRow.classList.contains('issues-row')) {
                                    // Always toggle issues-row
                                    detailRow.classList.toggle('show');
                                } else if (detailRow.classList.contains('previous-values-row')) {
                                    if (expanding) {
                                        // Show previous-values-row if this row has original data (was tuned)
                                        if (row.hasAttribute('data-original-country') || 
                                            row.hasAttribute('data-original-region') || 
                                            row.hasAttribute('data-original-city')) {
                                            detailRow.classList.add('show');
                                        }
                                    } else {
                                        // If collapsing, hide previous-values-row
                                        detailRow.classList.remove('show');
                                    }
                                }
                                detailRow = detailRow.nextElementSibling;
                            }
                            // Refresh pagination to show/hide the toggled detail rows
                            updatePagination();
                        }
                    });
                } else {
                    row.style.cursor = 'default';
                }
            });
            
            // Toggle Expand All button
            let isExpanded = false;
            document.getElementById('toggleExpandAll').addEventListener('click', function() {
                const expandableRows = document.querySelectorAll('.expandable-row');
                const button = document.getElementById('toggleExpandAll');
                
                if (!isExpanded) {
                    // Show all
                    expandableRows.forEach(row => {
                        const checkbox = row.querySelector('.row-checkbox');
                        if (!checkbox || !checkbox.checked) return; // Only work on checked rows
                        
                        const hasError = row.getAttribute('data-has-error') === 'true';
                        const hasWarning = row.getAttribute('data-has-warning') === 'true';
                        const hasSuggestion = row.getAttribute('data-has-suggestion') === 'true';
                        
                        // Only expand rows with issues
                        if (hasError || hasWarning || hasSuggestion) {
                            let detailRow = row.nextElementSibling;
                            while (detailRow && detailRow.classList.contains('expand-details-row')) {
                                // Show issues-row always
                                if (detailRow.classList.contains('issues-row')) {
                                    detailRow.classList.add('show');
                                } else if (detailRow.classList.contains('previous-values-row')) {
                                    // Show previous-values-row if this row has original data (was tuned)
                                    if (row.hasAttribute('data-original-country') || 
                                        row.hasAttribute('data-original-region') || 
                                        row.hasAttribute('data-original-city')) {
                                        detailRow.classList.add('show');
                                    }
                                }
                                detailRow = detailRow.nextElementSibling;
                            }
                        }
                    });
                    button.innerHTML = '<i class="bi bi-arrows-collapse" style="margin-right: 5px;"></i>Collapse Rows';
                    isExpanded = true;
                } else {
                    // Collapse all
                    expandableRows.forEach(row => {
                        const checkbox = row.querySelector('.row-checkbox');
                        if (!checkbox || !checkbox.checked) return; // Only work on checked rows
                        
                        let detailRow = row.nextElementSibling;
                        while (detailRow && detailRow.classList.contains('expand-details-row')) {
                            detailRow.classList.remove('show');
                            detailRow = detailRow.nextElementSibling;
                        }
                    });
                    button.innerHTML = '<i class="bi bi-arrows-expand" style="margin-right: 5px;"></i>Expand Rows';
                    isExpanded = false;
                }
                
                // Refresh pagination to show expanded rows correctly
                updatePagination();
            });
            
            // Toggle Filters button
            document.getElementById('toggleFilters').addEventListener('click', function() {
                const filterRow = document.getElementById('filterRow');
                const button = document.getElementById('toggleFilters');
                
                if (filterRow.style.display === 'none') {
                    filterRow.style.display = '';
                    button.innerHTML = '<i class="bi bi-funnel-fill" style="margin-right: 5px;"></i>Hide Filters';
                } else {
                    filterRow.style.display = 'none';
                    button.innerHTML = '<i class="bi bi-funnel-fill" style="margin-right: 5px;"></i>Show Filters';
                }
            });
            
            // Tune All button
            document.getElementById('tuneAll').addEventListener('click', async function() {
                const expandableRows = document.querySelectorAll('.expandable-row');
                
                let updatedCount = 0;
                let skippedCount = 0;
                
                expandableRows.forEach(dataRow => {
                    const checkbox = dataRow.querySelector('.row-checkbox');
                    if (!checkbox || !checkbox.checked) {
                        skippedCount++;
                        return; // Only work on checked rows
                    }
                    
                    const isTunable = dataRow.getAttribute('data-tunable') === 'true';
                    if (!isTunable) {
                        skippedCount++;
                        return;
                    }
                    
                    const tunedCountry = dataRow.getAttribute('data-tuned-country');
                    const tunedRegion = dataRow.getAttribute('data-tuned-region');
                    const tunedCity = dataRow.getAttribute('data-tuned-city');
                    
                    if (!tunedCountry && !tunedRegion && !tunedCity) {
                        skippedCount++;
                        return;
                    }
                    
                    const cells = dataRow.querySelectorAll('td');
                    if (cells.length <= CELL_INDEX.STATUS) {
                        skippedCount++;
                        return;
                    }
                    
                    if (!dataRow.hasAttribute('data-original-country')) {
                        dataRow.setAttribute('data-original-country', cells[CELL_INDEX.COUNTRY].textContent);
                        dataRow.setAttribute('data-original-region', cells[CELL_INDEX.REGION].textContent);
                        dataRow.setAttribute('data-original-city', cells[CELL_INDEX.CITY].textContent);
                    }
                    
                    if (tunedCountry) cells[CELL_INDEX.COUNTRY].textContent = tunedCountry;
                    if (tunedRegion) cells[CELL_INDEX.REGION].textContent = tunedRegion;
                    if (tunedCity) cells[CELL_INDEX.CITY].textContent = tunedCity;
                    
                    // Find the previous values row
                    const previousValuesRow = dataRow.nextElementSibling;
                    if (previousValuesRow && previousValuesRow.classList.contains('previous-values-row')) {
                        previousValuesRow.classList.add('show');
                        
                        const defaultCountry = previousValuesRow.querySelector('.default-country');
                        const defaultRegion = previousValuesRow.querySelector('.default-region');
                        const defaultCity = previousValuesRow.querySelector('.default-city');
                        
                        if (defaultCountry) defaultCountry.textContent = dataRow.getAttribute('data-original-country');
                        if (defaultRegion) defaultRegion.textContent = dataRow.getAttribute('data-original-region');
                        if (defaultCity) defaultCity.textContent = dataRow.getAttribute('data-original-city');
                    }
                    
                    updatedCount++;
                });
                
                // Refresh pagination to update displayed values
                updatePagination();
                
                alert(`Successfully updated ${updatedCount} rows (${skippedCount} rows skipped - not tunable or no tuned values available)`);
            });
            
            // Add filter event listeners
            document.getElementById('filterCountry').addEventListener('input', filterTable);
            document.getElementById('filterRegion').addEventListener('input', filterTable);
            document.getElementById('filterCity').addEventListener('input', filterTable);
            document.getElementById('filterStatus').addEventListener('change', filterTable);
            document.getElementById('filterIpPrefix').addEventListener('input', filterTable);
            
            // Reset filters button
            document.getElementById('resetFilters').addEventListener('click', function() {
                document.getElementById('filterCountry').value = '';
                document.getElementById('filterRegion').value = '';
                document.getElementById('filterCity').value = '';
                document.getElementById('filterStatus').value = '';
                document.getElementById('filterIpPrefix').value = '';
                
                // Reset to all rows and update pagination
                allRows = Array.from(document.querySelectorAll('.expandable-row'));
                currentPage = 1;
                updatePagination();
            });
            
            // Download CSV button
            document.getElementById('downloadCSV').addEventListener('click', function() {
                const rows = document.querySelectorAll('.expandable-row');
                const rowDataMap = {};
                
                // Collect all data rows with their line numbers
                rows.forEach(row => {
                    const cells = row.querySelectorAll('td');
                    if (cells.length > CELL_INDEX.CITY) {
                        const lineNum = parseInt(cells[CELL_INDEX.LINE].textContent.trim());
                        const ipPrefix = cells[CELL_INDEX.IP_PREFIX].textContent.trim();
                        const country = cells[CELL_INDEX.COUNTRY].textContent.trim();
                        const region = cells[CELL_INDEX.REGION].textContent.trim();
                        const city = cells[CELL_INDEX.CITY].textContent.trim();
                        
                        rowDataMap[lineNum] = [ipPrefix, country, region, city];
                    }
                });
                
                // Build CSV content with comments in the correct positions
                const csvLines = [];
                
                // Find max line number
                const allLineNumbers = Object.keys(rowDataMap).map(n => parseInt(n));
                const maxLine = Math.max(...allLineNumbers);
                
                // Iterate through all line numbers from 1 to maxLine
                for (let lineNum = 1; lineNum <= maxLine; lineNum++) {
                    if (lineNum in commentMap) {
                        // This line is a comment
                        csvLines.push(commentMap[lineNum]);
                    } else if (rowDataMap[lineNum]) {
                        // This line is a data row
                        const row = rowDataMap[lineNum];
                        const csvRow = row.map(cell => {
                            // Escape quotes and wrap in quotes if contains comma, quote, or newline
                            const cellStr = String(cell);
                            if (cellStr.includes(',') || cellStr.includes('"') || cellStr.includes('\n')) {
                                return '"' + cellStr.replace(/"/g, '""') + '"';
                            }
                            return cellStr;
                        }).join(',');
                        csvLines.push(csvRow);
                    }
                    // If neither comment nor data, skip the line (maintains original CSV structure)
                }
                
                const csvContent = csvLines.join('\n');
                
                // Create download link
                const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
                const link = document.createElement('a');
                const url = URL.createObjectURL(blob);
                
                // Get filename from inputFileMetrics, ensure it has .csv extension
                let filename =
                    document.getElementById('inputFileMetrics')
                        .textContent
                        .trim()
                        .split(/[/\\]/)
                        .pop() || 'geofeed_report.csv';
                if (!filename.toLowerCase().endsWith('.csv')) {
                    filename += '.csv';
                }
                
                link.setAttribute('href', url);
                link.setAttribute('download', filename);
                link.style.visibility = 'hidden';
                document.body.appendChild(link);
                link.click();
                document.body.removeChild(link);
            });
            
            // Pagination event listeners
            document.getElementById('pageSize').addEventListener('change', function() {
                const value = this.value;
                if (value === 'all') {
                    rowsPerPage = allRows.length;
                } else {
                    rowsPerPage = parseInt(value);
                }
                currentPage = 1;
                updatePagination();
            });
            
            document.getElementById('firstPage').addEventListener('click', function() {
                currentPage = 1;
                updatePagination();
            });
            
            document.getElementById('prevPage').addEventListener('click', function() {
                if (currentPage > 1) {
                    currentPage--;
                    updatePagination();
                }
            });
            
            document.getElementById('nextPage').addEventListener('click', function() {
                const totalPages = Math.ceil(allRows.length / rowsPerPage);
                if (currentPage < totalPages) {
                    currentPage++;
                    updatePagination();
                }
            });
            
            document.getElementById('lastPage').addEventListener('click', function() {
                currentPage = Math.ceil(allRows.length / rowsPerPage);
                updatePagination();
            });
            
            initSummaryMap();
        });
        
        /**
         * Initializes pagination on page load
         * @returns {void}
         */
        function initializePagination() {
            updatePagination();
        }
        
        
        function initSummaryMap() {
            try {
                const summaryMapEl = document.getElementById('summaryMap');
                if (!summaryMapEl) return;
                summaryMapInstance = L.map('summaryMap', {
                    scrollWheelZoom: true,
                    dragging: true,
                    zoomControl: true,
                    center: [20, 0],
                    zoom: 2
                });
                L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
                    attribution: '\u00a9 <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
                    maxZoom: 19
                }).addTo(summaryMapInstance);
                summaryLayerGroup = L.layerGroup().addTo(summaryMapInstance);
                // Parse and cache all row data once
                summaryRowData = [];
                const expandableRows = document.querySelectorAll('.expandable-row');
                expandableRows.forEach(row => {
                    const cells        = row.querySelectorAll('td');
                    const hasError     = row.getAttribute('data-has-error')      === 'true';
                    const hasWarning   = row.getAttribute('data-has-warning')    === 'true';
                    const hasSugg      = row.getAttribute('data-has-suggestion') === 'true';
                    let color = '#28a745';
                    if (hasError)        color = '#dc3545';
                    else if (hasWarning) color = '#fd7e14';
                    else if (hasSugg)    color = '#17a2b8';
                    const country  = cells[CELL_INDEX.COUNTRY]   ? cells[CELL_INDEX.COUNTRY].textContent.trim()   : '';
                    const region   = cells[CELL_INDEX.REGION]    ? cells[CELL_INDEX.REGION].textContent.trim()    : '';
                    const city     = cells[CELL_INDEX.CITY]      ? cells[CELL_INDEX.CITY].textContent.trim()      : '';
                    const parts    = [];
                    if (city && region && country) {
                        parts.push('<b>' + escapeHtml(city) + '</b>' + ' &bull; ' + escapeHtml(region) + ' &bull; ' + escapeHtml(country));
                    } else if (city && country) {
                        parts.push('<b>' + escapeHtml(city) + '</b>' + ' &bull; ' + escapeHtml(country));
                    } else if (region && country) {
                        parts.push('<b>' + escapeHtml(region) + '</b>' + ' &bull; ' + escapeHtml(country));
                    } else if (country)  {
                        parts.push('<b>' + escapeHtml(country) + '</b>');
                    }                               
                    summaryRowData.push({
                        rowId:    row.id,
                        bbox:     row.getAttribute('data-bounding-box') || '',
                        h3cells:  row.getAttribute('data-h3-cells')     || '',
                        color:    color,
                        popup:    parts.join('<br>')
                    });
                });
                setTimeout(() => {
                    summaryMapInstance.invalidateSize();
                    switchMapMode('bbox');
                }, 350);
            } catch (err) {
                console.error('initSummaryMap error:', err);
            }
        }
        
        function switchMapMode(mode) {
            const toggle = document.getElementById('mapModeToggle');
            const bboxText = document.getElementById('mapModeBboxText');
            const h3Text   = document.getElementById('mapModeH3Text');
            // If called with no argument, act as a toggle
            if (mode === undefined) {
                mode = currentMapMode === 'bbox' ? 'h3' : 'bbox';
            }
            if (mode === 'h3') {
                toggle.classList.add('h3mode');
                bboxText.classList.remove('active');
                h3Text.classList.add('active');
            } else {
                toggle.classList.remove('h3mode');
                bboxText.classList.add('active');
                h3Text.classList.remove('active');
            }
            renderSummaryMapLayers(mode);
        }
        
        function renderSummaryMapLayers(mode) {
            if (!summaryMapInstance || !summaryLayerGroup) return;
            currentMapMode = mode;
            summaryLayerGroup.clearLayers();

            // Determine which row IDs are visible on the current page
            const startIndex = (currentPage - 1) * rowsPerPage;
            const endIndex = Math.min(startIndex + rowsPerPage, allRows.length);
            const visibleIds = new Set();
            for (let i = startIndex; i < endIndex; i++) {
                visibleIds.add(allRows[i].id);
            }

            const tempBoundsLayers = [];
            const seenBboxKeys = new Set(); // deduplicate identical bounding boxes / h3 cell sets

            summaryRowData.forEach(data => {
                // Only render entries whose row is on the current page
                if (!visibleIds.has(data.rowId)) return;

                if (mode === 'bbox') {
                    const raw = data.bbox.replace(/[\[\]]/g, '').trim();
                    const pts = raw.split(/[\s,]+/).map(Number);
                    if (pts.length !== 4 || pts.some(v => isNaN(v))) return;

                    // Skip duplicate bounding boxes
                    const bboxKey = pts.join(',');
                    if (seenBboxKeys.has(bboxKey)) return;
                    seenBboxKeys.add(bboxKey);

                    const [west, north, east, south] = pts;
                    const lb = [[south, west], [north, east]];
                    const rect = L.rectangle(lb, {
                        color: data.color, weight: 3, opacity: 1,
                        fillColor: data.color, fillOpacity: 0.35
                    });
                    rect.bindPopup(data.popup);
                    summaryLayerGroup.addLayer(rect);
                    const cm = L.circleMarker([(south + north) / 2, (west + east) / 2], {
                        radius: 5, color: data.color, fillColor: data.color, fillOpacity: 0.9, weight: 1
                    });
                    cm.bindPopup(data.popup);
                    summaryLayerGroup.addLayer(cm);
                    tempBoundsLayers.push(L.rectangle(lb));
                } else {
                    // H3 cells mode
                    const raw = data.h3cells.replace(/[\[\]]/g, '').trim();
                    if (!raw) return;

                    // Skip duplicate h3 cell sets
                    if (seenBboxKeys.has(raw)) return;
                    seenBboxKeys.add(raw);

                    const cellIds = raw.split(/[\s,]+/).filter(Boolean);
                    cellIds.forEach(cellId => {
                        try {
                            const boundary = h3.cellToBoundary(cellId); // returns [[lat,lng],...]
                            const poly = L.polygon(boundary, {
                                color: data.color, weight: 2, opacity: 0.9,
                                fillColor: data.color, fillOpacity: 0.35
                            });
                            poly.bindPopup(data.popup);
                            summaryLayerGroup.addLayer(poly);
                            const b = poly.getBounds();
                            tempBoundsLayers.push(L.rectangle([[b.getSouth(), b.getWest()], [b.getNorth(), b.getEast()]]));
                        } catch (e) {
                            console.warn('H3 render error for cell', cellId, e);
                        }
                    });
                }
            });
            if (tempBoundsLayers.length > 0) {
                const group = L.featureGroup(tempBoundsLayers);
                summaryMapInstance.fitBounds(group.getBounds(), { padding: [40, 40], maxZoom: 14 });
            }
        }
        
        
        /**
         * Updates pagination display for current page
         * Shows/hides rows based on current page and rowsPerPage
         * @returns {void}
         */
        function updatePagination() {
            const totalPages = Math.max(1, Math.ceil(allRows.length / rowsPerPage));
            
            // Ensure currentPage is within valid range
            if (currentPage > totalPages) {
                currentPage = totalPages;
            }
            if (currentPage < 1) {
                currentPage = 1;
            }
            
            const startIndex = (currentPage - 1) * rowsPerPage;
            const endIndex = Math.min(startIndex + rowsPerPage, allRows.length);
            
            // Hide ALL expandable rows first (not just filtered ones)
            const allExpandableRows = document.querySelectorAll('.expandable-row');
            allExpandableRows.forEach(row => {
                row.style.display = 'none';
                // Hide associated detail rows
                let nextRow = row.nextElementSibling;
                while (nextRow && nextRow.classList.contains('expand-details-row')) {
                    nextRow.style.display = 'none';
                    nextRow = nextRow.nextElementSibling;
                }
            });
            
            // Show rows for current page
            for (let i = startIndex; i < endIndex; i++) {
                const row = allRows[i];
                row.style.display = '';
                // Show associated detail rows if they have 'show' class
                let nextRow = row.nextElementSibling;
                while (nextRow && nextRow.classList.contains('expand-details-row')) {
                    if (nextRow.classList.contains('show')) {
                        nextRow.style.display = '';
                    }
                    nextRow = nextRow.nextElementSibling;
                }
            }
            
            // Update pagination info
            if (allRows.length === 0) {
                document.getElementById('paginationInfo').textContent = 'Showing 0 of 0 rows';
            } else {
                document.getElementById('paginationInfo').textContent = 
                    `Showing ${startIndex + 1}-${endIndex} of ${allRows.length} rows`;
            }
            
            // Update button states
            document.getElementById('firstPage').disabled = currentPage === 1 || allRows.length === 0;
            document.getElementById('prevPage').disabled = currentPage === 1 || allRows.length === 0;
            document.getElementById('nextPage').disabled = currentPage === totalPages || allRows.length === 0;
            document.getElementById('lastPage').disabled = currentPage === totalPages || allRows.length === 0;
            
            // Update page numbers
            updatePageNumbers(totalPages);
            
            // Refresh the Geographic Coverage Map to show only current-page entries
            renderSummaryMapLayers(currentMapMode);
        }
        
        /**
         * Updates page number buttons display with ellipsis for large page counts
         * @param {number} totalPages - Total number of pages available
         * @returns {void}
         */
        function updatePageNumbers(totalPages) {
            const pageNumbersContainer = document.getElementById('pageNumbers');
            pageNumbersContainer.innerHTML = '';
            
            // Show max 5 page numbers with ellipsis
            const maxVisible = 5;
            let startPage = Math.max(1, currentPage - Math.floor(maxVisible / 2));
            let endPage = Math.min(totalPages, startPage + maxVisible - 1);
            
            // Adjust if we're near the end
            if (endPage - startPage < maxVisible - 1) {
                startPage = Math.max(1, endPage - maxVisible + 1);
            }
            
            // Add first page and ellipsis if needed
            if (startPage > 1) {
                addPageButton(1);
                if (startPage > 2) {
                    const ellipsis = document.createElement('span');
                    ellipsis.textContent = '...';
                    ellipsis.style.padding = '6px 8px';
                    ellipsis.style.color = '#333';
                    pageNumbersContainer.appendChild(ellipsis);
                }
            }
            
            // Add page numbers
            for (let i = startPage; i <= endPage; i++) {
                addPageButton(i);
            }
            
            // Add ellipsis and last page if needed
            if (endPage < totalPages) {
                if (endPage < totalPages - 1) {
                    const ellipsis = document.createElement('span');
                    ellipsis.textContent = '...';
                    ellipsis.style.padding = '6px 8px';
                    ellipsis.style.color = '#333';
                    pageNumbersContainer.appendChild(ellipsis);
                }
                addPageButton(totalPages);
            }
        }
        
        /**
         * Creates and adds a page number button to pagination controls
         * @param {number} pageNum - Page number for the button
         * @returns {void}
         */
        function addPageButton(pageNum) {
            const pageNumbersContainer = document.getElementById('pageNumbers');
            const btn = document.createElement('button');
            btn.className = 'pagination-btn' + (pageNum === currentPage ? ' active' : '');
            btn.textContent = pageNum;
            btn.addEventListener('click', function() {
                currentPage = pageNum;
                updatePagination();
            });
            pageNumbersContainer.appendChild(btn);
        }
    </script>
</body>
</html>
