<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Flock You - Detection Dashboard</title>
    <link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&display=swap" rel="stylesheet">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            background: linear-gradient(135deg, #2d1b69 0%, #1a1033 50%, #4a1b69 100%);
            color: #e0e0e0;
            min-height: 100vh;
        }

        .header {
            background: rgba(26, 16, 51, 0.95);
            color: #e0e0e0;
            padding: 1rem;
            box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
            border-bottom: 2px solid #8b5cf6;
        }

        .header-content {
            display: grid;
            grid-template-areas: 
                "title controls buttons";
            grid-template-columns: auto 1fr auto;
            gap: 1rem;
            align-items: center;
            width: 100%;
        }

        .header-title {
            grid-area: title;
            text-align: left;
        }

        .header-controls {
            grid-area: controls;
            display: flex;
            flex-wrap: wrap;
            gap: 1rem;
            align-items: center;
            justify-content: center;
        }

        .header-buttons {
            grid-area: buttons;
            display: flex;
            gap: 0.5rem;
            align-items: center;
        }

        .header h1 {
            font-size: 1.6rem;
            margin: 0;
            white-space: nowrap;
        }

        .flock-you-title {
            font-family: 'Orbitron', 'Courier New', monospace;
            font-size: 1.4rem;
            font-weight: 700;
            color: #ec4899;
            text-shadow: 0 0 10px rgba(236, 72, 153, 0.5);
            letter-spacing: 2px;
            margin: 0;
            white-space: nowrap;
            flex-shrink: 0;
        }

        @media (max-width: 1200px) {
            .flock-you-title {
                font-size: 1.3rem;
            }
        }

        .controls {
            display: flex;
            gap: 0.75rem;
            align-items: center;
            flex-wrap: wrap;
        }

        .control-group {
            display: flex;
            align-items: center;
            gap: 0.25rem;
            position: relative;
            flex-shrink: 0;
        }

        .device-control-group {
            display: flex;
            align-items: center;
            gap: 0.25rem;
            flex-shrink: 0;
        }

        .port-controls {
            display: flex;
            align-items: center;
            gap: 0.25rem;
        }

        .refresh-btn {
            background: linear-gradient(135deg, #6b7280 0%, #9ca3af 100%);
            color: white;
            font-weight: 600;
            border: 1px solid #4b5563;
            padding: 0.3rem 0.5rem;
            font-size: 0.8rem;
            min-width: auto;
            width: 32px;
            height: 32px;
            display: flex;
            align-items: center;
            justify-content: center;
        }

        .refresh-btn:hover {
            background: linear-gradient(135deg, #9ca3af 0%, #d1d5db 100%);
            transform: translateY(-1px);
            box-shadow: 0 2px 8px rgba(107, 114, 128, 0.4);
        }

        .refresh-btn:active {
            transform: translateY(0);
        }

        .action-buttons {
            display: flex;
            align-items: center;
            gap: 0.5rem;
            flex-shrink: 0;
        }



        .control-group label {
            font-weight: 600;
            color: #e0e0e0;
            font-size: 0.75rem;
            white-space: nowrap;
            min-width: fit-content;
        }

        select, button {
            padding: 0.3rem 0.6rem;
            border: none;
            border-radius: 6px;
            font-size: 0.8rem;
            cursor: pointer;
            transition: all 0.3s ease;
        }

        select {
            background: #2d1b69;
            color: #e0e0e0;
            border: 1px solid #8b5cf6;
            min-width: 100px;
            max-width: 140px;
            font-size: 0.75rem;
        }

        select:focus {
            outline: none;
            border-color: #c084fc;
            box-shadow: 0 0 0 2px rgba(139, 92, 246, 0.3);
        }

        button {
            background: linear-gradient(135deg, #8b5cf6 0%, #a855f7 100%);
            color: white;
            font-weight: 600;
            border: 1px solid #7c3aed;
        }

        button:hover {
            background: linear-gradient(135deg, #a855f7 0%, #c084fc 100%);
            transform: translateY(-2px);
            box-shadow: 0 4px 12px rgba(139, 92, 246, 0.4);
        }

        button.danger {
            background: linear-gradient(135deg, #dc2626 0%, #ef4444 100%);
        }

        button.danger:hover {
            background: linear-gradient(135deg, #ef4444 0%, #f87171 100%);
        }

        .status {
            display: flex;
            gap: 1rem;
            align-items: center;
        }

        .status-group {
            display: flex;
            align-items: center;
            gap: 0.5rem;
        }

        .serial-terminal-btn {
            background: linear-gradient(135deg, #059669 0%, #10b981 100%);
            color: white;
            font-weight: 600;
            border: 1px solid #047857;
            padding: 0.3rem 0.6rem;
            font-size: 0.75rem;
        }

        .serial-terminal-btn:hover {
            background: linear-gradient(135deg, #10b981 0%, #34d399 100%);
            transform: translateY(-2px);
            box-shadow: 0 4px 12px rgba(16, 185, 129, 0.4);
        }

        .oui-search-btn {
            background: linear-gradient(135deg, #7c3aed 0%, #8b5cf6 100%);
            color: white;
            font-weight: 600;
            border: 1px solid #6d28d9;
            padding: 0.3rem 0.6rem;
            font-size: 0.75rem;
        }

        .oui-search-btn:hover {
            background: linear-gradient(135deg, #8b5cf6 0%, #a855f7 100%);
            transform: translateY(-2px);
            box-shadow: 0 4px 12px rgba(124, 58, 237, 0.4);
        }

        .map-btn {
            background: linear-gradient(135deg, #0891b2 0%, #06b6d4 100%);
            color: white;
            font-weight: 600;
            border: 1px solid #0e7490;
            padding: 0.3rem 0.6rem;
            font-size: 0.75rem;
        }

        .map-btn:hover {
            background: linear-gradient(135deg, #06b6d4 0%, #22d3ee 100%);
            transform: translateY(-2px);
            box-shadow: 0 4px 12px rgba(8, 145, 178, 0.4);
        }

        .serial-terminal-container {
            background: rgba(45, 27, 105, 0.8);
            border: 1px solid #8b5cf6;
            border-radius: 12px;
            margin-top: 1rem;
            overflow: hidden;
            box-shadow: 0 8px 32px rgba(139, 92, 246, 0.3);
        }

        .serial-terminal-header {
            background: rgba(45, 27, 105, 0.9);
            padding: 1rem 1.5rem;
            border-bottom: 1px solid #8b5cf6;
            display: flex;
            justify-content: space-between;
            align-items: center;
            flex-wrap: wrap;
            gap: 1rem;
        }

        .serial-terminal-header h2 {
            color: #c084fc;
            font-size: 1.2rem;
            font-weight: 600;
            margin: 0;
        }

        .serial-controls {
            display: flex;
            align-items: center;
            gap: 1rem;
            flex-wrap: wrap;
        }

        .terminal-filter-group {
            display: flex;
            align-items: center;
            gap: 0.5rem;
        }

        .terminal-filter-group label {
            color: #c084fc;
            font-size: 0.85rem;
            font-weight: 600;
        }

        .terminal-filter-select {
            background: rgba(15, 10, 26, 0.95);
            border: 1px solid #8b5cf6;
            border-radius: 4px;
            color: #e0e0e0;
            padding: 0.3rem 0.6rem;
            font-size: 0.8rem;
            min-width: 100px;
        }

        .terminal-filter-select:focus {
            outline: none;
            border-color: #c084fc;
            box-shadow: 0 0 0 2px rgba(139, 92, 246, 0.3);
        }

        .serial-status {
            color: #a855f7;
            font-size: 0.9rem;
            font-weight: 500;
        }

        .clear-terminal-btn {
            background: linear-gradient(135deg, #dc2626 0%, #ef4444 100%);
            color: white;
            font-size: 0.8rem;
            padding: 0.3rem 0.8rem;
            border: none;
            border-radius: 6px;
            cursor: pointer;
            transition: all 0.3s ease;
        }

        .clear-terminal-btn:hover {
            background: linear-gradient(135deg, #ef4444 0%, #f87171 100%);
            transform: translateY(-1px);
        }

        .serial-terminal-content {
            background: rgba(15, 10, 26, 0.95);
            padding: 1rem;
            height: 300px;
            overflow-y: auto;
            font-family: 'Courier New', monospace;
            font-size: 13px;
            line-height: 1.4;
            color: #e0e0e0;
            white-space: pre-wrap;
            word-wrap: break-word;
            scrollbar-width: thin;
            scrollbar-color: #8b5cf6 #2d1b69;
        }

        .serial-terminal-content::-webkit-scrollbar {
            width: 8px;
        }

        .serial-terminal-content::-webkit-scrollbar-track {
            background: #2d1b69;
            border-radius: 4px;
        }

        .serial-terminal-content::-webkit-scrollbar-thumb {
            background: #8b5cf6;
            border-radius: 4px;
        }

        .serial-terminal-content::-webkit-scrollbar-thumb:hover {
            background: #a855f7;
        }

        .terminal-placeholder {
            color: #6b7280;
            font-style: italic;
            text-align: center;
            margin-top: 2rem;
        }

        .serial-line {
            margin: 0;
            padding: 0;
        }

        .serial-line.detection {
            color: #c084fc;
            font-weight: 600;
        }

        .serial-line.error {
            color: #f87171;
        }

        .serial-line.info {
            color: #60a5fa;
        }

        .serial-line.success {
            color: #34d399;
        }

        .serial-line.warning {
            color: #fbbf24;
        }

        .oui-search-container {
            background: rgba(45, 27, 105, 0.8);
            border: 1px solid #8b5cf6;
            border-radius: 12px;
            margin-top: 1rem;
            overflow: hidden;
            box-shadow: 0 8px 32px rgba(139, 92, 246, 0.3);
        }

        .oui-search-header {
            background: rgba(45, 27, 105, 0.9);
            padding: 1rem 1.5rem;
            border-bottom: 1px solid #8b5cf6;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .oui-search-header h2 {
            color: #c084fc;
            font-size: 1.2rem;
            font-weight: 600;
            margin: 0;
        }

        .oui-search-controls {
            display: flex;
            align-items: center;
            gap: 1rem;
        }

        .oui-status {
            color: #a855f7;
            font-size: 0.9rem;
            font-weight: 500;
        }

        .view-all-btn {
            background: linear-gradient(135deg, #059669 0%, #10b981 100%);
            color: white;
            font-size: 0.8rem;
            padding: 0.3rem 0.8rem;
            border: none;
            border-radius: 6px;
            cursor: pointer;
            transition: all 0.3s ease;
        }

        .view-all-btn:hover {
            background: linear-gradient(135deg, #10b981 0%, #34d399 100%);
            transform: translateY(-1px);
        }

        .refresh-db-btn {
            background: linear-gradient(135deg, #f59e0b 0%, #fbbf24 100%);
            color: white;
            font-size: 0.8rem;
            padding: 0.3rem 0.8rem;
            border: none;
            border-radius: 6px;
            cursor: pointer;
            transition: all 0.3s ease;
        }

        .refresh-db-btn:hover {
            background: linear-gradient(135deg, #fbbf24 0%, #fcd34d 100%);
            transform: translateY(-1px);
        }

        .clear-oui-btn {
            background: linear-gradient(135deg, #dc2626 0%, #ef4444 100%);
            color: white;
            font-size: 0.8rem;
            padding: 0.3rem 0.8rem;
            border: none;
            border-radius: 6px;
            cursor: pointer;
            transition: all 0.3s ease;
        }

        .clear-oui-btn:hover {
            background: linear-gradient(135deg, #ef4444 0%, #f87171 100%);
            transform: translateY(-1px);
        }

        .oui-search-content {
            padding: 1rem;
        }

        .search-input-group {
            display: flex;
            gap: 0.5rem;
            margin-bottom: 1rem;
        }

        .oui-search-input {
            flex: 1;
            background: rgba(15, 10, 26, 0.95);
            border: 1px solid #8b5cf6;
            border-radius: 6px;
            color: #e0e0e0;
            padding: 0.5rem;
            font-size: 0.9rem;
        }

        .oui-search-input:focus {
            outline: none;
            border-color: #c084fc;
            box-shadow: 0 0 0 2px rgba(139, 92, 246, 0.3);
            background: rgba(15, 10, 26, 0.95);
            color: #e0e0e0;
        }

        .oui-search-input::placeholder {
            color: #6b7280;
        }

        .search-btn {
            background: linear-gradient(135deg, #8b5cf6 0%, #a855f7 100%);
            color: white;
            font-size: 0.9rem;
            padding: 0.5rem 1rem;
            border: none;
            border-radius: 6px;
            cursor: pointer;
            transition: all 0.3s ease;
        }

        .search-btn:hover {
            background: linear-gradient(135deg, #a855f7 0%, #c084fc 100%);
            transform: translateY(-1px);
        }

        .search-results {
            background: rgba(15, 10, 26, 0.95);
            border: 1px solid #8b5cf6;
            border-radius: 6px;
            padding: 1rem;
            max-height: 400px;
            overflow-y: auto;
            scrollbar-width: thin;
            scrollbar-color: #8b5cf6 #2d1b69;
        }

        .oui-results-grid {
            display: grid;
            grid-template-columns: repeat(4, 1fr);
            gap: 0.3rem;
            max-height: 350px;
            overflow-y: auto;
        }

        .oui-result-item {
            background: rgba(45, 27, 105, 0.5);
            border: 1px solid #8b5cf6;
            border-radius: 4px;
            padding: 0.3rem;
            margin-bottom: 0;
            font-size: 0.7rem;
            min-height: 40px;
        }

        .oui-result-item:last-child {
            margin-bottom: 0;
        }

        .oui-mac {
            color: #c084fc;
            font-weight: 600;
            font-family: 'Courier New', monospace;
            font-size: 0.75rem;
        }

        .oui-manufacturer {
            color: #e0e0e0;
            margin-top: 0.25rem;
            font-size: 0.7rem;
            line-height: 1.2;
            word-wrap: break-word;
        }

        .search-results::-webkit-scrollbar {
            width: 8px;
        }

        .search-results::-webkit-scrollbar-track {
            background: #2d1b69;
            border-radius: 4px;
        }

        .search-results::-webkit-scrollbar-thumb {
            background: #8b5cf6;
            border-radius: 4px;
        }

        .search-results::-webkit-scrollbar-thumb:hover {
            background: #a855f7;
        }

        .search-placeholder {
            color: #6b7280;
            font-style: italic;
            text-align: center;
            margin-top: 2rem;
        }

        .oui-result-item {
            background: rgba(45, 27, 105, 0.5);
            border: 1px solid #8b5cf6;
            border-radius: 6px;
            padding: 0.75rem;
            margin-bottom: 0.5rem;
        }

        .oui-result-item:last-child {
            margin-bottom: 0;
        }

        .oui-mac {
            color: #c084fc;
            font-weight: 600;
            font-family: 'Courier New', monospace;
        }

        .oui-manufacturer {
            color: #e0e0e0;
            margin-top: 0.25rem;
        }

        .alias-display {
            cursor: pointer;
            padding: 0.25rem 0.5rem;
            border-radius: 4px;
            display: inline-block;
            min-width: 100px;
            background: #1e40af;
            border: 1px solid #3b82f6;
            color: #93c5fd;
            font-weight: 600;
            font-size: 0.85rem;
        }

        .alias-display:hover {
            background: #2563eb;
            border-color: #60a5fa;
            color: white;
        }

        .alias-display em {
            color: #6b7280;
            font-style: italic;
        }

        .alias-input {
            background: rgba(45, 27, 105, 0.8);
            border: 1px solid #8b5cf6;
            border-radius: 4px;
            color: #e0e0e0;
            padding: 2px 6px;
            font-size: 0.9rem;
            width: 120px;
        }

        .alias-input:focus {
            outline: none;
            border-color: #c084fc;
            box-shadow: 0 0 0 2px rgba(139, 92, 246, 0.3);
        }

        .status-indicator {
            width: 12px;
            height: 12px;
            border-radius: 50%;
            background: #dc2626;
            animation: pulse 2s infinite;
            box-shadow: 0 0 8px rgba(220, 38, 38, 0.6);
        }

        .status-indicator.active {
            background: #10b981;
            box-shadow: 0 0 8px rgba(16, 185, 129, 0.6);
        }

        .device-info {
            font-size: 0.8rem;
            color: #d1d5db;
            margin-top: 0.25rem;
        }



        @keyframes pulse {
            0% { opacity: 1; }
            50% { opacity: 0.5; }
            100% { opacity: 1; }
        }

        @keyframes spin {
            from { transform: rotate(0deg); }
            to { transform: rotate(360deg); }
        }

        .main-content {
            padding: 1rem;
            width: 100%;
        }

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

        .stat-card {
            background: rgba(45, 27, 105, 0.8);
            padding: 1.5rem;
            border-radius: 12px;
            box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
            text-align: center;
            border: 1px solid #8b5cf6;
            backdrop-filter: blur(10px);
        }

        .stat-number {
            font-size: 2rem;
            font-weight: bold;
            color: #c084fc;
        }

        .stat-label {
            color: #d1d5db;
            margin-top: 0.5rem;
        }

        .stat-hint {
            color: #9ca3af;
            font-size: 0.7rem;
            margin-top: 0.25rem;
            font-style: italic;
        }

        .detections-container {
            background: rgba(45, 27, 105, 0.8);
            border-radius: 12px;
            box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
            overflow: hidden;
            border: 1px solid #8b5cf6;
            backdrop-filter: blur(10px);
        }

        .detections-header {
            background: rgba(26, 16, 51, 0.9);
            padding: 1rem;
            border-bottom: 1px solid #8b5cf6;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .detections-header h2 {
            color: #c084fc;
            font-size: 1.2rem;
            font-weight: 600;
            margin: 0;
        }

        .detections-list {
            max-height: 600px;
            overflow-y: auto;
            scrollbar-width: thin;
            scrollbar-color: #8b5cf6 #2d1b69;
        }

        .detections-list::-webkit-scrollbar {
            width: 8px;
        }

        .detections-list::-webkit-scrollbar-track {
            background: #2d1b69;
            border-radius: 4px;
        }

        .detections-list::-webkit-scrollbar-thumb {
            background: #8b5cf6;
            border-radius: 4px;
        }

        .detections-list::-webkit-scrollbar-thumb:hover {
            background: #a855f7;
        }

        .detection-item {
            background: #1e3a8a;
            border: 1px solid #3b82f6;
            border-radius: 6px;
            padding: 0.5rem;
            margin-bottom: 0.5rem;
            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
            min-height: 5rem;
        }

        .detection-item:hover {
            background: #1e40af;
            border-color: #60a5fa;
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.4);
        }

        .detection-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 0.5rem;
        }

        .detection-header-left {
            display: flex;
            align-items: center;
            gap: 0.5rem;
        }

        .gps-link {
            color: #93c5fd;
            font-size: 0.8rem;
            font-weight: 600;
            text-decoration: none;
            padding: 0.1rem 0.3rem;
            border-radius: 3px;
            background: rgba(147, 197, 253, 0.1);
            border: 1px solid #3b82f6;
            transition: all 0.2s ease;
        }

        .gps-link:hover {
            background: #3b82f6;
            color: white;
            text-decoration: none;
        }

        .detection-type-badge {
            display: flex;
            align-items: center;
            gap: 0.5rem;
        }

        .detection-type {
            background: #2563eb;
            color: white;
            padding: 0.25rem 0.6rem;
            border-radius: 4px;
            font-size: 0.9rem;
            font-weight: 700;
            text-transform: uppercase;
            letter-spacing: 0.5px;
        }

        .gps-tag {
            background: #22c55e;
            color: white;
            padding: 0.15rem 0.4rem;
            border-radius: 3px;
            font-size: 0.7rem;
            font-weight: 600;
            text-transform: uppercase;
            letter-spacing: 0.3px;
        }

        .detection-count {
            background: #059669;
            color: white;
            padding: 0.2rem 0.5rem;
            border-radius: 4px;
            font-size: 0.85rem;
            font-weight: 700;
            min-width: 2.5rem;
            text-align: center;
        }

        .detection-time {
            color: #93c5fd;
            font-size: 0.9rem;
            font-weight: 600;
        }

        .detection-details {
            display: flex;
            flex-wrap: wrap;
            gap: 1rem;
            font-size: 0.9rem;
            align-items: center;
        }

        .detail-item {
            display: flex;
            align-items: center;
            white-space: nowrap;
        }

        .detail-label {
            font-weight: 700;
            color: #93c5fd;
            font-size: 0.9rem;
            margin-right: 0.5rem;
        }

        .detail-value {
            color: #ffffff;
            font-weight: 600;
            font-size: 0.9rem;
        }

        .detection-timing {
            grid-column: 1 / -1;
            background: rgba(16, 185, 129, 0.1);
            border: 1px solid rgba(16, 185, 129, 0.2);
            border-radius: 4px;
            padding: 0.2rem;
            margin-top: 0.15rem;
            font-size: 0.65rem;
        }



        .no-detections {
            text-align: center;
            padding: 3rem;
            color: #d1d5db;
        }

        .export-buttons {
            display: flex;
            gap: 1rem;
            margin-top: 1rem;
            align-items: center;
        }

        .detection-search-group {
            margin-right: 0.5rem;
        }

        .detection-search-input {
            background: rgba(15, 10, 26, 0.95);
            border: 1px solid #8b5cf6;
            border-radius: 6px;
            color: #e0e0e0;
            padding: 0.4rem 0.8rem;
            font-size: 0.8rem;
            width: 200px;
        }

        .detection-search-input:focus {
            outline: none;
            border-color: #c084fc;
            box-shadow: 0 0 0 2px rgba(139, 92, 246, 0.3);
            background: rgba(15, 10, 26, 0.95);
            color: #e0e0e0;
        }

        .detection-search-input::placeholder {
            color: #6b7280;
        }

        .export-btn {
            background: linear-gradient(135deg, #8b5cf6 0%, #a855f7 100%);
            border: 1px solid #7c3aed;
        }

        .export-btn:hover {
            background: linear-gradient(135deg, #a855f7 0%, #c084fc 100%);
            box-shadow: 0 4px 12px rgba(139, 92, 246, 0.4);
        }

        .clear-btn {
            background: linear-gradient(135deg, #ec4899 0%, #f472b6 100%);
            border: 1px solid #db2777;
        }

        .clear-btn:hover {
            background: linear-gradient(135deg, #f472b6 0%, #f9a8d4 100%);
            box-shadow: 0 4px 12px rgba(236, 72, 153, 0.4);
        }

        .export-dropdown {
            position: relative;
            display: inline-block;
        }

        .export-dropdown-content {
            display: none;
            position: absolute;
            right: 0;
            background: rgba(45, 27, 105, 0.95);
            min-width: 160px;
            box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);
            border: 1px solid #8b5cf6;
            border-radius: 6px;
            z-index: 1000;
        }

        .export-dropdown-content a {
            color: #e0e0e0;
            padding: 8px 12px;
            text-decoration: none;
            display: block;
            font-size: 0.8rem;
            transition: background-color 0.3s;
        }

        .export-dropdown-content a:hover {
            background: rgba(139, 92, 246, 0.3);
        }

        .dropdown-toggle {
            cursor: pointer;
        }

        .map-container {
            background: rgba(45, 27, 105, 0.8);
            border: 1px solid #8b5cf6;
            border-radius: 12px;
            margin-top: 1rem;
            overflow: hidden;
            box-shadow: 0 8px 32px rgba(139, 92, 246, 0.3);
        }

        .map-header {
            background: rgba(45, 27, 105, 0.9);
            padding: 1rem 1.5rem;
            border-bottom: 1px solid #8b5cf6;
            display: flex;
            justify-content: space-between;
            align-items: center;
            flex-wrap: wrap;
            gap: 1rem;
        }

        .map-header h2 {
            color: #c084fc;
            font-size: 1.2rem;
            font-weight: 600;
            margin: 0;
        }

        .map-controls {
            display: flex;
            align-items: center;
            gap: 1rem;
            flex-wrap: wrap;
        }

        .map-layer-group,
        .map-filter-group {
            display: flex;
            align-items: center;
            gap: 0.5rem;
        }

        .map-layer-group label,
        .map-filter-group label {
            color: #c084fc;
            font-size: 0.85rem;
            font-weight: 600;
        }

        .map-layer-select,
        .map-filter-select {
            background: rgba(15, 10, 26, 0.95);
            border: 1px solid #8b5cf6;
            border-radius: 4px;
            color: #e0e0e0;
            padding: 0.3rem 0.6rem;
            font-size: 0.8rem;
            min-width: 120px;
        }

        .map-layer-select:focus,
        .map-filter-select:focus {
            outline: none;
            border-color: #c084fc;
            box-shadow: 0 0 0 2px rgba(139, 92, 246, 0.3);
        }

        .map-status {
            color: #a855f7;
            font-size: 0.9rem;
            font-weight: 500;
        }

        .clear-map-btn {
            background: linear-gradient(135deg, #dc2626 0%, #ef4444 100%);
            color: white;
            font-size: 0.8rem;
            padding: 0.3rem 0.8rem;
            border: none;
            border-radius: 6px;
            cursor: pointer;
            transition: all 0.3s ease;
        }

        .clear-map-btn:hover {
            background: linear-gradient(135deg, #ef4444 0%, #f87171 100%);
            transform: translateY(-1px);
        }

        .map-content {
            padding: 0;
        }

        .leaflet-popup-content {
            color: #333;
            font-size: 0.9rem;
        }

        .leaflet-popup-content h3 {
            margin: 0 0 0.5rem 0;
            color: #ec4899;
        }

        .map-legend {
            background: rgba(15, 10, 26, 0.9);
            border: 1px solid #8b5cf6;
            border-radius: 6px;
            padding: 0.5rem;
            margin-top: 0.5rem;
            font-size: 0.8rem;
            display: flex;
            flex-wrap: wrap;
            align-items: center;
            gap: 1rem;
        }

        .legend-title {
            color: #c084fc;
            margin: 0;
            font-size: 0.85rem;
            font-weight: 600;
        }

        .legend-items {
            display: flex;
            flex-wrap: wrap;
            gap: 1rem;
            align-items: center;
        }

        .legend-item {
            display: flex;
            align-items: center;
            gap: 0.3rem;
            font-size: 0.75rem;
            white-space: nowrap;
        }

        .legend-marker {
            width: 12px;
            height: 12px;
            border-radius: 50%;
            border: 2px solid;
        }

        .wifi-session { background: #ef4444; border-color: #22c55e; }
        .wifi-cumulative { background: #ef4444; border-color: #f59e0b; }
        .ble-session { background: #3b82f6; border-color: #22c55e; }
        .ble-cumulative { background: #3b82f6; border-color: #f59e0b; }

        @media (max-width: 1200px) {
            .header-content {
                grid-template-areas: 
                    "title title"
                    "controls buttons";
                grid-template-columns: 1fr auto;
            }
            
            .header-title {
                text-align: center;
            }
            
            .header-controls {
                justify-content: center;
            }
            
            .header-buttons {
                justify-content: center;
            }
        }

        @media (max-width: 900px) {
            .header-content {
                grid-template-areas: 
                    "title title"
                    "controls controls"
                    "buttons buttons";
                grid-template-columns: 1fr;
            }
            
            .header-title {
                text-align: center;
            }
            
            .header-controls {
                justify-content: center;
            }
            
            .header-buttons {
                justify-content: center;
            }
        }

        @media (max-width: 768px) {
            .header {
                padding: 0.75rem;
            }
            
            .flock-you-title {
                font-size: 1.1rem;
                letter-spacing: 1px;
            }
            
            .header-controls {
                margin-top: 0.5rem;
            }
            
            .controls {
                flex-direction: column;
                align-items: stretch;
                gap: 0.75rem;
                width: 100%;
            }
            
            .device-control-group,
            .control-group {
                width: 100%;
                justify-content: space-between;
            }
            
            .action-buttons {
                flex-direction: column;
                gap: 0.5rem;
                width: 100%;
            }
            
            .serial-terminal-btn,
            .oui-search-btn,
            .map-btn {
                width: 100%;
                text-align: center;
            }
            
            .detection-details {
                grid-template-columns: 1fr;
            }
            
            select {
                min-width: 80px;
                max-width: none;
                flex: 1;
            }
            
            .port-controls {
                flex: 1;
                gap: 0.25rem;
            }
            
            .refresh-btn {
                width: 28px;
                height: 28px;
                font-size: 0.7rem;
            }
        }
    </style>
</head>
<body>

    <div class="header">
        <div class="header-content">
            <div class="header-title">
                <h1 class="flock-you-title">FLOCK-YOU</h1>
            </div>
            
            <div class="header-controls">
                <div class="controls">
                    <div class="device-control-group">
                        <div class="status-indicator" id="flockStatus"></div>
                        <label>Sniffer:</label>
                        <div class="port-controls">
                            <select id="flockDeviceSelect">
                                <option value="">Select Port</option>
                            </select>
                            <button id="refreshFlockPortsBtn" class="refresh-btn" title="Refresh ports" onclick="loadFlockPorts()">↻</button>
                        </div>
                        <button id="connectFlockBtn">Connect</button>
                        <button id="disconnectFlockBtn" class="danger" style="display: none;">Disconnect</button>
                    </div>
                    
                    <div class="device-control-group">
                        <div class="status-indicator" id="gpsStatus"></div>
                        <label>GPS:</label>
                        <select id="gpsSourceSelect" title="GPS source">
                            <option value="serial">Serial NMEA</option>
                            <option value="gpsd">gpsd</option>
                        </select>
                        <div class="port-controls" id="gpsSerialControls">
                            <select id="gpsPortSelect">
                                <option value="">Select Port</option>
                            </select>
                            <button id="refreshGpsPortsBtn" class="refresh-btn" title="Refresh ports" onclick="loadGpsPorts()">↻</button>
                        </div>
                        <div class="port-controls" id="gpsdControls" style="display: none;">
                            <input type="text" id="gpsdHostInput" placeholder="host" value="localhost" style="width: 8em;">
                            <input type="number" id="gpsdPortInput" placeholder="port" value="2947" min="1" max="65535" style="width: 5em;">
                        </div>
                        <button id="connectGpsBtn">Connect</button>
                        <button id="disconnectGpsBtn" class="danger" style="display: none;">Disconnect</button>
                    </div>
                    
                    <div class="control-group">
                        <label>Filter:</label>
                        <select id="filterSelect">
                            <option value="all">All Detections</option>
                            <option value="probe_request">WiFi Probe Requests</option>
                            <option value="beacon">WiFi Beacons</option>
                            <option value="mac_prefix">MAC Address</option>
                            <option value="device_name">BLE Device Name</option>
                        </select>
                    </div>
                </div>
            </div>
            
            <div class="header-buttons">
                <div class="action-buttons">
                    <button id="serial-terminal-btn" class="serial-terminal-btn" onclick="toggleSerialTerminal()">Terminal</button>
                    <button id="oui-search-btn" class="oui-search-btn" onclick="toggleOuiSearch()">OUI</button>
                    <button id="map-btn" class="map-btn" onclick="toggleMap()">Map</button>
                </div>
            </div>
        </div>
    </div>

    <div class="main-content">
        <div class="stats-grid">
            <div class="stat-card">
                <div class="stat-number" id="totalDetections">0</div>
                <div class="stat-label">Session Detections</div>
                <div class="stat-hint">(Hover for cumulative)</div>
            </div>
            <div class="stat-card">
                <div class="stat-number" id="wifiDetections">0</div>
                <div class="stat-label">WiFi Detections</div>
                <div class="stat-hint">(Hover for cumulative)</div>
            </div>
            <div class="stat-card">
                <div class="stat-number" id="bleDetections">0</div>
                <div class="stat-label">BLE Detections</div>
                <div class="stat-hint">(Hover for cumulative)</div>
            </div>
            <div class="stat-card">
                <div class="stat-number" id="gpsDetections">0</div>
                <div class="stat-label">GPS Tagged</div>
                <div class="stat-hint">(Hover for cumulative)</div>
            </div>
        </div>

        <div class="detections-container">
            <div class="detections-header">
                <h2>Detections</h2>
                <div class="export-buttons">
                    <div class="detection-search-group">
                        <input type="text" id="detectionSearchInput" class="detection-search-input" placeholder="Search detections..." onkeyup="searchDetections()">
                    </div>
                    <div class="export-dropdown">
                        <button class="export-btn dropdown-toggle" onclick="toggleExportDropdown()">Export ▼</button>
                        <div class="export-dropdown-content" id="exportDropdown">
                            <a href="#" onclick="exportCSV('session')">Session CSV</a>
                            <a href="#" onclick="exportKML('session')">Session KML</a>
                            <a href="#" onclick="exportCSV('cumulative')">Cumulative CSV</a>
                            <a href="#" onclick="exportKML('cumulative')">Cumulative KML</a>
                        </div>
                    </div>
                    <div class="export-dropdown">
                        <button class="export-btn dropdown-toggle" style="background:linear-gradient(135deg,#059669 0%,#10b981 100%);border-color:#047857" onclick="toggleImportDropdown()">Import ▼</button>
                        <div class="export-dropdown-content" id="importDropdown">
                            <a href="#" onclick="importFile('json')">Import JSON (from ESP32)</a>
                            <a href="#" onclick="importFile('csv')">Import CSV (from ESP32)</a>
                            <a href="#" onclick="importFile('kml')">Import KML (from ESP32)</a>
                        </div>
                    </div>
                    <input type="file" id="importFileInput" accept=".json,.csv,.kml" style="display:none" onchange="handleImportFile()">
                    <button class="clear-btn" onclick="clearDetections()">Clear All</button>
                </div>
            </div>
            <div class="detections-list" id="detectionsList">
                <div class="no-detections">
                    <h3>No detections yet</h3>
                    <p>Start scanning with your Flock You device to see detections here</p>
                </div>
            </div>
        </div>

        <div class="serial-terminal-container" id="serialTerminalContainer" style="display: none;">
            <div class="serial-terminal-header">
                <h2>Serial Terminal</h2>
                <div class="serial-controls">
                    <div class="terminal-filter-group">
                        <label for="terminalFilter">Filter:</label>
                        <select id="terminalFilter" class="terminal-filter-select" onchange="filterTerminalData()">
                            <option value="all">All Data</option>
                            <option value="sniffer">Sniffer Only</option>
                            <option value="gps">GPS Only</option>
                        </select>
                    </div>
                    <span class="serial-status">Status: <span id="serialConnectionStatus">Disconnected</span></span>
                    <button class="clear-terminal-btn" onclick="clearSerialTerminal()">Clear Terminal</button>
                </div>
            </div>
            <div class="serial-terminal-content" id="serialTerminalOutput">
                <div class="terminal-placeholder">Connect to a Sniffer device to see serial output...</div>
            </div>
        </div>

        <div class="oui-search-container" id="ouiSearchContainer" style="display: none;">
            <div class="oui-search-header">
                <h2>OUI Database Search</h2>
                <div class="oui-search-controls">
                    <span class="oui-status">Search IEEE OUI Database</span>
                    <button class="view-all-btn" onclick="viewAllOui()">View All</button>
                    <button class="refresh-db-btn" onclick="refreshOuiDatabase()">Refresh DB</button>
                    <button class="clear-oui-btn" onclick="clearOuiResults()">Clear Results</button>
                </div>
            </div>
            <div class="oui-search-content">
                <div class="search-input-group">
                    <input type="text" id="ouiSearchInput" class="oui-search-input" placeholder="Enter MAC address (e.g., 00:11:22) or manufacturer name..." onkeypress="handleOuiSearch(event)">
                    <button class="search-btn" onclick="searchOui()">Search</button>
                </div>
                <div class="search-results" id="ouiSearchResults">
                    <div class="search-placeholder">Enter a MAC address or manufacturer name to search...</div>
                </div>
            </div>
        </div>

        <div class="map-container" id="mapContainer" style="display: none;">
            <div class="map-header">
                <h2>Detection Map</h2>
                <div class="map-controls">
                    <div class="map-layer-group">
                        <label for="mapLayer">Layer:</label>
                        <select id="mapLayer" class="map-layer-select" onchange="changeMapLayer()">
                            <option value="osm">OpenStreetMap</option>
                            <option value="satellite">Satellite</option>
                            <option value="topo">Topographic</option>
                        </select>
                    </div>
                    <div class="map-filter-group">
                        <label for="mapFilter">Data:</label>
                        <select id="mapFilter" class="map-filter-select" onchange="filterMapData()">
                            <option value="session">Session Only</option>
                            <option value="cumulative">Cumulative Only</option>
                            <option value="both">Session + Cumulative</option>
                        </select>
                    </div>
                    <span class="map-status">Showing: <span id="mapDetectionCount">0</span> detections</span>
                    <button class="clear-map-btn" onclick="clearMapMarkers()">Clear Markers</button>
                </div>
            </div>
            <div class="map-content" id="mapContent">
                <div id="map" style="height: 400px; width: 100%;"></div>
                <div class="map-legend">
                    <h4 class="legend-title">Legend:</h4>
                    <div class="legend-items">
                        <div class="legend-item">
                            <div class="legend-marker wifi-session"></div>
                            <span>WiFi (Session)</span>
                        </div>
                        <div class="legend-item">
                            <div class="legend-marker wifi-cumulative"></div>
                            <span>WiFi (Cumulative)</span>
                        </div>
                        <div class="legend-item">
                            <div class="legend-marker ble-session"></div>
                            <span>BLE (Session)</span>
                        </div>
                        <div class="legend-item">
                            <div class="legend-marker ble-cumulative"></div>
                            <span>BLE (Cumulative)</span>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>

    <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
    <script>
        const socket = io({
            transports: ['websocket', 'polling'],
            reconnection: true,
            reconnectionAttempts: 5,
            reconnectionDelay: 1000,
            reconnectionDelayMax: 5000
        });
        let detections = [];
        let cumulativeDetections = [];
        let gpsConnected = false;
        const max_reconnect_attempts = 5;
        let userInteractingWithPorts = false; // Flag to prevent auto-refresh interference
        let terminalFilter = 'all';
        let allTerminalData = [];
        let map = null;
        let mapMarkers = [];
        let mapLayers = {};
        let mapFilter = 'session';

        // Initialize
        document.addEventListener('DOMContentLoaded', function() {
            loadDetections();
            loadCumulativeDetections();
            loadFlockPorts();
            loadGpsPorts();
            setupEventListeners();
            loadStatus();
            loadSettings();
            
            // Periodic status refresh every 5 seconds
            setInterval(loadStatus, 5000);
            
            // Periodic port refresh every 30 seconds (reduced from 10 seconds)
            setInterval(function() {
                // Only refresh if user is not actively interacting with ports
                if (!userInteractingWithPorts) {
                    loadFlockPorts();
                    loadGpsPorts();
                }
            }, 30000);
            
            // Connection health check every 30 seconds
            setInterval(function() {
                if (!socket.connected) {
                    console.log('Socket disconnected, attempting to reconnect...');
                    socket.connect();
                }
            }, 30000);
        });

        function setupEventListeners() {
            document.getElementById('filterSelect').addEventListener('change', function() {
                filterDetections(this.value);
                saveSettings();
            });

            document.getElementById('flockDeviceSelect').addEventListener('change', function() {
                saveSettings();
            });

            document.getElementById('gpsPortSelect').addEventListener('change', function() {
                saveSettings();
            });

            document.getElementById('gpsSourceSelect').addEventListener('change', function() {
                applyGpsSource(this.value);
                saveSettings();
            });
            document.getElementById('gpsdHostInput').addEventListener('change', saveSettings);
            document.getElementById('gpsdPortInput').addEventListener('change', saveSettings);
            // Initial visibility pass — reflect whatever the dropdown starts at.
            applyGpsSource(document.getElementById('gpsSourceSelect').value);

            document.getElementById('connectFlockBtn').addEventListener('click', connectFlock);
            document.getElementById('disconnectFlockBtn').addEventListener('click', disconnectFlock);
            document.getElementById('connectGpsBtn').addEventListener('click', connectGps);
            document.getElementById('disconnectGpsBtn').addEventListener('click', disconnectGps);
            
            // Add event listeners for port dropdowns to prevent auto-refresh interference
            const flockSelect = document.getElementById('flockDeviceSelect');
            const gpsSelect = document.getElementById('gpsPortSelect');
            
            flockSelect.addEventListener('focus', () => { userInteractingWithPorts = true; });
            flockSelect.addEventListener('blur', () => { 
                setTimeout(() => { userInteractingWithPorts = false; }, 1000); 
            });
            flockSelect.addEventListener('change', () => { userInteractingWithPorts = true; });
            
            gpsSelect.addEventListener('focus', () => { userInteractingWithPorts = true; });
            gpsSelect.addEventListener('blur', () => { 
                setTimeout(() => { userInteractingWithPorts = false; }, 1000); 
            });
            gpsSelect.addEventListener('change', () => { userInteractingWithPorts = true; });
        }

        function loadDetections() {
            fetch('/api/detections')
                .then(response => response.json())
                .then(data => {
                    console.log('Loaded detections:', data.length);
                    detections = data;
                    updateStats();
                    renderDetections();
                })
                .catch(error => {
                    console.error('Error loading detections:', error);
                });
        }

        function applyGpsSource(source) {
            const isGpsd = (source === 'gpsd');
            document.getElementById('gpsSerialControls').style.display = isGpsd ? 'none' : '';
            document.getElementById('gpsdControls').style.display = isGpsd ? '' : 'none';
        }

        function loadSettings() {
            fetch('/api/settings')
                .then(response => response.json())
                .then(settings => {
                    console.log('Loaded settings:', settings);

                    // Apply settings to UI
                    if (settings.gps_source) {
                        document.getElementById('gpsSourceSelect').value = settings.gps_source;
                        applyGpsSource(settings.gps_source);
                    }
                    if (settings.gps_port) {
                        document.getElementById('gpsPortSelect').value = settings.gps_port;
                    }
                    if (settings.gpsd_host) {
                        document.getElementById('gpsdHostInput').value = settings.gpsd_host;
                    }
                    if (settings.gpsd_port) {
                        document.getElementById('gpsdPortInput').value = settings.gpsd_port;
                    }
                    if (settings.flock_port) {
                        document.getElementById('flockDeviceSelect').value = settings.flock_port;
                    }
                    if (settings.filter) {
                        document.getElementById('filterSelect').value = settings.filter;
                        filterDetections(settings.filter);
                    }
                })
                .catch(error => {
                    console.error('Error loading settings:', error);
                });
        }

        function saveSettings() {
            const settings = {
                gps_source: document.getElementById('gpsSourceSelect').value,
                gps_port: document.getElementById('gpsPortSelect').value,
                gpsd_host: document.getElementById('gpsdHostInput').value,
                gpsd_port: parseInt(document.getElementById('gpsdPortInput').value, 10) || 2947,
                flock_port: document.getElementById('flockDeviceSelect').value,
                filter: document.getElementById('filterSelect').value
            };
            
            fetch('/api/settings', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify(settings)
            })
            .then(response => response.json())
            .then(data => {
                console.log('Settings saved:', data);
            })
            .catch(error => {
                console.error('Error saving settings:', error);
            });
        }

        function loadGpsPorts() {
            const select = document.getElementById('gpsPortSelect');
            const refreshBtn = document.getElementById('refreshGpsPortsBtn');
            const currentSelection = select.value; // Preserve current selection
            
            // Show loading state
            refreshBtn.textContent = '⟳';
            refreshBtn.style.animation = 'spin 1s linear infinite';
            
            // Add a subtle visual indicator if this is an auto-refresh
            if (!userInteractingWithPorts) {
                refreshBtn.title = 'Auto-refreshing ports...';
            }
            
            fetch('/api/gps/ports')
                .then(response => response.json())
                .then(ports => {
                    console.log('GPS ports loaded:', ports.length);
                    
                    select.innerHTML = '<option value="">Select GPS Port</option>';
                    ports.forEach(port => {
                        const option = document.createElement('option');
                        option.value = port.device;
                        option.textContent = `${port.device} - ${port.description}`;
                        select.appendChild(option);
                    });
                    
                    // Restore selection if it still exists in the new list
                    if (currentSelection && ports.some(p => p.device === currentSelection)) {
                        select.value = currentSelection;
                    }
                    
                    // Reset refresh button
                    refreshBtn.textContent = '↻';
                    refreshBtn.style.animation = '';
                    refreshBtn.title = 'Refresh ports';
                })
                .catch(error => {
                    console.error('Error loading GPS ports:', error);
                    // Reset refresh button on error
                    refreshBtn.textContent = '↻';
                    refreshBtn.style.animation = '';
                    refreshBtn.title = 'Refresh ports';
                });
        }

        function loadFlockPorts() {
            const select = document.getElementById('flockDeviceSelect');
            const refreshBtn = document.getElementById('refreshFlockPortsBtn');
            const currentSelection = select.value; // Preserve current selection
            
            // Show loading state
            refreshBtn.textContent = '⟳';
            refreshBtn.style.animation = 'spin 1s linear infinite';
            
            // Add a subtle visual indicator if this is an auto-refresh
            if (!userInteractingWithPorts) {
                refreshBtn.title = 'Auto-refreshing ports...';
            }
            
            fetch('/api/flock/ports')
                .then(response => response.json())
                .then(ports => {
                    console.log('Flock ports loaded:', ports.length);
                    
                    select.innerHTML = '<option value="">Select Flock You Port</option>';
                    ports.forEach(port => {
                        const option = document.createElement('option');
                        option.value = port.device;
                        option.textContent = `${port.device} - ${port.description}`;
                        select.appendChild(option);
                    });
                    
                    // Restore selection if it still exists in the new list
                    if (currentSelection && ports.some(p => p.device === currentSelection)) {
                        select.value = currentSelection;
                    }
                    
                    // Reset refresh button
                    refreshBtn.textContent = '↻';
                    refreshBtn.style.animation = '';
                    refreshBtn.title = 'Refresh ports';
                })
                .catch(error => {
                    console.error('Error loading Flock ports:', error);
                    // Reset refresh button on error
                    refreshBtn.textContent = '↻';
                    refreshBtn.style.animation = '';
                    refreshBtn.title = 'Refresh ports';
                });
        }

        function connectFlock() {
            const port = document.getElementById('flockDeviceSelect').value;
            if (!port) {
                alert('Please select a Sniffer port');
                return;
            }

            console.log('Connecting to Flock device on port:', port);
            
            fetch('/api/flock/connect', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify({ port: port })
            })
            .then(response => response.json())
            .then(data => {
                console.log('Flock connection response:', data);
                if (data.status === 'success') {
                    updateFlockStatus(true);
                    document.getElementById('connectFlockBtn').style.display = 'none';
                    document.getElementById('disconnectFlockBtn').style.display = 'inline-block';
                } else {
                    alert('Flock You connection failed: ' + data.message);
                }
            })
            .catch(error => {
                console.error('Flock connection error:', error);
                alert('Connection failed: ' + error.message);
            });
        }

        function disconnectFlock() {
            console.log('Disconnecting Flock device');
            
            fetch('/api/flock/disconnect', {
                method: 'POST'
            })
            .then(response => response.json())
            .then(data => {
                console.log('Flock disconnect response:', data);
                if (data.status === 'success') {
                    updateFlockStatus(false);
                    document.getElementById('connectFlockBtn').style.display = 'inline-block';
                    document.getElementById('disconnectFlockBtn').style.display = 'none';
                }
            })
            .catch(error => {
                console.error('Flock disconnect error:', error);
            });
        }

        function connectGps() {
            const source = document.getElementById('gpsSourceSelect').value || 'serial';
            const body = { source: source };
            let label;

            if (source === 'serial') {
                const port = document.getElementById('gpsPortSelect').value;
                if (!port) {
                    alert('Please select a GPS serial port');
                    return;
                }
                body.port = port;
                label = `serial ${port}`;
            } else if (source === 'gpsd') {
                const host = (document.getElementById('gpsdHostInput').value || 'localhost').trim();
                const port = parseInt(document.getElementById('gpsdPortInput').value, 10) || 2947;
                body.host = host;
                body.port = port;
                label = `gpsd ${host}:${port}`;
            } else {
                alert('Unknown GPS source: ' + source);
                return;
            }

            console.log('Connecting GPS:', label);

            fetch('/api/gps/connect', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(body)
            })
            .then(response => response.json())
            .then(data => {
                console.log('GPS connection response:', data);
                if (data.status === 'success') {
                    gpsConnected = true;
                    updateGpsStatus(true);
                    document.getElementById('connectGpsBtn').style.display = 'none';
                    document.getElementById('disconnectGpsBtn').style.display = 'inline-block';
                } else {
                    alert('GPS connection failed: ' + data.message);
                }
            })
            .catch(error => {
                console.error('GPS connection error:', error);
                alert('Connection failed: ' + error.message);
            });
        }

        function disconnectGps() {
            console.log('Disconnecting GPS device');
            
            fetch('/api/gps/disconnect', {
                method: 'POST'
            })
            .then(response => response.json())
            .then(data => {
                console.log('GPS disconnect response:', data);
                if (data.status === 'success') {
                    gpsConnected = false;
                    updateGpsStatus(false);
                    document.getElementById('connectGpsBtn').style.display = 'inline-block';
                    document.getElementById('disconnectGpsBtn').style.display = 'none';
                }
            })
            .catch(error => {
                console.error('GPS disconnect error:', error);
            });
        }

        // Inject a value into a <select> as a fresh option if it isn't
        // already present, then select it. Used on page-load status
        // restoration so a connected port shows up in the dropdown even
        // when the port list refresh hasn't run yet (or the device has
        // dropped off `list_ports`).
        function ensureSelectHasValue(selectEl, value, labelFallback) {
            if (!selectEl || !value) return;
            const has = Array.from(selectEl.options).some(o => o.value === value);
            if (!has) {
                const opt = document.createElement('option');
                opt.value = value;
                opt.textContent = labelFallback || value;
                selectEl.appendChild(opt);
            }
            selectEl.value = value;
        }

        // Freeze or unfreeze the Sniffer / GPS port controls so they can't
        // be edited while a device is live. Also hides the refresh button
        // during connection to keep the auto-refresh loop from clobbering
        // the shown value.
        function setFlockControlsLocked(locked) {
            const sel = document.getElementById('flockDeviceSelect');
            const refresh = document.getElementById('refreshFlockPortsBtn');
            if (sel) sel.disabled = locked;
            if (refresh) refresh.style.display = locked ? 'none' : '';
        }

        function setGpsControlsLocked(locked) {
            const source = document.getElementById('gpsSourceSelect');
            const portSel = document.getElementById('gpsPortSelect');
            const refresh = document.getElementById('refreshGpsPortsBtn');
            const host = document.getElementById('gpsdHostInput');
            const port = document.getElementById('gpsdPortInput');
            if (source)  source.disabled  = locked;
            if (portSel) portSel.disabled = locked;
            if (host)    host.disabled    = locked;
            if (port)    port.disabled    = locked;
            if (refresh) refresh.style.display = locked ? 'none' : '';
        }

        function updateFlockStatus(connected) {
            const indicator = document.getElementById('flockStatus');
            if (indicator) indicator.classList.toggle('active', !!connected);
            document.getElementById('connectFlockBtn').style.display    = connected ? 'none' : 'inline-block';
            document.getElementById('disconnectFlockBtn').style.display = connected ? 'inline-block' : 'none';
            setFlockControlsLocked(!!connected);
            setFlockExtraControls(!!connected);
        }

        function updateGpsStatus(connected) {
            const indicator = document.getElementById('gpsStatus');
            if (indicator) indicator.classList.toggle('active', !!connected);
            document.getElementById('connectGpsBtn').style.display    = connected ? 'none' : 'inline-block';
            document.getElementById('disconnectGpsBtn').style.display = connected ? 'inline-block' : 'none';
            setGpsControlsLocked(!!connected);
        }

        function loadStatus() {
            fetch('/api/status')
                .then(response => response.json())
                .then(data => {
                    console.log('Status loaded:', data);

                    // Sniffer: reflect the actual connected port in the
                    // dropdown so the user can see what's connected after
                    // a refresh, and lock the controls.
                    if (data.flock_connected && data.flock_port) {
                        ensureSelectHasValue(
                            document.getElementById('flockDeviceSelect'),
                            data.flock_port,
                            data.flock_port + ' - connected'
                        );
                    }
                    updateFlockStatus(data.flock_connected);

                    // GPS: source-aware restore. For serial, pin the
                    // connected serial port; for gpsd, populate host/port
                    // inputs from the reported endpoint.
                    if (data.gps_connected) {
                        const source = data.gps_source || 'serial';
                        const srcSel = document.getElementById('gpsSourceSelect');
                        if (srcSel) {
                            srcSel.value = source;
                            if (typeof applyGpsSource === 'function') applyGpsSource(source);
                        }
                        if (source === 'serial' && data.gps_port) {
                            ensureSelectHasValue(
                                document.getElementById('gpsPortSelect'),
                                data.gps_port,
                                data.gps_port + ' - connected'
                            );
                        } else if (source === 'gpsd' && data.gpsd_endpoint) {
                            const [host, port] = String(data.gpsd_endpoint).split(':');
                            const hostEl = document.getElementById('gpsdHostInput');
                            const portEl = document.getElementById('gpsdPortInput');
                            if (hostEl && host)      hostEl.value = host;
                            if (portEl && port)      portEl.value = port;
                        }
                        gpsConnected = true;
                    }
                    updateGpsStatus(data.gps_connected);
                })
                .catch(error => {
                    console.error('Error loading status:', error);
                });
        }

        function filterDetections(filter) {
            if (filter === 'all') {
                renderDetections(detections);
            } else {
                const filtered = detections.filter(d => d.detection_method === filter);
                renderDetections(filtered);
            }
        }

        function searchDetections() {
            const searchQuery = document.getElementById('detectionSearchInput').value.toLowerCase().trim();
            
            if (!searchQuery) {
                // If search is empty, show all detections
                renderDetections(detections);
                return;
            }
            
            // Search through all detection fields
            const filtered = detections.filter(detection => {
                return (
                    detection.mac_address.toLowerCase().includes(searchQuery) ||
                    (detection.manufacturer && detection.manufacturer.toLowerCase().includes(searchQuery)) ||
                    (detection.alias && detection.alias.toLowerCase().includes(searchQuery)) ||
                    detection.detection_method.toLowerCase().includes(searchQuery) ||
                    detection.protocol.toLowerCase().includes(searchQuery) ||
                    detection.detection_time.toLowerCase().includes(searchQuery) ||
                    (detection.ssid && detection.ssid.toLowerCase().includes(searchQuery)) ||
                    (detection.device_name && detection.device_name.toLowerCase().includes(searchQuery))
                );
            });
            
            renderDetections(filtered);
        }

        function updateStats() {
            const total = detections.length;
            const wifi = detections.filter(d => d.protocol === 'wifi').length;
            const ble = detections.filter(d => d.protocol === 'bluetooth_le' || d.protocol === 'bluetooth_classic').length;
            const gps = detections.filter(d => d.gps).length;

            document.getElementById('totalDetections').textContent = total;
            document.getElementById('wifiDetections').textContent = wifi;
            document.getElementById('bleDetections').textContent = ble;
            document.getElementById('gpsDetections').textContent = gps;
            
            // Also update with cumulative stats in tooltip or additional display
            fetch('/api/stats')
                .then(response => response.json())
                .then(stats => {
                    document.getElementById('totalDetections').title = `Session: ${stats.session.total} | Cumulative: ${stats.cumulative.total}`;
                    document.getElementById('wifiDetections').title = `Session: ${stats.session.wifi} | Cumulative: ${stats.cumulative.wifi}`;
                    document.getElementById('bleDetections').title = `Session: ${stats.session.ble} | Cumulative: ${stats.cumulative.ble}`;
                    document.getElementById('gpsDetections').title = `Session: ${stats.session.gps} | Cumulative: ${stats.cumulative.gps}`;
                })
                .catch(error => {
                    console.error('Error loading stats:', error);
                });
        }

        function renderDetections(detectionsToRender = detections) {
            const container = document.getElementById('detectionsList');
            
            if (detectionsToRender.length === 0) {
                container.innerHTML = `
                    <div class="no-detections">
                        <h3>No detections found</h3>
                        <p>Try changing the filter or wait for new detections</p>
                    </div>
                `;
                return;
            }
            
            container.innerHTML = detectionsToRender.map(detection => {
                // Get detection count and timing info
                const count = detection.detection_count || 1;
                const lastSeen = detection.last_seen ? new Date(detection.last_seen).toLocaleTimeString() : 'Unknown';
                
                // Use last known values for signal data
                const rssi = detection.last_rssi !== undefined ? detection.last_rssi : detection.rssi;
                const channel = detection.last_channel || detection.channel;
                const ssid = detection.last_ssid || detection.ssid;
                const deviceName = detection.last_device_name || detection.device_name;
                
                // Build essential fields in a compact layout
                const essentialFields = [];
                if (detection.protocol) essentialFields.push(['Protocol', detection.protocol]);
                if (detection.mac_address) essentialFields.push(['MAC', detection.mac_address]);
                if (rssi !== undefined) essentialFields.push(['RSSI', `${rssi} dBm`]);
                if (channel) essentialFields.push(['Channel', channel]);
                if (ssid) essentialFields.push(['SSID', ssid]);
                if (deviceName) essentialFields.push(['Device', deviceName]);
                if (detection.manufacturer) essentialFields.push(['Manufacturer', detection.manufacturer]);
                
                // Build the details HTML
                const detailsHtml = essentialFields.map(([label, value]) => `
                    <div class="detail-item">
                        <span class="detail-label">${label}:</span>
                        <span class="detail-value">${value}</span>
                    </div>
                `).join('');

                // Build GPS link if available (header only)
                let gpsLink = '';
                if (detection.gps && detection.gps.latitude !== undefined && detection.gps.longitude !== undefined) {
                    const lat = detection.gps.latitude;
                    const lon = detection.gps.longitude;
                    const osmUrl = `https://www.openstreetmap.org/?mlat=${lat}&mlon=${lon}&zoom=18`;
                    gpsLink = `<a href="${osmUrl}" target="_blank" class="gps-link">${lat.toFixed(4)}, ${lon.toFixed(4)}</a>`;
                }

                return `
                    <div class="detection-item">
                        <div class="detection-header">
                            <div class="detection-header-left">
                                <div class="detection-type-badge">
                                    <span class="detection-type">${detection.detection_method ? detection.detection_method.toUpperCase() : 'UNKNOWN'}</span>
                                    <span class="detection-count">${count}×</span>
                                    ${detection.gps && detection.gps.latitude !== undefined ? '<span class="gps-tag">GPS</span>' : ''}
                                </div>
                                ${gpsLink}
                            </div>
                            <span class="detection-time">${lastSeen}</span>
                        </div>
                        <div class="detection-details">
                            ${detailsHtml}
                            <div class="detail-item">
                                <span class="detail-label">Alias:</span>
                                <span class="alias-display" onclick="editAlias(${detection.id})">
                                    ${detection.alias || '<em>Click to add alias</em>'}
                                </span>
                            </div>
                        </div>
                    </div>
                `;
            }).join('');
        }

        function exportCSV(type = 'session') {
            window.location.href = `/api/export/csv?type=${type}`;
            closeExportDropdown();
        }

        function exportKML(type = 'session') {
            window.location.href = `/api/export/kml?type=${type}`;
            closeExportDropdown();
        }

        function toggleExportDropdown() {
            const dropdown = document.getElementById('exportDropdown');
            dropdown.style.display = dropdown.style.display === 'block' ? 'none' : 'block';
            document.getElementById('importDropdown').style.display = 'none';
        }

        function toggleImportDropdown() {
            const dropdown = document.getElementById('importDropdown');
            dropdown.style.display = dropdown.style.display === 'block' ? 'none' : 'block';
            document.getElementById('exportDropdown').style.display = 'none';
        }

        function closeExportDropdown() {
            document.getElementById('exportDropdown').style.display = 'none';
        }

        function closeImportDropdown() {
            document.getElementById('importDropdown').style.display = 'none';
        }

        let pendingImportType = 'json';

        function importFile(type) {
            pendingImportType = type;
            const input = document.getElementById('importFileInput');
            input.accept = type === 'json' ? '.json' : type === 'csv' ? '.csv' : '.kml';
            input.click();
            closeImportDropdown();
        }

        function handleImportFile() {
            const input = document.getElementById('importFileInput');
            const file = input.files[0];
            if (!file) return;

            const formData = new FormData();
            formData.append('file', file);

            const endpoint = pendingImportType === 'json' ? '/api/import/json' : pendingImportType === 'csv' ? '/api/import/csv' : '/api/import/kml';

            fetch(endpoint, {
                method: 'POST',
                body: formData
            })
            .then(response => response.json())
            .then(data => {
                if (data.status === 'success') {
                    alert(data.message);
                    loadDetections();
                    loadCumulativeDetections();
                    updateStats();
                } else {
                    alert('Import error: ' + data.message);
                }
            })
            .catch(error => {
                console.error('Import error:', error);
                alert('Import failed: ' + error.message);
            });

            // Reset input so same file can be re-selected
            input.value = '';
        }

        // Close dropdowns when clicking outside
        document.addEventListener('click', function(event) {
            const exportDD = document.querySelector('.export-dropdown');
            const allDropdowns = document.querySelectorAll('.export-dropdown');
            let clickedInside = false;
            allDropdowns.forEach(dd => {
                if (dd.contains(event.target)) clickedInside = true;
            });
            if (!clickedInside) {
                closeExportDropdown();
                closeImportDropdown();
            }
        });

        function clearDetections() {
            if (confirm('Are you sure you want to clear all detections?')) {
                fetch('/api/clear', {
                    method: 'POST'
                })
                .then(response => response.json())
                .then(data => {
                    if (data.status === 'success') {
                        detections = [];
                        updateStats();
                        renderDetections();
                    }
                });
            }
        }



        // Socket connection events
        socket.on('connect', function() {
            console.log('Socket connected');
        });

        socket.on('disconnect', function() {
            console.log('Socket disconnected');
        });

        socket.on('reconnect', function(attemptNumber) {
            console.log('Socket reconnected after', attemptNumber, 'attempts');
            // Reload data after reconnection
            loadDetections();
            loadStatus();
        });

        socket.on('reconnect_failed', function() {
            console.log('Socket reconnection failed');
        });

        socket.on('heartbeat', function() {
            socket.emit('heartbeat');
        });

        socket.on('heartbeat_ack', function() {
            // Heartbeat acknowledged
        });

        // Socket.IO event handlers
        socket.on('new_detection', function(detection) {
            console.log('New detection received:', detection);
            
            // Validate detection data
            if (!detection || !detection.id) {
                console.error('Invalid detection data received:', detection);
                return;
            }
            
            // Check if detection already exists (prevent duplicates)
            const existingIndex = detections.findIndex(d => d.id === detection.id);
            if (existingIndex !== -1) {
                console.log('Detection already exists, updating:', detection.id);
                detections[existingIndex] = detection;
            } else {
                detections.unshift(detection);
            }
            
            updateStats();
            renderDetections();
            
            // Update map if visible
            if (map && document.getElementById('mapContainer').style.display !== 'none') {
                updateMapMarkers();
            }
        });

        socket.on('detection_updated', function(detection) {
            console.log('Detection updated:', detection);
            
            // Validate detection data
            if (!detection || !detection.id) {
                console.error('Invalid detection update data received:', detection);
                return;
            }
            
            // Update existing detection
            const existingIndex = detections.findIndex(d => d.id === detection.id);
            if (existingIndex !== -1) {
                detections[existingIndex] = detection;
                updateStats();
                renderDetections();
                
                // Update map if visible
                if (map && document.getElementById('mapContainer').style.display !== 'none') {
                    updateMapMarkers();
                }
            }
        });

        socket.on('gps_update', function(gpsData) {
            console.log('GPS Update:', gpsData);
        });

        socket.on('gps_disconnected', function() {
            console.log('GPS disconnected');
            updateGpsStatus(false);
            const connectBtn = document.getElementById('connectGpsBtn');
            const disconnectBtn = document.getElementById('disconnectGpsBtn');
            if (connectBtn) connectBtn.style.display = 'inline-block';
            if (disconnectBtn) disconnectBtn.style.display = 'none';
        });

        socket.on('flock_disconnected', function() {
            console.log('Sniffer device disconnected');
            updateFlockStatus(false);
            const connectBtn = document.getElementById('connectFlockBtn');
            const disconnectBtn = document.getElementById('disconnectFlockBtn');
            if (connectBtn) connectBtn.style.display = 'inline-block';
            if (disconnectBtn) disconnectBtn.style.display = 'none';
        });

        socket.on('detections_cleared', function() {
            console.log('All detections cleared');
            detections = [];
            updateStats();
            renderDetections();
            
            // Clear map markers
            if (map) {
                clearMapMarkers();
            }
        });

        socket.on('flock_reconnected', function(data) {
            console.log('Sniffer device reconnected:', data);
            updateFlockStatus(true);
            document.getElementById('connectFlockBtn').style.display = 'none';
            document.getElementById('disconnectFlockBtn').style.display = 'inline-block';
            if (document.getElementById('serialTerminalContainer').style.display !== 'none') {
                addSerialLine(`Sniffer device reconnected on ${data.port}`, 'success');
            }
        });

        socket.on('gps_reconnected', function(data) {
            console.log('GPS device reconnected:', data);
            updateGpsStatus(true);
            document.getElementById('connectGpsBtn').style.display = 'none';
            document.getElementById('disconnectGpsBtn').style.display = 'inline-block';
            if (document.getElementById('serialTerminalContainer').style.display !== 'none') {
                addSerialLine(`GPS device reconnected on ${data.port}`, 'success');
            }
        });

        socket.on('reconnect_failed', function(data) {
            console.log('Reconnection failed:', data);
            const device = data.device === 'flock' ? 'Sniffer' : 'GPS';
            if (document.getElementById('serialTerminalContainer').style.display !== 'none') {
                addSerialLine(`${device} reconnection failed after ${max_reconnect_attempts} attempts`, 'error');
            }
        });

        function toggleSerialTerminal() {
            const container = document.getElementById('serialTerminalContainer');
            const button = document.getElementById('serial-terminal-btn');
            
            if (container.style.display === 'none') {
                container.style.display = 'block';
                button.textContent = 'Hide Terminal';
                button.style.background = 'linear-gradient(135deg, #dc2626 0%, #ef4444 100%)';
                
                // Start serial connection if device is connected
                const flockPort = document.getElementById('flockDeviceSelect').value;
                if (flockPort) {
                    startSerialConnection(flockPort);
                } else {
                    addSerialLine('No Sniffer device selected', 'error');
                }
            } else {
                container.style.display = 'none';
                button.textContent = 'Serial Terminal';
                button.style.background = 'linear-gradient(135deg, #059669 0%, #10b981 100%)';
            }
        }

        function toggleOuiSearch() {
            const container = document.getElementById('ouiSearchContainer');
            const button = document.getElementById('oui-search-btn');
            
            if (container.style.display === 'none') {
                container.style.display = 'block';
                button.textContent = 'Hide Search';
                button.style.background = 'linear-gradient(135deg, #dc2626 0%, #ef4444 100%)';
            } else {
                container.style.display = 'none';
                button.textContent = 'Search OUI';
                button.style.background = 'linear-gradient(135deg, #7c3aed 0%, #8b5cf6 100%)';
            }
        }

        function startSerialConnection(port) {
            const statusElement = document.getElementById('serialConnectionStatus');
            const outputElement = document.getElementById('serialTerminalOutput');
            
            statusElement.textContent = 'Connecting...';
            statusElement.style.color = '#fbbf24';
            
            // Clear placeholder
            if (outputElement.querySelector('.terminal-placeholder')) {
                outputElement.innerHTML = '';
            }
            
            // Add connection message
            addSerialLine(`Connecting to ${port} at 115200 baud...`, 'info');
            
            // Request serial terminal connection
            socket.emit('request_serial_terminal', {port: port});
            
            // Set a timeout for connection
            setTimeout(() => {
                if (statusElement.textContent === 'Connecting...') {
                    statusElement.textContent = 'Connection Timeout';
                    statusElement.style.color = '#f87171';
                    addSerialLine('Connection timeout - check device connection', 'error');
                }
            }, 5000);
        }

        function addSerialLine(text, type = 'normal') {
            console.log('Adding serial line:', text, 'type:', type);
            
            // Store all terminal data
            allTerminalData.push({ text, type, timestamp: Date.now() });
            
            // Keep only last 1000 lines
            if (allTerminalData.length > 1000) {
                allTerminalData.shift();
            }
            
            // Apply current filter
            if (shouldShowLine(text, type)) {
                displaySerialLine(text, type);
            }
        }

        function shouldShowLine(text, type) {
            if (terminalFilter === 'all') return true;
            
            const textLower = text.toLowerCase();
            if (terminalFilter === 'gps' && (textLower.includes('gps') || textLower.includes('$gp') || textLower.includes('$gn') || textLower.includes('nmea'))) {
                return true;
            }
            if (terminalFilter === 'sniffer' && !textLower.includes('gps') && (textLower.includes('detection') || textLower.includes('wifi') || textLower.includes('ble') || textLower.includes('mac') || textLower.includes('channel') || textLower.includes('rssi'))) {
                return true;
            }
            
            return false;
        }

        function displaySerialLine(text, type) {
            const outputElement = document.getElementById('serialTerminalOutput');
            
            if (!outputElement) {
                console.error('Serial terminal output element not found');
                return;
            }
            
            // Remove placeholder if it exists
            const placeholder = outputElement.querySelector('.terminal-placeholder');
            if (placeholder) {
                placeholder.remove();
            }
            
            const line = document.createElement('div');
            line.className = `serial-line ${type}`;
            line.textContent = text;
            outputElement.appendChild(line);
            outputElement.scrollTop = outputElement.scrollHeight;
        }

        function filterTerminalData() {
            terminalFilter = document.getElementById('terminalFilter').value;
            const outputElement = document.getElementById('serialTerminalOutput');
            
            // Clear current display
            outputElement.innerHTML = '';
            
            // Re-display filtered data
            allTerminalData.forEach(item => {
                if (shouldShowLine(item.text, item.type)) {
                    displaySerialLine(item.text, item.type);
                }
            });
            
            if (outputElement.children.length === 0) {
                outputElement.innerHTML = '<div class="terminal-placeholder">No data matches current filter...</div>';
            }
        }

        function clearSerialTerminal() {
            allTerminalData = [];
            const outputElement = document.getElementById('serialTerminalOutput');
            outputElement.innerHTML = '<div class="terminal-placeholder">Terminal cleared...</div>';
        }

        // Serial terminal socket events
        socket.on('serial_data', function(data) {
            console.log('Received serial data:', data);
            if (data && typeof data === 'string') {
                addSerialLine(data, 'normal');
            } else {
                console.error('Invalid serial data received:', data);
            }
        });

        socket.on('serial_connected', function() {
            console.log('Serial terminal connected');
            const statusElement = document.getElementById('serialConnectionStatus');
            if (statusElement) {
                statusElement.textContent = 'Connected';
                statusElement.style.color = '#34d399';
            }
            addSerialLine('Serial connection established', 'success');
        });

        socket.on('serial_disconnected', function() {
            console.log('Serial terminal disconnected');
            const statusElement = document.getElementById('serialConnectionStatus');
            if (statusElement) {
                statusElement.textContent = 'Disconnected';
                statusElement.style.color = '#f87171';
            }
            addSerialLine('Serial connection lost', 'error');
        });

        socket.on('serial_error', function(data) {
            console.log('Serial error:', data);
            if (data && data.message) {
                addSerialLine(`Error: ${data.message}`, 'error');
            } else {
                addSerialLine('Unknown serial error', 'error');
            }
        });



        socket.on('flock_reconnected', function(data) {
            updateFlockStatus(true);
            document.getElementById('connectFlockBtn').style.display = 'none';
            document.getElementById('disconnectFlockBtn').style.display = 'inline-block';
            addSerialLine(`Sniffer device reconnected on ${data.port}`, 'success');
        });

        socket.on('gps_reconnected', function(data) {
            updateGpsStatus(true);
            document.getElementById('connectGpsBtn').style.display = 'none';
            document.getElementById('disconnectGpsBtn').style.display = 'inline-block';
            addSerialLine(`GPS device reconnected on ${data.port}`, 'success');
        });

        socket.on('reconnect_failed', function(data) {
            const device = data.device === 'flock' ? 'Sniffer' : 'GPS';
            addSerialLine(`${device} reconnection failed after ${max_reconnect_attempts} attempts`, 'error');
        });

        // Alias editing functions
        function editAlias(detectionId) {
            const aliasDisplay = event.target;
            const currentAlias = aliasDisplay.textContent.replace('<em>Click to add alias</em>', '').trim();
            
            const input = document.createElement('input');
            input.type = 'text';
            input.className = 'alias-input';
            input.value = currentAlias;
            input.placeholder = 'Enter alias...';
            
            input.addEventListener('blur', function() {
                saveAlias(detectionId, input.value);
            });
            
            input.addEventListener('keypress', function(e) {
                if (e.key === 'Enter') {
                    saveAlias(detectionId, input.value);
                }
            });
            
            aliasDisplay.innerHTML = '';
            aliasDisplay.appendChild(input);
            input.focus();
        }

        function saveAlias(detectionId, alias) {
            fetch('/api/detection/alias', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify({
                    id: detectionId,
                    alias: alias
                })
            })
            .then(response => response.json())
            .then(data => {
                if (data.status === 'success') {
                    // Update the display
                    const aliasDisplay = event.target.parentElement;
                    aliasDisplay.innerHTML = alias || '<em>Click to add alias</em>';
                }
            })
            .catch(error => {
                console.error('Error saving alias:', error);
            });
        }

        // Handle detection updates
        socket.on('detection_updated', function(detection) {
            console.log('Detection updated:', detection);
            // Update the detection in our local array
            const index = detections.findIndex(d => d.id === detection.id);
            if (index !== -1) {
                detections[index] = detection;
                updateStats();
                renderDetections();
            } else {
                console.warn('Detection not found for update:', detection.id);
            }
        });

        // OUI Search functions
        function handleOuiSearch(event) {
            if (event.key === 'Enter') {
                searchOui();
            }
        }

        function searchOui() {
            const query = document.getElementById('ouiSearchInput').value.trim();
            if (!query) {
                return;
            }

            fetch('/api/oui/search', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify({ query: query })
            })
            .then(response => response.json())
            .then(data => {
                displayOuiResults(data.results);
            })
            .catch(error => {
                console.error('Error searching OUI:', error);
                displayOuiResults([]);
            });
        }

        function displayOuiResults(results) {
            const resultsContainer = document.getElementById('ouiSearchResults');
            
            if (results.length === 0) {
                resultsContainer.innerHTML = '<div class="search-placeholder">No results found</div>';
                return;
            }

            // Check if this is a "view all" request (more than 50 results)
            const isViewAll = results.length > 50;
            
            if (isViewAll) {
                // Use grid layout for view all - show all entries
                const resultsHtml = results.map(result => `
                    <div class="oui-result-item">
                        <div class="oui-mac">${formatMacAddress(result.mac)}</div>
                        <div class="oui-manufacturer">${result.manufacturer}</div>
                    </div>
                `).join('');
                
                resultsContainer.innerHTML = `<div class="oui-results-grid">${resultsHtml}</div>`;
            } else {
                // Use single column for search results
                const resultsHtml = results.map(result => `
                    <div class="oui-result-item">
                        <div class="oui-mac">${formatMacAddress(result.mac)}</div>
                        <div class="oui-manufacturer">${result.manufacturer}</div>
                    </div>
                `).join('');
                
                resultsContainer.innerHTML = resultsHtml;
            }
        }

        function clearOuiResults() {
            document.getElementById('ouiSearchInput').value = '';
            document.getElementById('ouiSearchResults').innerHTML = '<div class="search-placeholder">Enter a MAC address or manufacturer name to search...</div>';
        }

        function formatMacAddress(mac) {
            // Add colons every 2 characters
            return mac.replace(/(.{2})(?=.{2})/g, '$1:');
        }

        function viewAllOui() {
            fetch('/api/oui/all')
            .then(response => response.json())
            .then(data => {
                displayOuiResults(data.results);
            })
            .catch(error => {
                console.error('Error loading all OUI entries:', error);
                displayOuiResults([]);
            });
        }

        function refreshOuiDatabase() {
            const button = document.querySelector('.refresh-db-btn');
            const originalText = button.textContent;
            button.textContent = 'Refreshing...';
            button.disabled = true;
            
            fetch('/api/oui/refresh', {
                method: 'POST'
            })
            .then(response => response.json())
            .then(data => {
                if (data.status === 'success') {
                    alert(`Database refreshed successfully! Loaded ${data.count} entries.`);
                } else {
                    alert('Error refreshing database: ' + data.message);
                }
            })
            .catch(error => {
                console.error('Error refreshing database:', error);
                alert('Error refreshing database. Please try again.');
            })
            .finally(() => {
                button.textContent = originalText;
                button.disabled = false;
            });
        }

        // Map functions
        function toggleMap() {
            const container = document.getElementById('mapContainer');
            const button = document.getElementById('map-btn');
            
            if (container.style.display === 'none') {
                container.style.display = 'block';
                button.textContent = 'Hide Map';
                button.style.background = 'linear-gradient(135deg, #dc2626 0%, #ef4444 100%)';
                
                if (!map) {
                    initializeMap();
                }
                // Load fresh cumulative data when opening map
                loadCumulativeDetections();
                updateMapMarkers();
            } else {
                container.style.display = 'none';
                button.textContent = 'Map';
                button.style.background = 'linear-gradient(135deg, #0891b2 0%, #06b6d4 100%)';
            }
        }

        function initializeMap() {
            // Initialize the map
            map = L.map('map').setView([37.7749, -122.4194], 10); // Default to San Francisco
            
            // Define map layers
            mapLayers.osm = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
                attribution: '© OpenStreetMap contributors',
                maxZoom: 19
            });
            
            mapLayers.satellite = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {
                attribution: '© Esri, DigitalGlobe, GeoEye, Earthstar Geographics, CNES/Airbus DS',
                maxZoom: 19
            });
            
            mapLayers.topo = L.tileLayer('https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png', {
                attribution: '© OpenTopoMap contributors',
                maxZoom: 19
            });
            
            // Add default layer
            mapLayers.osm.addTo(map);
        }

        function changeMapLayer() {
            const selectedLayer = document.getElementById('mapLayer').value;
            
            // Remove all layers
            Object.values(mapLayers).forEach(layer => {
                map.removeLayer(layer);
            });
            
            // Add selected layer
            mapLayers[selectedLayer].addTo(map);
        }

        function updateMapMarkers() {
            if (!map) return;
            
            // Clear existing markers
            clearMapMarkers();
            
            // Get filtered detection data based on current filter
            let detectionsToShow = [];
            
            if (mapFilter === 'session') {
                detectionsToShow = detections.filter(d => d.gps && d.gps.latitude && d.gps.longitude);
            } else if (mapFilter === 'cumulative') {
                detectionsToShow = cumulativeDetections.filter(d => d.gps && d.gps.latitude && d.gps.longitude);
            } else if (mapFilter === 'both') {
                // Combine session and cumulative, avoiding duplicates by MAC address
                const sessionGps = detections.filter(d => d.gps && d.gps.latitude && d.gps.longitude);
                const cumulativeGps = cumulativeDetections.filter(d => d.gps && d.gps.latitude && d.gps.longitude);
                
                // Create a map of MAC addresses from session data
                const sessionMacs = new Set(sessionGps.map(d => d.mac_address));
                
                // Add session detections first
                detectionsToShow = [...sessionGps];
                
                // Add cumulative detections that aren't in current session
                cumulativeGps.forEach(d => {
                    if (!sessionMacs.has(d.mac_address)) {
                        detectionsToShow.push(d);
                    }
                });
            }
            
            detectionsToShow.forEach((detection, index) => {
                const lat = detection.gps.latitude;
                const lng = detection.gps.longitude;
                
                // Determine if this is from session or cumulative
                const isSessionData = detections.some(d => d.mac_address === detection.mac_address);
                
                // Create custom icon based on detection type and data source
                let iconColor = detection.protocol === 'wifi' ? '#ef4444' : '#3b82f6'; // red for wifi, blue for ble
                let borderColor = isSessionData ? '#22c55e' : '#f59e0b'; // green border for session, orange for cumulative
                
                const icon = L.divIcon({
                    className: 'custom-marker',
                    html: `<div style="background-color: ${iconColor}; width: 12px; height: 12px; border-radius: 50%; border: 2px solid ${borderColor}; box-shadow: 0 0 4px rgba(0,0,0,0.5);"></div>`,
                    iconSize: [16, 16],
                    iconAnchor: [8, 8]
                });
                
                const marker = L.marker([lat, lng], { icon }).addTo(map);
                
                // Create popup content with data source indicator
                const dataSource = isSessionData ? 'Session' : 'Cumulative';
                const aliasText = detection.alias ? `<strong>Alias:</strong> ${detection.alias}<br>` : '';
                
                // GPS accuracy indicator
                let gpsAccuracy = '';
                if (detection.gps.time_diff !== undefined && detection.gps.time_diff !== null) {
                    const timeDiff = detection.gps.time_diff;
                    if (timeDiff < 5) {
                        gpsAccuracy = ` <span style="color: #22c55e;">✓ Precise (${timeDiff.toFixed(1)}s)</span>`;
                    } else if (timeDiff < 15) {
                        gpsAccuracy = ` <span style="color: #f59e0b;">~ Good (${timeDiff.toFixed(1)}s)</span>`;
                    } else {
                        gpsAccuracy = ` <span style="color: #ef4444;">⚠ Approximate (${timeDiff.toFixed(1)}s)</span>`;
                    }
                } else {
                    gpsAccuracy = ` <span style="color: #6b7280;">? Unknown accuracy</span>`;
                }
                
                const popupContent = `
                    <h3>${detection.alias || `Detection #${detection.id}`} (${dataSource})</h3>
                    ${aliasText}
                    <strong>Protocol:</strong> ${detection.protocol}<br>
                    <strong>Method:</strong> ${detection.detection_method}<br>
                    <strong>MAC:</strong> ${detection.mac_address}<br>
                    ${detection.ssid ? `<strong>SSID:</strong> ${detection.ssid}<br>` : ''}
                    ${detection.manufacturer ? `<strong>Manufacturer:</strong> ${detection.manufacturer}<br>` : ''}
                    <strong>RSSI:</strong> ${detection.last_rssi || detection.rssi} dBm<br>
                    <strong>GPS:</strong> ${lat.toFixed(6)}, ${lng.toFixed(6)}${gpsAccuracy}<br>
                    <strong>Satellites:</strong> ${detection.gps.satellites}<br>
                    <strong>Count:</strong> ${detection.detection_count || 1}<br>
                    <strong>Source:</strong> ${dataSource} Data
                `;
                
                marker.bindPopup(popupContent);
                mapMarkers.push(marker);
            });
            
            // Update detection count
            document.getElementById('mapDetectionCount').textContent = detectionsToShow.length;
            
            // Fit map to markers if any exist
            if (mapMarkers.length > 0) {
                const group = new L.featureGroup(mapMarkers);
                map.fitBounds(group.getBounds().pad(0.1));
            }
        }

        function filterMapData() {
            mapFilter = document.getElementById('mapFilter').value;
            updateMapMarkers();
        }

        function loadCumulativeDetections() {
            fetch('/api/stats')
                .then(response => response.json())
                .then(stats => {
                    // Load cumulative detections from server if available
                    // For now, we'll fetch them via a new endpoint
                    return fetch('/api/detections?type=cumulative');
                })
                .then(response => response.json())
                .then(data => {
                    cumulativeDetections = data;
                    console.log('Loaded cumulative detections:', cumulativeDetections.length);
                    
                    // Update map if it's open and showing cumulative data
                    if (map && document.getElementById('mapContainer').style.display !== 'none') {
                        updateMapMarkers();
                    }
                })
                .catch(error => {
                    console.error('Error loading cumulative detections:', error);
                    cumulativeDetections = [];
                });
        }

        function clearMapMarkers() {
            mapMarkers.forEach(marker => {
                map.removeLayer(marker);
            });
            mapMarkers = [];
            document.getElementById('mapDetectionCount').textContent = '0';
        }
    </script>
</body>
</html>
